Skip to content

Commit 25196ba

Browse files
committed
rp2/machine_i2c_target: Use direct register access.
Signed-off-by: Damien George <[email protected]>
1 parent 0b403b9 commit 25196ba

File tree

1 file changed

+104
-28
lines changed

1 file changed

+104
-28
lines changed

ports/rp2/machine_i2c_target.c

Lines changed: 104 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
// This file is never compiled standalone, it's included directly from
2828
// extmod/machine_i2c_target.c via MICROPY_PY_MACHINE_I2C_TARGET_INCLUDEFILE.
2929

30-
#include "pico/i2c_slave.h"
30+
#include "hardware/i2c.h"
3131

3232
// TODO: should be in a common i2c header
3333
#ifdef MICROPY_HW_I2C_NO_DEFAULT_PINS
@@ -75,6 +75,7 @@ typedef struct _machine_i2c_target_obj_t {
7575
i2c_inst_t *const i2c_inst;
7676
mp_hal_pin_obj_t scl;
7777
mp_hal_pin_obj_t sda;
78+
uint8_t state;
7879
} machine_i2c_target_obj_t;
7980

8081
static machine_i2c_target_data_t i2c_target_data[4];
@@ -85,41 +86,102 @@ static machine_i2c_target_obj_t machine_i2c_target_obj[] = {
8586
};
8687

8788
/******************************************************************************/
88-
// pico-sdk bindings
89+
// RP2xxx hardware bindings
8990

