From 64ce976b97af76024fe18b8321e3f9f39e77d667 Mon Sep 17 00:00:00 2001 From: true Date: Thu, 24 Oct 2024 19:36:11 -0700 Subject: [PATCH] i2c master seems to be working OLED is now rendering "Supercon" text. Moved several functions to RAM to speed up operation. There is a significant speed hit when executing from Flash on this MCU. --- nametag8_CH592/user/comm/i2c.h | 4 +- nametag8_CH592/user/comm/soft_i2c_master.c | 49 +++++++++++--------- nametag8_CH592/user/comm/soft_i2c_master.h | 4 +- nametag8_CH592/user/hw/ssd1306.h | 2 +- nametag8_CH592/user/main.c | 53 +++++++++++++--------- nametag8_CH592/user/render/draw_ssd1306.c | 17 +++++++ 6 files changed, 82 insertions(+), 47 deletions(-) diff --git a/nametag8_CH592/user/comm/i2c.h b/nametag8_CH592/user/comm/i2c.h index 4476725..54269aa 100644 --- a/nametag8_CH592/user/comm/i2c.h +++ b/nametag8_CH592/user/comm/i2c.h @@ -18,7 +18,9 @@ #include "soft_i2c_master.h" - +/* known bugs / limitations: + * - there is no ack on address. code always assumes slave is responding. + */ #define i2c_init() i2cm_init() #define i2c_start() SetSysClock(SYSCLK_FREQ_USEI2C); i2cm_start() #define i2c_restart() i2cm_restart() diff --git a/nametag8_CH592/user/comm/soft_i2c_master.c b/nametag8_CH592/user/comm/soft_i2c_master.c index 329d405..3476518 100644 --- a/nametag8_CH592/user/comm/soft_i2c_master.c +++ b/nametag8_CH592/user/comm/soft_i2c_master.c @@ -19,12 +19,12 @@ #include -#define CYCLES_TO_HI 16 -#define CYCLES_TO_LO 16 +#define CYCLES_TO_HI 5 // these settings will result in ~500KHz (32MHz HCLK) +#define CYCLES_TO_LO 2 -#define CYCLES_RD 2 // cycles spent in read routine -#define CYCLES_EXTRA_WR_HI 2 // extra cycles spent in write routine -#define CYCLES_EXTRA_WR_LO 4 +#define CYCLES_RD 2 // cycles spent in read routine +#define CYCLES_WR_HI 2 // extra cycles spent in write routine +#define CYCLES_WR_LO 4 #define SDA_PIN 4 @@ -62,18 +62,18 @@ static volatile uint16_t spin; #define bit_delay_hi() spin = delay_hi; while(spin--) #define bit_delay_lo() spin = delay_lo; while(spin--) -#define rd_delay() spin = delay_hi - CYCLES_RD; while(spin--) +#define rd_delay() bit_delay_hi() -#define wr_delay_hi() spin = delay_hi - CYCLES_EXTRA_WR_HI; while(spin--) -#define wr_delay_lo() spin = delay_lo - CYCLES_EXTRA_WR_LO; while(spin--) +#define wr_delay_hi() bit_delay_hi() +#define wr_delay_lo() bit_delay_hi() // spin = delay_hi - CYCLES_EXTRA_WR_HI; while(spin--) // re-run init any time the clock speed changes to recalculate delays. void i2cm_init() { - uint32_t sysclk; - uint32_t cycles; + //uint32_t sysclk; + //uint32_t cycles; // configure GPIO SCL_IN_HI(); @@ -83,13 +83,14 @@ void i2cm_init() SDA_SET_LO(); // configure timer - sysclk = GetSysClock(); - cycles = sysclk / 500000; + //sysclk = GetSysClock(); + //cycles = sysclk / 500000; - delay_hi = (cycles - CYCLES_TO_HI - 2) / 4; - delay_lo = (cycles - CYCLES_TO_LO - 2) / 4; + delay_hi = CYCLES_TO_HI; + delay_lo = CYCLES_TO_LO; } +__attribute__((section(".ramfunc"))) void i2cm_start() { SDA_IN_HI(); bit_delay_hi(); @@ -98,6 +99,7 @@ void i2cm_start() SCL_OUTLO(); bit_delay_lo(); } +__attribute__((section(".ramfunc"))) void i2cm_restart() { SDA_IN_HI(); bit_delay_hi(); @@ -105,6 +107,7 @@ void i2cm_restart() i2cm_start(); } +__attribute__((section(".ramfunc"))) void i2cm_stop() { SDA_OUTLO(); bit_delay_lo(); @@ -118,11 +121,13 @@ void i2cm_stop() } // returns: data byte +__attribute__((section(".ramfunc"))) uint8_t i2cm_rd(uint8_t ack) { uint8_t x, in = 0; SDA_IN_HI(); + for (x = 8; x; x--) { in <<= 1; // clock next bit @@ -142,29 +147,30 @@ uint8_t i2cm_rd(uint8_t ack) SCL_IN_HI(); bit_delay_hi(); SDA_IN_HI(); - SCL_OUTLO(); + SCL_OUTLO(); bit_delay_lo(); return in; } // returns: possible ack from target +__attribute__((section(".ramfunc"))) uint8_t i2cm_wr(uint8_t dat) { uint8_t x; uint8_t ack; - SCL_IN_HI(); - while (!SCL_GET()); // clock stretch + SCL_OUTLO(); for (x = 8; x; x--) { if (dat & 0x80) { SDA_IN_HI(); } else { SDA_OUTLO(); } SCL_IN_HI(); wr_delay_hi(); + while (!SCL_GET()); // clock stretch dat <<= 1; - SCL_OUTLO(); + SCL_OUTLO(); bit_delay_lo(); } SDA_IN_HI(); @@ -172,11 +178,12 @@ uint8_t i2cm_wr(uint8_t dat) ack = SDA_GET(); - SCL_OUTLO(); + SCL_OUTLO(); bit_delay_lo(); return ack; } // use a left-aligned address with this implementation. +__attribute__((section(".ramfunc"))) uint8_t i2cm_addr(uint8_t addr, uint8_t reading_bit) { addr &= ~0x1; @@ -185,13 +192,13 @@ uint8_t i2cm_addr(uint8_t addr, uint8_t reading_bit) } -void i2cm_rdbuf(uint8_t *dat, uint8_t len) +void i2cm_rdbuf(uint8_t *dat, uint16_t len) { while (len--) *dat++ = i2cm_rd(len > 0); // i2cm_stop(); } -void i2cm_wrbuf(const uint8_t *dat, uint8_t len) +void i2cm_wrbuf(const uint8_t *dat, uint16_t len) { uint8_t nack; diff --git a/nametag8_CH592/user/comm/soft_i2c_master.h b/nametag8_CH592/user/comm/soft_i2c_master.h index 80da7b6..0f67499 100644 --- a/nametag8_CH592/user/comm/soft_i2c_master.h +++ b/nametag8_CH592/user/comm/soft_i2c_master.h @@ -20,8 +20,8 @@ uint8_t i2cm_rd(uint8_t ack); uint8_t i2cm_wr(uint8_t dat); uint8_t i2cm_addr(uint8_t addr, uint8_t reading_bit); -void i2cm_rdbuf(uint8_t *dat, uint8_t len); -void i2cm_wrbuf(const uint8_t *dat, uint8_t len); +void i2cm_rdbuf(uint8_t *dat, uint16_t len); +void i2cm_wrbuf(const uint8_t *dat, uint16_t len); diff --git a/nametag8_CH592/user/hw/ssd1306.h b/nametag8_CH592/user/hw/ssd1306.h index 89e2330..9600c45 100644 --- a/nametag8_CH592/user/hw/ssd1306.h +++ b/nametag8_CH592/user/hw/ssd1306.h @@ -82,7 +82,7 @@ extern uint8_t oled_fb[((SSD1306_HEIGHT >> 3) * SSD1306_WIDTH) + 1]; -#define ssd1306_write(dat, len, cb) i2c_addr(SSD1306_I2C_ADDR, 1); i2c_wrbuf(dat, len); if (cb) ssd1306_cb(0) +#define ssd1306_write(dat, len, cb) i2c_addr((SSD1306_I2C_ADDR << 1), 0); i2c_wrbuf(dat, len); if (cb) ssd1306_cb(0) #define ssd1306_idle() 0 diff --git a/nametag8_CH592/user/main.c b/nametag8_CH592/user/main.c index df91198..f5cfbb6 100644 --- a/nametag8_CH592/user/main.c +++ b/nametag8_CH592/user/main.c @@ -43,7 +43,7 @@ // global settings -#define OLED_UPDATE_RATE 64 // framerate of OLED; (256*0.75) / OLED_UPDATE_RATE +#define OLED_UPDATE_RATE 8 // framerate of OLED; (256*(6/8)) / OLED_UPDATE_RATE; 8 = 24FPS // flags #define FLAG_OLED_UPDATE (1 << 0) @@ -87,6 +87,7 @@ void ch59x_xtal_conf() * trigger an interrupt when bits match a mask. at this time we're not using the RTC * for anything else so putting RTC should suffice for our main tick interrupt. */ +__HIGH_CODE void rtc_reset_trig() { sys_safe_access_enable(); @@ -113,6 +114,7 @@ void rtcisr_init() PFIC_EnableIRQ(RTC_IRQn); } +__HIGH_CODE void oled_update_done() { int16_t a; @@ -228,9 +230,10 @@ int main() // configure port-based interrupts (used for ch32sub interrupt) port_intr_init(); - // start up the OLED + // start up the OLED and menu system ssd1306fb_set_target(&oled); ssd1306_init(1); // we'll try to init later too, since sometimes they fail + menu_stop(0); // lol. yes, this "starts" the "menu" in nametag mode // configure main program tick interrupt rtcisr_init(); @@ -289,31 +292,37 @@ void RTC_IRQHandler(void) uptime_sec = (uint8_t)((uptime ) % 60); } - // render and update RGBLED at 64Hz - if ((st_tick & 0x3) == 0x3) { - // make sure a valid program is selected - if (uconf.ledprog_rgb_idx > (sizeof(rgb_pgm) / sizeof(rgb_pgm[0]))) { - uconf.ledprog_rgb_idx = 0; + // operations + switch (st_tick & 0x7) { + case 0: + case 3: { + // make sure a valid program is selected + if (uconf.ledprog_rgb_idx > (sizeof(rgb_pgm) / sizeof(rgb_pgm[0]))) { + uconf.ledprog_rgb_idx = 0; + } + + // send any rendered data now + rgbled_send(); + + // defer rendering + flags |= FLAG_RGBLED_RUN_PROG; + break; } - - // send any rendered data now - rgbled_send(); - - // defer rendering - flags |= FLAG_RGBLED_RUN_PROG; - } - - // render and update the oled during non-rgbled frames - else { - oled_tick++; - if (oled_tick >= OLED_UPDATE_RATE) { - oled_tick = 0; - flags |= FLAG_OLED_UPDATE; + case 1: { + accel_poll(); + // no break + } + default: { + oled_tick++; + if (oled_tick >= OLED_UPDATE_RATE) { + oled_tick = 0; + flags |= FLAG_OLED_UPDATE; + } } } // read accelerometer data at 32Hz if ((st_tick & 0x7) == 0x7) { - accel_poll(); + } } diff --git a/nametag8_CH592/user/render/draw_ssd1306.c b/nametag8_CH592/user/render/draw_ssd1306.c index afab911..a9cb899 100644 --- a/nametag8_CH592/user/render/draw_ssd1306.c +++ b/nametag8_CH592/user/render/draw_ssd1306.c @@ -4,6 +4,11 @@ * * mostly implemented by true * some functions shamelessly copied and modified from interwebs + * + * todo: + * - new MCU is fast enough to convert entire framebuffer quickly. + * it may save time and power to render a standard pixel framebuffer, + * then convert. would also make code much cleaner. need to test. */ #include "draw_ssd1306.h" @@ -47,6 +52,7 @@ void ssd1306fb_set_color(uint8_t color) * there are assumptions about pages as well; Y pages will always * start on a boundary. */ +__HIGH_CODE void ssd1306fb_rotate(SSD1306 *src, SSD1306 *dst, int8_t rot) { int8_t x, y, nx, ny; @@ -100,6 +106,7 @@ void ssd1306fb_rotate(SSD1306 *src, SSD1306 *dst, int8_t rot) * draws from data stored in horizontal priority page byte order * (useful for copying between buffers) */ +__HIGH_CODE void ssd1306fb_copy(int8_t x, int8_t y, uint8_t width, uint8_t height, const uint8_t *dat) { uint16_t i; @@ -189,6 +196,7 @@ void ssd1306fb_copy(int8_t x, int8_t y, uint8_t width, uint8_t height, const uin /* * draws from data stored in vertical priority page byte order (why? why not just horizontal? ...) */ +__HIGH_CODE void ssd1306fb_draw_raw(int8_t x, int8_t y, uint8_t width, uint8_t height, const uint8_t *dat, uint16_t dat_offset, uint16_t bytes) { uint16_t i; @@ -280,6 +288,7 @@ void ssd1306fb_draw_raw(int8_t x, int8_t y, uint8_t width, uint8_t height, const } } +__HIGH_CODE void ssd1306fb_draw_pix(uint8_t x, uint8_t y) { uint8_t *fb; @@ -302,6 +311,7 @@ void ssd1306fb_draw_pix(uint8_t x, uint8_t y) } } +__HIGH_CODE void ssd1306fb_draw_hline(uint8_t x1, uint8_t x2, uint8_t y) { uint8_t t; @@ -332,6 +342,7 @@ void ssd1306fb_draw_hline(uint8_t x1, uint8_t x2, uint8_t y) } } +__HIGH_CODE void ssd1306fb_draw_vline(uint8_t x, uint8_t y1, uint8_t y2) { uint8_t t; @@ -407,6 +418,7 @@ void ssd1306fb_draw_vline(uint8_t x, uint8_t y1, uint8_t y2) } } +__HIGH_CODE void ssd1306fb_draw_line(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2) { int8_t t; @@ -453,6 +465,7 @@ void ssd1306fb_draw_line(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2) } } +__HIGH_CODE void ssd1306fb_draw_rect(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2) { ssd1306fb_draw_vline(x1, y1 + 1, y2 - 1); @@ -463,6 +476,7 @@ void ssd1306fb_draw_rect(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2) // note: rect fill not working right now, lol +__HIGH_CODE void ssd1306fb_draw_rect_fill(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2) { uint8_t t; @@ -476,6 +490,7 @@ void ssd1306fb_draw_rect_fill(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2) } while (y1++ < y2); } +__HIGH_CODE void ssd1306fb_draw_circle(int8_t x, int8_t y, uint8_t radius) { int8_t xs = 0; @@ -508,6 +523,7 @@ void ssd1306fb_draw_circle(int8_t x, int8_t y, uint8_t radius) /* * character string drawing functions draw at the cursor position. */ +__HIGH_CODE uint8_t ssd1306fb_get_str_width(const uint8_t *font, const char *str, uint8_t len, int8_t extra_spacing) { uint8_t first = font[FONT_FIRST_CHAR_POS]; @@ -537,6 +553,7 @@ uint8_t ssd1306fb_get_font_height(const uint8_t *font) return font[FONT_HEIGHT_POS]; } +__HIGH_CODE void ssd1306fb_internal_str(const uint8_t *font, const char *str, uint8_t len, int8_t spacing) { const uint8_t height = font[FONT_HEIGHT_POS];