diff options
author | Josh Rahm <joshuarahm@gmail.com> | 2024-11-30 23:57:56 -0700 |
---|---|---|
committer | Josh Rahm <joshuarahm@gmail.com> | 2024-11-30 23:57:56 -0700 |
commit | 708ce9d264b271ac5eb4a64fcb1e814cbe378ac2 (patch) | |
tree | 9e98e5a8e1988686398ec2d4252b4060d9877ca0 | |
parent | 7df284ea01ea0015770d626a1eb7ae7f62d48bb3 (diff) | |
download | ch573-708ce9d264b271ac5eb4a64fcb1e814cbe378ac2.tar.gz ch573-708ce9d264b271ac5eb4a64fcb1e814cbe378ac2.tar.bz2 ch573-708ce9d264b271ac5eb4a64fcb1e814cbe378ac2.zip |
Implement basic LEDs checkpoint.
-rw-r--r-- | fdl/ch573/spi.fdl | 18 | ||||
-rw-r--r-- | include/spi.h | 5 | ||||
-rw-r--r-- | include/ws2812b.h | 63 | ||||
-rw-r--r-- | src/main.c | 107 | ||||
-rw-r--r-- | src/spi.c | 16 | ||||
-rw-r--r-- | src/ws2812b.c | 115 |
6 files changed, 250 insertions, 74 deletions
diff --git a/fdl/ch573/spi.fdl b/fdl/ch573/spi.fdl index cec88f3..bdd1e24 100644 --- a/fdl/ch573/spi.fdl +++ b/fdl/ch573/spi.fdl @@ -144,29 +144,17 @@ package ch573.spi { assert_pos(0x14); /** SPI0 DMA Buffer Current Address */ - reg (16) : struct { - /** DMA buffer current address (lower 14 bits valid) */ - dma_now : (14); - reserved(2); - }; + reg dma_now(16); reserved(16); assert_pos(0x18); /** SPI0 DMA Buffer Start Address */ - reg (16) : struct { - /** DMA buffer start address (lower 14 bits valid) */ - dma_beg : (14); - reserved(2); - }; + reg dma_beg(16); reserved(16); assert_pos(0x1C); /** SPI0 DMA Buffer End Address */ - reg (16) : struct { - /** DMA buffer end address (lower 14 bits valid) */ - dma_end : (14); - reserved(2); - }; + reg dma_end(16); }; instance spi0 at spi_base : spi_t; diff --git a/include/spi.h b/include/spi.h index a3ed89b..7d33139 100644 --- a/include/spi.h +++ b/include/spi.h @@ -2,6 +2,11 @@ #include <stdlib.h> +#include "ch573/spi.h" + +#define SPI CH573_SPI__SPI_T_INTF +#define SPI0 ch573_spi__spi0 + void enable_spi(void); void run_spi(void); diff --git a/include/ws2812b.h b/include/ws2812b.h new file mode 100644 index 0000000..2845bc5 --- /dev/null +++ b/include/ws2812b.h @@ -0,0 +1,63 @@ +#pragma once + +#include <stdint.h> +#include <stdlib.h> + +#define WIRE_BYTES_PER_COLOR 9 +#define PADDING_BYTES 2 +#define TOTAL_BYTES_PER_LED (PADDING_BYTES + WIRE_BYTES_PER_COLOR) + +enum ws2812b_byte_order { + BYTE_ORDER_BGR = 0, + BYTE_ORDER_GBR = 1, +}; + +struct ws2812b_buf { + uint8_t* buf; /* Size of the buffer. */ + size_t total_alloc; /* total number of bytes allocated to the buffer. */ + size_t cur; /* Current output cursor in the buffer */ + + /* The byte order this driver should use. WS2811's use BGR, WS2812b's use GBR. + */ + enum ws2812b_byte_order byte_order; + + /* pointer to the dma_now register. needed to synchronize with the DMA. */ + volatile uint16_t* dma_now_reg; + + /* pointer to the dma_end register. */ + volatile uint16_t* dma_end_reg; +}; + +struct rgb_compiled { + union { + struct __attribute__((packed)) { + uint32_t first_bits; + uint32_t second_bits; + uint8_t last_bits; + }; + uint8_t buf[9]; + }; +}; + +typedef struct { + union { + struct __attribute__((packed)) { + uint8_t r; + uint8_t g; + uint8_t b; + }; + uint32_t color; + }; +} rgb_t; + +int write_rgb(struct ws2812b_buf* out, rgb_t color); + +void start_dma(struct ws2812b_buf* buf); + +void make_wsb2812b(struct ws2812b_buf* out, void* buf, size_t n_alloc); + +void compile_color( + rgb_t color, struct rgb_compiled* out, enum ws2812b_byte_order byte_order); + +// ch must have at least 73 characters free. +void compiled_color_dbg_str(struct rgb_compiled* c, char* out); @@ -1,15 +1,17 @@ #include <stdint.h> #include <stdio.h> -#include "string.h" #include "ch573/gpio.h" #include "ch573/pwr.h" #include "ch573/uart.h" #include "clock.h" #include "spi.h" +#include "string.h" #include "system.h" +#include "ws2812b.h" #define GPIO_PORT_A ch573_gpio__gpio_port_a +#define GPIO_PORT_B ch573_gpio__gpio_port_b #define GPIO_PORT CH573_GPIO__GPIO_PORT_T_INTF #define UART1 ch573_uart__uart1 @@ -121,6 +123,10 @@ static void fast_delay() } } +#define N_LEDS 100 + +extern int uart1_FILE_get(FILE* f); + /* * Main routine. This is called on_reset once everything else has been set up. */ @@ -130,63 +136,60 @@ int main(void) set_system_clock_60Mhz(); + printf("What is your name? "); + char buf[1024]; enable_spi(); - char buf[1024]; - memset(buf, 0xf0, sizeof(buf)); - for (int i = 0; i < 50; ++ i) { - buf[i] = 0xaa; - buf[sizeof(buf) - i] = 0xaa; - } + // char buf[1024]; + // memset(buf, 0xf0, sizeof(buf)); + // for (int i = 0; i < 50; ++ i) { + // buf[i] = 0xaa; + // buf[sizeof(buf) - i] = 0xaa; + // } + + size_t n = sizeof(buf); + struct ws2812b_buf ws_buf; + make_wsb2812b(&ws_buf, buf, n); + rgb_t color; + color.color = 0; + color.r = 0xff; + color.g = 0x00; + color.b = 0x00; + uint8_t time = 0xff; + + // for (size_t i = 0; i < N_LEDS; ++i) { + // char* loc = buf + i * TOTAL_BYTES_PER_LED; + // printf( + // "Bytes: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", + // loc[0], + // loc[1], + // loc[2], + // loc[3], + // loc[4], + // loc[5], + // loc[6], + // loc[7], + // loc[8], + // loc[9], + // loc[10]); + // } + GPIO_PORT.dir.set(GPIO_PORT_B, DIR_OUT, 7); while (1) { - printf("Xfer.\n"); - dma_transfer(buf, sizeof(buf)); + ws_buf.cur = 0; + for (int i = 0; i < N_LEDS; ++i) { + color.r = time; + color.g = 0; + color.b = 0xff - time; + write_rgb(&ws_buf, color); + } + time --; + GPIO_PORT.out.set(GPIO_PORT_B, ON, 7); + // printf("WS_BUF: %p, %zu\n", ws_buf.buf, ws_buf.cur); + start_dma(&ws_buf); wait_for_dma(); + basic_delay(); } - // run_spi(); - - // GPIO_PORT.dir.set(GPIO_PORT_A, DIR_OUT, 8); - // GPIO_PORT.pd_drv.set(GPIO_PORT_A, 0, 8); - // GPIO_PORT.out.set(GPIO_PORT_A, OFF, 8); - - // GPIO_PORT.dir.set(GPIO_PORT_A, DIR_OUT, 11); - // GPIO_PORT.pd_drv.set(GPIO_PORT_A, 0, 11); - - // GPIO_PORT.dir.set(GPIO_PORT_A, DIR_IN, 10); - // GPIO_PORT.pd_drv.set(GPIO_PORT_A, PD_DRV_OPEN_DRAIN, 11); - - // set_system_clock_6Mhz(); - // uint32_t reg = (*(uint32_t*)0x40001008); - // reg &= ~0x1f; - // reg |= 10; - // enter_safe_mode(); - // (*(uint32_t*)0x40001008) = reg; - - - // for (int i = 0;; ++i) { - // GPIO_PORT.out.set(GPIO_PORT_A, ON, 8); - // GPIO_PORT.out.set(GPIO_PORT_A, OFF, 8); - // } - - // clock_cfg_t cfg; - // for (int i = 0;; ++i) { - // if (i % 16 == 0) { - // if (cur_clock) { - // set_system_clock_60Mhz(); - // } else { - // set_system_clock_6Mhz(); - // } - // get_system_clock(&cfg); - // printf("Cur Clock Mhz: %d\n", get_clock_freq(&cfg)); - // cur_clock = !cur_clock; - // } - - // basic_delay(); - // GPIO_PORT.out.set(GPIO_PORT_A, ON, 8); - // basic_delay(); - // GPIO_PORT.out.set(GPIO_PORT_A, OFF, 8); - // } return 0; } @@ -7,9 +7,6 @@ #define MAX_SPI_FIFO 8 -#define SPI CH573_SPI__SPI_T_INTF -#define SPI0 ch573_spi__spi0 - #define GPIO_PORT_A ch573_gpio__gpio_port_a #define GPIO_PORT_B ch573_gpio__gpio_port_b #define GPIO_PORT CH573_GPIO__GPIO_PORT_T_INTF @@ -19,7 +16,7 @@ void enable_spi(void) { - GPIO_I.pin_alternate.pin_spi0.set(GPIO, OFF); + GPIO_I.pin_alternate.pin_spi0.set(GPIO, ON); GPIO_PORT.out.set(GPIO_PORT_A, ON, 12); GPIO_PORT.dir.set(GPIO_PORT_A, DIR_OUT, 12); @@ -34,6 +31,8 @@ void enable_spi(void) GPIO_PORT.out.set(GPIO_PORT_B, OFF, 14); GPIO_PORT.out.set(GPIO_PORT_B, ON, 12); + GPIO_PORT.pu.set(GPIO_PORT_B, ENABLED, 14); + GPIO_PORT.dir.set(GPIO_PORT_B, DIR_IN, 13); GPIO_PORT.dir.set(GPIO_PORT_B, DIR_IN, 15); GPIO_PORT.dir.set(GPIO_PORT_A, DIR_IN, 13); @@ -44,7 +43,7 @@ void enable_spi(void) // SPI.ctrl_mod.set(SPI0, 0xe0); // Set mosi and sck SPI.ctrl_mod.all_clear.set(SPI0, 0); - SPI.ctrl_mod.pin_enable.set(SPI0, 0x7); // Set mosi and sck + SPI.ctrl_mod.pin_enable.set(SPI0, 0x2); // Set mosi and sck SPI.ctrl_cfg.auto_if.set(SPI0, 1); SPI.ctrl_cfg.dma_enable.set(SPI0, OFF); } @@ -62,8 +61,11 @@ void enable_spi(void) void dma_transfer(void* output_buffer, size_t len) { // Clear everything. - SPI.ctrl_mod.all_clear.set(SPI0, 1); - SPI.ctrl_mod.all_clear.set(SPI0, 0); + // SPI.ctrl_mod.all_clear.set(SPI0, 1); + // SPI.ctrl_mod.all_clear.set(SPI0, 0); + // SPI.ctrl_cfg.dma_enable.set(SPI0, OFF); + + // printf("Start DMA: %p, %zu\n", output_buffer, len); SPI.ctrl_mod.fifo_dir.set(SPI0, FIFO_DIR_OUTPUT); SPI.dma_beg.set(SPI0, (uint32_t)output_buffer); diff --git a/src/ws2812b.c b/src/ws2812b.c new file mode 100644 index 0000000..8547b5d --- /dev/null +++ b/src/ws2812b.c @@ -0,0 +1,115 @@ +#include "ws2812b.h" + +#include <string.h> + +#include "spi.h" + +// either 0b110 when (n) is true, otherwise 0b100 +#define BIT_TO_SPI_DATA(v, n) ((BIT_N(v, n)) ? 6 : 4) +#define BIT_N(v, n) ((v) & (1 << (n))) + +#define BYTE_1(n) ((n) << 0) +#define BYTE_2(n) ((n) << 8) +#define BYTE_3(n) ((n) << 16) +#define BYTE_4(n) ((n) << 24) + +#define COLOR_BYTE_1(c) \ + (BIT_TO_SPI_DATA((c), 7) << 5 | BIT_TO_SPI_DATA((c), 6) << 2 | \ + BIT_TO_SPI_DATA((c), 5) >> 1) + +#define COLOR_BYTE_2(c) \ + ((BIT_TO_SPI_DATA((c), 5) & 1) << 7 | BIT_TO_SPI_DATA((c), 4) << 4 | \ + BIT_TO_SPI_DATA((c), 3) << 1 | BIT_TO_SPI_DATA((c), 2) >> 2) + +#define COLOR_BYTE_3(c) \ + ((BIT_TO_SPI_DATA((c), 2) & 3) << 6 | BIT_TO_SPI_DATA((c), 1) << 3 | \ + BIT_TO_SPI_DATA((c), 0) << 0) + +// Clips a pointer the bottom 14 bits for comparison with the DMA. +#define CLIP_TO_DMA_BITS(ptr) ((uint16_t)(((uint32_t)(ptr)) & 0x3fff)) + +inline static void complie_color_inline( + rgb_t color, struct rgb_compiled* out, enum ws2812b_byte_order byte_order) +{ + if (byte_order == BYTE_ORDER_GBR) { + uint8_t tmp = color.b; + color.b = color.g; + color.g = tmp; + } + + out->first_bits = + BYTE_1(COLOR_BYTE_1(color.b)) | BYTE_2(COLOR_BYTE_2(color.b)) | + BYTE_3(COLOR_BYTE_3(color.b)) | BYTE_4(COLOR_BYTE_1(color.g)); + + out->second_bits = + BYTE_1(COLOR_BYTE_2(color.g)) | BYTE_2(COLOR_BYTE_3(color.g)) | + BYTE_3(COLOR_BYTE_1(color.r)) | BYTE_4(COLOR_BYTE_2(color.r)); + + out->last_bits = COLOR_BYTE_3(color.r); +} + +void compile_color( + rgb_t color, struct rgb_compiled* out, enum ws2812b_byte_order byte_order) +{ + complie_color_inline(color, out, byte_order); +} + +int write_rgb(struct ws2812b_buf* out, rgb_t color) +{ + struct rgb_compiled comp; + complie_color_inline(color, &comp, out->byte_order); + + // Make sure there's enough memory. + if (out->total_alloc < out->cur + TOTAL_BYTES_PER_LED) { + return 1; + } + + // Wait for DMA if it's running. + volatile uint16_t* dma_now = out->dma_now_reg; + + // If the dma_now is not set, we won't wait for the DMA. + if (dma_now) { + uint16_t dma_loc = *dma_now; + uint16_t dma_end = *out->dma_end_reg; + while ((dma_loc - CLIP_TO_DMA_BITS(out->buf + out->cur) < + TOTAL_BYTES_PER_LED) && + (dma_loc < dma_end)) { + dma_loc = *dma_now; + } + } + + memcpy(out->buf + out->cur, comp.buf, WIRE_BYTES_PER_COLOR); + memset(out->buf + out->cur + WIRE_BYTES_PER_COLOR, 0, PADDING_BYTES); + + out->cur += TOTAL_BYTES_PER_LED; + + return 0; +} + +void start_dma(struct ws2812b_buf* buf) +{ + buf->dma_end_reg = &SPI0->dma_end; + buf->dma_now_reg = &SPI0->dma_now; + + dma_transfer(buf->buf, buf->cur); +} + +void make_wsb2812b(struct ws2812b_buf* out, void* buf, size_t n_alloc) +{ + memset(out, 0, sizeof(*out)); + out->buf = buf; + out->total_alloc = n_alloc; +} + +void compiled_color_dbg_str(struct rgb_compiled* c, char* out) +{ + uint8_t cur = 0; + for (int i = 0; i < 9; ++i) { + cur = c->buf[i]; + for (int j = 0; j < 8; ++j) { + *(out++) = (cur & 0x80) ? '1' : '0'; + cur <<= 1; + } + } + *(out++) = 0; +} |