qmk_firmware/keyboards/rkb1/i2c_slave.c

111 lines
4.4 KiB
C

#include "i2c_slave.h"
#include "hardware/i2c.h"
#include "hardware/gpio.h"
#include "hardware/irq.h"
#include <string.h>
#include "transactions.h"
// Code taken from here: https://github.com/harsha-vk/weeder_bot/blob/0bb2ac3f627dc2b3867480d210c0536ae0f684a5/assets/raspberry_pi_pico_code/src/i2c_slave.cpp
static volatile bool is_callback_executor = false;
volatile uint8_t i2c_slave_reg[I2C_SLAVE_REG_COUNT];
static volatile uint8_t buffer_address;
static volatile bool sub_has_register_set = false;
static volatile bool transfer_in_progress = false;
enum i2c_event_t { I2C_SUB_RECEIVE, I2C_SUB_REQUEST, I2C_SUB_FINISH };
static void i2c_sub_handler(enum i2c_event_t event) {
// uint8_t ack = 1;
switch (event) {
case I2C_SUB_RECEIVE: {
while (i2c_get_read_available(i2c1)) {
if (!sub_has_register_set) {
i2c_read_raw_blocking(i2c1, (uint8_t *)&buffer_address, 1);
if (buffer_address >= I2C_SLAVE_REG_COUNT) {
// ack = 0;
buffer_address = 0;
}
sub_has_register_set = true;
is_callback_executor = buffer_address == split_transaction_table[I2C_EXECUTE_CALLBACK].initiator2target_offset;
} else {
// find out how many bytes we can safely read
uint32_t bytes_left = I2C_SLAVE_REG_COUNT - buffer_address;
uint32_t bytes_to_read = i2c_get_read_available(i2c1);
uint32_t num_bytes = (bytes_left < bytes_to_read) ? bytes_left : bytes_to_read;
i2c_read_raw_blocking(i2c1, (uint8_t *)i2c_slave_reg + buffer_address, num_bytes);
buffer_address += num_bytes;
if (buffer_address >= I2C_SLAVE_REG_COUNT) buffer_address = 0;
// If we're intending to execute a transaction callback, do so, as we've just received the transaction ID
if (is_callback_executor) {
split_transaction_desc_t *trans = &split_transaction_table[split_shmem->transaction_id];
if (trans->slave_callback) {
trans->slave_callback(trans->initiator2target_buffer_size, split_trans_initiator2target_buffer(trans), trans->target2initiator_buffer_size, split_trans_target2initiator_buffer(trans));
}
}
}
}
} break;
case I2C_SUB_REQUEST: {
i2c_write_raw_blocking(i2c1, (uint8_t *)i2c_slave_reg + buffer_address, 1);
buffer_address++;
break;
}
case I2C_SUB_FINISH:
sub_has_register_set = false;
is_callback_executor = false;
break;
}
// TODO: ack??
}
static void finish_transfer(void) {
transfer_in_progress = false;
i2c_sub_handler(I2C_SUB_FINISH);
}
static void i2c_handler(void) {
i2c_hw_t *hw = i2c_get_hw(i2c1);
uint32_t intr_stat = hw->intr_stat;
if (intr_stat == 0) return; // spurious
if (intr_stat & I2C_IC_INTR_STAT_R_TX_ABRT_BITS) {
hw->clr_tx_abrt;
finish_transfer();
}
if (intr_stat & I2C_IC_INTR_STAT_R_START_DET_BITS) {
hw->clr_start_det;
finish_transfer();
}
if (intr_stat & I2C_IC_INTR_STAT_R_STOP_DET_BITS) {
hw->clr_stop_det;
finish_transfer();
}
if (intr_stat & I2C_IC_INTR_STAT_R_RX_FULL_BITS) {
transfer_in_progress = true;
i2c_sub_handler(I2C_SUB_RECEIVE);
}
if (intr_stat & I2C_IC_INTR_STAT_R_RD_REQ_BITS) {
hw->clr_rd_req;
transfer_in_progress = true;
i2c_sub_handler(I2C_SUB_REQUEST);
}
}
void cos_i2c_slave_init(uint8_t address) {
i2c_init(i2c1, 100 * 1000);
i2c_set_slave_mode(i2c1, true, address);
gpio_set_function(18, GPIO_FUNC_I2C);
gpio_set_function(19, GPIO_FUNC_I2C);
gpio_pull_up(18);
gpio_pull_up(19);
i2c_hw_t *hw = i2c_get_hw(i2c1);
hw->intr_mask = I2C_IC_INTR_MASK_M_RX_FULL_BITS | I2C_IC_INTR_MASK_M_RD_REQ_BITS | I2C_IC_RAW_INTR_STAT_TX_ABRT_BITS | I2C_IC_INTR_MASK_M_STOP_DET_BITS | I2C_IC_INTR_MASK_M_START_DET_BITS;
irq_set_exclusive_handler(I2C1_IRQ, i2c_handler);
irq_set_enabled(I2C1_IRQ, true);
}
void cos_i2c_slave_stop(void) {
i2c_set_slave_mode(i2c1, false, 0);
i2c_deinit(i2c1);
}