diff options
| -rw-r--r-- | ports/mimxrt/boards/common.ld | 2 | ||||
| -rw-r--r-- | ports/mimxrt/machine_i2c.c | 13 | ||||
| -rw-r--r-- | ports/mimxrt/machine_i2c.h | 38 | ||||
| -rw-r--r-- | ports/mimxrt/machine_i2c_target.c | 168 | ||||
| -rw-r--r-- | ports/mimxrt/main.c | 1 | ||||
| -rw-r--r-- | ports/mimxrt/mpconfigport.h | 5 |
6 files changed, 217 insertions, 10 deletions
diff --git a/ports/mimxrt/boards/common.ld b/ports/mimxrt/boards/common.ld index 477ba38bc..dcbc0a423 100644 --- a/ports/mimxrt/boards/common.ld +++ b/ports/mimxrt/boards/common.ld @@ -98,7 +98,7 @@ SECTIONS .text : { . = ALIGN(4); - *(EXCLUDE_FILE(*fsl_flexspi.o *gc.o *vm.o *parse*.o *runtime*.o *map.o *mpirq.o ) .text*) /* .text* sections (code) */ + *(EXCLUDE_FILE(*fsl_flexspi.o *gc.o *vm.o *runtime*.o *map.o *mpirq.o *machine_i2c_target.o *fsl_lpi2c.o) .text*) /* .text* sections (code) */ *(.rodata) /* .rodata sections (constants, strings, etc.) */ *(.rodata*) /* .rodata* sections (constants, strings, etc.) */ *(.glue_7) /* glue arm to thumb code */ diff --git a/ports/mimxrt/machine_i2c.c b/ports/mimxrt/machine_i2c.c index d170804f4..aa128e6ff 100644 --- a/ports/mimxrt/machine_i2c.c +++ b/ports/mimxrt/machine_i2c.c @@ -33,11 +33,7 @@ #include "fsl_iomuxc.h" #include "fsl_lpi2c.h" - -#define DEFAULT_I2C_ID (0) -#define DEFAULT_I2C_FREQ (400000) -#define DEFAULT_I2C_DRIVE (6) -#define DEFAULT_I2C_TIMEOUT (50000) +#include "machine_i2c.h" typedef struct _machine_i2c_obj_t { mp_obj_base_t base; @@ -57,12 +53,11 @@ typedef struct _iomux_table_t { uint32_t configRegister; } iomux_table_t; -static const uint8_t i2c_index_table[] = MICROPY_HW_I2C_INDEX; -static LPI2C_Type *i2c_base_ptr_table[] = LPI2C_BASE_PTRS; +const uint8_t i2c_index_table[] = MICROPY_HW_I2C_INDEX; +LPI2C_Type *i2c_base_ptr_table[] = LPI2C_BASE_PTRS; +const uint8_t micropy_hw_i2c_num = MICROPY_HW_I2C_NUM; static const iomux_table_t iomux_table[] = { IOMUX_TABLE_I2C }; -#define MICROPY_HW_I2C_NUM ARRAY_SIZE(i2c_index_table) - #define SCL (iomux_table[index]) #define SDA (iomux_table[index + 1]) diff --git a/ports/mimxrt/machine_i2c.h b/ports/mimxrt/machine_i2c.h new file mode 100644 index 000000000..c6d561ca5 --- /dev/null +++ b/ports/mimxrt/machine_i2c.h @@ -0,0 +1,38 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2025 Damien P. George + * Copyright (c) 2025 Robert Hammelrath + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#define DEFAULT_I2C_ID (0) +#define DEFAULT_I2C_FREQ (400000) +#define DEFAULT_I2C_DRIVE (6) +#define DEFAULT_I2C_TIMEOUT (50000) +#define DEFAULT_I2C_FILTER_NS (200) +#define MICROPY_HW_I2C_NUM ARRAY_SIZE(i2c_index_table) + +extern const uint8_t i2c_index_table[]; +extern LPI2C_Type *i2c_base_ptr_table[]; +extern bool lpi2c_set_iomux(int8_t hw_i2c, uint8_t drive); +extern const uint8_t micropy_hw_i2c_num; diff --git a/ports/mimxrt/machine_i2c_target.c b/ports/mimxrt/machine_i2c_target.c new file mode 100644 index 000000000..aa408071f --- /dev/null +++ b/ports/mimxrt/machine_i2c_target.c @@ -0,0 +1,168 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2025 Damien P. George + * Copyright (c) 2025 Robert Hammelrath + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +// This file is never compiled standalone, it's included directly from +// extmod/machine_i2c_target.c via MICROPY_PY_MACHINE_I2C_TARGET_INCLUDEFILE. + +#include "fsl_lpi2c.h" +#include "machine_i2c.h" +#include CLOCK_CONFIG_H + +typedef struct _machine_i2c_target_obj_t { + mp_obj_base_t base; + LPI2C_Type *i2c_inst; + uint8_t i2c_id; + uint8_t addr; + lpi2c_slave_config_t slave_config; + lpi2c_slave_handle_t handle; +} machine_i2c_target_obj_t; + +static void lpi2c_slave_callback(LPI2C_Type *base, lpi2c_slave_transfer_t *xfer, void *param) { + machine_i2c_target_obj_t *self = (machine_i2c_target_obj_t *)param; + machine_i2c_target_data_t *data = &machine_i2c_target_data[self->i2c_id]; + + switch (xfer->event) { + case kLPI2C_SlaveAddressMatchEvent: + // Controller addressed us. + machine_i2c_target_data_addr_match(data, xfer->receivedAddress & 1); + break; + case kLPI2C_SlaveReceiveEvent: + // Data from controller is available for reading. + machine_i2c_target_data_write_request(self, data); + break; + case kLPI2C_SlaveTransmitEvent: + // Controller is requesting data. + machine_i2c_target_data_read_request(self, data); + break; + case kLPI2C_SlaveCompletionEvent: + // Transfer done. + machine_i2c_target_data_stop(data); + break; + default: + break; + } +} + +/******************************************************************************/ +// I2CTarget port implementation + +static inline size_t mp_machine_i2c_target_get_index(machine_i2c_target_obj_t *self) { + return self->i2c_id; +} + +static inline void mp_machine_i2c_target_event_callback(machine_i2c_target_irq_obj_t *irq) { + mp_irq_handler(&irq->base); +} + +static size_t mp_machine_i2c_target_read_bytes(machine_i2c_target_obj_t *self, size_t len, uint8_t *buf) { + // LPI2C_Type *i2c_inst = self->i2c_inst; + // mp_int_t i = 0; + // mp_int_t val = 0; + // while (i < len && !((val = i2c_inst->SRDR) & LPI2C_SRDR_RXEMPTY_MASK)) { + // buf[i++] = (uint8_t)(val & 0xff); + // } + // return i; + // Simple and fast version for len == 1 + buf[0] = (uint8_t)(self->i2c_inst->SRDR); + return 1; +} + +static size_t mp_machine_i2c_target_write_bytes(machine_i2c_target_obj_t *self, size_t len, const uint8_t *buf) { + self->i2c_inst->STDR = buf[0]; + return 1; +} + +static inline void mp_machine_i2c_target_irq_config(machine_i2c_target_obj_t *self, unsigned int trigger) { + (void)self; + (void)trigger; +} + +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) { + enum { ARG_id, ARG_addr, ARG_addrsize, ARG_mem, ARG_mem_addrsize, ARG_drive }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_id, MP_ARG_INT, {.u_int = DEFAULT_I2C_ID} }, + { MP_QSTR_addr, MP_ARG_REQUIRED | MP_ARG_INT }, + { MP_QSTR_addrsize, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 7} }, + { MP_QSTR_mem, MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE}}, + { MP_QSTR_mem_addrsize, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 8} }, + { MP_QSTR_drive, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = DEFAULT_I2C_DRIVE} }, + }; + + // Parse arguments. + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + // Get I2C bus. + int i2c_id = args[ARG_id].u_int; + if (i2c_id < 0 || i2c_id >= micropy_hw_i2c_num || i2c_index_table[i2c_id] == 0) { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("I2C(%d) doesn't exist"), i2c_id); + } + int i2c_hw_id = i2c_index_table[i2c_id]; // the hw i2c number 1..n + + // Get I2C Object. + machine_i2c_target_obj_t *self = mp_obj_malloc_with_finaliser(machine_i2c_target_obj_t, &machine_i2c_target_type); + self->i2c_id = i2c_id; + self->i2c_inst = i2c_base_ptr_table[i2c_hw_id]; + uint8_t drive = args[ARG_drive].u_int; + if (drive < 1 || drive > 7) { + drive = DEFAULT_I2C_DRIVE; + } + // Set the target address. + self->addr = args[ARG_addr].u_int; + // Initialise data. + MP_STATE_PORT(machine_i2c_target_mem_obj)[i2c_id] = args[ARG_mem].u_obj; + machine_i2c_target_data_t *data = &machine_i2c_target_data[self->i2c_id]; + machine_i2c_target_data_init(data, args[ARG_mem].u_obj, args[ARG_mem_addrsize].u_int); + + // Initialise the GPIO pins + lpi2c_set_iomux(i2c_hw_id, drive); + // Initialise the I2C peripheral + LPI2C_SlaveGetDefaultConfig(&self->slave_config); + self->slave_config.address0 = self->addr; + self->slave_config.sdaGlitchFilterWidth_ns = DEFAULT_I2C_FILTER_NS; + self->slave_config.sclGlitchFilterWidth_ns = DEFAULT_I2C_FILTER_NS; + LPI2C_SlaveInit(self->i2c_inst, &self->slave_config, BOARD_BOOTCLOCKRUN_LPI2C_CLK_ROOT); + // Create the LPI2C handle for the non-blocking transfer + LPI2C_SlaveTransferCreateHandle(self->i2c_inst, &self->handle, lpi2c_slave_callback, self); + // Start accepting I2C transfers on the LPI2C slave peripheral + status_t reVal = LPI2C_SlaveTransferNonBlocking(self->i2c_inst, &self->handle, + kLPI2C_SlaveAddressMatchEvent | kLPI2C_SlaveTransmitEvent | kLPI2C_SlaveReceiveEvent | kLPI2C_SlaveCompletionEvent); + if (reVal != kStatus_Success) { + mp_raise_ValueError(MP_ERROR_TEXT("cannot start I2C")); + } + return MP_OBJ_FROM_PTR(self); +} + +static void mp_machine_i2c_target_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + machine_i2c_target_obj_t *self = MP_OBJ_TO_PTR(self_in); + mp_printf(print, "I2CTarget(%u, addr=%u)", self->i2c_id, self->addr); +} + +// Stop the Slave transfer and free the memory objects. +static void mp_machine_i2c_target_deinit(machine_i2c_target_obj_t *self) { + LPI2C_SlaveDeinit(self->i2c_inst); +} diff --git a/ports/mimxrt/main.c b/ports/mimxrt/main.c index 6b9d4fa17..7166171f1 100644 --- a/ports/mimxrt/main.c +++ b/ports/mimxrt/main.c @@ -55,6 +55,7 @@ #endif #include "systick.h" +#include "extmod/modmachine.h" #include "extmod/modnetwork.h" #include "extmod/vfs.h" diff --git a/ports/mimxrt/mpconfigport.h b/ports/mimxrt/mpconfigport.h index e1c605f45..8ceb3b418 100644 --- a/ports/mimxrt/mpconfigport.h +++ b/ports/mimxrt/mpconfigport.h @@ -92,6 +92,11 @@ uint32_t trng_random_u32(void); #define MICROPY_PY_MACHINE_PWM (1) #define MICROPY_PY_MACHINE_PWM_INCLUDEFILE "ports/mimxrt/machine_pwm.c" #define MICROPY_PY_MACHINE_I2C (1) +#define MICROPY_PY_MACHINE_I2C_TARGET (1) +#define MICROPY_PY_MACHINE_I2C_TARGET_INCLUDEFILE "ports/mimxrt/machine_i2c_target.c" +#define MICROPY_PY_MACHINE_I2C_TARGET_MAX (FSL_FEATURE_SOC_LPI2C_COUNT) +#define MICROPY_PY_MACHINE_I2C_TARGET_HARD_IRQ (1) +#define MICROPY_PY_MACHINE_I2C_TARGET_FINALISER (1) #ifndef MICROPY_PY_MACHINE_I2S #define MICROPY_PY_MACHINE_I2S (0) #endif |
