more i2c slave work

this code is still surely not working, is incomplete, and guaranteed has problems
This commit is contained in:
true 2024-10-30 19:08:31 -07:00
parent 6be6cee2ac
commit 52435ea3f0
10 changed files with 226 additions and 113 deletions

View File

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

View File

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

View File

@ -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 <stdint.h>
struct I2CSoftSlave addon_i2c;
void i2css_addon_init()
{
}

View File

@ -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_ */

View File

@ -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 <CH59x_common.h>
#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);
}

View File

@ -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_ */

View File

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

View File

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

View File

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

View File

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