Skip to content

Commit 23a4850

Browse files
committed
rp2/machine_i2c_target: Implement I2CTargetMemory.
Signed-off-by: Damien George <[email protected]>
1 parent 1450273 commit 23a4850

File tree

3 files changed

+225
-0
lines changed

3 files changed

+225
-0
lines changed

ports/rp2/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,7 @@ set(PICO_SDK_COMPONENTS
233233
pico_binary_info
234234
pico_bootrom
235235
pico_flash
236+
pico_i2c_slave
236237
pico_multicore
237238
pico_platform
238239
pico_platform_compiler

ports/rp2/machine_i2c_target.c

Lines changed: 222 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,222 @@
1+
/*
2+
* This file is part of the MicroPython project, http://micropython.org/
3+
*
4+
* The MIT License (MIT)
5+
*
6+
* Copyright (c) 2025 Damien P. George
7+
*
8+
* Permission is hereby granted, free of charge, to any person obtaining a copy
9+
* of this software and associated documentation files (the "Software"), to deal
10+
* in the Software without restriction, including without limitation the rights
11+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12+
* copies of the Software, and to permit persons to whom the Software is
13+
* furnished to do so, subject to the following conditions:
14+
*
15+
* The above copyright notice and this permission notice shall be included in
16+
* all copies or substantial portions of the Software.
17+
*
18+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24+
* THE SOFTWARE.
25+
*/
26+
27+
// This file is never compiled standalone, it's included directly from
28+
// extmod/machine_i2c_target.c via MICROPY_PY_MACHINE_I2C_TARGET_INCLUDEFILE.
29+
30+
#include "pico/i2c_slave.h"
31+
32+
// TODO: should be in a common i2c header
33+
#ifdef MICROPY_HW_I2C_NO_DEFAULT_PINS
34+
35+
// With no default I2C, need to require the pin args.
36+
#define MICROPY_HW_I2C0_SCL (0)
37+
#define MICROPY_HW_I2C0_SDA (0)
38+
#define MICROPY_HW_I2C1_SCL (0)
39+
#define MICROPY_HW_I2C1_SDA (0)
40+
#define MICROPY_I2C_PINS_ARG_OPTS MP_ARG_REQUIRED
41+
42+
#else
43+
44+
// Most boards do not require pin args.
45+
#define MICROPY_I2C_PINS_ARG_OPTS 0
46+
47+
#ifndef MICROPY_HW_I2C0_SCL
48+
#if PICO_DEFAULT_I2C == 0
49+
#define MICROPY_HW_I2C0_SCL (PICO_DEFAULT_I2C_SCL_PIN)
50+
#define MICROPY_HW_I2C0_SDA (PICO_DEFAULT_I2C_SDA_PIN)
51+
#else
52+
#define MICROPY_HW_I2C0_SCL (9)
53+
#define MICROPY_HW_I2C0_SDA (8)
54+
#endif
55+
#endif
56+
57+
#ifndef MICROPY_HW_I2C1_SCL
58+
#if PICO_DEFAULT_I2C == 1
59+
#define MICROPY_HW_I2C1_SCL (PICO_DEFAULT_I2C_SCL_PIN)
60+
#define MICROPY_HW_I2C1_SDA (PICO_DEFAULT_I2C_SDA_PIN)
61+
#else
62+
#define MICROPY_HW_I2C1_SCL (7)
63+
#define MICROPY_HW_I2C1_SDA (6)
64+
#endif
65+
#endif
66+
#endif
67+
68+
// SDA/SCL on even/odd pins, I2C0/I2C1 on even/odd pairs of pins.
69+
// TODO: should be in a common i2c header
70+
#define IS_VALID_SCL(i2c, pin) (((pin) & 1) == 1 && (((pin) & 2) >> 1) == (i2c))
71+
#define IS_VALID_SDA(i2c, pin) (((pin) & 1) == 0 && (((pin) & 2) >> 1) == (i2c))
72+
73+
typedef struct _machine_i2c_target_obj_t {
74+
mp_obj_base_t base;
75+
i2c_inst_t *const i2c_inst;
76+
mp_hal_pin_obj_t scl;
77+
mp_hal_pin_obj_t sda;
78+
} machine_i2c_target_obj_t;
79+
80+
static machine_i2c_target_data_t i2c_target_data[4];
81+
82+
static machine_i2c_target_obj_t machine_i2c_target_obj[] = {
83+
{{&machine_i2c_target_type}, i2c0, MICROPY_HW_I2C0_SCL, MICROPY_HW_I2C0_SDA},
84+
{{&machine_i2c_target_type}, i2c1, MICROPY_HW_I2C1_SCL, MICROPY_HW_I2C1_SDA},
85+
};
86+
87+
/******************************************************************************/
88+
// pico-sdk bindings
89+
90+
static void i2c_target_handler(i2c_inst_t *i2c, i2c_slave_event_t event) {
91+
unsigned int i2c_id = i2c == i2c0 ? 0 : 1;
92+
machine_i2c_target_obj_t *self = &machine_i2c_target_obj[i2c_id];
93+
machine_i2c_target_data_t *data = &i2c_target_data[i2c_id];
94+
95+
switch (event) {
96+
// 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;
103+
104+
// 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;
113+
}
114+
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;
120+
}
121+
}
122+
123+
/******************************************************************************/
124+
// I2CTarget port implementation
125+
126+
static void mp_machine_i2c_target_deinit_all_port(void) {
127+
}
128+
129+
static void mp_machine_i2c_target_event_callback(machine_i2c_target_irq_obj_t *irq) {
130+
mp_irq_handler(&irq->base);
131+
}
132+
133+
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);
135+
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);
138+
}
139+
return i;
140+
}
141+
142+
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]);
144+
return 1;
145+
}
146+
147+
static void mp_machine_i2c_target_irq_config(machine_i2c_target_obj_t *self, unsigned int trigger) {
148+
}
149+
150+
static mp_obj_t mp_machine_i2c_target_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) {
151+
// TODO: reconsider order of arguments
152+
enum { ARG_id, ARG_addr, ARG_addrsize, ARG_mem, ARG_mem_addrsize, ARG_scl, ARG_sda };
153+
static const mp_arg_t allowed_args[] = {
154+
#ifdef PICO_DEFAULT_I2C
155+
{ MP_QSTR_id, MP_ARG_INT, {.u_int = PICO_DEFAULT_I2C} },
156+
#else
157+
{ MP_QSTR_id, MP_ARG_INT | MP_ARG_REQUIRED },
158+
#endif
159+
{ MP_QSTR_addr, MP_ARG_REQUIRED | MP_ARG_INT },
160+
{ MP_QSTR_addrsize, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 7} },
161+
{ MP_QSTR_mem, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} },
162+
{ MP_QSTR_mem_addrsize, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 8} },
163+
{ MP_QSTR_scl, MICROPY_I2C_PINS_ARG_OPTS | MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} },
164+
{ MP_QSTR_sda, MICROPY_I2C_PINS_ARG_OPTS | MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} },
165+
};
166+
167+
// Parse args.
168+
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
169+
mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
170+
171+
int i2c_id = args[ARG_id].u_int;
172+
173+
// Check if the I2C bus is valid
174+
if (i2c_id < 0 || i2c_id >= MP_ARRAY_SIZE(machine_i2c_target_obj)) {
175+
mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("I2C(%d) doesn't exist"), i2c_id);
176+
}
177+
178+
// Get static peripheral object.
179+
machine_i2c_target_obj_t *self = &machine_i2c_target_obj[i2c_id];
180+
181+
// Initialise data.
182+
MP_STATE_PORT(i2c_target_mem_obj)[i2c_id] = args[ARG_mem].u_obj;
183+
machine_i2c_target_data_t *data = &i2c_target_data[i2c_id];
184+
machine_i2c_target_data_init(data, args[ARG_mem].u_obj, args[ARG_mem_addrsize].u_int);
185+
186+
// Set SCL/SDA pins if configured.
187+
if (args[ARG_scl].u_obj != mp_const_none) {
188+
int scl = mp_hal_get_pin_obj(args[ARG_scl].u_obj);
189+
if (!IS_VALID_SCL(i2c_id, scl)) {
190+
mp_raise_ValueError(MP_ERROR_TEXT("bad SCL pin"));
191+
}
192+
self->scl = scl;
193+
}
194+
if (args[ARG_sda].u_obj != mp_const_none) {
195+
int sda = mp_hal_get_pin_obj(args[ARG_sda].u_obj);
196+
if (!IS_VALID_SDA(i2c_id, sda)) {
197+
mp_raise_ValueError(MP_ERROR_TEXT("bad SDA pin"));
198+
}
199+
self->sda = sda;
200+
}
201+
202+
// Initialise the I2C target.
203+
// TODO: don't reinitialize if no arguments given.
204+
i2c_slave_init(self->i2c_inst, args[ARG_addr].u_int, i2c_target_handler);
205+
gpio_set_function(self->scl, GPIO_FUNC_I2C);
206+
gpio_set_function(self->sda, GPIO_FUNC_I2C);
207+
gpio_set_pulls(self->scl, true, 0);
208+
gpio_set_pulls(self->sda, true, 0);
209+
210+
return MP_OBJ_FROM_PTR(self);
211+
}
212+
213+
static void mp_machine_i2c_target_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
214+
machine_i2c_target_obj_t *self = MP_OBJ_TO_PTR(self_in);
215+
i2c_hw_t *i2c_hw = i2c_get_hw(self->i2c_inst);
216+
mp_printf(print, "I2CTarget(%u, addr=%u, scl=%u, sda=%u)",
217+
self - &machine_i2c_target_obj[0], i2c_hw->sar, self->scl, self->sda);
218+
}
219+
220+
static void mp_machine_i2c_target_deinit(machine_i2c_target_obj_t *self) {
221+
i2c_slave_deinit(self->i2c_inst);
222+
}

ports/rp2/mpconfigport.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,8 @@
171171
#define MICROPY_PY_MACHINE_PWM (1)
172172
#define MICROPY_PY_MACHINE_PWM_INCLUDEFILE "ports/rp2/machine_pwm.c"
173173
#define MICROPY_PY_MACHINE_I2C (1)
174+
#define MICROPY_PY_MACHINE_I2C_TARGET (1)
175+
#define MICROPY_PY_MACHINE_I2C_TARGET_INCLUDEFILE "ports/rp2/machine_i2c_target.c"
174176
#define MICROPY_PY_MACHINE_SOFTI2C (1)
175177
#define MICROPY_PY_MACHINE_I2S (1)
176178
#define MICROPY_PY_MACHINE_I2S_INCLUDEFILE "ports/rp2/machine_i2s.c"

0 commit comments

Comments
 (0)