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
Loading
Loading
Loading
Loading
+67 −0
Original line number Diff line number Diff line

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

projects/step-up/adc.c

0 → 100644
+256 −0
Original line number Diff line number Diff line
#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();
}
+117 −0
Original line number Diff line number Diff line
#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__ */
+81 −0
Original line number Diff line number Diff line
#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
+92 −0
Original line number Diff line number Diff line
#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
}
Loading