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.
This commit is contained in:
true 2024-10-24 19:36:11 -07:00
parent 261f5bdfbf
commit 64ce976b97
6 changed files with 82 additions and 47 deletions

View File

@ -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()

View File

@ -19,12 +19,12 @@
#include <stdint.h>
#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;

View File

@ -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);

View File

@ -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

View File

@ -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();
}
}

View File

@ -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];