diff --git a/nametag8_CH592/user/comm/soft_i2c_slave.c b/nametag8_CH592/user/comm/soft_i2c_slave.c index d063670..039262a 100644 --- a/nametag8_CH592/user/comm/soft_i2c_slave.c +++ b/nametag8_CH592/user/comm/soft_i2c_slave.c @@ -16,8 +16,8 @@ -#define I2CSS_ERR_TIMEOUT 3 // timeout in bit-times before i2c slave releases control -#define I2CSS_IDLE_TIMEOUT 4 // idle timeout required in bit-times before i2c slave can be addressed again +#define I2CSS_ERR_TIMEOUT 10 // timeout in bit-times before i2c slave releases control +#define I2CSS_IDLE_TIMEOUT 3 // idle timeout required in bit-times before i2c slave can be addressed again // todo: use with struct instead of hard-coding to allow for multiple slaves, but make it fast too @@ -84,8 +84,8 @@ static inline int8_t i2css_ack(uint16_t bit_time) SDA_OUTLO(); - if (i2css_wait_for_scl_hi()) return I2CSS_ACK_ERROR; - ret = i2css_wait_for_scl_lo(); + if (i2css_wait_for_scl_hi()) ret = I2CSS_ACK_ERROR; + else ret = i2css_wait_for_scl_lo(); SDA_IN_HI(); @@ -308,8 +308,19 @@ int8_t i2css_blocking(struct I2CSoftSlave *slave) __HIGH_CODE int8_t i2css_start(struct I2CSoftSlave *slave) { + int8_t ret; uint8_t scl; + if (idle_timeout) { + // we're still waiting for bus idle time before we reply to requests + // this will now reset the timeout + if (idle_timeout < I2CSS_IDLE_TIMEOUT) { + idle_timeout = I2CSS_IDLE_TIMEOUT; + } + + return I2CSS_IN_TIMEOUT; + } + // idle our clock and check for start/stop SCL_IN_HI(); scl = SCL_GET(); @@ -317,14 +328,25 @@ int8_t i2css_start(struct I2CSoftSlave *slave) // module will use this bit time for this communication bit_time = slave->bit_time; - // ensure SDA pin will go low when set as an output + // if SCL is high, this may be a start condition. + if (scl) { + ret = i2css_blocking(slave); + } else ret = I2CSS_BUS_BUSY; + + R16_PA_INT_IF = 1 << SDA_PIN; + + return ret; +} + +void i2css_init() +{ + // configure GPIO + SCL_IN_HI(); + SCL_SET_LO(); + + SDA_IN_HI(); SDA_SET_LO(); - // if SCL is high, this should be a start condition. - if (scl) { - return i2css_blocking(slave); - } - - // if it isn't, there must be some other communication going on, and it doesn't involve us - return I2CSS_BUS_BUSY; + // configure interrupt + GPIOA_ITModeCfg(1 << SDA_PIN, GPIO_ITMode_FallEdge); } diff --git a/nametag8_CH592/user/comm/soft_i2c_slave.h b/nametag8_CH592/user/comm/soft_i2c_slave.h index 26207bc..dea1800 100644 --- a/nametag8_CH592/user/comm/soft_i2c_slave.h +++ b/nametag8_CH592/user/comm/soft_i2c_slave.h @@ -17,9 +17,10 @@ typedef struct I2CSoftSlave { uint8_t addr; uint8_t addr_last_seen; uint16_t bit_time; - void (*start_cb)(); // call on any new read/write operation + void (*start_cb)(); // call on any new read/write operation uint8_t (*rd_cb)(); // returns data int8_t (*wr_cb)(uint8_t data); // return 0 to ACK, all other NACK + void (*err_cb)(uint8_t); // called if there was an error during transfer } I2CSoftSlave; @@ -29,6 +30,7 @@ enum I2CSoftSlave_Error { I2CSS_STOP, I2CSS_NACK, I2CSS_ADDR_NO_MATCH, + I2CSS_IN_TIMEOUT, // errors I2CSS_START_SCL_NOT_LOW = -1, I2CSS_ADDR_READ_ERROR = -2, // could be timeout, start or stop condition detected diff --git a/nametag8_CH592/user/comm/soft_i2c_slave_handler.c b/nametag8_CH592/user/comm/soft_i2c_slave_handler.c deleted file mode 100644 index 9bcaa50..0000000 --- a/nametag8_CH592/user/comm/soft_i2c_slave_handler.c +++ /dev/null @@ -1,22 +0,0 @@ -/* - * i2c_slave_handler.c - * - * Created on: Oct 30, 2024 - * Author: true - */ - -#include "soft_i2c_slave_handler.h" -#include "soft_i2c_slave.h" - -#include - - - -struct I2CSoftSlave addon_i2c; - - - -void i2css_addon_init() -{ - -} diff --git a/nametag8_CH592/user/comm/soft_i2c_slave_handler.h b/nametag8_CH592/user/comm/soft_i2c_slave_handler.h deleted file mode 100644 index 68389e8..0000000 --- a/nametag8_CH592/user/comm/soft_i2c_slave_handler.h +++ /dev/null @@ -1,17 +0,0 @@ -/* - * soft_i2c_slave_handler.h - * - * Created on: Oct 30, 2024 - * Author: true - */ - -#ifndef USER_COMM_SOFT_I2C_SLAVE_HANDLER_H_ -#define USER_COMM_SOFT_I2C_SLAVE_HANDLER_H_ - - - -extern struct I2CSoftSlave addon_i2c; - - - -#endif /* USER_COMM_SOFT_I2C_SLAVE_HANDLER_H_ */ diff --git a/nametag8_CH592/user/hw/gat/gat_i2c.c b/nametag8_CH592/user/hw/gat/gat_i2c.c index c1aaaf0..6dad038 100644 --- a/nametag8_CH592/user/hw/gat/gat_i2c.c +++ b/nametag8_CH592/user/hw/gat/gat_i2c.c @@ -1,15 +1,38 @@ /* * gat_i2c.c * - * Created on: Oct 11, 2024 - * Author: true - * - * implements a shoddy i2c slave. + * implements a shoddy i2c slave for the addon header. * maximum speed 100KHz tested. * note: not yet tested. + * + * data is segmented by page. read always starts from byte 0 of selected page. + * + * write begins with 1 byte page index, followed by offset / index, followed by data. + * + * + * pages: + * + * - general: user input emulation and config functions. + * data size is 2 bytes. + * - 0: button input push mask. 0b0000_TL_TR_BR_BL. + * - 1: button release mask. + * - 2: nametag i2c clear timeout in frames. reverts to programmed name at 0. 0xff to disable. + * not yet implemented. + * + * - nametag: live update the nae shown in nametag mode. + * data size 16 bytes. byte 0 is length, bytes 1-15 are name data. + * this does not change the nametag in settings / nvram, only the live update. + * any update to this page will set general[1] to 0x20 if general[1] is not set to 0xff. + * if length == 0, normal name will be shown. for empty screen, send a space character with length = 1. + * example data: + * 0x84 (addr), 0x01 (nametag), 0x00 (start at first byte), 0x06 (name is 6 chars long), "Hello!" + * + * - raw: send raw data to the oled. send in 128 byte chunks. + * not yet implemented. */ #include +#include "global.h" #include "gat_gpio.h" @@ -19,104 +42,201 @@ -#define GAT_ADDR_BASE 0x30 -#define GAT_I2C_SPEED 100000 +#define GAT_ADDR_BASE 0x30 +#define GAT_I2C_SPEED 100000 -#define GAT_DATA_SIZE 256 +#define GAT_DATA_SIZE 128 + +#define GAT_PAGE_GENERAL 0 +#define GAT_PAGE_NAMETAG_TEXT 1 +#define GAT_PAGE_RAW1 2 +#define GAT_PAGE_RAW2 3 +#define GAT_PAGE_RAW3 4 +#define GAT_PAGE_RAW4 5 + +#define GAT_PAGESIZE_GENERAL 3 +#define GAT_PAGESIZE_NAMETAG 16 -struct I2CSoftSlave gat_slave; +struct I2CSoftSlave addon_i2c; enum { - GAT_MODE_STARTING, - GAT_MODE_READ, - GAT_MODE_WRITE + I2C_STATE_STARTING, + I2C_STATE_READ, + I2C_STATE_OFFSET, + I2C_STATE_WRITE }; -uint8_t gat_state; -uint16_t gat_data_idx; -uint8_t gat_data[GAT_DATA_SIZE]; +static uint8_t i2c_state; + +static uint8_t gat_page = 0; +static uint8_t gat_offset; + +static uint8_t page_general[GAT_PAGESIZE_GENERAL]; -uint8_t gat_i2c_read_cb() +// page functions +static uint8_t gat_read(uint8_t *page, uint8_t maxlen) { - uint8_t ret; + if (gat_offset > maxlen) return 0xff; + else return page[gat_offset++]; +} - switch (gat_state) { - case GAT_MODE_STARTING: { - gat_state = GAT_MODE_READ; - // no break +uint8_t gat_page_general_get(uint8_t idx) +{ + if (idx > sizeof(page_general)) return 0; + else return page_general[idx]; +} + +uint8_t gat_page_general_set(uint8_t idx, uint8_t dat) +{ + if (idx >= sizeof(page_general)) return I2CSS_NACK; + page_general[idx] = dat; + return I2CSS_OK; +} + + +// i2c comms functions +void addon_i2c_start_cb() +{ + i2c_state = I2C_STATE_STARTING; +} + +// master reading from addon +__HIGH_CODE +uint8_t addon_i2c_read_cb() +{ + switch (i2c_state) { + case I2C_STATE_STARTING: { + i2c_state = I2C_STATE_READ; + gat_offset = 0; } - case GAT_MODE_READ: { + // no break + + case I2C_STATE_READ: { // can't read past end of buffer; just send 0xff - if (gat_data_idx == GAT_DATA_SIZE) { - ret = 0xff; - } else { - ret = gat_data[gat_data_idx & 0xff]; - if (gat_data_idx <= GAT_DATA_SIZE) gat_data_idx++; + switch (gat_page) { + case GAT_PAGE_GENERAL: { + return gat_read(page_general, GAT_PAGESIZE_GENERAL); + } + case GAT_PAGE_NAMETAG_TEXT: { + // to implement + break; + } + case GAT_PAGE_RAW1: + case GAT_PAGE_RAW2: + case GAT_PAGE_RAW3: + case GAT_PAGE_RAW4: { + // to implement + break; + } } - return ret; + + // data wasn't sent above, so send empty byte + return 0xff; } + default: { return 0; } } } -int8_t gat_i2c_write_cb(uint8_t dat) +// master writing to addon +__HIGH_CODE +int8_t addon_i2c_write_cb(uint8_t dat) { - switch (gat_state) { + int8_t ret = I2CSS_OK; - case GAT_MODE_STARTING: { + switch (i2c_state) { + case I2C_STATE_STARTING: { // master wrote our address - gat_data_idx = dat; - gat_state = GAT_MODE_WRITE; + gat_page = dat; + i2c_state = I2C_STATE_OFFSET; break; } - case GAT_MODE_WRITE: { - if (gat_data_idx >= GAT_DATA_SIZE) { - return I2CSS_NACK; - } else { - gat_data[gat_data_idx++] = dat; + + case I2C_STATE_OFFSET: { + gat_offset = dat; + i2c_state = I2C_STATE_WRITE; + + if (gat_offset > GAT_DATA_SIZE) { + gat_offset = GAT_DATA_SIZE; + ret = I2CSS_NACK; } - return I2CSS_OK; + + break; } + + case I2C_STATE_WRITE: { + if (gat_offset >= GAT_DATA_SIZE) { + ret = I2CSS_NACK; + break; + } + + switch (gat_page) { + case GAT_PAGE_GENERAL: { + ret = gat_page_general_set(gat_offset, dat); + break; + } + case GAT_PAGE_NAMETAG_TEXT: { + // to implement + ret = I2CSS_NACK; + break; + } + case GAT_PAGE_RAW1: + case GAT_PAGE_RAW2: + case GAT_PAGE_RAW3: + case GAT_PAGE_RAW4: { + // to implement + ret = I2CSS_NACK; + break; + } + } + + gat_offset++; + } + default: { return I2CSS_NACK; } } + + return ret; } -void gat_i2c_start_cb() +void addon_i2c_err_cb(uint8_t error) { - gat_state = GAT_MODE_STARTING; + } - -void gat_i2c_init() +void addon_i2c_init() { // determine I2C address from GP pin identification - gat_slave.addr = gat_id + GAT_ADDR_BASE; + addon_i2c.addr = gat_id + GAT_ADDR_BASE; // roughly guess bit time length for expected speed - gat_slave.bit_time = GetSysClock() / GAT_I2C_SPEED / 4; + addon_i2c.bit_time = GetSysClock() / GAT_I2C_SPEED / 2; // set up callbacks - gat_slave.start_cb = gat_i2c_start_cb; - gat_slave.rd_cb = gat_i2c_read_cb; - gat_slave.wr_cb = gat_i2c_write_cb; + addon_i2c.start_cb = addon_i2c_start_cb; + addon_i2c.rd_cb = addon_i2c_read_cb; + addon_i2c.wr_cb = addon_i2c_write_cb; + addon_i2c.err_cb = addon_i2c_err_cb; - // enable interrupt on SDA pin to detect start condition - // this assumes GPIO has already been configured + // configure i2c pins and pin interrupt + i2css_init(); } -void gat_i2c_irq_cb() +void addon_i2c_irq_cb() { - i2css_start(&gat_slave); + SetSysClock(SYSCLK_FREQ_USEI2C); + i2css_start(&addon_i2c); + SetSysClock(SYSCLK_FREQ_IDLE); } diff --git a/nametag8_CH592/user/hw/gat/gat_i2c.h b/nametag8_CH592/user/hw/gat/gat_i2c.h index 6f3b95a..bb89257 100644 --- a/nametag8_CH592/user/hw/gat/gat_i2c.h +++ b/nametag8_CH592/user/hw/gat/gat_i2c.h @@ -10,4 +10,13 @@ +void addon_i2c_init(); + +void addon_i2c_irq_cb(); + +uint8_t gat_page_general_get(uint8_t idx); +void gat_page_general_set(uint8_t idx, uint8_t dat); + + + #endif /* USER_GAT_GAT_I2C_H_ */ diff --git a/nametag8_CH592/user/main.c b/nametag8_CH592/user/main.c index 161adbc..5edadcf 100644 --- a/nametag8_CH592/user/main.c +++ b/nametag8_CH592/user/main.c @@ -30,6 +30,7 @@ #include "comm/i2c.h" #include "hw/gat/gat_gpio.h" +#include "hw/gat/gat_i2c.h" #include "led/rgbled.h" @@ -133,7 +134,7 @@ void rtcisr_init() rtc_reset_trig(); - PFIC_SetPriority(RTC_IRQn, 0x10); // rtc is second highest priority interrupt + PFIC_SetPriority(RTC_IRQn, 0x00); // rtc is highest priority interrupt PFIC_EnableIRQ(RTC_IRQn); } @@ -403,7 +404,7 @@ int main() rgbled_init(); // initialize RGBLED controller gat_gpio_init(); // configure GAT aux GPIOs, get gat ID - // todo: implement // configure GAT i2c slave + addon_i2c_init(); // configure GAT i2c slave ssd1306fb_set_target(&oled); // start up the OLED and menu system ssd1306_init(1); // we'll try to init later too, since sometimes OLED fails to init diff --git a/nametag8_CH592/user/port_intr.c b/nametag8_CH592/user/port_intr.c index 26ed606..f0b5c16 100644 --- a/nametag8_CH592/user/port_intr.c +++ b/nametag8_CH592/user/port_intr.c @@ -14,10 +14,8 @@ #include "CH59x_common.h" #include "port_intr.h" -#include "comm/soft_i2c_slave.h" -#include "comm/soft_i2c_slave_handler.h" - #include "hw/ch32sub.h" +#include "hw/gat/gat_i2c.h" #include @@ -50,7 +48,7 @@ void port_intr_init() // clear state R16_PB_INT_IF = 0xff; - PFIC_SetPriority(GPIO_A_IRQn, 0x00); // high priority + PFIC_SetPriority(GPIO_A_IRQn, 0x10); // high priority PFIC_EnableIRQ(GPIO_A_IRQn); // then enable interrupt @@ -72,9 +70,7 @@ void GPIOA_IRQHandler(void) // addon header i2c data pin if (flag & (1 << 13)) { - SetSysClock(SYSCLK_FREQ_USEI2C); - i2css_start(&addon_i2c); - SetSysClock(SYSCLK_FREQ_IDLE); + addon_i2c_irq_cb(); } } diff --git a/nametag8_CH592/user/user_config.c b/nametag8_CH592/user/user_config.c index c6edc29..2b19333 100644 --- a/nametag8_CH592/user/user_config.c +++ b/nametag8_CH592/user/user_config.c @@ -62,6 +62,8 @@ void uconf_defaults() uconf.ledprog_rgb_idx = 4; + uconf.addon_i2c_addr = 0; + for (i = 0; i < 8; i++) { uconf.ledprog_rgb[i] = 0; } diff --git a/nametag8_CH592/user/user_config.h b/nametag8_CH592/user/user_config.h index d6e8a33..ad588de 100644 --- a/nametag8_CH592/user/user_config.h +++ b/nametag8_CH592/user/user_config.h @@ -81,7 +81,7 @@ typedef struct UserConf { uint8_t altcolor_sat; uint8_t altcolor_val; // 50 uint8_t ledprog_rgb_idx; - uint8_t padding0; // 52 + uint8_t addon_i2c_addr; // 52 uint8_t ledprog_rgb[16]; // 68 uint8_t ledprog_rgb_data[16][8]; // 196 uint8_t padding1[48]; // 244