Commit edebb613 authored by Jan Kasprzak's avatar Jan Kasprzak
Browse files

Imported firmware from Project Bike Lights

These are firmware source code files imported verbatim from
the Project Bike Lights, as of commit
c30006aaf666f7cff3a6ab949c613c2f8cc6163b:

http://www.fi.muni.cz/~kas/git/?p=bike-lights.git;a=tree;f=firmware;hb=c30006aaf666f7cff3a6ab949c613c2f8cc6163b

These will not run directly on Tinyboard - I just want to have
documented all the changes I plan to make against these source code
files (and possibly to merge some future patches into both projects).
parent 5aa70c4e
PROGRAM=lights
SRC=main.c logging.c adc.c pwm.c tmr.c pwmled.c gpio.c ambient.c pattern.c \
buttons.c battery.c control.c
OBJ=$(SRC:.c=.o)
MCU=attiny861a
# AVRDUDE_MCU=$(MCU)
AVRDUDE_MCU=attiny861
AVRDUDE_PROGRAMMER=usbasp
CFLAGS=-Wall -Os -mmcu=$(MCU) -DUSE_LOGGING=1 -DF_CPU=1000000UL -std=gnu99
LDFLAGS=
AVRDUDE_FLAGS= -p$(AVRDUDE_MCU) -c $(AVRDUDE_PROGRAMMER)
FORMAT=ihex
CC=avr-gcc
OBJCOPY=avr-objcopy
OBJDUMP=avr-objdump
AVRDUDE=avrdude
all: $(PROGRAM).hex $(PROGRAM).eep
program: $(PROGRAM).hex $(PROGRAM).eep
$(AVRDUDE) $(AVRDUDE_FLAGS) -U flash:w:$(PROGRAM).hex:i -U eeprom:w:$(PROGRAM).eep:i
program_flash: $(PROGRAM).hex
$(AVRDUDE) $(AVRDUDE_FLAGS) -U flash:w:$(PROGRAM).hex:i
program_eeprom: $(PROGRAM).eep
$(AVRDUDE) $(AVRDUDE_FLAGS) eeprom:w:$(PROGRAM).eep:i
dump_eeprom:
$(AVRDUDE) $(AVRDUDE_FLAGS) -U eeprom:r:eeprom.raw:r
od -tx1 eeprom.raw
objdump: $(PROGRAM).elf
$(OBJDUMP) --disassemble $<
.PRECIOUS : $(OBJ) $(PROGRAM).elf
%.hex: %.elf
$(OBJCOPY) -O $(FORMAT) -R .eeprom $< $@
%.eep: %.elf
$(OBJCOPY) -j .eeprom --set-section-flags=.eeprom="alloc,load" \
--change-section-lma .eeprom=0 -O $(FORMAT) $< $@
%.elf: $(OBJ)
$(CC) $(CFLAGS) $(OBJ) -o $@ $(LDFLAGS)
%.o: %.c lights.h Makefile
$(CC) -c $(CFLAGS) $< -o $@
%.s: %.c lights.h Makefile
$(CC) -S -c $(CFLAGS) $< -o $@
%.o: %.S
$(CC) -c $(CFLAGS) $< -o $@
clean:
rm -f $(PROGRAM).hex $(PROGRAM).eep $(PROGRAM).elf *.o *.s eeprom.raw
.PHONY: all clean dump_eeprom program program_flash program_eeprom objdump
#include <avr/io.h>
#include <avr/interrupt.h>
#include "lights.h"
#define AMBIENT_ADC N_PWMLEDS
#define BATTERY_ADC (N_PWMLEDS + 1)
#define ADC1_GAIN20 (N_PWMLEDS + 2)
#define BUTTON_ADC (N_PWMLEDS + 3)
#define ZERO_ADC (N_PWMLEDS + 4)
#define NUM_ADCS ZERO_ADC
struct {
unsigned char read_zero_log : 2;
unsigned char read_drop_log : 2;
unsigned char read_keep_log : 4;
} adc_params[NUM_ADCS] = {
{ 0, 1, PWMLED_ADC_SHIFT }, // pwmled 1
{ 0, 1, PWMLED_ADC_SHIFT }, // pwmled 2
{ 0, 1, PWMLED_ADC_SHIFT }, // pwmled 3
{ 0, 1, AMBIENT_ADC_SHIFT }, // ambient
{ 0, 1, 0 }, // battery
{ 0, 1, 0 }, // gain20
{ 0, 1, 0 }, // buttons
};
volatile static unsigned char current_adc, current_slow_adc;
static uint16_t adc_sum, zero_count, drop_count, read_count, n_reads_log;
#define ADC1_GAIN20_OFFSET_SHIFT 6
static uint16_t adc1_gain20_offset;
static void setup_mux(unsigned char n)
{
/* ADC numbering: PWM LEDs first, then others, zero at the end */
switch (n) {
case 0: // pwmled 1: 1.1V, ADC0,1 (PA0,1), gain 20
ADMUX = _BV(REFS1) | _BV(MUX3) | _BV(MUX1) | _BV(MUX0);
break;
case 1: // pwmled 2: 1.1V, ADC2,1 (PA2,1), gain 20
ADMUX = _BV(REFS1) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
break;
case 2: // pwmled 3: 1.1V, ADC4 (PA5), single-ended
ADMUX = _BV(REFS1) | _BV(MUX2);
break;
case AMBIENT_ADC: // ambient light: 1.1V, ADC5 (PA6), single-ended
ADMUX = _BV(REFS1) | _BV(MUX2) | _BV(MUX0);
break;
case BATTERY_ADC: // batt voltage: 1.1V, ADC6 (PA7), single-ended
ADMUX = _BV(REFS1) | _BV(MUX2) | _BV(MUX1);
break;
case ADC1_GAIN20: // gain stage offset: 1.1V, ADC1,1, gain 20
ADMUX = _BV(REFS1) | _BV(MUX3) | _BV(MUX2) | _BV(MUX0);
break;
case BUTTON_ADC: // buttons: 1.1V, ADC3, single-ended
PORTA |= _BV(PA3); // +5V to the voltage splitter
ADMUX = _BV(REFS1) | _BV(MUX1) | _BV(MUX0);
break;
case ZERO_ADC: // zero: 1.1V, ADC1 (PA1), single-ended
ADMUX = _BV(REFS1) | _BV(MUX0);
break;
}
}
static void start_next_adc()
{
if (current_adc == 0) {
if (current_slow_adc > N_PWMLEDS) {
// read one of the non-PWMLED ADCs
current_adc = --current_slow_adc;
} else {
// no more non-PWMLEDs to do, start with PWMLEDs
current_adc = N_PWMLEDS-1;
}
} else if (current_adc >= N_PWMLEDS) {
// one of the non-PWMLED ADCs just finished, skip to PWMLEDs.
current_adc = N_PWMLEDS-1;
} else {
// next PWMLED
current_adc--;
}
#if 0
log_byte(0x90 + current_adc); // debug ADC switching
#endif
adc_sum = 0;
// we use the last iteration of zero_count to set up the MUX
// to its final destination, hence the "1 +" below:
if (adc_params[current_adc].read_zero_log)
zero_count = 1 + (1 << (adc_params[current_adc].read_zero_log-1));
else
zero_count = 1;
if (adc_params[current_adc].read_drop_log)
drop_count = 1 << (adc_params[current_adc].read_drop_log - 1);
else
drop_count = 0;
read_count = 1 << adc_params[current_adc].read_keep_log;
n_reads_log = adc_params[current_adc].read_keep_log;
// set up mux, start one-shot conversion
if (zero_count > 1)
setup_mux(ZERO_ADC);
else
setup_mux(current_adc);
ADCSRA |= _BV(ADSC);
}
void timer_start_slow_adcs()
{
if (current_slow_adc > N_PWMLEDS) { // Don't start if in progress
log_byte(0x80 + current_slow_adc);
} else {
current_slow_adc = NUM_ADCS;
// TODO: kick the watchdog here
}
}
/*
* Single synchronous ADC conversion.
* Has to be called with IRQs disabled (or with the ADC IRQ disabled).
*/
static uint16_t read_adc_sync()
{
uint16_t rv;
ADCSRA |= _BV(ADSC); // start the conversion
// wait for the conversion to finish
while((ADCSRA & _BV(ADIF)) == 0)
;
rv = ADCW;
ADCSRA |= _BV(ADIF); // clear the IRQ flag
return rv;
}
void init_adc()
{
unsigned char i;
current_slow_adc = NUM_ADCS;
current_adc = 0;
ADCSRA = _BV(ADEN) // enable
| _BV(ADPS1) | _BV(ADPS0) // CLK/8 = 125 kHz
// | _BV(ADPS2) // CLK/16 = 62.5 kHz
;
// ADCSRB |= _BV(GSEL); // gain 8 or 32
// Disable digital input on all bits used by ADC
DIDR0 = _BV(ADC0D) | _BV(ADC1D) | _BV(ADC2D) | _BV(ADC3D)
| _BV(ADC4D) | _BV(ADC5D) | _BV(ADC6D);
// 1.1V, ADC1,1, gain 20
ADMUX = _BV(REFS1) | _BV(MUX3) | _BV(MUX2) | _BV(MUX0);
/* Do first conversion and drop the result */
read_adc_sync();
adc1_gain20_offset = 0;
for (i = 0; i < (1 << ADC1_GAIN20_OFFSET_SHIFT); i++) {
adc1_gain20_offset += read_adc_sync()
- (adc1_gain20_offset >> ADC1_GAIN20_OFFSET_SHIFT);
}
ADCSRA |= _BV(ADIE); // enable IRQ
start_next_adc();
}
void susp_adc()
{
ADCSRA = 0;
DIDR0 = 0;
}
static void adc1_gain20_adc(uint16_t adcsum)
{
// running average
adc1_gain20_offset += adcsum
- (adc1_gain20_offset >> ADC1_GAIN20_OFFSET_SHIFT);
}
ISR(ADC_vect) { // IRQ handler
uint16_t adcval = ADCW;
if (zero_count) {
if (zero_count > 1) {
ADCSRA |= _BV(ADSC);
zero_count--;
return;
} else {
setup_mux(current_adc);
zero_count = 0;
/* fall through */
}
}
if (drop_count) {
ADCSRA |= _BV(ADSC); // drop this one, start the next
drop_count--;
return;
}
if (read_count) {
ADCSRA |= _BV(ADSC);
adc_sum += adcval;
read_count--;
return;
}
/*
* Now we have performed read_count measurements and have them
* in adc_sum.
*/
// For inputs with gain, subtract the measured gain stage offset
if (current_adc < 2) {
uint16_t offset = adc1_gain20_offset
>> (ADC1_GAIN20_OFFSET_SHIFT - n_reads_log);
if (adc_sum > offset)
adc_sum -= offset;
else
adc_sum = 0;
}
switch (current_adc) {
case 0:
case 1:
case 2:
pwmled_adc(current_adc, adc_sum);
break;
case AMBIENT_ADC:
ambient_adc(adc_sum);
break;
case BATTERY_ADC:
battery_adc(adc_sum);
break;
case BUTTON_ADC:
button_adc(adc_sum);
break;
case ADC1_GAIN20:
adc1_gain20_adc(adcval);
break;
}
start_next_adc();
}
#ifndef LIGHTS_H__
#define LIGHTS_H__ 1
#define TESTING_FW 1
#define N_LEDS 7
#define N_PWMLEDS 3
#define N_PWMLED_MODES 4
#define N_BUTTONS 2
/* logging.c */
#ifdef USE_LOGGING
void init_log();
void log_set_state(unsigned char val);
void log_flush();
void log_byte(unsigned char byte);
void log_word(uint16_t word);
#else
void inline init_log() { }
void inline log_set_state(unsigned char val) { }
void inline log_flush() { }
void inline log_byte(unsigned char byte) { }
void inline log_word(uint16_t word) { }
#endif
/* adc.c */
#define PWMLED_ADC_SHIFT 1 /* 1<<1 measurements per single callback */
void init_adc();
void susp_adc();
void timer_start_slow_adcs();
/* pwm.c */
/*
* The real Timer/Counter 1 frequency should not be too close to the
* A/D converter frequency (125 kHz). Note that this is not the Top
* value of T/C 1, it is shifted by PWM_STEP_SHIFT as described in pwm.c
*/
#define PWM_MAX 0x780
void init_pwm();
void susp_pwm();
void pwm_off(unsigned char n);
void pwm_set(unsigned char n, uint16_t stride);
void pwm_timer();
/* tmr.c */
extern volatile uint16_t jiffies;
void init_tmr();
void susp_tmr();
/* pwmled.c */
void init_pwmled();
void pwmled_adc(unsigned char n, uint16_t adcval);
void pwmled_set_mode(unsigned char n, unsigned char mode);
/* gpio.c */
void init_gpio();
void susp_gpio();
void gpio_set(unsigned char n, unsigned char on);
/* ambient.c */
#define AMBIENT_ADC_SHIFT 0 /* 1 measurement per callback */
void init_ambient();
extern volatile unsigned char ambient_zone;
void ambient_adc(uint16_t adc_val);
/* pattern.c */
typedef struct {
unsigned char mode: 3;
unsigned char duration: 5;
} pattern_t;
#define PATTERN_END { 0, 0 }
void init_pattern();
void patterns_next_tick();
void led_set_pattern(unsigned char led, pattern_t *pattern);
pattern_t *number_pattern(unsigned char num, unsigned char inv);
void pattern_reload();
/* buttons.c */
#define MAX_USER_PARAMS 3
void init_buttons();
void susp_buttons();
void timer_check_buttons();
void button_adc(uint16_t adcval);
unsigned char get_user_param(unsigned char param);
unsigned char buttons_wait_for_release();
unsigned char buttons_setup_in_progress();
pattern_t *buttons_setup_status0_pattern_select();
pattern_t *buttons_setup_status1_pattern_select();
/* battery.c */
extern volatile unsigned char battery_critical;
void battery_adc();
void init_battery();
unsigned char battery_gauge();
/* control.c */
extern pattern_t on1_pattern[];
void init_control();
void brake_on();
void brake_off();
void toggle_dim_mode();
void set_panic_mode();
pattern_t *pwmled0_pattern_select();
pattern_t *pwmled1_pattern_select();
pattern_t *pwmled2_pattern_select();
pattern_t *status_led_pattern_select();
pattern_t *illumination_led_pattern_select();
pattern_t *laser_pattern_select();
/* main.c */
void power_down();
#endif /* !LIGHTS_H__ */
#ifdef USE_LOGGING
#include <avr/io.h>
#include <avr/eeprom.h>
#include "lights.h"
#define LOG_BUFFER 128
static unsigned char log_buffer_ee[LOG_BUFFER] EEMEM;
static unsigned char log_buffer_count;
static unsigned char log_buffer[LOG_BUFFER];
static unsigned char log_state EEMEM;
/* Upper 4 bits are reset count, lower 4 bits are reset reason from MCUSR */
static unsigned char reboot_count EEMEM = 0;
static unsigned char can_write_eeprom = 0;
static uint16_t flushed_end;
void log_set_state(unsigned char val)
{
if (can_write_eeprom)
eeprom_write_byte(&log_state, val);
}
void init_log()
{
unsigned char r_count;
r_count = eeprom_read_byte(&reboot_count);
r_count >>= 4;
if (r_count < 5) {
r_count++;
eeprom_write_byte(&reboot_count,
(r_count << 4) | (MCUSR & 0xF));
MCUSR = 0;
can_write_eeprom = 1;
} else {
//eeprom_write_byte(&log_state, 0xFF);
can_write_eeprom = 0;
}
log_set_state(1);
log_buffer_count = 0;
flushed_end = 0;
}
void log_byte(unsigned char byte) {
if (log_buffer_count >= LOG_BUFFER)
return;
// eeprom_write_word(&log_buffer[log_buffer_count], word);
log_buffer[log_buffer_count++] = byte;
if (log_buffer_count == LOG_BUFFER)
log_flush();
}
void log_word(uint16_t word) {
log_byte(word & 0xFF);
log_byte(word >> 8);
}
void log_flush() {
unsigned char i;
if (!can_write_eeprom)
return;
for (i=flushed_end; i < log_buffer_count; i++) {
eeprom_write_byte(&log_buffer_ee[i],
log_buffer[i]);
}
flushed_end = i;
if (flushed_end == LOG_BUFFER)
log_set_state(0x42);
}
#endif
#include <avr/io.h>
#include <util/delay.h>
#include <avr/sleep.h>
#include <avr/interrupt.h>
#include <avr/power.h>
#include <avr/wdt.h>
#include "lights.h"
static void hw_setup()
{
wdt_enable(WDTO_1S);
init_battery();
init_pwm();
init_adc();
init_tmr();
init_buttons();
init_pwmled();
init_gpio();
init_ambient();
init_pattern();
init_control();
set_sleep_mode(SLEEP_MODE_IDLE);
}
static void hw_suspend()
{
susp_pwm();
susp_adc();
susp_tmr();
susp_gpio();
susp_buttons();
wdt_disable();
}
void power_down()
{
hw_suspend();
do {
// G'night
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
sleep_enable();
sleep_bod_disable();
sei();
sleep_cpu();
// G'morning
cli();
sleep_disable();
// allow wakeup by long button-press only
} while (!buttons_wait_for_release());
// ok, so I will wake up
hw_setup();
}
int main(void)
{
init_log();
power_usi_disable(); // Once for lifetime
ACSRA |= _BV(ACD); // disable analog comparator
log_set_state(3);
hw_setup();
power_down();
sei();
#if 1
while (1) {
wdt_reset();
sleep_mode();
}
#endif
#if 0
DDRB |= _BV(PB2);
while (1) {
PORTB |= _BV( PB2 );
_delay_ms(200);
PORTB &=~ _BV( PB2 );
_delay_ms(200);
}
#endif
}
#include <avr/io.h>
#include <stdlib.h> // for NULL
#include "lights.h"
static unsigned char led_counters[N_LEDS];
static pattern_t *led_patterns[N_LEDS];
static pattern_t boot_pattern[] = {
{ 1, 0x6 },
{ 0, 0x6 },