From 97445401bfcbf414defd10bd8eee62bf144ce6e2 Mon Sep 17 00:00:00 2001 From: Noah Metz Date: Wed, 15 Jul 2026 17:28:33 -0600 Subject: [PATCH] Fixed LED driver bugs, and added serial command file --- Makefile | 14 ++++----- main.c | 72 ++++++++++++++++++++++++++++++++++++++----- source/cmd.c | 65 +++++++++++++++++++++++++++++++++++++++ source/cmd.h | 14 +++++++++ source/led.c | 86 ++++++++++++++++++++++++++++++++++++++++++++-------- source/led.h | 11 ++++--- 6 files changed, 229 insertions(+), 33 deletions(-) create mode 100644 source/cmd.c create mode 100644 source/cmd.h diff --git a/Makefile b/Makefile index 1b9a2a2..e2d5432 100644 --- a/Makefile +++ b/Makefile @@ -83,7 +83,7 @@ endif # # Define project name here -PROJECT = USB_TEST +PROJECT = TWINKLE_STICK # Target settings. MCU = cortex-m0 @@ -109,12 +109,7 @@ include $(CHIBIOS)/os/rt/rt.mk include $(CHIBIOS)/os/common/ports/ARMv6-M/compilers/GCC/mk/port.mk # Auto-build files in ./source recursively. include $(CHIBIOS)/tools/mk/autobuild.mk -# Other files (optional). -include $(CHIBIOS)/os/test/test.mk -include $(CHIBIOS)/test/rt/rt_test.mk -include $(CHIBIOS)/test/oslib/oslib_test.mk include $(CHIBIOS)/os/hal/lib/streams/streams.mk -include $(CHIBIOS)/os/various/shell/shell.mk # Define linker script file here. LDSCRIPT= $(STARTUPLD)/STM32F072xB.ld @@ -122,7 +117,6 @@ LDSCRIPT= $(STARTUPLD)/STM32F072xB.ld # C sources that can be compiled in ARM or THUMB mode depending on the global # setting. CSRC = $(ALLCSRC) \ - $(TESTSRC) \ main.c # C++ sources that can be compiled in ARM or THUMB mode depending on the global @@ -136,7 +130,7 @@ ASMSRC = $(ALLASMSRC) ASMXSRC = $(ALLXASMSRC) # Inclusion directories. -INCDIR = $(CONFDIR) $(ALLINC) $(TESTINC) +INCDIR = $(CONFDIR) $(ALLINC) # Define C warning options here. CWARN = -Wall -Wextra -Wundef -Wstrict-prototypes @@ -192,6 +186,10 @@ flash: $(BUILDDIR)/$(PROJECT).bin openocd -f tools/flash.cfg rm -f tools/firmware.bin +framesize: $(BUILDDIR)/$(PROJECT).elf + @base=$$(arm-none-eabi-nm $< | awk '/__heap_base__/{print $$1}'); \ + end=$$(arm-none-eabi-nm $< | awk '/__heap_end__/{print $$1}'); \ + echo "Framebuffer: $$(( 16#$$end - 16#$$base )) bytes" # # Custom rules ############################################################################## diff --git a/main.c b/main.c index 85f03c8..9bd7886 100644 --- a/main.c +++ b/main.c @@ -1,5 +1,6 @@ #include #include +#include #include "ch.h" #include "hal.h" @@ -7,6 +8,7 @@ #include "usbcfg.h" #include "led.h" #include "log.h" +#include "cmd.h" /* * HID report sizes (data only, excluding the report ID byte prepended by the @@ -74,6 +76,54 @@ static void serial_write(const uint8_t *data, size_t len) { chnWrite((BaseChannel *)&SDU1, data, len); } +static void serial_writes(const char *data) { + chnWrite((BaseChannel *)&SDU1, (uint8_t *)data, strlen(data)); +} + +static void cmd_resize(int argc, char **argv) { + if (argc < 3) { + serial_writes("usage: resize \r\n"); + return; + } + uint8_t driver = (uint8_t)atoi(argv[1]); + uint8_t count = (uint8_t)atoi(argv[2]); + + if(driver >= NUM_DRIVERS) { + serial_writes("invalid driver: "); + serial_writes(argv[1]); + serial_writes("\r\n"); + return; + } + + led_resize(driver, count); + serial_writes("ok\r\n"); +} + +static void cmd_set(int argc, char **argv) { + if (argc < 3) { + serial_writes("usage: set \r\n"); + return; + } + + uint8_t driver = (uint8_t)atoi(argv[1]); + uint8_t color = (uint8_t)atoi(argv[2]); + + if(driver >= NUM_DRIVERS) { + serial_writes("invalid driver: "); + serial_writes(argv[1]); + serial_writes("\r\n"); + return; + } + + if(color >= C_PALETTE_SIZE) { + serial_writes("invalid color: "); + serial_writes(argv[2]); + serial_writes("\r\n"); + return; + } + memset(led_drivers[driver].framebuffer, color, led_drivers[driver].num_leds); +} + #define SERIAL_LINE_BUF 128 static THD_WORKING_AREA(waSerial, 512); @@ -101,8 +151,8 @@ static THD_FUNCTION(SerialThread, arg) { if (ch == '\r' || ch == '\n') { chnWrite((BaseChannel *)&SDU1, (uint8_t *)"\r\n", 2); if (len > 0) { - chnWrite((BaseChannel *)&SDU1, (uint8_t *)buf, (size_t)len); - chnWrite((BaseChannel *)&SDU1, (uint8_t *)"\r\n", 2); + buf[len] = '\0'; + cmd_dispatch(buf); len = 0; } chnWrite((BaseChannel *)&SDU1, (uint8_t *)"> ", 2); @@ -133,7 +183,7 @@ int main(void) { usbStart(&USBD1, &usbcfg); usbConnectBus(&USBD1); - chThdCreateStatic(waSerial, sizeof(waSerial), NORMALPRIO+1, SerialThread, NULL); + chThdCreateStatic(waSerial, sizeof(waSerial), NORMALPRIO-1, SerialThread, NULL); led_init(chThdGetSelfX()); @@ -142,14 +192,20 @@ int main(void) { } log_init(serial_write); + cmd_init(serial_write); + cmd_register("resize", cmd_resize); + cmd_register("set", cmd_set); chThdSleepMilliseconds(3000); - uint8_t color = C_DARK_PURPLE; - while(true) { - color = (color == C_DARK_PURPLE) ? C_DARK_CYAN : C_DARK_PURPLE; - memset(framebuffer, color, sizeof(framebuffer)); +#define ANIM_FRAMERATE 60 + systime_t prev_frame = chVTGetSystemTimeX(); + systime_t next_frame = chVTGetSystemTimeX(); + while (true) { + prev_frame = next_frame; + next_frame = chTimeAddX(prev_frame, TIME_US2I(1000000/ANIM_FRAMERATE)); + //update animations led_draw(event_listeners); - chThdSleepMilliseconds(500); + chThdSleepUntilWindowed(prev_frame, next_frame); } chThdSleepMilliseconds(TIME_INFINITE); } diff --git a/source/cmd.c b/source/cmd.c new file mode 100644 index 0000000..095dc31 --- /dev/null +++ b/source/cmd.c @@ -0,0 +1,65 @@ +#include "cmd.h" +#include + +#define CMD_TABLE_SIZE 4 + +typedef struct { + const char *name; + cmd_fn_t fn; +} cmd_entry_t; + +static cmd_entry_t table[CMD_TABLE_SIZE]; +static cmd_write_fn write_fn; + +static uint32_t djb2(const char *str) { + uint32_t hash = 5381; + unsigned char c; + while ((c = (unsigned char)*str++)) + hash = ((hash << 5) + hash) + c; + return hash; +} + +void cmd_init(cmd_write_fn fn) { + write_fn = fn; + memset(table, 0, sizeof(table)); +} + +void cmd_register(const char *name, cmd_fn_t fn) { + uint32_t idx = djb2(name) & (CMD_TABLE_SIZE - 1); + for (int i = 0; i < CMD_TABLE_SIZE; i++) { + uint32_t slot = (idx + (uint32_t)i) & (CMD_TABLE_SIZE - 1); + if (table[slot].name == NULL) { + table[slot].name = name; + table[slot].fn = fn; + return; + } + } + while (1) {} // table full +} + +void cmd_dispatch(char *line) { + char *argv[8]; + int argc = 0; + + char *tok = strtok(line, " \t"); + while (tok != NULL && argc < 8) { + argv[argc++] = tok; + tok = strtok(NULL, " \t"); + } + + if (argc == 0) return; + + uint32_t idx = djb2(argv[0]) & (CMD_TABLE_SIZE - 1); + for (int i = 0; i < CMD_TABLE_SIZE; i++) { + uint32_t slot = (idx + (uint32_t)i) & (CMD_TABLE_SIZE - 1); + if (table[slot].name == NULL) break; + if (strcmp(table[slot].name, argv[0]) == 0) { + table[slot].fn(argc, argv); + return; + } + } + + if (write_fn) { + write_fn((const uint8_t *)"unknown command\r\n", 17); + } +} diff --git a/source/cmd.h b/source/cmd.h new file mode 100644 index 0000000..65a19fe --- /dev/null +++ b/source/cmd.h @@ -0,0 +1,14 @@ +#ifndef CMD_H +#define CMD_H + +#include +#include + +typedef void (*cmd_fn_t)(int argc, char **argv); +typedef void (*cmd_write_fn)(const uint8_t *data, size_t len); + +void cmd_init(cmd_write_fn fn); +void cmd_register(const char *name, cmd_fn_t fn); +void cmd_dispatch(char *line); + +#endif // CMD_H diff --git a/source/led.c b/source/led.c index cfda108..e06dcc2 100644 --- a/source/led.c +++ b/source/led.c @@ -1,12 +1,14 @@ #include "led.h" -#include "log.h" #include "stm32_dma.h" #include +extern uint8_t __heap_base__[]; +extern uint8_t __heap_end__[]; + led_ctx_t led_drivers[NUM_DRIVERS]; ws2812_signal_t signals[C_PALETTE_SIZE] = {0}; dma_ctx_t transfer_dma[2] = {0}; -uint8_t framebuffer[DRIVER_FB_SIZE]; +uint8_t * const framebuffer = __heap_base__; event_listener_t event_listeners[NUM_DRIVERS]; led_t palette[C_PALETTE_SIZE] = { @@ -115,7 +117,7 @@ int setup_led_transfer(led_ctx_t * driver) { driver->done = 0; driver->tx_cnt = 0; driver->buf_cnt = 0; - driver->tx_led_cnt = LEDS_PER_DRIVER; + driver->tx_led_cnt = driver->num_leds; // Setup the first LEDS_PER_IRQ leds driver->dma_buf_ptr = &driver->dma_buf[0]; fill_dmabuf(driver); @@ -169,15 +171,22 @@ PWMConfig pwmcfg = { 0 }; +extern led_cfg_t led_cfgs[NUM_DRIVERS]; + void driver_init(led_cfg_t * cfg, thread_t * draw_thread) { - if(cfg->num > NUM_DRIVERS){ + if(cfg->num >= NUM_DRIVERS){ chSysHalt("Tried to initialize a driver outside the max driver range"); } led_ctx_t * driver = &led_drivers[cfg->num]; driver->num = cfg->num; - // Set the driver framebuffer to the location in the global framebuffer - driver->framebuffer = &framebuffer[cfg->num * LEDS_PER_DRIVER]; + driver->num_leds = cfg->num_leds; + + uint32_t offset = 0; + for (uint8_t i = 0; i < cfg->num; i++) { + offset += led_cfgs[i].num_leds; + } + driver->framebuffer = framebuffer + offset; driver->pwm_driver = cfg->pwm_driver; driver->tim_channel = cfg->tim_channel; driver->draw_thread = draw_thread; @@ -200,12 +209,12 @@ void driver_init(led_cfg_t * cfg, thread_t * draw_thread) { pwmEnableChannel(driver->pwm_driver, driver->tim_channel, 0); } -led_cfg_t led_cfgs[5] = { - {STM32_DMA_STREAM_ID(1, 5), &PWMD15, 0, GPIOA, 2, 0, 0}, - {STM32_DMA_STREAM_ID(1, 3), &PWMD16, 0, GPIOA, 6, 5, 1}, - {STM32_DMA_STREAM_ID(1, 1), &PWMD17, 0, GPIOA, 7, 5, 2}, - {STM32_DMA_STREAM_ID(1, 2), &PWMD1, 0, GPIOA, 8, 2, 3}, - {STM32_DMA_STREAM_ID(1, 4), &PWMD3, 0, GPIOB, 4, 1, 4}, +led_cfg_t led_cfgs[NUM_DRIVERS] = { + {STM32_DMA_STREAM_ID(1, 4), &PWMD3, 0, GPIOB, 4, 1, 0, 10}, + {STM32_DMA_STREAM_ID(1, 5), &PWMD15, 0, GPIOA, 2, 0, 1, 10}, + {STM32_DMA_STREAM_ID(1, 3), &PWMD16, 0, GPIOA, 6, 5, 2, 10}, + {STM32_DMA_STREAM_ID(1, 1), &PWMD17, 0, GPIOA, 7, 5, 3, 10}, + {STM32_DMA_STREAM_ID(1, 2), &PWMD1, 0, GPIOA, 8, 2, 4, 10}, }; ws2812_signal_t convert_palette(led_t * colour) { @@ -229,6 +238,14 @@ ws2812_signal_t convert_palette(led_t * colour) { } void led_init(thread_t * draw_thread) { + size_t total_leds = 0; + for (size_t i = 0; i < NUM_DRIVERS; i++) { + total_leds += led_cfgs[i].num_leds; + } + if (total_leds > (size_t)((uint8_t *)__heap_end__ - (uint8_t *)__heap_base__)) { + chSysHalt(NULL); + } + for(size_t i = 0; i < C_PALETTE_SIZE; i++) { signals[i] = convert_palette(&palette[i]); } @@ -246,6 +263,8 @@ void led_init(thread_t * draw_thread) { if(transfer_dma[0].stream == NULL || transfer_dma[1].stream == NULL) { chSysHalt("Failed to allocate DMA streams for led transfers"); } + + memset(framebuffer, 0x00, total_leds); } void led_draw(event_listener_t* event_listeners) { @@ -278,3 +297,46 @@ void led_draw(event_listener_t* event_listeners) { } } } + +size_t led_fb_used(void) { + size_t total = 0; + for (uint8_t i = 0; i < NUM_DRIVERS; i++) { + total += led_drivers[i].num_leds; + } + return total; +} + +void led_resize(uint8_t driver_num, uint8_t new_num_leds) { + if (driver_num >= NUM_DRIVERS) return; + + size_t new_total = 0; + size_t old_total = led_drivers[driver_num].num_leds; + for (uint8_t i = 0; i < NUM_DRIVERS; i++) { + new_total += (i == driver_num) ? new_num_leds : led_drivers[i].num_leds; + } + if (new_total > (size_t)((uint8_t *)__heap_end__ - (uint8_t *)__heap_base__)) return; + + uint32_t new_offsets[NUM_DRIVERS]; + uint32_t off = 0; + for (uint8_t i = 0; i < NUM_DRIVERS; i++) { + new_offsets[i] = off; + off += (i == driver_num) ? new_num_leds : led_drivers[i].num_leds; + } + + if (new_num_leds >= led_drivers[driver_num].num_leds) { + for (int i = (int)NUM_DRIVERS - 1; i > (int)driver_num; i--) { + memmove(framebuffer + new_offsets[i], led_drivers[i].framebuffer, led_drivers[i].num_leds); + } + } else { + for (uint8_t i = driver_num + 1; i < NUM_DRIVERS; i++) { + memmove(framebuffer + new_offsets[i], led_drivers[i].framebuffer, led_drivers[i].num_leds); + } + } + + led_drivers[driver_num].num_leds = new_num_leds; + for (uint8_t i = driver_num; i < NUM_DRIVERS; i++) { + led_drivers[i].framebuffer = framebuffer + new_offsets[i]; + } + + memset(led_drivers[driver_num].framebuffer + old_total, 0, new_total - old_total); +} diff --git a/source/led.h b/source/led.h index acc8a6f..653fbd9 100644 --- a/source/led.h +++ b/source/led.h @@ -17,7 +17,7 @@ #define SIG_HIGH (((PWM_PERIOD * 3) / 4) - 1) #define SIG_RESET (PWM_PERIOD - 1) -#define LEDS_PER_IRQ (30) +#define LEDS_PER_IRQ (8) #define BYTES_PER_LED 3 #define BITS_PER_LED (8*BYTES_PER_LED) #define DMA_BUF_HALF (LEDS_PER_IRQ * BITS_PER_LED) @@ -32,9 +32,6 @@ #define LED_EVENT_BUFRDY LED_EVENT(1) #define NUM_DRIVERS 5 -#define LEDS_PER_DRIVER 14 - -#define DRIVER_FB_SIZE (LEDS_PER_DRIVER*NUM_DRIVERS) typedef struct { uint8_t g; @@ -87,6 +84,7 @@ typedef struct led_ctx { stm32_gpio_t * port; uint8_t pin; uint8_t * framebuffer; + uint8_t num_leds; uint8_t num; thread_t * draw_thread; } led_ctx_t; @@ -99,6 +97,7 @@ typedef struct led_cfg { uint8_t pin; uint8_t af; uint8_t num; + uint8_t num_leds; } led_cfg_t; typedef struct dma_ctx { @@ -108,7 +107,7 @@ typedef struct dma_ctx { } dma_ctx_t; extern led_ctx_t led_drivers[NUM_DRIVERS]; -extern uint8_t framebuffer[NUM_DRIVERS*LEDS_PER_DRIVER]; +extern uint8_t * const framebuffer; extern led_t palette[C_PALETTE_SIZE]; extern event_listener_t event_listeners[NUM_DRIVERS]; @@ -118,5 +117,7 @@ int setup_led_transfer(led_ctx_t * driver); void led_init(thread_t * draw_thread); void led_draw(event_listener_t* event_listeners); void fill_dmabuf(led_ctx_t * driver); +void led_resize(uint8_t driver_num, uint8_t new_num_leds); +size_t led_fb_used(void); #endif // LED_H