From 812c7e3327698e2135123ef6d779ffad4d2863d8 Mon Sep 17 00:00:00 2001 From: Markus Koch Date: Sun, 4 Feb 2024 19:59:12 +0100 Subject: [PATCH] fw: Implement initial firmware --- firmware/.gitignore | 3 + firmware/Makefile | 21 ++++ firmware/app.c | 147 +++++++++++++++++++++++++++ firmware/flash.sh | 9 ++ firmware/lib/button.c | 87 ++++++++++++++++ firmware/lib/button.h | 14 +++ firmware/lib/hal.h | 51 ++++++++++ firmware/lib/light_ws2812.c | 194 ++++++++++++++++++++++++++++++++++++ firmware/lib/light_ws2812.h | 97 ++++++++++++++++++ firmware/lib/mma8653.c | 98 ++++++++++++++++++ firmware/lib/mma8653.h | 13 +++ firmware/lib/pm.c | 122 +++++++++++++++++++++++ firmware/lib/pm.h | 22 ++++ firmware/lib/system.c | 85 ++++++++++++++++ firmware/lib/system.h | 17 ++++ firmware/lib/timers.c | 36 +++++++ firmware/lib/timers.h | 21 ++++ 17 files changed, 1037 insertions(+) create mode 100644 firmware/.gitignore create mode 100644 firmware/Makefile create mode 100644 firmware/app.c create mode 100755 firmware/flash.sh create mode 100644 firmware/lib/button.c create mode 100644 firmware/lib/button.h create mode 100644 firmware/lib/hal.h create mode 100644 firmware/lib/light_ws2812.c create mode 100644 firmware/lib/light_ws2812.h create mode 100644 firmware/lib/mma8653.c create mode 100644 firmware/lib/mma8653.h create mode 100644 firmware/lib/pm.c create mode 100644 firmware/lib/pm.h create mode 100644 firmware/lib/system.c create mode 100644 firmware/lib/system.h create mode 100644 firmware/lib/timers.c create mode 100644 firmware/lib/timers.h diff --git a/firmware/.gitignore b/firmware/.gitignore new file mode 100644 index 0000000..9c16f5b --- /dev/null +++ b/firmware/.gitignore @@ -0,0 +1,3 @@ +*.o +*.bin +*.hex diff --git a/firmware/Makefile b/firmware/Makefile new file mode 100644 index 0000000..da04acf --- /dev/null +++ b/firmware/Makefile @@ -0,0 +1,21 @@ +MCU=atmega8 +CFLAGS=-g -Wall --param=min-pagesize=0 -mcall-prologues -mmcu=$(MCU) -O2 -DF_CPU=16000000 +LDFLAGS=-Wl,-gc-sections -Wl,-relax +CC=avr-gcc +TARGET=leddie-firmware +OBJECT_FILES=lib/light_ws2812.o lib/pm.o lib/mma8653.o lib/timers.o lib/button.o lib/system.o app.o + +all: $(TARGET).hex + +clean: + rm -f *.o *.hex *.obj *.hex + rm -f lib/*.o + +%.hex: %.obj + avr-objcopy -R .eeprom -O ihex $< $@ + +%.obj: $(OBJECT_FILES) + $(CC) $(CFLAGS) $(OBJECT_FILES) $(LDFLAGS) -o $@ + +program: $(TARGET).hex + avrdude -p $(MCU) -c usbasp -U flash:w:$(TARGET).hex diff --git a/firmware/app.c b/firmware/app.c new file mode 100644 index 0000000..5a9db04 --- /dev/null +++ b/firmware/app.c @@ -0,0 +1,147 @@ +#include "lib/system.h" +#include "lib/hal.h" +#include + +#define TIMER_INMENU TIMER_APP0 +#define TIMER_AUX TIMER_APP1 + +void axl_test() +{ + int ret; + struct axl_result measurements; + + int dir_x; + int dir_y; + int ledno; + int intens; + + if (block_for(TIMER_AUX, timer_ms(50))) { + display_clear(0); + + #define THRES 10 + ret = mma8653_get_measurements(&measurements); + dir_x = 0; + if (measurements.x > THRES) + dir_x = -1; + else if (measurements.x < -THRES) + dir_x = 1; + dir_y = 0; + if (measurements.y > THRES) + dir_y = 1; + else if (measurements.y < -THRES) + dir_y = -1; + + if (dir_y == -1) + ledno = dir_x + 1; + else if (dir_y == 0) + ledno = 3 + 1 -dir_x; + else if (dir_y == 1) + ledno = dir_x + 6 + 1; + + intens = abs(measurements.x + measurements.y); + intens = intens >> 2; + if (intens < 1) + intens = 1; + if (measurements.z > 0) + led[ledno].b = intens; + else + led[ledno].r = intens; + + if (ret) + led[ret].r = 0x10; + + display_update(); + } +} + + +void led_test(int first_launch) +{ + static int i; + + if (first_launch) { + i = 0; + led[0].r = 1; + } + + if (block_for(TIMER_AUX, timer_ms(30))) { + led[i].g = 0x0; + i = (i + 1) % STRIPLEN; + led[i].g = 0x10; + if (led[i].r && led[i].b) { + led[i].r = 0; + led[i].b = 1; + } else if (led[i].r) { + led[i].b = 1; + } else { + led[i].r = 1; + led[i].b = 0; + } + display_update(); + } +} + +void system_test() +{ + // Display power states on first face + if (HAL_ASSERTED_N(VLED_EN_N)) + led[0].b = 1; + else + led[0].b = 0; + + if (HAL_ASSERTED_N(CHG_ACTIVE_N)) + led[1].r = 1; + else + led[1].r = 0; + + if (block_for(TIMER_AUX, timer_ms(50))) + display_update(); +} + +void app_init() +{ + return; +} + +#define APP_COUNT 3 +enum app_return app_mainloop() +{ + static int app_sel = 0; + static int clr = 1; + + if (button_event(EV_PRESS)) { + app_sel = (app_sel + 1) % APP_COUNT; + display_clear(); + for (int i = 0; i < STRIPLEN; i += 9) { + led[app_sel + i].g = 4; + } + display_update(); + timer_set(TIMER_INMENU, timer_ms(750)); + clr = 1; + } else if (button_event(EV_LONGPRESS)) { + return SUSPEND; + } + + if (timer_expired(TIMER_INMENU)) { + if (clr) { + display_clear(); + } + switch (app_sel) { + case 0: + led_test(clr); + break; + case 1: + axl_test(); + break; + case 2: + system_test(); + break; + default: + app_sel = 0; + break; + } + clr = 0; + } + + return RUN; +} diff --git a/firmware/flash.sh b/firmware/flash.sh new file mode 100755 index 0000000..9dc7d03 --- /dev/null +++ b/firmware/flash.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +while [ 1 ]; do + make program + if [ $? -eq 0 ]; then + break; + fi + sleep 1 +done diff --git a/firmware/lib/button.c b/firmware/lib/button.c new file mode 100644 index 0000000..011f6a3 --- /dev/null +++ b/firmware/lib/button.c @@ -0,0 +1,87 @@ +#include +#include "button.h" +#include "timers.h" +#include "hal.h" +#include + +#include "pm.h" // TODO: FOR DEBUGGING ONLY + +volatile enum BUTTON_EVENTS button_event_status = NONE; + +// User button interrupt +ISR(INT1_vect) +{ + _delay_ms(50); + if (HAL_ASSERTED_N(BUTTON_N)) { // Button down event + switch (button_event_status) { + case NONE: // Start processing button from NONE, + case EV_BOOT: // or drop boot event if not polled for, + case EV_PRESS: // or drop current keypress to prevent unhandled keypresses from blocking other would-be-handled keypresses + case EV_DOUBLEPRESS: + case EV_LONGPRESS: + case EV_DOUBLELONGPRESS: + button_event_status = WAIT_FOR_RELEASE_1; + timer_set(TIMER_BUTTON, timer_ms(500)); + break; + case WAIT_FOR_SECOND: + button_event_status = WAIT_FOR_RELEASE_2; + timer_set(TIMER_BUTTON, timer_ms(500)); + break; + default: + break; + } + } else { // Button up event + switch (button_event_status) { + case WAIT_FOR_RELEASE_1: + button_event_status = WAIT_FOR_SECOND; + break; + case WAIT_FOR_RELEASE_2: + button_event_status = EV_DOUBLEPRESS; + break; + default: + break; + } + } + + return; +} + +void button_init() +{ + HAL_INIT(BUTTON_N, INPUT, ON); // Pull-up for user button + button_event_status = EV_BOOT; + + MCUCR = (1 << ISC10); // Any-edge interrupt + GICR = (1 << INT1); // Turn on button interrupt +} + +void button_systick_cb() +{ + if (button_event_status != 0) { // TODO: we should not enter on pending EV? + if (timer_expired(TIMER_BUTTON)) { + switch (button_event_status) { + case WAIT_FOR_RELEASE_1: + button_event_status = EV_LONGPRESS; + break; + case WAIT_FOR_RELEASE_2: + button_event_status = EV_DOUBLELONGPRESS; + pm_reset(); // TODO: For debugging purposes only + break; + case WAIT_FOR_SECOND: + button_event_status = EV_PRESS; + break; + default: + break; + } + } + } +} + +int button_event(enum BUTTON_EVENTS ev) +{ + if (button_event_status == ev) { + button_event_status = NONE; // Mark as handled + return 1; + } + return 0; +} diff --git a/firmware/lib/button.h b/firmware/lib/button.h new file mode 100644 index 0000000..494459a --- /dev/null +++ b/firmware/lib/button.h @@ -0,0 +1,14 @@ +#ifndef _BUTTON_H +#define _BUTTON_H + +enum BUTTON_EVENTS { + NONE, WAIT_FOR_RELEASE_1, WAIT_FOR_SECOND, WAIT_FOR_RELEASE_2, // Intermediate states + EV_PRESS, EV_DOUBLEPRESS, EV_LONGPRESS, EV_DOUBLELONGPRESS, EV_BOOT// Button events +}; + +void button_init(); +int button_event(enum BUTTON_EVENTS ev); +extern void button_systick_cb(); +void button_systick_cb(); + +#endif // _BUTTON_H diff --git a/firmware/lib/hal.h b/firmware/lib/hal.h new file mode 100644 index 0000000..b90a540 --- /dev/null +++ b/firmware/lib/hal.h @@ -0,0 +1,51 @@ +#ifndef _HAL_H +#define _HAL_H + +#include + +#define HAL_OUTPUT(name) PORT_##name(DDR) |= PIN_##name +#define HAL_INPUT(name) PORT_##name(DDR) &= ~(PIN_##name) +#define HAL_SET(name) PORT_##name(PORT) |= PIN_##name +#define HAL_CLEAR(name) PORT_##name(PORT) &= ~(PIN_##name) +#define HAL_ISHIGH(name) (PORT_##name(PIN) & PIN_##name) + +#define HAL_ON(name) HAL_SET(name) +#define HAL_ON_N(name) HAL_CLEAR(name) +#define HAL_OFF(name) HAL_CLEAR(name) +#define HAL_OFF_N(name) HAL_SET(name) +#define HAL_ASSERTED(name) HAL_ISHIGH(name) +#define HAL_ASSERTED_N(name) (!HAL_ISHIGH(name)) + +#define HAL_INIT(name, type, default) HAL_##default(name); HAL_##type(name) + +#define PORT_VBUS_SENSE(t) t##B +#define PIN_VBUS_SENSE (1 << 2) + +#define PORT_VLED_EN_N(t) t##D +#define PIN_VLED_EN_N (1 << 6) // Hotfix + +#define PORT_CHG_ACTIVE_N(t) t##D +#define PIN_CHG_ACTIVE_N (1 << 7) // Hotfix + + +#define PORT_BAT_SENSE(t) t##C +#define PIN_BAT_SENSE (1 << 0) + +#define PORT_BAT_SENSE_EN_N(t) t##C +#define PIN_BAT_SENSE_EN_N (1 << 1) + + +#define PORT_AXL_EN(t) t##C +#define PIN_AXL_EN (3 << 2) + +#define PORT_AXL_I2C(t) t##C +#define PIN_AXL_I2C (3 << 4) + + +#define PORT_BUTTON_N(t) t##D +#define PIN_BUTTON_N (1 << 3) + + +// LED pin defined in `light_ws2812.h`. + +#endif diff --git a/firmware/lib/light_ws2812.c b/firmware/lib/light_ws2812.c new file mode 100644 index 0000000..ec23f69 --- /dev/null +++ b/firmware/lib/light_ws2812.c @@ -0,0 +1,194 @@ +/* +* light weight WS2812 lib V2.0b +* +* Controls WS2811/WS2812/WS2812B RGB-LEDs +* Author: Tim (cpldcpu@gmail.com) +* +* Jan 18th, 2014 v2.0b Initial Version +* Nov 29th, 2015 v2.3 Added SK6812RGBW support +* +* License: GNU GPL v2+ (see License.txt) +*/ + +#include "light_ws2812.h" +#include +#include +#include + +// Normally ws2812_sendarray_mask() runs under disabled-interrupt condition, +// undefine if you want to accept interrupts in that function. +#define interrupt_is_disabled + +// Setleds for standard RGB +void inline ws2812_setleds(struct cRGB *ledarray, uint16_t leds) +{ + ws2812_setleds_pin(ledarray,leds, _BV(ws2812_pin)); +} + +void inline ws2812_setleds_pin(struct cRGB *ledarray, uint16_t leds, uint8_t pinmask) +{ + ws2812_sendarray_mask((uint8_t*)ledarray,leds+leds+leds,pinmask); + _delay_us(ws2812_resettime); +} + +// Setleds for SK6812RGBW +void inline ws2812_setleds_rgbw(struct cRGBW *ledarray, uint16_t leds) +{ + ws2812_sendarray_mask((uint8_t*)ledarray,leds<<2,_BV(ws2812_pin)); + _delay_us(ws2812_resettime); +} + +void ws2812_sendarray(uint8_t *data,uint16_t datlen) +{ + ws2812_sendarray_mask(data,datlen,_BV(ws2812_pin)); +} + +/* + This routine writes an array of bytes with RGB values to the Dataout pin + using the fast 800kHz clockless WS2811/2812 protocol. +*/ + +// Timing in ns +#define w_zeropulse 350 //300 +#define w_onepulse 800 //600 +#define w_totalperiod 1250 //1100 + +// Fixed cycles used by the inner loop +#define w_fixedlow 2 +#define w_fixedhigh 4 +#define w_fixedtotal 8 + +// Insert NOPs to match the timing, if possible +#define w_zerocycles (((F_CPU/1000)*w_zeropulse )/1000000) +#define w_onecycles (((F_CPU/1000)*w_onepulse +000000)/1000000) +#define w_totalcycles (((F_CPU/1000)*w_totalperiod +500000)/1000000) + +// w1 - nops between rising edge and falling edge - low +#define w1 (w_zerocycles-w_fixedlow) +// w2 nops between fe low and fe high +#define w2 (w_onecycles-w_fixedhigh-w1) +// w3 nops to complete loop +#define w3 (w_totalcycles-w_fixedtotal-w1-w2) + +#if w1>0 + #define w1_nops w1 +#else + #define w1_nops 0 +#endif + +// The only critical timing parameter is the minimum pulse length of the "0" +// Warn or throw error if this timing can not be met with current F_CPU settings. +#define w_lowtime ((w1_nops+w_fixedlow)*1000000)/(F_CPU/1000) +#if w_lowtime>550 + #error "Light_ws2812: Sorry, the clock speed is too low. Did you set F_CPU correctly?" +#elif w_lowtime>450 + #warning "Light_ws2812: The timing is critical and may only work on WS2812B, not on WS2812(S)." + #warning "Please consider a higher clockspeed, if possible" +#endif + +#if w2>0 +#define w2_nops w2 +#else +#define w2_nops 0 +#endif + +#if w3>0 +#define w3_nops w3 +#else +#define w3_nops 0 +#endif + +#define w_nop1 "nop \n\t" +#ifdef interrupt_is_disabled +#define w_nop2 "brid .+0 \n\t" +#else +#define w_nop2 "brtc .+0 \n\t" +#endif +#define w_nop4 w_nop2 w_nop2 +#define w_nop8 w_nop4 w_nop4 +#define w_nop16 w_nop8 w_nop8 + +void inline ws2812_sendarray_mask(uint8_t *data,uint16_t datlen,uint8_t maskhi) +{ + uint8_t curbyte,ctr,masklo; + uint8_t sreg_prev; + + ws2812_DDRREG |= maskhi; // Enable output + + masklo =~maskhi&ws2812_PORTREG; + maskhi |= ws2812_PORTREG; + + sreg_prev=SREG; +#ifdef interrupt_is_disabled + cli(); +#endif + + while (datlen--) { + curbyte=*data++; + + asm volatile( + " ldi %0,8 \n\t" +#ifndef interrupt_is_disabled + " clt \n\t" +#endif + "loop%=: \n\t" + " out %2,%3 \n\t" // '1' [01] '0' [01] - re +#if (w1_nops&1) +w_nop1 +#endif +#if (w1_nops&2) +w_nop2 +#endif +#if (w1_nops&4) +w_nop4 +#endif +#if (w1_nops&8) +w_nop8 +#endif +#if (w1_nops&16) +w_nop16 +#endif + " sbrs %1,7 \n\t" // '1' [03] '0' [02] + " out %2,%4 \n\t" // '1' [--] '0' [03] - fe-low + " lsl %1 \n\t" // '1' [04] '0' [04] +#if (w2_nops&1) + w_nop1 +#endif +#if (w2_nops&2) + w_nop2 +#endif +#if (w2_nops&4) + w_nop4 +#endif +#if (w2_nops&8) + w_nop8 +#endif +#if (w2_nops&16) + w_nop16 +#endif + " out %2,%4 \n\t" // '1' [+1] '0' [+1] - fe-high +#if (w3_nops&1) +w_nop1 +#endif +#if (w3_nops&2) +w_nop2 +#endif +#if (w3_nops&4) +w_nop4 +#endif +#if (w3_nops&8) +w_nop8 +#endif +#if (w3_nops&16) +w_nop16 +#endif + + " dec %0 \n\t" // '1' [+2] '0' [+2] + " brne loop%=\n\t" // '1' [+3] '0' [+4] + : "=&d" (ctr) + : "r" (curbyte), "I" (_SFR_IO_ADDR(ws2812_PORTREG)), "r" (maskhi), "r" (masklo) + ); + } + + SREG=sreg_prev; +} diff --git a/firmware/lib/light_ws2812.h b/firmware/lib/light_ws2812.h new file mode 100644 index 0000000..1475779 --- /dev/null +++ b/firmware/lib/light_ws2812.h @@ -0,0 +1,97 @@ +/* + * light weight WS2812 lib include + * + * Version 2.3 - Nev 29th 2015 + * Author: Tim (cpldcpu@gmail.com) + * + * Please do not change this file! All configuration is handled in "ws2812_config.h" + * + * License: GNU GPL v2+ (see License.txt) + + + */ + +#ifndef LIGHT_WS2812_H_ +#define LIGHT_WS2812_H_ + +#include +#include + +/////////////////////////////////////////////////////////////////////// +// Define Reset time in µs. +// +// This is the time the library spends waiting after writing the data. +// +// WS2813 needs 300 µs reset time +// WS2812 and clones only need 50 µs +// +/////////////////////////////////////////////////////////////////////// +#if !defined(ws2812_resettime) +#define ws2812_resettime 300 +#endif + +/////////////////////////////////////////////////////////////////////// +// Define I/O pin +/////////////////////////////////////////////////////////////////////// +#if !defined(ws2812_port) +#define ws2812_port D // Data port +#endif + +#if !defined(ws2812_pin) +#define ws2812_pin 5 // Data out pin +#endif + +/* + * Structure of the LED array + * + * cRGB: RGB for WS2812S/B/C/D, SK6812, SK6812Mini, SK6812WWA, APA104, APA106 + * cRGBW: RGBW for SK6812RGBW + */ + +struct cRGB { uint8_t g; uint8_t r; uint8_t b; }; +struct cRGBW { uint8_t g; uint8_t r; uint8_t b; uint8_t w;}; + + + +/* User Interface + * + * Input: + * ledarray: An array of GRB data describing the LED colors + * number_of_leds: The number of LEDs to write + * pinmask (optional): Bitmask describing the output bin. e.g. _BV(PB0) + * + * The functions will perform the following actions: + * - Set the data-out pin as output + * - Send out the LED data + * - Wait 50µs to reset the LEDs + */ + +void ws2812_setleds (struct cRGB *ledarray, uint16_t number_of_leds); +void ws2812_setleds_pin (struct cRGB *ledarray, uint16_t number_of_leds,uint8_t pinmask); +void ws2812_setleds_rgbw(struct cRGBW *ledarray, uint16_t number_of_leds); + +/* + * Old interface / Internal functions + * + * The functions take a byte-array and send to the data output as WS2812 bitstream. + * The length is the number of bytes to send - three per LED. + */ + +void ws2812_sendarray (uint8_t *array,uint16_t length); +void ws2812_sendarray_mask(uint8_t *array,uint16_t length, uint8_t pinmask); + + +/* + * Internal defines + */ +#if !defined(CONCAT) +#define CONCAT(a, b) a ## b +#endif + +#if !defined(CONCAT_EXP) +#define CONCAT_EXP(a, b) CONCAT(a, b) +#endif + +#define ws2812_PORTREG CONCAT_EXP(PORT,ws2812_port) +#define ws2812_DDRREG CONCAT_EXP(DDR,ws2812_port) + +#endif /* LIGHT_WS2812_H_ */ diff --git a/firmware/lib/mma8653.c b/firmware/lib/mma8653.c new file mode 100644 index 0000000..34f83e6 --- /dev/null +++ b/firmware/lib/mma8653.c @@ -0,0 +1,98 @@ +#include "mma8653.h" +#include + +#define AXL_ADDR 0x1D +#define AXL_ERR_RET(e) if(1){err = e; goto err;} + +#define TRY(c) if(1){ret = c; if (ret) return c;} +static int mma8653_i2c_transfer(int write, uint8_t reg, uint8_t *data, uint8_t length) +{ + int err = 0; + + // WRITE TARGET REGISTER + // Send start + TWCR = (1<x = buffer[1]; + measurements->y = buffer[2]; + measurements->z = buffer[3]; + + return 0; +} diff --git a/firmware/lib/mma8653.h b/firmware/lib/mma8653.h new file mode 100644 index 0000000..eb3b352 --- /dev/null +++ b/firmware/lib/mma8653.h @@ -0,0 +1,13 @@ +#ifndef _MMA8653_H +#define _MMA8653_H + +#include "light_ws2812.h" + +struct axl_result { + int x, y, z; +}; + +int mma8653_init(); +int mma8653_get_measurements(struct axl_result *measurements); + +#endif diff --git a/firmware/lib/pm.c b/firmware/lib/pm.c new file mode 100644 index 0000000..42ccc77 --- /dev/null +++ b/firmware/lib/pm.c @@ -0,0 +1,122 @@ +#include "pm.h" + +#include +#include +#include +#include + +void pm_reset() +{ + DDRB = 0; + DDRC = 0; + DDRD = 0; + PORTB = 0; + PORTC = 0; + PORTD = 0; + + GICR = 0; + + // Reset to bootloader + WDTCR = (1 << WDE) | (1 << WDP2); + while(1); + + /* + // Reset to application + asm("ldi R16,0x00"); + asm("push R16"); + asm("push R16"); + asm("push R16"); + asm("push R16"); + asm("ret"); + while (1); + */ +} + +void pm_axl_on() +{ + HAL_ON(AXL_EN); +} + +void pm_axl_off() +{ + HAL_OFF(AXL_EN); +} + +void pm_init() +{ + cli(); + + HAL_INIT(VBUS_SENSE, INPUT, OFF); // USB connected indicator + HAL_INIT(VLED_EN_N, OUTPUT, OFF_N); // VLED DCDC converter enable + HAL_INIT(CHG_ACTIVE_N, INPUT, ON); // Battery charging status input + HAL_INIT(BAT_SENSE, INPUT, OFF); // No pull-up on Batt sense ADC input + HAL_INIT(BAT_SENSE_EN_N, INPUT, OFF); // Disable using High-Z + HAL_INIT(AXL_EN, OUTPUT, OFF); // Turn off AXL + HAL_INIT(AXL_I2C, INPUT, OFF); // No Pull-ups on I2C + + // Hardware bug after sleep. Need to manually turn off->on I2C transceiver. + TWCR &= ~((1 << TWSTO) | (1 << TWEN)); + + sei(); +} + +void pm_disable_periphery() +{ + cli(); + + // Turn off HW + TCCR1B = 0; + TIMSK = 0; + WDTCR = 0; + TWCR = 0; + SPCR = 0; + ADCSRA = 0; + + DDRB = 0; + DDRC = 0; + DDRD = 0; + PORTB = 0; + PORTC = 0; + PORTD = 0; +} + +void pm_suspend() +{ + pm_disable_periphery(); + + // Activate wake from button + HAL_INIT(BUTTON_N, INPUT, ON); // Pull-up for user button + MCUCR = (1 << SM1) | (1 << SE); + + sei(); + sleep_cpu(); + cli(); +} + +void pm_led_on_auto() +{ + if (pm_ac_connected()) + pm_led_off(); + else + pm_led_on(); +} + +void pm_led_on() +{ + HAL_ON_N(VLED_EN_N); +} + +void pm_led_off() +{ + HAL_OFF_N(VLED_EN_N); +} + +int pm_ac_connected() +{ + return HAL_ASSERTED(VBUS_SENSE); +} + +int pm_charging() +{ + return HAL_ASSERTED_N(CHG_ACTIVE_N); +} diff --git a/firmware/lib/pm.h b/firmware/lib/pm.h new file mode 100644 index 0000000..2148ec8 --- /dev/null +++ b/firmware/lib/pm.h @@ -0,0 +1,22 @@ +#ifndef _PM_H +#define _PM_H + +#include "hal.h" + +void pm_reset(); +void pm_init(); +void pm_suspend(); + +void pm_axl_on(); +void pm_axl_off(); + +void pm_led_on(); +void pm_led_off(); +void pm_led_on_auto(); + +int pm_ac_connected(); +int pm_charging(); + +void pm_disable_periphery(); + +#endif diff --git a/firmware/lib/system.c b/firmware/lib/system.c new file mode 100644 index 0000000..bda2719 --- /dev/null +++ b/firmware/lib/system.c @@ -0,0 +1,85 @@ +#include +#include +#include +#include + +#include "system.h" +#include "pm.h" + +extern void app_init(); +extern enum app_return app_mainloop(); + +struct cRGB led[STRIPLEN]; + +void display_update() { + ws2812_setleds(led, STRIPLEN); +} + +void display_clear() { + memset(led, 0, STRIPLEN * 3); +} + +void system_init() +{ + pm_disable_periphery(); + button_init(); + TIFR = 0xFF; // Clear all interrupt flags + pm_init(); + pm_axl_on(); + _delay_ms(50); + mma8653_init(); + while (HAL_ASSERTED_N(BUTTON_N)) // Prevent boot while button is pressed + _delay_ms(25); + timers_init(); +} + +enum BOOT_COLOR {RED, BLUE, GREEN}; +void boot_animation(enum BOOT_COLOR color_offset) +{ + _delay_ms(10); + display_clear(); + for(int i = 0; i +#include + +extern void systick_cb(); + +volatile uint16_t timer[TIMERS_COUNT]; + +void timers_init() { + // Configure Systick + TCCR1B = (1 << CS11) | (1 << CS10) | (1 << WGM12); // CLKio / 64, CTC + OCR1A = 250 * SYSTICK_MS; + TIMSK = (1 << OCIE1A); +} + +uint8_t block_for(uint8_t channel, uint16_t time_systicks) +{ + if (timer_expired(channel)) { + timer_set(channel, time_systicks); + return 1; + } + return 0; +} + +ISR (TIMER1_COMPA_vect) +{ + uint8_t i; + for (i = 0; i < TIMERS_COUNT; ++i) { + if (timer[i] != 0) { + timer[i]--; + } + } + + systick_cb(); +} diff --git a/firmware/lib/timers.h b/firmware/lib/timers.h new file mode 100644 index 0000000..182a9c1 --- /dev/null +++ b/firmware/lib/timers.h @@ -0,0 +1,21 @@ +#ifndef _TIMERS_H +#define _TIMERS_H + +#include + +#define SYSTICK_MS 1 // Systick interval in ms +enum TIMERS{TIMER_BUTTON, + TIMER_APP0, + TIMER_APP1, + TIMERS_COUNT}; + +extern volatile uint16_t timer[TIMERS_COUNT]; + +#define timer_ms(ms) (ms / SYSTICK_MS) +#define timer_set(channel, time_systicks) if (1) {timer[channel] = time_systicks;} +#define timer_expired(channel) (timer[channel] == 0) + +void timers_init(); +uint8_t block_for(uint8_t channel, uint16_t time_systicks); + +#endif // _TIMERS_H