aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJosh Rahm <joshuarahm@gmail.com>2024-11-30 23:57:56 -0700
committerJosh Rahm <joshuarahm@gmail.com>2024-11-30 23:57:56 -0700
commit708ce9d264b271ac5eb4a64fcb1e814cbe378ac2 (patch)
tree9e98e5a8e1988686398ec2d4252b4060d9877ca0
parent7df284ea01ea0015770d626a1eb7ae7f62d48bb3 (diff)
downloadch573-708ce9d264b271ac5eb4a64fcb1e814cbe378ac2.tar.gz
ch573-708ce9d264b271ac5eb4a64fcb1e814cbe378ac2.tar.bz2
ch573-708ce9d264b271ac5eb4a64fcb1e814cbe378ac2.zip
Implement basic LEDs checkpoint.
-rw-r--r--fdl/ch573/spi.fdl18
-rw-r--r--include/spi.h5
-rw-r--r--include/ws2812b.h63
-rw-r--r--src/main.c107
-rw-r--r--src/spi.c16
-rw-r--r--src/ws2812b.c115
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);
diff --git a/src/main.c b/src/main.c
index 5f883f9..199f04b 100644
--- a/src/main.c
+++ b/src/main.c
@@ -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;
}
diff --git a/src/spi.c b/src/spi.c
index 6d9fb2a..5c84b77 100644
--- a/src/spi.c
+++ b/src/spi.c
@@ -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;
+}