From: Jan "Yenya" Kasprzak Date: Wed, 13 Apr 2016 16:21:32 +0000 (+0200) Subject: Experimental step-up driver for chain of 5630 LEDs. X-Git-Url: https://www.fi.muni.cz/~kas/git//home/kas/public_html/git/?a=commitdiff_plain;h=HEAD;p=tinyboard.git Experimental step-up driver for chain of 5630 LEDs. --- diff --git a/projects/step-up-1/Makefile b/projects/step-up-1/Makefile new file mode 100644 index 0000000..b655283 --- /dev/null +++ b/projects/step-up-1/Makefile @@ -0,0 +1,70 @@ + +PROGRAM=lights +# SRC=version.c main.c logging.c pwm.c adc.c pwmled.c pattern.c buttons.c \ +# control.c battery.c wdt.c +SRC=main.c logging.c pwm.c +OBJ=$(SRC:.c=.o) + + +MCU=attiny25 +AVRDUDE_MCU=$(MCU) +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) -B10 + +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 + +version.c: + ./version.pl > version.c + +.PHONY: all clean dump_eeprom program program_flash program_eeprom objdump version.c + diff --git a/projects/step-up-1/lights.h b/projects/step-up-1/lights.h new file mode 100644 index 0000000..e3bd6e3 --- /dev/null +++ b/projects/step-up-1/lights.h @@ -0,0 +1,87 @@ +#ifndef LIGHTS_H__ +#define LIGHTS_H__ 1 + +#define N_PWMLED_MODES 4 + +/* 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 */ +extern volatile unsigned char need_battery_adc; +extern volatile unsigned char need_pwmled_adc; +extern volatile unsigned char adc_enabled; +void init_adc(); +void susp_adc(); +void start_next_adc(); + +/* pwm.c */ +#define PWM_MAX 0xFF +extern volatile unsigned char pwm_enabled; +void init_pwm(); +void susp_pwm(); +void pwm_off(); +void pwm_set(uint8_t stride); + +/* pwmled.c */ +void init_pwmled(); +void pwmled_adc(uint16_t adcval); +void pwmled_set_target(unsigned char mode); +void pwmled_on_off(unsigned char on); + +/* pattern.c */ +void init_pattern(); +void patterns_next_tick(); +void led_set_pattern(unsigned char led, unsigned char bits_len, + unsigned char bits_start, unsigned char *data); +void led_set_number_pattern(unsigned char led, + unsigned char num, unsigned char inv); +void pattern_reload(); + +/* buttons.c */ +void init_buttons(); +void susp_buttons(); +void timer_check_buttons(); +unsigned char buttons_wait_for_release(); +void status_led_on_off(unsigned char on); + +/* battery.c */ +void battery_adc(); +void init_battery(); +unsigned char battery_gauge(); + +/* control.c */ +void init_control(); +void long_press_start(); +void long_press(); +void short_press(); +void brake_on(); +void brake_off(); +void pwmled_pattern_select(unsigned char led); +void status_led_pattern_select(unsigned char led); +#define ERR_BATTERY 1 +#define ERR_PWMLED 2 +void set_error(unsigned char err); + +/* wdt.c */ +extern volatile uint16_t jiffies; +void init_wdt(); +void susp_wdt(); + +/* main.c */ +void power_down(); + +#endif /* !LIGHTS_H__ */ + diff --git a/projects/step-up-1/logging.c b/projects/step-up-1/logging.c new file mode 100644 index 0000000..4582d27 --- /dev/null +++ b/projects/step-up-1/logging.c @@ -0,0 +1,81 @@ +#ifdef USE_LOGGING + +#include +#include + +#include "lights.h" + +#define LOG_BUFFER 32 +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 + diff --git a/projects/step-up-1/main.c b/projects/step-up-1/main.c new file mode 100644 index 0000000..6d38f06 --- /dev/null +++ b/projects/step-up-1/main.c @@ -0,0 +1,236 @@ +#include +#include +#include +#include +#include +#include + +#include "lights.h" + +#if 0 +static void hw_setup() +{ + power_all_disable(); + +// init_battery(); + init_pwm(); +// init_adc(); +// init_wdt(); +// +// init_buttons(); + +// init_pwmled(); +// init_pattern(); +// init_control(); + +// set_sleep_mode(SLEEP_MODE_IDLE); +} + +static void hw_suspend() +{ + susp_pwm(); + susp_adc(); + susp_wdt(); + + susp_buttons(); + + power_all_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(); +} +#endif + + +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() +{ + power_adc_enable(); + ACSR |= _BV(ACD); // but disable the analog comparator + + ADCSRA = _BV(ADEN) // enable + // | _BV(ADPS1) | _BV(ADPS0) // CLK/8 = 125 kHz + | _BV(ADPS2) // CLK/16 = 62.5 kHz + ; + + // Disable digital input on all bits used by ADC + DIDR0 = _BV(ADC3D) | _BV(ADC2D); + + ADMUX = _BV(REFS1) | _BV(MUX1) | _BV(MUX0); + + /* Do first conversion and drop the result */ + read_adc_sync(); + + // ADCSRA |= _BV(ADIE); // enable IRQ +} + +int main(void) +{ + uint16_t a, p, c, target; + int32_t sum; + uint8_t micro; + init_log(); + + log_set_state(3); + + init_pwm(); + init_adc(); + // init_pwmled(); + + pwm_set(1); + // setup_mux(0); + // pwmled_on_off(1); + // pwmled_set_target(1); + + sum = 0; + c = 0; + micro = 0; + sei(); + + target = 0xf0; +#if 0 + while(1) { + unsigned char p; + for (p = 0xd4; p <= 0xd5; p+= 0x1) { + unsigned char i, j; + uint16_t sum; + + pwm_set(p); + + for (j = 0; j < 12; j++) { + sum = 0; + for (i = 0; i < 2; i++) { + sum += read_adc_sync(); + } + log_byte(sum); + for (i = 0; i < 14; i++) { + sum += read_adc_sync(); + } + } + log_flush(); + } + } +#endif + while (1) { + sum += target - read_adc_sync() - (sum >> 7); + micro++; + if (micro > 7) + micro = 0; + + p = ((sum >> 4) + micro) >> 3; + if (p > 0xd8) + p = 0xd8; + pwm_set(p); + if (++c > 200) { + log_byte(p); + log_flush(); + c = 0; + } + } + + p = a = c = 0; + while (1) { + uint16_t i; + for (i = 0; i < 25; i++) { + uint16_t val; + val = read_adc_sync(); + a += val - (a >> 5); + } + + if (a < (42 << 5) && p < 250) { + p++; + } else if (p > 1 && a > (38 << 5)) { + p--; + } + pwm_set(p); + if (++c > 1000) { + log_byte(0xbb); + // log_word(read_adc_sync()); + log_word(a); + log_word(p); + log_flush(); + c = 0; + } + } + + while(1) { + uint16_t i, t; + for (t = 0; t < 3; t++) { + pwmled_set_target(t); + for (i = 0; i < 1000; i++) { + need_pwmled_adc = 1; + while (need_pwmled_adc == 1) + ; + } + } + } + +#if 0 + hw_setup(); + power_down(); + +#if 1 + while (1) { + cli(); + if (pwm_enabled) { + set_sleep_mode(SLEEP_MODE_IDLE); + } else if (adc_enabled) { + set_sleep_mode(SLEEP_MODE_ADC); + } else { + set_sleep_mode(SLEEP_MODE_PWR_DOWN); + } + + sleep_enable(); + // keep BOD active, no sleep_bod_disable(); + sei(); + sleep_cpu(); + sleep_disable(); + } +#endif + +#if 0 + DDRB |= _BV(PB2); + while (1) { + PORTB |= _BV( PB2 ); + _delay_ms(200); + PORTB &=~ _BV( PB2 ); + _delay_ms(200); + } +#endif +#endif +} diff --git a/projects/step-up-1/pwm.c b/projects/step-up-1/pwm.c new file mode 100644 index 0000000..b93a4fa --- /dev/null +++ b/projects/step-up-1/pwm.c @@ -0,0 +1,74 @@ +#include +#include +#include +#include +#include + +#include "lights.h" + +/* + * Single PWM channel on OC1B (pin PB4 of Tiny45). + * Counts from 0 to 0xFF, without OCR1C compare. + */ + +volatile unsigned char pwm_enabled; + +static void inline enable_pll() +{ + /* Async clock */ + PLLCSR = _BV(PLLE) | _BV(LSM); + + /* Synchronize to the phase lock */ + _delay_us(100); + while ((PLLCSR & _BV(PLOCK)) == 0) + ; + PLLCSR |= _BV(PCKE); +} + +void init_pwm() +{ + pwm_enabled = 0; + power_timer1_enable(); + + TCCR1 = _BV(CTC1) | _BV(CS11); // pll_clk/2 + GTCCR = _BV(COM1B1) | _BV(PWM1B); + + OCR1C = PWM_MAX; + OCR1B = 0; // initial stride is 0 + + DDRB &= ~(_BV( PB4 )); + PORTB &= ~_BV(PB4); // set to zero +} + +void susp_pwm() +{ + DDRB &= ~(_BV( PB4 )); + PORTB &= ~(_BV( PB4 )); + TCCR1 = 0; + TIMSK = 0; + TIFR = 0; + + PLLCSR &= ~(_BV(PLLE) | _BV(PCKE)); +} + +void pwm_off() +{ + OCR1B = 0; + DDRB &= ~_BV(PB4); + + PLLCSR &= ~(_BV(PLLE) | _BV(PCKE)); + power_timer1_disable(); + pwm_enabled = 0; +} + +void pwm_set(uint8_t stride) +{ + OCR1B = stride; + + if (!pwm_enabled) { + power_timer1_enable(); + enable_pll(); + DDRB |= _BV(PB4); + pwm_enabled = 1; + } +}