From: Jan "Yenya" Kasprzak Date: Tue, 28 Aug 2012 13:25:01 +0000 (+0200) Subject: lights.c split into more modules X-Git-Tag: gedasymbols-20120913~54 X-Git-Url: https://www.fi.muni.cz/~kas/git//home/kas/public_html/git/?p=bike-lights.git;a=commitdiff_plain;h=4b93ea55987c25fcce022403a505ca1749dc329c lights.c split into more modules --- diff --git a/Makefile b/Makefile index 99b9793..989bffb 100644 --- a/Makefile +++ b/Makefile @@ -1,15 +1,15 @@ - PROGRAM=lights -SRC=lights.c +SRC=main.c logging.c adc.c pwm.c tmr.c pwmled.c OBJ=$(SRC:.c=.o) + MCU=attiny861a # AVRDUDE_MCU=$(MCU) AVRDUDE_MCU=attiny861 AVRDUDE_PROGRAMMER=usbasp -CFLAGS=-Os -mmcu=$(MCU) +CFLAGS=-Wall -Os -mmcu=$(MCU) -DUSE_LOGGING=1 -DF_CPU=1000000UL LDFLAGS= AVRDUDE_FLAGS= -p$(AVRDUDE_MCU) -c $(AVRDUDE_PROGRAMMER) @@ -38,7 +38,6 @@ dump_eeprom: objdump: $(PROGRAM).elf $(OBJDUMP) --disassemble $< - .PRECIOUS : $(OBJ) $(PROGRAM).elf %.hex: %.elf @@ -51,10 +50,10 @@ objdump: $(PROGRAM).elf %.elf: $(OBJ) $(CC) $(CFLAGS) $(OBJ) -o $@ $(LDFLAGS) -%.o: %.c +%.o: %.c lights.h Makefile $(CC) -c $(CFLAGS) $< -o $@ -%.s: %.c +%.s: %.c lights.h Makefile $(CC) -S -c $(CFLAGS) $< -o $@ %.o: %.S diff --git a/adc.c b/adc.c new file mode 100644 index 0000000..a37e958 --- /dev/null +++ b/adc.c @@ -0,0 +1,79 @@ +#include +#include + +#include "lights.h" + +static unsigned char adc_mux[] = { // pwmleds should be first + // 0: pwmled 0: 1.1V, ADC3 (PA4), single-ended + _BV(REFS1) | _BV(MUX1) | _BV(MUX0), + // 1: pwmled 1: 1.1V, ADC0,1 (PA0,1), gain 1 or 8 + _BV(REFS1) | _BV(MUX3) | _BV(MUX2), + // 2: pwmled 2: 1.1V, ADC2,1 (PA2,1), gain 1 or 8 + _BV(REFS1) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1) | _BV(MUX0), + // 3: ambient light: 1.1V, ADC4 (PA5), single-ended + _BV(REFS1) | _BV(MUX2), + // 4: batt voltage: 1.1V, ADC5 (PA6), single-ended + _BV(REFS1) | _BV(MUX2) | _BV(MUX0), +}; + +#define LAST_ADC (sizeof(adc_mux)/sizeof(char)) +volatile static unsigned char current_adc = LAST_ADC; + +static void start_next_adc() +{ + while (current_adc > 0) { + --current_adc; + + // test if current_adc should be measured + if (current_adc < N_PWMLEDS && pwmled_is_on(current_adc)) + goto found; + // TODO ambient light, battery sense, etc. + } + + // all ADCs have been handled + current_adc = LAST_ADC; + return; +found: + // ADCSRB |= _BV(GSEL); // gain 8 or 32 + ADMUX = adc_mux[current_adc]; // set up mux, start one-shot conversion + ADCSRA |= _BV(ADSC); +} + +void init_adc() +{ + 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); + + ADCSRA |= _BV(ADSC); + + /* Do first conversion and drop the result */ + while ((ADCSRA & _BV(ADIF)) == 0) + ; + ADCSRA |= _BV(ADIF); // clear the IRQ flag + + ADCSRA |= _BV(ADIE); // enable IRQ +} + +ISR(ADC_vect) { // IRQ handler + uint16_t adcval = ADCW; + + if (current_adc < N_PWMLEDS) + pwmled_adc(current_adc, adcval); + // TODO ambient light, battery sense, etc. + + start_next_adc(); +} + +void timer_start_adcs() +{ + if (current_adc == LAST_ADC) // Don't start if in progress + start_next_adc(); +} + diff --git a/lights.c b/lights.c deleted file mode 100644 index d05fa1c..0000000 --- a/lights.c +++ /dev/null @@ -1,273 +0,0 @@ -#include -#include -#include -#include -#include - -volatile uint16_t adcval; -unsigned char led_is_on = 0; -volatile unsigned char adccount = 0; - -typedef struct { - unsigned char pwmval, expected; -} led_level_t; - -led_level_t led_modes[2] = { - { 0x50, 0x38 }, - { 0x38, 0x04 }, -}; - -unsigned char led_mode = 0; -unsigned char led_mode_changed = 0; - -static void inline led_on() -{ - DDRB |= _BV( PB5 ); - PORTA |= _BV( PA0 ); - led_is_on = 1; -} - -static void inline led_off() -{ - led_is_on = 0; - DDRB &= ~_BV( PB5 ); - PORTA &=~ _BV( PA0 ); -// ADCSRA &= ~(_BV(ADIE) | _BV(ADIF)); -} - -/* ------------ Logging/Debugging ----------- */ - -unsigned char debug_state EEMEM; - -static void inline debug_setstate(unsigned char val) -{ - eeprom_write_byte(&debug_state, val); -} - -#define LOG_BUFFER 64 -uint16_t log_buffer[LOG_BUFFER] EEMEM; -unsigned char log_buffer_count; -uint16_t log_buffer2[LOG_BUFFER]; -volatile unsigned char stop = 0; - -static void inline init_log() -{ - debug_setstate(1); - log_buffer_count = 0; -} - -static void log_word(uint16_t word) { - if (log_buffer_count >= LOG_BUFFER) - return; - - // eeprom_write_word(&log_buffer[log_buffer_count], word); - log_buffer2[log_buffer_count] = word; - log_buffer_count++; - - if (log_buffer_count == LOG_BUFFER) { - unsigned char i; - for (i=0; i < LOG_BUFFER; i++) { - eeprom_write_word(&log_buffer[i], - log_buffer2[i]); - } - debug_setstate(0x42); - } -} - -/* ------------ Timer ----------- */ - -volatile uint16_t jiffies = 0; - -static void inline init_tmr() -{ - TCCR0A = _BV(WGM00); - TCCR0B = _BV(CS02) | _BV(CS00); // 1 kHz - OCR0A = 12; // 100 Hz - TIMSK |= _BV(OCIE0A); - DDRA |= _BV( PA0 ); - - jiffies = 0; -} - -static void inline tmr_handler() -{ - unsigned char c = jiffies & 0x0F; - unsigned char c1 = jiffies & 0x7F; - - ++jiffies; - -#if 0 - if (c == 1) - led_on(); - - if (c == 9) - led_off(); -#endif - - if (c == 0x02 || c == 0x08) - led_on(); - - if (c == 0x05 || c == 0x0b) - led_off(); - -#if 1 - if (c1 == 0x10) { - led_mode = 0; - led_mode_changed = 1; - OCR1D = led_modes[led_mode].pwmval; - } else if (c1 == 0x70) { - led_mode = 1; - led_mode_changed = 1; - OCR1D = led_modes[led_mode].pwmval; - } -#endif - - ADCSRA |= _BV(ADSC); -#if 0 - if (led_is_on && adcval != 0xFFEE) { - adcval = 0xFFEE; - - ADCSRA |= _BV(ADIF) | _BV(ADIE) | _BV(ADSC); - } -#endif -} - -ISR(TIMER0_COMPA_vect) -{ - tmr_handler(); -} - -/* ------------ PWM ----------- */ - -static void inline init_pwm() -{ - /* Async clock */ - PLLCSR = _BV(LSM) | _BV(PLLE); - _delay_ms(1); - while (PLLCSR & _BV(PLOCK) == 0) - ; - PLLCSR |= _BV(PCKE); - - TCCR1C = _BV(COM1D0) | _BV(COM1D1) | _BV(PWM1D); - TCCR1A = _BV(COM1A0) | _BV(COM1A1) | _BV(COM1B0) | _BV(COM1B1) | _BV(PWM1A) | _BV(PWM1B); - // TCCR1B = 0x80| _BV(CS13) | _BV(CS11); - TCCR1B = _BV(7) // PWM1X: PWM inversion mode - | _BV(CS10) // no clock prescaling - ; - OCR1C = 0xFF; // TOP value - OCR1D = OCR1B = OCR1A = 0; - // OCR1D = 0; - DDRB |= _BV( PB5 ); - PORTB &= ~(_BV( PB5 ) | _BV( PB1 ) | _BV( PB3 )); - - // led_off(); - // TIMSK |= _BV(TOIE1); -} - -#if 0 -static void inline pwm_handler() -{ - // TIMSK &= ~_BV(TOIE1); - // OCR1D = pwmval; -} - -ISR(TIMER1_OVF_vect) -{ - pwm_handler(); -} -#endif - -/* ------------ A/D Converter ----------- */ - -static void inline init_adc() -{ - ADCSRA = _BV(ADEN) // enable - | _BV(ADPS1) | _BV(ADPS0) // CLK/8 = 125 kHz - // | _BV(ADPS2) // CLK/16 = 62.5 kHz - ; - ADMUX = _BV(REFS1) // 1.1V internal reference - | _BV(MUX4)|_BV(MUX3) // port ADC5-6, gain 1 - ; - // ADCSRB = _BV(REFS2); - ADCSRB |= _BV(GSEL); - // Disable digital input on all bits used by ADC - DIDR0 = _BV(ADC5D)|_BV(ADC6D) | _BV(AREFD); - ADCSRA |= _BV(ADIE); -} - -static void inline adc_handler() -{ - uint16_t new_pwm = led_modes[led_mode].pwmval; - uint16_t old_pwm = new_pwm; - uint16_t adc_exp = led_modes[led_mode].expected; - - adcval = ADCW; - adccount++; - - // log_word(((adcval & 0x3FC) << 6) | pwmval); - - if (!led_is_on) - return; - - // ADCSRA &= ~(_BV(ADIE) | _BV(ADIF)); - - if (led_mode_changed) { - led_mode_changed = 0; - goto set_pwm; - } - - log_word(((adcval & 0xFF) << 8) | old_pwm); - - if (2*adcval > 5*adc_exp) { // >2.5x expected, lower significantly - new_pwm = 2*old_pwm/3; - } else if (3*adcval > 4*adc_exp) { // >1.33x expected, lower a bit - new_pwm = old_pwm - 1; - } else if (4*adcval < 3*adc_exp) { // 0.75x expected, raise a bit - new_pwm = old_pwm + 1; - } - - if (new_pwm > 0x60) { // odpojeno? - new_pwm = 0x60; - } - if (new_pwm < 2) { // zkrat? - new_pwm = 2; - } - -set_pwm: - if (new_pwm != old_pwm) { - led_modes[led_mode].pwmval = new_pwm; - OCR1D = new_pwm; - } - // ADCSRA |= _BV(ADSC); -} - -ISR(ADC_vect) -{ - adc_handler(); -} - -int main(void) -{ - _delay_ms(1500); - init_log(); - - init_pwm(); - init_adc(); - init_tmr(); - - led_on(); - debug_setstate(3); - - sei(); - while (1) - ; // sleep_mode(); - -#if 0 - while (1) { - PORTA |= _BV( PA0 ); - _delay_ms(200); - PORTA &=~ _BV( PA0 ); - _delay_ms(200); - } -#endif -} diff --git a/lights.h b/lights.h new file mode 100644 index 0000000..d6e0cbd --- /dev/null +++ b/lights.h @@ -0,0 +1,45 @@ +#ifndef LIGHTS_H__ +#define LIGHTS_H__ 1 + +#define N_LEDS 5 +#define N_PWMLEDS 3 +#define N_PWMLED_MODES 4 + +/* logging.c */ +#ifdef USE_LOGGING +void log_set_state(unsigned char val); +void log_init(); +void log_flush(); +void log_byte(unsigned char byte); +void log_word(uint16_t word); +#else +void inline log_set_state(unsigned char val) { } +void inline log_init() { } +void inline log_flush() { } +void inline log_byte(unsigned char byte) { } +void inline log_word(uint16_t word) { } +#endif + +/* adc.c */ +void init_adc(); +void timer_start_adcs(); + +/* pwm.c */ +void init_pwm(); +void pwm_on(unsigned char n); +void pwm_off(unsigned char n); +void pwm_set(unsigned char n, unsigned char stride); + +/* tmr.c */ +extern volatile uint16_t jiffies; +void init_tmr(); + +/* pwmled.c */ +void pwmled_init(); +void pwmled_adc(unsigned char n, uint16_t adcval); +void pwmled_set_mode(unsigned char n, unsigned char mode); +unsigned char pwmled_is_on(unsigned char n); + + +#endif /* !LIGHTS_H__ */ + diff --git a/logging.c b/logging.c new file mode 100644 index 0000000..04f9515 --- /dev/null +++ b/logging.c @@ -0,0 +1,53 @@ +#ifdef USE_LOGGING + +#include +#include + +#include "lights.h" + +#define LOG_BUFFER 128 +static unsigned char log_state EEMEM; +static unsigned char log_buffer_ee[LOG_BUFFER] EEMEM; +static unsigned char log_buffer_count; +static unsigned char log_buffer[LOG_BUFFER]; + +void log_set_state(unsigned char val) +{ + eeprom_write_byte(&log_state, val); +} + +void log_init() +{ + log_set_state(1); + log_buffer_count = 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; + + log_buffer_count = LOG_BUFFER; + for (i=0; i < LOG_BUFFER; i++) { + eeprom_write_byte(&log_buffer_ee[i], + log_buffer[i]); + } + log_set_state(0x42); +} + +#endif + diff --git a/main.c b/main.c new file mode 100644 index 0000000..324b884 --- /dev/null +++ b/main.c @@ -0,0 +1,36 @@ +#include +#include +#include +#include + +#include "lights.h" + +int main(void) +{ + _delay_ms(1500); + log_init(); + + init_pwm(); + init_adc(); + init_tmr(); + + pwmled_init(); + + log_set_state(3); + + sei(); +#if 1 + while (1) + ; // sleep_mode(); +#endif + +#if 0 + DDRB |= _BV(PB2); + while (1) { + PORTB |= _BV( PB2 ); + _delay_ms(200); + PORTB &=~ _BV( PB2 ); + _delay_ms(200); + } +#endif +} diff --git a/pattern.c b/pattern.c new file mode 100644 index 0000000..386f9df --- /dev/null +++ b/pattern.c @@ -0,0 +1,54 @@ +#include "lights.h" + +typedef struct { + unsigned char mode: 3; + unsigned char duration: 5; +} pattern_t; + +static unsigned char led_counters[N_LEDS]; +static pattern_t *led_patterns[N_LEDS]; + +#define PATTERN_END { 0, 0 } +pattern_t off_pattern[] = { + { 0, 5 }, + PATTERN_END +}; + +pattern_t blink_pattern[] = { + { 1, 5 }, + { 0, 5 }, + PATTERN_END +}; + +void pattern_init() +{ + unsigned char i; + + for (i = 0; i < N_LEDS; i++) { + led_counters[i] = 0; + led_patterns[i] = off_pattern; + } +} + +void patterns_next_tick() +{ + unsigned char i; + + for (i = 0; i < N_LEDS; i++) { + if (led_counters[i] == 0) { + led_patterns[i]++; + if (led_patterns[i]->duration == 0) { // END + led_patterns[i] = pattern_select(i); + } + led_counters[i] = led_patterns[i]->duration; + if (led_patterns[i]->mode == 0) { + led_off(i); + } else { + led_set_level(i, led_patterns[i]->mode - 1); + } + } + + led_counters[i]--; + } +} + diff --git a/pwm.c b/pwm.c new file mode 100644 index 0000000..af4e4fa --- /dev/null +++ b/pwm.c @@ -0,0 +1,72 @@ +#include +#include +#include + +#include "lights.h" + +void init_pwm() +{ + /* Async clock */ + PLLCSR = _BV(LSM) | _BV(PLLE); + + /* Synchronize to the phase lock */ + _delay_ms(1); + while ((PLLCSR & _BV(PLOCK)) == 0) + ; + PLLCSR |= _BV(PCKE); + + TCCR1C = _BV(COM1D0) | _BV(COM1D1) | _BV(PWM1D); + TCCR1A = _BV(COM1A0) | _BV(COM1A1) | _BV(COM1B0) | _BV(COM1B1) | _BV(PWM1A) | _BV(PWM1B); + TCCR1B = _BV(7) // PWM1X: PWM inversion mode + | _BV(CS10) // no clock prescaling + ; + OCR1C = 0xFF; // TOP value + + OCR1D = OCR1B = OCR1A = 0; // initial stride is 0 + + DDRB &= ~(_BV( PB1 ) | _BV( PB3 ) | _BV( PB5 )); // tristate it + PORTB &= ~(_BV( PB1 ) | _BV( PB3 ) | _BV( PB5 )); // set to zero +} + +void pwm_on(unsigned char n) +{ + switch (n) { + case 0: DDRB |= _BV(PB1); break; + case 1: DDRB |= _BV(PB3); break; + case 2: DDRB |= _BV(PB5); break; + } +} + +void pwm_off(unsigned char n) +{ + switch (n) { + case 0: DDRB &= ~_BV(PB1); break; + case 1: DDRB &= ~_BV(PB3); break; + case 2: DDRB &= ~_BV(PB5); break; + } +} + +void pwm_set(unsigned char n, unsigned char stride) +{ + switch (n) { + case 0: OCR1A = stride; break; + case 1: OCR1B = stride; break; + case 2: OCR1D = stride; break; + } +} + +#if 0 +static void inline pwm_handler() +{ + OCR1A = pwmval[0]; + OCR1B = pwmval[1]; + OCR1D = pwmval[2]; + TIMSK &= ~_BV(TOIE1); +} + +ISR(TIMER1_OVF_vect) +{ + pwm_handler(); +} +#endif + diff --git a/pwmled.c b/pwmled.c new file mode 100644 index 0000000..4861793 --- /dev/null +++ b/pwmled.c @@ -0,0 +1,180 @@ +#include + +#include "lights.h" + +static unsigned char pwm_vals[N_PWMLEDS*N_PWMLED_MODES]; +static unsigned char adc_vals[N_PWMLEDS*N_PWMLED_MODES] = { + /* pwmled0 */ + 0x04, 0x14, 0x24, 0x38, + /* pwmled1 */ + 0x04, 0x14, 0x24, 0x38, + /* pwmled2 */ + 0x04, 0x14, 0x24, 0x38, +}; + +static unsigned char pwmled_state[N_PWMLEDS]; +#define ST_DISABLED 0 +#define ST_PROBING 1 +#define ST_OFF 2 +#define ST_ON 3 + +static unsigned char pwmled_mode[N_PWMLEDS]; + +static unsigned char pwm_probes[N_PWMLEDS]; + +static void start_probing(unsigned char n) +{ + pwmled_state[n] = ST_PROBING; + pwm_set(n, 0); + pwm_on(n); + pwm_probes[n] = 0; +} + +void pwmled_init() +{ + unsigned char i; + + for (i = 0; i < N_PWMLEDS*N_PWMLED_MODES; i++) + pwm_vals[i] = 0; + + for (i = 0; i < N_PWMLEDS; i++) { + start_probing(i); + } +} + +unsigned char pwmled_is_on(unsigned char n) +{ + unsigned char st = pwmled_state[n]; + if (st == ST_PROBING || st == ST_ON) + return 1; + else + return 0; +} + +static void inline probing_adc(unsigned char n, uint16_t adcval) +{ + unsigned char need_bigger = 0, i; + unsigned char *pwm_p = &pwm_vals[n*N_PWMLED_MODES]; + unsigned char *adc_p = &adc_vals[n*N_PWMLED_MODES]; + unsigned char pwm = pwm_probes[n]; + +#if 0 + log_byte(n); + log_byte(0xF4); + log_word(adcval); +#endif + +#if 0 + if (pwm == 0 && adcval > 0) { // non-zero voltage with zero PWM? + pwmled_state[n] = ST_DISABLED; + log_byte(n); + log_byte(0xF0); + log_word(adcval); + return; + } +#endif + + for (i = 0; i < N_PWMLED_MODES; i++, pwm_p++, adc_p++) { + uint16_t adc = *adc_p; + if (adc >= adcval) { + *pwm_p = pwm; + need_bigger = 1; + } + } + +#if 0 + if ((n == 1 && pwm > 0x35) || adcval != 0) { + log_byte(n); + log_byte(0xF3); + log_byte(pwm); + log_word(adcval); + } +#endif + + if (!need_bigger) { // successfully probed + pwm_off(n); + // pwm_set(n, 0); + pwmled_state[n] = ST_OFF; + log_byte(n); + log_byte(0xF1); + + return; + } + + if (pwm >= 0x60) { // over the maximum! + pwmled_state[n] = ST_DISABLED; + log_byte(n); + log_byte(0xF2); + pwm_off(n); + // pwm_set(n, 0); + return; + } + + // try to increase + pwm++; + pwm_probes[n] = pwm; + pwm_set(n, pwm); +} + +// Feedback loop +static void inline on_adc(unsigned char n, uint16_t adcval) +{ +#if 0 + uint16_t new_pwm = led_modes[led_mode].pwmval; + uint16_t old_pwm = new_pwm; + uint16_t adc_exp = led_modes[led_mode].expected; + + log_word(((adcval & 0xFF) << 8) | old_pwm); + + if (2*adcval > 5*adc_exp) { // >2.5x expected, lower significantly + new_pwm = 2*old_pwm/3; + } else if (3*adcval > 4*adc_exp) { // >1.33x expected, lower a bit + new_pwm = old_pwm - 1; + } else if (4*adcval < 3*adc_exp) { // 0.75x expected, raise a bit + new_pwm = old_pwm + 1; + } + + if (new_pwm > 0x60) { // odpojeno? + new_pwm = 0x60; + } + if (new_pwm < 2) { // zkrat? + new_pwm = 2; + } + +set_pwm: + if (new_pwm != old_pwm) { + led_modes[led_mode].pwmval = new_pwm; + OCR1D = new_pwm; + } + // ADCSRA |= _BV(ADSC); +#endif +} + +void pwmled_adc(unsigned char n, uint16_t adcval) +{ + unsigned char i, probing; + switch (pwmled_state[n]) { + case ST_PROBING: + probing_adc(n, adcval); + + probing = 0; + for (i = 0; i < N_PWMLEDS; i++) + if (pwmled_state[i] == ST_PROBING) + probing = 1; + + if (!probing) { + for (i = 0; i < N_PWMLEDS; i++) + log_byte(pwmled_state[i]); + + for (i = 0; i < N_PWMLEDS*N_PWMLED_MODES; i++) + log_byte(pwm_vals[i]); + log_flush(); + } + + return; + case ST_ON: + on_adc(n, adcval); + return; + // WTF am I doing in this function then? + } +} diff --git a/tmr.c b/tmr.c new file mode 100644 index 0000000..b8fb502 --- /dev/null +++ b/tmr.c @@ -0,0 +1,25 @@ +#include +#include + +#include "lights.h" + +volatile uint16_t jiffies; + +void init_tmr() +{ + TCCR0A = _BV(WGM00); + TCCR0B = _BV(CS02) | _BV(CS00); // CLK/1024 = 1 kHz + OCR0A = 10; // 100 Hz + TIMSK |= _BV(OCIE0A); + + jiffies = 0; +} + +ISR(TIMER0_COMPA_vect) +{ + ++jiffies; + + // patterns_next_tick(); + timer_start_adcs(); +} +