90-
static void i2c_target_handler(i2c_inst_t *i2c, i2c_slave_event_t event) {
91+
static void i2c_target_handler(i2c_inst_t *i2c) {
9192
unsigned int i2c_id = i2c == i2c0 ? 0 : 1;
9293
machine_i2c_target_obj_t *self = &machine_i2c_target_obj[i2c_id];
9394
machine_i2c_target_data_t *data = &i2c_target_data[i2c_id];
9495

95-
switch (event) {
96+
// Get the interrupt status.
97+
uint32_t intr_stat = i2c->hw->intr_stat;
98+
99+
if (intr_stat & I2C_IC_INTR_STAT_R_TX_ABRT_BITS) {
100+
// Clear the TX_ABRT condition.
101+
(void)i2c->hw->clr_tx_abrt;
102+
}
103+
104+
if (intr_stat & I2C_IC_INTR_STAT_R_RX_FULL_BITS) {
96105
// Data from controller is available for reading.
97-
case I2C_SLAVE_RECEIVE:
98-
if (data->state == STATE_IDLE) {
99-
machine_i2c_target_data_addr_match(data, false);
100-
}
101-
machine_i2c_target_data_write_request(self, data);
102-
break;
106+
// Mask interrupt until I2C_DATA_CMD is read from.
107+
i2c->hw->intr_mask &= ~I2C_IC_INTR_MASK_M_RX_FULL_BITS;
108+
if (self->state != STATE_WRITING) {
109+
machine_i2c_target_data_addr_match(data, false);
110+
}
111+
machine_i2c_target_data_write_request(self, data);
112+
self->state = STATE_WRITING;
113+
}
103114

115+
if (intr_stat & (I2C_IC_INTR_STAT_R_RD_REQ_BITS | I2C_IC_INTR_STAT_R_RX_DONE_BITS)) {
104116
// Controller is requesting data.
105-
case I2C_SLAVE_REQUEST: {
106-
if (data->state == STATE_IDLE) {
107-
machine_i2c_target_data_addr_match(data, true);
108-
}
109-
// i2c_write_byte_raw(i2c, 0x12);
110-
// break;
111-
machine_i2c_target_data_read_request(self, data);
112-
break;
117+
(void)i2c->hw->clr_rx_done;
118+
(void)i2c->hw->clr_rd_req;
119+
i2c->hw->intr_mask &= ~I2C_IC_INTR_MASK_M_RD_REQ_BITS;
120+
if (self->state != STATE_READING) {
121+
machine_i2c_target_data_addr_match(data, true);
113122
}
123+
machine_i2c_target_data_read_request(self, data);
124+
self->state = STATE_READING;
125+
}
114126

115-
// Controller has sent a Stop or Restart signal.
116-
case I2C_SLAVE_FINISH:
117-
// TODO handle the fact this could be a restart
118-
machine_i2c_target_data_restart_or_stop(data);
119-
break;
127+
if (intr_stat & I2C_IC_INTR_STAT_R_STOP_DET_BITS) {
128+
// Controller has generated a stop condition.
129+
(void)i2c->hw->clr_stop_det;
130+
if (self->state == STATE_IDLE) {
131+
machine_i2c_target_data_addr_match(data, false);
132+
}
133+
machine_i2c_target_data_restart_or_stop(data);
134+
self->state = STATE_IDLE;
120135
}
121136
}
122137

138+
static void i2c_slave_irq_handler(void) {
139+
uint i2c_index = __get_current_exception() - VTABLE_FIRST_IRQ - I2C0_IRQ;
140+
i2c_inst_t *i2c = i2c_get_instance(i2c_index);
141+
i2c_target_handler(i2c);
142+
}
143+
144+
static void i2c_slave_init(i2c_inst_t *i2c, uint16_t addr, bool addr_10bit) {
145+
i2c->hw->enable = 0;
146+
147+
// Note: The I2C slave does clock stretching implicitly after a RD_REQ, while the Tx FIFO is empty.
148+
// Clock stretching while the Rx FIFO is full is also enabled by default.
149+
i2c->hw->con = I2C_IC_CON_STOP_DET_IFADDRESSED_BITS;
150+
if (addr_10bit) {
151+
i2c->hw->con |= I2C_IC_CON_IC_10BITADDR_SLAVE_BITS;
152+
}
153+
i2c->hw->sar = addr;
154+
i2c->hw->tx_tl = 1;
155+
i2c->hw->rx_tl = 0; // interrupt when at least 1 byte is available
156+
157+
// Enable interrupts.
158+
i2c->hw->intr_mask =
159+
I2C_IC_INTR_MASK_M_STOP_DET_BITS
160+
| I2C_IC_INTR_MASK_M_RX_DONE_BITS
161+
| I2C_IC_INTR_MASK_M_TX_ABRT_BITS
162+
| I2C_IC_INTR_MASK_M_RD_REQ_BITS
163+
| I2C_IC_INTR_MASK_M_RX_FULL_BITS
164+
;
165+
166+
i2c->hw->enable = 1;
167+
168+
// Enable interrupt for current core.
169+
uint i2c_index = i2c_hw_index(i2c);
170+
uint num = I2C0_IRQ + i2c_index;
171+
irq_set_exclusive_handler(num, i2c_slave_irq_handler);
172+
irq_set_enabled(num, true);
173+
}
174+
175+
static void i2c_slave_deinit(i2c_inst_t *i2c) {
176+
uint i2c_index = i2c_hw_index(i2c);
177+
uint num = I2C0_IRQ + i2c_index;
178+
irq_set_enabled(num, false);
179+
irq_remove_handler(num, i2c_slave_irq_handler);
180+
181+
i2c->hw->intr_mask = 0;
182+
i2c->hw->enable = 0;
183+
}
184+
123185
/******************************************************************************/
124186
// I2CTarget port implementation
125187

@@ -131,16 +193,29 @@ static void mp_machine_i2c_target_event_callback(machine_i2c_target_irq_obj_t *i
131193
}
132194

133195
static mp_int_t mp_machine_i2c_target_read_bytes(machine_i2c_target_obj_t *self, size_t len, uint8_t *buf) {
134-
i2c_hw_t *i2c_hw = i2c_get_hw(self->i2c_inst);
196+
i2c_hw_t *i2c_hw = self->i2c_inst->hw;
197+
198+
// Read from the RX FIFO.
135199
mp_int_t i = 0;
136-
while (i < len && i2c_hw->status & I2C_IC_STATUS_RFNE_BITS) {
137-
buf[i++] = i2c_read_byte_raw(self->i2c_inst);
200+
while (i < len && (i2c_hw->status & I2C_IC_STATUS_RFNE_BITS)) {
201+
buf[i++] = i2c_hw->data_cmd;
138202
}
203+
204+
// Re-enable RX_FULL interrupt.
205+
i2c_hw->intr_mask |= I2C_IC_INTR_MASK_M_RX_FULL_BITS;
206+
139207
return i;
140208
}
141209

142210
static mp_int_t mp_machine_i2c_target_write_bytes(machine_i2c_target_obj_t *self, size_t len, const uint8_t *buf) {
143-
i2c_write_byte_raw(self->i2c_inst, buf[0]);
211+
i2c_hw_t *i2c_hw = self->i2c_inst->hw;
212+
213+
// Write to the TX FIFO.
214+
i2c_hw->data_cmd = buf[0];
215+
216+
// Re-enable RD_REQ interrupt.
217+
i2c_hw->intr_mask |= I2C_IC_INTR_MASK_M_RD_REQ_BITS;
218+
144219
return 1;
145220
}
146221

@@ -179,6 +254,7 @@ static mp_obj_t mp_machine_i2c_target_make_new(const mp_obj_type_t *type, size_t
179254
machine_i2c_target_obj_t *self = &machine_i2c_target_obj[i2c_id];
180255

181256
// Initialise data.
257+
self->state = STATE_IDLE;
182258
MP_STATE_PORT(i2c_target_mem_obj)[i2c_id] = args[ARG_mem].u_obj;
183259
machine_i2c_target_data_t *data = &i2c_target_data[i2c_id];
184260
machine_i2c_target_data_init(data, args[ARG_mem].u_obj, args[ARG_mem_addrsize].u_int);
@@ -201,7 +277,7 @@ static mp_obj_t mp_machine_i2c_target_make_new(const mp_obj_type_t *type, size_t
201277

202278
// Initialise the I2C target.
203279
// TODO: don't reinitialize if no arguments given.
204-
i2c_slave_init(self->i2c_inst, args[ARG_addr].u_int, i2c_target_handler);
280+
i2c_slave_init(self->i2c_inst, args[ARG_addr].u_int, args[ARG_addrsize].u_int == 10);
205281
gpio_set_function(self->scl, GPIO_FUNC_I2C);
206282
gpio_set_function(self->sda, GPIO_FUNC_I2C);
207283
gpio_set_pulls(self->scl, true, 0);

0 commit comments

Comments
 (0)