Skip to content

Commit 9073270

Browse files
iabdalkaderdpgeorge
authored andcommitted
alif/machine_i2c: Add machine.I2C peripheral support.
Signed-off-by: iabdalkader <[email protected]>
1 parent 82bae65 commit 9073270

File tree

3 files changed

+305
-0
lines changed

3 files changed

+305
-0
lines changed

ports/alif/alif.mk

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@ SRC_C = \
117117
alif_flash.c \
118118
fatfs_port.c \
119119
machine_pin.c \
120+
machine_i2c.c \
120121
main.c \
121122
modalif.c \
122123
mphalport.c \
@@ -186,6 +187,7 @@ ALIF_SRC_C += $(addprefix $(ALIF_DFP_REL_TOP)/,\
186187
Device/common/source/pm.c \
187188
Device/core/$(MCU_CORE)/source/startup_$(MCU_CORE).c \
188189
drivers/source/adc.c \
190+
drivers/source/i2c.c \
189191
drivers/source/mhu_driver.c \
190192
drivers/source/mhu_receiver.c \
191193
drivers/source/mhu_sender.c \

ports/alif/machine_i2c.c

Lines changed: 301 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,301 @@
1+
/*
2+
* This file is part of the MicroPython project, http://micropython.org/
3+
*
4+
* The MIT License (MIT)
5+
*
6+
* Copyright (c) 2024-2025 OpenMV LLC.
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+
#include "py/runtime.h"
28+
#include "py/mphal.h"
29+
#include "py/mperrno.h"
30+
#include "extmod/modmachine.h"
31+
32+
#if MICROPY_HW_ENABLE_HW_I2C
33+
#include "i2c.h"
34+
35+
#define I2C_DEFAULT_FREQ (400000)
36+
#define I2C_DEFAULT_TIMEOUT (50000)
37+
38+
#define I2C_DAT_INDEX (0)
39+
#define I2C_TX_FIFO_LEN (I2C_FIFO_DEPTH / 2)
40+
#define I2C_RX_FIFO_LEN (I2C_FIFO_DEPTH / 2)
41+
42+
#define I2C_SPEED(freq) \
43+
((freq) <= 100000 ? I2C_SPEED_STANDARD : \
44+
((freq) <= 400000 ? I2C_SPEED_FAST : \
45+
I2C_SPEED_FASTPLUS))
46+
47+
#define I2C_IC_CON_SPEED(freq) \
48+
((freq) <= 100000 ? I2C_IC_CON_SPEED_STANDARD : \
49+
((freq) <= 400000 ? I2C_IC_CON_SPEED_FAST : \
50+
I2C_IC_CON_SPEED_HIGH))
51+
#define I2C_IC_CON_MASTER_TX_EMPTY_CTRL (1 << 8)
52+
53+
#define I2C_IC_STATUS_RFNE I2C_IC_STATUS_RECEIVE_FIFO_NOT_EMPTY
54+
#define I2C_IC_STATUS_TFNF I2C_IC_STATUS_TRANSMIT_FIFO_NOT_FULL
55+
#define I2C_STAT_ERRORS (I2C_IC_INTR_STAT_TX_ABRT | I2C_IC_INTR_STAT_TX_OVER | \
56+
I2C_IC_INTR_STAT_RX_OVER | I2C_IC_INTR_STAT_RX_UNDER)
57+
58+
#define debug_printf(...) // mp_printf(&mp_plat_print, "i2c.c: " __VA_ARGS__)
59+
#define I2C_CHECK_ERRORS(base) \
60+
if (base->I2C_RAW_INTR_STAT & I2C_STAT_ERRORS) { \
61+
uint32_t status = base->I2C_RAW_INTR_STAT; \
62+
debug_printf("status: 0x%lx raw_int: 0x%lx abort: 0x%lx line: %d\n", \
63+
base->I2C_STATUS, status, base->I2C_TX_ABRT_SOURCE, __LINE__); \
64+
(void)status; \
65+
(void)base->I2C_CLR_TX_ABRT; \
66+
(void)base->I2C_CLR_ACTIVITY; \
67+
return -MP_EIO; \
68+
}
69+
70+
typedef struct _machine_i2c_obj_t {
71+
mp_obj_base_t base;
72+
uint32_t i2c_id;
73+
I2C_Type *i2c;
74+
mp_hal_pin_obj_t scl;
75+
mp_hal_pin_obj_t sda;
76+
uint32_t freq;
77+
uint32_t timeout;
78+
} machine_i2c_obj_t;
79+
80+
static machine_i2c_obj_t machine_i2c_obj[] = {
81+
#if defined(MICROPY_HW_I2C0_SCL)
82+
[0] = {{&machine_i2c_type}, 0, (I2C_Type *)I2C0_BASE, MICROPY_HW_I2C0_SCL, MICROPY_HW_I2C0_SDA},
83+
#endif
84+
#if defined(MICROPY_HW_I2C1_SCL)
85+
[1] = {{&machine_i2c_type}, 1, (I2C_Type *)I2C1_BASE, MICROPY_HW_I2C1_SCL, MICROPY_HW_I2C1_SDA},
86+
#endif
87+
#if defined(MICROPY_HW_I2C2_SCL)
88+
[2] = {{&machine_i2c_type}, 2, (I2C_Type *)I2C2_BASE, MICROPY_HW_I2C2_SCL, MICROPY_HW_I2C2_SDA},
89+
#endif
90+
#if defined(MICROPY_HW_I2C3_SCL)
91+
[3] = {{&machine_i2c_type}, 3, (I2C_Type *)I2C3_BASE, MICROPY_HW_I2C3_SCL, MICROPY_HW_I2C3_SDA},
92+
#endif
93+
};
94+
95+
static void machine_i2c_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
96+
machine_i2c_obj_t *self = MP_OBJ_TO_PTR(self_in);
97+
mp_printf(print, "I2C(%u, freq=%u, scl=%q, sda=%q, timeout=%u)",
98+
self->i2c_id, self->freq, self->scl->name, self->sda->name, self->timeout);
99+
}
100+
101+
mp_obj_t machine_i2c_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) {
102+
enum { ARG_id, ARG_freq, ARG_scl, ARG_sda, ARG_timeout };
103+
static const mp_arg_t allowed_args[] = {
104+
{ MP_QSTR_id, MP_ARG_REQUIRED | MP_ARG_OBJ },
105+
{ MP_QSTR_freq, MP_ARG_INT, {.u_int = I2C_DEFAULT_FREQ} },
106+
{ MP_QSTR_scl, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} },
107+
{ MP_QSTR_sda, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} },
108+
{ MP_QSTR_timeout, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = I2C_DEFAULT_TIMEOUT} },
109+
};
110+
111+
// Parse args.
112+
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
113+
mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
114+
115+
// Get I2C bus.
116+
int i2c_id = mp_obj_get_int(args[ARG_id].u_obj);
117+
if (i2c_id < 0 || i2c_id >= MP_ARRAY_SIZE(machine_i2c_obj) || !machine_i2c_obj[i2c_id].i2c) {
118+
mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("I2C(%d) doesn't exist"), i2c_id);
119+
}
120+
121+
// Get static peripheral object.
122+
machine_i2c_obj_t *self = &machine_i2c_obj[i2c_id];
123+
124+
// Set args
125+
self->freq = args[ARG_freq].u_int;
126+
self->timeout = args[ARG_timeout].u_int;
127+
128+
// here we would check the scl/sda pins and configure them, but it's not implemented
129+
if (args[ARG_scl].u_obj != mp_const_none || args[ARG_sda].u_obj != mp_const_none) {
130+
mp_raise_ValueError(MP_ERROR_TEXT("explicit choice of scl/sda is not implemented"));
131+
}
132+
133+
// Disable I2C controller.
134+
i2c_disable(self->i2c);
135+
136+
// Configure I2C pins.
137+
mp_hal_pin_config(self->scl, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_UP,
138+
MP_HAL_PIN_SPEED_LOW, MP_HAL_PIN_DRIVE_8MA, MP_HAL_PIN_ALT_I2C, true);
139+
mp_hal_pin_config(self->sda, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_UP,
140+
MP_HAL_PIN_SPEED_LOW, MP_HAL_PIN_DRIVE_8MA, MP_HAL_PIN_ALT_I2C, true);
141+
142+
// Initialize I2C controller.
143+
self->i2c->I2C_CON = I2C_IC_CON_ENABLE_MASTER_MODE |
144+
I2C_IC_CON_MASTER_RESTART_EN |
145+
I2C_IC_CON_MASTER_TX_EMPTY_CTRL |
146+
I2C_IC_CON_SPEED(self->freq);
147+
148+
// Configure FIFO threshold.
149+
self->i2c->I2C_TX_TL = I2C_TX_FIFO_LEN;
150+
self->i2c->I2C_RX_TL = I2C_RX_FIFO_LEN;
151+
152+
// Configure clock.
153+
i2c_master_set_clock(self->i2c, GetSystemAPBClock() / 1000, I2C_SPEED(self->freq));
154+
155+
// Enable I2C controller.
156+
i2c_clear_all_interrupt(self->i2c);
157+
i2c_enable(self->i2c);
158+
159+
return MP_OBJ_FROM_PTR(self);
160+
}
161+
162+
static int machine_i2c_poll_flags(I2C_Type *base, uint32_t flags, uint32_t timeout_us) {
163+
mp_uint_t tick_start = mp_hal_ticks_us();
164+
while (!(base->I2C_STATUS & flags)) {
165+
I2C_CHECK_ERRORS(base);
166+
if ((mp_hal_ticks_us() - tick_start) >= timeout_us) {
167+
return -MP_ETIMEDOUT;
168+
}
169+
// Can't delay or handle pending events here otherwise we risk
170+
// the FIFO getting empty, which will generate a STOP condition.
171+
}
172+
return 0;
173+
}
174+
175+
static int machine_i2c_write(machine_i2c_obj_t *self, uint8_t *buf, size_t tx_size) {
176+
mp_uint_t tick_start = mp_hal_ticks_us();
177+
for (size_t tx_idx = 0; tx_idx < tx_size;) {
178+
// Write data to FIFO
179+
if (self->i2c->I2C_STATUS & I2C_IC_STATUS_TFNF) {
180+
self->i2c->I2C_DATA_CMD = (uint16_t)buf[tx_idx++];
181+
I2C_CHECK_ERRORS(self->i2c);
182+
tick_start = mp_hal_ticks_us();
183+
}
184+
185+
// Check for timeout
186+
if ((mp_hal_ticks_us() - tick_start) >= self->timeout) {
187+
return -MP_ETIMEDOUT;
188+
}
189+
}
190+
return 0;
191+
}
192+
193+
static int machine_i2c_read(machine_i2c_obj_t *self, uint8_t *buf, size_t rx_size) {
194+
mp_uint_t tick_start = mp_hal_ticks_us();
195+
for (size_t tx_idx = 0, rx_idx = 0; rx_idx < rx_size;) {
196+
// Write command to FIFO
197+
if (tx_idx < rx_size && (self->i2c->I2C_STATUS & I2C_IC_STATUS_TFNF)) {
198+
self->i2c->I2C_DATA_CMD = I2C_IC_DATA_CMD_READ_REQ;
199+
I2C_CHECK_ERRORS(self->i2c);
200+
++tx_idx;
201+
}
202+
203+
// Read data from FIFO
204+
while (rx_idx < rx_size && (self->i2c->I2C_STATUS & I2C_IC_STATUS_RFNE)) {
205+
buf[rx_idx++] = self->i2c->I2C_DATA_CMD & 0xFF;
206+
tick_start = mp_hal_ticks_us();
207+
}
208+
209+
// Check for timeout
210+
if ((mp_hal_ticks_us() - tick_start) >= self->timeout) {
211+
return -MP_ETIMEDOUT;
212+
}
213+
}
214+
return 0;
215+
}
216+
217+
int machine_i2c_transfer(mp_obj_base_t *self_in, uint16_t addr, size_t n, mp_machine_i2c_buf_t *bufs, unsigned int flags) {
218+
int ret = 0;
219+
uint32_t bytes = 0;
220+
machine_i2c_obj_t *self = (machine_i2c_obj_t *)self_in;
221+
222+
// The DesignWare I2C IP on AE3 is configured such that it auto-generates a STOP
223+
// condition when the TX FIFO gets empty. In other words, the code can't have any
224+
// control over STOP condition generation. The only fix for this would be to buffer
225+
// complete read/write sequences and send them out when the STOP flag is set.
226+
if (!(flags & MP_MACHINE_I2C_FLAG_STOP)) {
227+
mp_raise_ValueError(MP_ERROR_TEXT("nostop flag is not supported"));
228+
}
229+
230+
i2c_clear_all_interrupt(self->i2c);
231+
i2c_set_target_addr(self->i2c, addr, I2C_7BIT_ADDRESS, 0);
232+
233+
// Workaround issue with hardware I2C not accepting zero-length writes.
234+
if (!bufs->len) {
235+
mp_machine_i2c_buf_t bufs = { 0 };
236+
237+
mp_machine_soft_i2c_obj_t soft_i2c = {
238+
.base = { &mp_machine_soft_i2c_type },
239+
.scl = self->scl,
240+
.sda = self->sda,
241+
.us_timeout = self->timeout,
242+
.us_delay = 500000 / self->freq + 1,
243+
};
244+
245+
// Switch pins to GPIO/OD.
246+
mp_hal_pin_config(self->scl, MP_HAL_PIN_MODE_OPEN_DRAIN, MP_HAL_PIN_PULL_UP,
247+
MP_HAL_PIN_SPEED_LOW, MP_HAL_PIN_DRIVE_8MA, MP_HAL_PIN_ALT_NONE, true);
248+
mp_hal_pin_config(self->sda, MP_HAL_PIN_MODE_OPEN_DRAIN, MP_HAL_PIN_PULL_UP,
249+
MP_HAL_PIN_SPEED_LOW, MP_HAL_PIN_DRIVE_8MA, MP_HAL_PIN_ALT_NONE, true);
250+
251+
// Perform the transfer.
252+
ret = mp_machine_soft_i2c_transfer(&soft_i2c.base, addr, 1, &bufs, flags);
253+
254+
// Re-configure I2C pins.
255+
mp_hal_pin_config(self->scl, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_UP,
256+
MP_HAL_PIN_SPEED_LOW, MP_HAL_PIN_DRIVE_8MA, MP_HAL_PIN_ALT_I2C, true);
257+
mp_hal_pin_config(self->sda, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_UP,
258+
MP_HAL_PIN_SPEED_LOW, MP_HAL_PIN_DRIVE_8MA, MP_HAL_PIN_ALT_I2C, true);
259+
260+
return ret;
261+
}
262+
263+
for (size_t i = 0; i < n; i++) {
264+
mp_machine_i2c_buf_t *buf = &bufs[i];
265+
if (i == 0 && (flags & MP_MACHINE_I2C_FLAG_WRITE1)) {
266+
ret = machine_i2c_write(self, buf->buf, buf->len);
267+
} else if (flags & MP_MACHINE_I2C_FLAG_READ) {
268+
ret = machine_i2c_read(self, buf->buf, buf->len);
269+
} else if (bufs->len != 0) {
270+
ret = machine_i2c_write(self, buf->buf, buf->len);
271+
}
272+
if (ret < 0) {
273+
return ret;
274+
}
275+
bytes += bufs->len;
276+
}
277+
278+
// Wait for TX FIFO empty
279+
ret = machine_i2c_poll_flags(self->i2c, I2C_IC_STATUS_TFE, self->timeout);
280+
if (ret < 0) {
281+
return ret;
282+
}
283+
284+
return bytes;
285+
}
286+
287+
static const mp_machine_i2c_p_t machine_i2c_p = {
288+
.transfer_supports_write1 = true,
289+
.transfer = machine_i2c_transfer,
290+
};
291+
292+
MP_DEFINE_CONST_OBJ_TYPE(
293+
machine_i2c_type,
294+
MP_QSTR_I2C,
295+
MP_TYPE_FLAG_NONE,
296+
make_new, machine_i2c_make_new,
297+
print, machine_i2c_print,
298+
protocol, &machine_i2c_p,
299+
locals_dict, &mp_machine_i2c_locals_dict
300+
);
301+
#endif // MICROPY_HW_ENABLE_HW_I2C

ports/alif/mpconfigport.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,8 @@
130130
#define MICROPY_PY_MACHINE_ADC_INCLUDEFILE "ports/alif/machine_adc.c"
131131
#define MICROPY_PY_MACHINE_DHT_READINTO (1)
132132
#define MICROPY_PY_MACHINE_PULSE (1)
133+
#define MICROPY_PY_MACHINE_I2C (MICROPY_HW_ENABLE_HW_I2C)
134+
#define MICROPY_PY_MACHINE_I2C_TRANSFER_WRITE1 (1)
133135
#define MICROPY_PY_MACHINE_SOFTI2C (1)
134136
#define MICROPY_PY_MACHINE_SOFTSPI (1)
135137
#define MICROPY_PY_MACHINE_TIMER (1)

0 commit comments

Comments
 (0)