From 135e74dca01bedee283c714b9bbdddc4f78079ed Mon Sep 17 00:00:00 2001 From: Markus Koch Date: Sun, 4 Feb 2024 19:59:12 +0100 Subject: [PATCH] wip: Leddie FW --- firmware/.gitignore | 3 + firmware/Makefile | 21 ++++ 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 | 121 ++++++++++++++++++++++ firmware/lib/pm.h | 17 ++++ firmware/main.c | 166 ++++++++++++++++++++++++++++++ 10 files changed, 781 insertions(+) create mode 100644 firmware/.gitignore create mode 100644 firmware/Makefile 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/main.c 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..c1bc8ca --- /dev/null +++ b/firmware/Makefile @@ -0,0 +1,21 @@ +MCU=atmega8 +CFLAGS=-g -Wall --param=min-pagesize=0 -mcall-prologues -mmcu=$(MCU) -Os -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 main.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/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..b017fb9 --- /dev/null +++ b/firmware/lib/pm.c @@ -0,0 +1,121 @@ +#include "pm.h" +#include "hal.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(); + + DDRB = 0; + DDRC = 0; + DDRD = 0; + PORTB = 0; + PORTC = 0; + PORTD = 0; + + 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 + HAL_INIT(BUTTON_N, INPUT, ON); // Pull-up for user button + + GICR = (1 << INT1); + + // Hardware bug after sleep. Need to manually turn off->on I2C transceiver. + TWCR &= ~((1 << TWSTO) | (1 << TWEN)); + + sei(); +} + +static void disable_periphery() +{ + cli(); + + WDTCR = 0; + TWCR = 0; + SPCR = 0; + ADCSRA = 0; + + DDRB = 0; + DDRC = 0; + DDRD = 0; + PORTB = 0; + PORTC = 0; + + HAL_INIT(BUTTON_N, INPUT, ON); // Pull-up for user button +} + +void pm_suspend() +{ + disable_periphery(); + + MCUCR = (1 << SM1) | (1 << SE); + + sei(); + sleep_cpu(); + cli(); +} + +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..462150e --- /dev/null +++ b/firmware/lib/pm.h @@ -0,0 +1,17 @@ +#ifndef _PM_H +#define _PM_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(); + +int pm_ac_connected(); +int pm_charging(); + +#endif diff --git a/firmware/main.c b/firmware/main.c new file mode 100644 index 0000000..7cb3ed9 --- /dev/null +++ b/firmware/main.c @@ -0,0 +1,166 @@ +#include +#include +#include +#include + +#include "lib/light_ws2812.h" +#include "lib/pm.h" +#include "lib/mma8653.h" + +#define STRIPLEN (6*9) +struct cRGB led[STRIPLEN]; +int brightness = 9; + +void update() { + ws2812_setleds(led, STRIPLEN); + if (!(PIND & (1 << 3))) + pm_reset(); +} + +int axl_test() +{ + int ret; + int c = 0; + struct axl_result measurements; + + int dir_x; + int dir_y; + int ledno; + int intens; + + pm_axl_on(); + _delay_ms(50); + + ret = mma8653_init(); + if (ret) { + led[8].b = 0x10; + update(); + while(1); + } + + while(1) { + if (pm_ac_connected()) { + led[10].g = 0; + led[10].b = 10; + pm_led_off(); + } else { + led[10].g = 10; + led[10].b = 0; + _delay_ms(25); + pm_led_on(); + } + if (pm_charging()) { + led[11].r = 10; + } else { + led[11].r = 0; + } + + c++; + + #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; + + /*if (ret) { + led[8].g = 0; + led[8].r = c & 1; + } else { + led[8].r = 0; + led[8].g = c & 1; + }*/ + update(); + //_delay_ms(50); + memset(led, 0, 9 * 3); + //update(); + _delay_ms(50); + } +} + +int main() +{ + pm_init(); + pm_led_on(); + + _delay_ms(1); + + memset(led, 0, STRIPLEN * 3); + for(int i = 0; i