diff options
| author | iabdalkader <i.abdalkader@gmail.com> | 2025-01-19 15:59:25 +0100 |
|---|---|---|
| committer | Damien George <damien@micropython.org> | 2025-04-09 00:22:32 +1000 |
| commit | 280e6e2a40dbddb3aec5424c1ca1bf9dea876dd0 (patch) | |
| tree | c90fd57995874dc6752b8557804b4162436ac6ad | |
| parent | 9073270c2e0daa1a3539aa14c0feadadd6c6b08d (diff) | |
alif/machine_spi: Add machine.SPI peripheral support.
Signed-off-by: iabdalkader <i.abdalkader@gmail.com>
Signed-off-by: Damien George <damien@micropython.org>
| -rw-r--r-- | ports/alif/alif.mk | 3 | ||||
| -rw-r--r-- | ports/alif/machine_spi.c | 312 | ||||
| -rw-r--r-- | ports/alif/mpconfigport.h | 1 |
3 files changed, 316 insertions, 0 deletions
diff --git a/ports/alif/alif.mk b/ports/alif/alif.mk index 5cfd37fc4..63003b58a 100644 --- a/ports/alif/alif.mk +++ b/ports/alif/alif.mk @@ -118,6 +118,7 @@ SRC_C = \ fatfs_port.c \ machine_pin.c \ machine_i2c.c \ + machine_spi.c \ main.c \ modalif.c \ mphalport.c \ @@ -188,6 +189,7 @@ ALIF_SRC_C += $(addprefix $(ALIF_DFP_REL_TOP)/,\ Device/core/$(MCU_CORE)/source/startup_$(MCU_CORE).c \ drivers/source/adc.c \ drivers/source/i2c.c \ + drivers/source/spi.c \ drivers/source/mhu_driver.c \ drivers/source/mhu_receiver.c \ drivers/source/mhu_sender.c \ @@ -206,6 +208,7 @@ ALIF_SRC_C += $(addprefix $(ALIF_DFP_REL_TOP)/,\ ) $(BUILD)/tinyusb_port/tusb_alif_dcd.o: CFLAGS += -Wno-unused-variable -DTUSB_ALIF_NO_IRQ_CFG=1 +$(BUILD)/$(ALIF_DFP_REL_TOP)/drivers/source/spi.o: CFLAGS += -Wno-maybe-uninitialized $(BUILD)/$(ALIF_DFP_REL_TOP)/se_services/source/services_host_boot.o: CFLAGS += -Wno-stringop-truncation $(BUILD)/$(ALIF_DFP_REL_TOP)/se_services/source/services_host_system.o: CFLAGS += -Wno-maybe-uninitialized diff --git a/ports/alif/machine_spi.c b/ports/alif/machine_spi.c new file mode 100644 index 000000000..d13c6368f --- /dev/null +++ b/ports/alif/machine_spi.c @@ -0,0 +1,312 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2024-2025 OpenMV LLC. + * + * 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. + */ + +#include "py/runtime.h" +#include "py/mphal.h" +#include "py/mperrno.h" +#include "extmod/modmachine.h" + +#if MICROPY_PY_MACHINE_SPI +#include "clk.h" +#include "spi.h" +#include "sys_ctrl_spi.h" + +typedef struct _machine_spi_obj_t { + mp_obj_base_t base; + uint8_t id; + SPI_Type *inst; + bool is_lp; +} machine_spi_obj_t; + +static machine_spi_obj_t machine_spi_obj[] = { + #if defined(MICROPY_HW_SPI0_SCK) + [0] = {{&machine_spi_type}, 0, (SPI_Type *)SPI0_BASE, false}, + #endif + #if defined(MICROPY_HW_SPI1_SCK) + [1] = {{&machine_spi_type}, 1, (SPI_Type *)SPI1_BASE, false}, + #endif + #if defined(MICROPY_HW_SPI2_SCK) + [2] = {{&machine_spi_type}, 2, (SPI_Type *)SPI2_BASE, false}, + #endif + #if defined(MICROPY_HW_SPI3_SCK) + [3] = {{&machine_spi_type}, 3, (SPI_Type *)SPI3_BASE, false}, + #endif + #if defined(MICROPY_HW_LPSPI0_SCK) + [4] = {{&machine_spi_type}, 4, (SPI_Type *)LPSPI0_BASE, true}, + #endif + +}; + +static inline uint32_t spi_get_clk(machine_spi_obj_t *spi) { + return spi->is_lp ? GetSystemCoreClock() : GetSystemAHBClock(); +} + +static void spi_init(machine_spi_obj_t *spi, uint32_t baudrate, + uint32_t polarity, uint32_t phase, uint32_t bits, uint32_t firstbit) { + const machine_pin_obj_t *pins[4] = { NULL, NULL, NULL, NULL }; + switch (spi->id) { + #if defined(MICROPY_HW_SPI0_SCK) + case 0: + pins[0] = MICROPY_HW_SPI0_SCK; + pins[1] = MICROPY_HW_SPI0_MISO; + pins[2] = MICROPY_HW_SPI0_MOSI; + #if defined(MICROPY_HW_SPI0_NSS) + pins[3] = MICROPY_HW_SPI0_NSS; + #endif + break; + #endif + #if defined(MICROPY_HW_SPI1_SCK) + case 1: + pins[0] = MICROPY_HW_SPI1_SCK; + pins[1] = MICROPY_HW_SPI1_MISO; + pins[2] = MICROPY_HW_SPI1_MOSI; + #if defined(MICROPY_HW_SPI1_NSS) + pins[3] = MICROPY_HW_SPI1_NSS; + #endif + break; + #endif + #if defined(MICROPY_HW_SPI2_SCK) + case 2: + pins[0] = MICROPY_HW_SPI2_SCK; + pins[1] = MICROPY_HW_SPI2_MISO; + pins[2] = MICROPY_HW_SPI2_MOSI; + #if defined(MICROPY_HW_SPI2_NSS) + pins[3] = MICROPY_HW_SPI2_NSS; + #endif + break; + #endif + #if defined(MICROPY_HW_SPI3_SCK) + case 3: + pins[0] = MICROPY_HW_SPI3_SCK; + pins[1] = MICROPY_HW_SPI3_MISO; + pins[2] = MICROPY_HW_SPI3_MOSI; + #if defined(MICROPY_HW_SPI3_NSS) + pins[3] = MICROPY_HW_SPI3_NSS; + #endif + break; + #endif + #if defined(MICROPY_HW_LPSPI0_SCK) + case 4: // LPSPI0 + pins[0] = MICROPY_HW_LPSPI0_SCK; + pins[1] = MICROPY_HW_LPSPI0_MISO; + pins[2] = MICROPY_HW_LPSPI0_MOSI; + #if defined(MICROPY_HW_LPSPI0_NSS) + pins[3] = MICROPY_HW_LPSPI0_NSS; + #endif + break; + #endif + default: + return; + } + + // Disable SPI. + spi_disable(spi->inst); + + // Enable clocks. + if (spi->is_lp) { + enable_lpspi_clk(); + } + + // Configure SPI pins. + for (size_t i = 0; i < MP_ARRAY_SIZE(pins) && pins[i]; i++) { + mp_hal_pin_config(pins[i], MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, + MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_8MA, MP_HAL_PIN_ALT_SPI, true); + } + + // Disable all interrupts. + spi_mask_interrupts(spi->inst); + + // Configure baudrate clock + spi_set_bus_speed(spi->inst, baudrate, spi_get_clk(spi)); + + // Configure FIFOs + spi_set_tx_threshold(spi->inst, 0); + spi_set_rx_threshold(spi->inst, 0); + if (!spi->is_lp) { + spi_set_rx_sample_delay(spi->inst, 0); + spi_set_tx_fifo_start_level(spi->inst, 0); + } + + // Configure SPI bus mode. + uint32_t spi_mode = (polarity << 1) | phase; + if (!spi->is_lp) { + spi_set_mode(spi->inst, spi_mode); + } else { + lpspi_set_mode(spi->inst, spi_mode); + } + + // Configure SPI bus protocol. + uint32_t spi_proto = SPI_PROTO_SPI; + if (!spi->is_lp) { + spi_set_protocol(spi->inst, spi_proto); + } else { + lpspi_set_protocol(spi->inst, spi_proto); + } + + // Configure SPI transfer mode. + if (!spi->is_lp) { + spi_mode_master(spi->inst); + } + + // Configure frame size. + if (!spi->is_lp) { + spi_set_dfs(spi->inst, bits); + } else { + lpspi_set_dfs(spi->inst, bits); + } + + // Configure slave select pin + spi_control_ss(spi->inst, 0, true); + if (!spi->is_lp) { + spi_set_sste(spi->inst, false); + } else { + lpspi_set_sste(spi->inst, false); + } + + // Clear IRQs. + (void)spi->inst->SPI_ICR; + + // Enable SPI. + spi_enable(spi->inst); +} + +static void machine_spi_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + machine_spi_obj_t *self = MP_OBJ_TO_PTR(self_in); + uint32_t baudrate = spi_get_bus_speed(self->inst, spi_get_clk(self)); + mp_printf(print, "SPI(%u, baudrate=%u, lp=%u)", self->id, baudrate, self->is_lp); +} + +mp_obj_t machine_spi_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_baudrate, ARG_polarity, ARG_phase, ARG_bits, ARG_firstbit, ARG_sck, ARG_mosi, ARG_miso }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_id, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_baudrate, MP_ARG_INT, {.u_int = 500000} }, + { MP_QSTR_polarity, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_phase, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_bits, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 8} }, + { MP_QSTR_firstbit, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_sck, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_mosi, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_miso, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + }; + + // Parse args. + 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 spi bus. + int spi_id = mp_obj_get_int(args[ARG_id].u_obj); + if (spi_id < 0 || spi_id >= MP_ARRAY_SIZE(machine_spi_obj) || !machine_spi_obj[spi_id].inst) { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("SPI(%d) doesn't exist"), spi_id); + } + + // Get static peripheral object. + machine_spi_obj_t *self = &machine_spi_obj[spi_id]; + + // here we would check the sck/mosi/miso pins and configure them, but it's not implemented + if (args[ARG_sck].u_obj != MP_OBJ_NULL || + args[ARG_mosi].u_obj != MP_OBJ_NULL || + args[ARG_miso].u_obj != MP_OBJ_NULL) { + mp_raise_ValueError(MP_ERROR_TEXT("explicit choice of sck/mosi/miso is not implemented")); + } + + // Initialize and configure SPI. + spi_init(self, args[ARG_baudrate].u_int, args[ARG_polarity].u_int, + args[ARG_phase].u_int, args[ARG_bits].u_int, args[ARG_firstbit].u_int); + + return MP_OBJ_FROM_PTR(self); +} + +static void machine_spi_init(mp_obj_base_t *self_in, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_baudrate, ARG_polarity, ARG_phase, ARG_bits, ARG_firstbit }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_baudrate, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, + { MP_QSTR_polarity, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, + { MP_QSTR_phase, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, + { MP_QSTR_bits, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, + { MP_QSTR_firstbit, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, + }; + + // Parse the arguments. + machine_spi_obj_t *self = (machine_spi_obj_t *)self_in; + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + // Initialize and configure SPI. + spi_init(self, args[ARG_baudrate].u_int, args[ARG_polarity].u_int, + args[ARG_phase].u_int, args[ARG_bits].u_int, args[ARG_firstbit].u_int); +} + +static void machine_spi_deinit(mp_obj_base_t *self_in) { + machine_spi_obj_t *self = (machine_spi_obj_t *)self_in; + // Disable all interrupts. + spi_mask_interrupts(self->inst); + // Disable SS pin. + spi_control_ss(self->inst, 0, 0); + // Disable SPI. + spi_disable(self->inst); + // Deinitialize GPIOs and clocks. + if (self->is_lp) { + disable_lpspi_clk(); + } +} + +static void machine_spi_transfer(mp_obj_base_t *self_in, size_t len, const uint8_t *src, uint8_t *dest) { + machine_spi_obj_t *self = (machine_spi_obj_t *)self_in; + spi_transfer_t spi_xfer = { + .tx_buff = src, + .tx_total_cnt = len, + .rx_buff = dest, + .rx_total_cnt = len, + .tx_default_val = 0xFF, + .tx_default_enable = true, + .mode = SPI_TMOD_TX_AND_RX, + }; + // TODO redo transfer_blocking to timeout and poll events. + if (!self->is_lp) { + spi_transfer_blocking(self->inst, &spi_xfer); + } else { + lpspi_transfer_blocking(self->inst, &spi_xfer); + } +} + +static const mp_machine_spi_p_t machine_spi_p = { + .init = machine_spi_init, + .deinit = machine_spi_deinit, + .transfer = machine_spi_transfer, +}; + +MP_DEFINE_CONST_OBJ_TYPE( + machine_spi_type, + MP_QSTR_SPI, + MP_TYPE_FLAG_NONE, + make_new, machine_spi_make_new, + print, machine_spi_print, + protocol, &machine_spi_p, + locals_dict, &mp_machine_spi_locals_dict + ); + +#endif // MICROPY_PY_MACHINE_SPI diff --git a/ports/alif/mpconfigport.h b/ports/alif/mpconfigport.h index b23886637..83f808b13 100644 --- a/ports/alif/mpconfigport.h +++ b/ports/alif/mpconfigport.h @@ -133,6 +133,7 @@ #define MICROPY_PY_MACHINE_I2C (MICROPY_HW_ENABLE_HW_I2C) #define MICROPY_PY_MACHINE_I2C_TRANSFER_WRITE1 (1) #define MICROPY_PY_MACHINE_SOFTI2C (1) +#define MICROPY_PY_MACHINE_SPI (1) #define MICROPY_PY_MACHINE_SOFTSPI (1) #define MICROPY_PY_MACHINE_TIMER (1) #define MICROPY_VFS (1) |
