feat: add spi.set_irq() support

This commit is contained in:
feng-arch 2025-09-20 14:11:34 +08:00
parent 54b2eb0def
commit c3e391b190
4 changed files with 122 additions and 6 deletions

View File

@ -76,6 +76,17 @@ static mp_obj_t mp_machine_spi_wait_done(size_t n_args, const mp_obj_t *args)
}
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_machine_spi_wait_done_obj, 1, 2, mp_machine_spi_wait_done);
static mp_obj_t mp_machine_spi_set_tx_irq(size_t n_args, const mp_obj_t *args, mp_map_t *kw_args){
mp_obj_base_t *s = (mp_obj_base_t *)MP_OBJ_TO_PTR(args[0]);
mp_machine_spi_p_t *spi_p = (mp_machine_spi_p_t *)MP_OBJ_TYPE_GET_SLOT(s->type, protocol);
if (spi_p->set_tx_isr != NULL)
{
spi_p->set_tx_isr(s, n_args - 1, args + 1, kw_args);
}
return mp_const_none;
}
static MP_DEFINE_CONST_FUN_OBJ_KW(machine_spi_set_tx_irq_obj, 1, mp_machine_spi_set_tx_irq);
static mp_obj_t mp_machine_spi_read(size_t n_args, const mp_obj_t *args)
{
vstr_t vstr;
@ -128,6 +139,7 @@ static const mp_rom_map_elem_t machine_spi_locals_dict_table[] = {
{MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&mp_machine_spi_write_obj)},
{MP_ROM_QSTR(MP_QSTR_write_readinto), MP_ROM_PTR(&mp_machine_spi_write_readinto_obj)},
{MP_ROM_QSTR(MP_QSTR_wait_done), MP_ROM_PTR(&mp_machine_spi_wait_done_obj)},
{MP_ROM_QSTR(MP_QSTR_set_tx_irq), MP_ROM_PTR(&machine_spi_set_tx_irq_obj)},
{MP_ROM_QSTR(MP_QSTR_MSB), MP_ROM_INT(MICROPY_PY_MACHINE_SPI_MSB)},
{MP_ROM_QSTR(MP_QSTR_LSB), MP_ROM_INT(MICROPY_PY_MACHINE_SPI_LSB)},

View File

@ -194,6 +194,7 @@ typedef struct _mp_machine_spi_p_t
void (*deinit)(mp_obj_base_t *obj); // can be NULL
void (*transfer)(mp_obj_base_t *obj, size_t len, const uint8_t *src, uint8_t *dest);
void (*wait_done)(mp_obj_base_t *obj); // can be NULL
void (*set_tx_isr)(mp_obj_base_t *obj, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); // can be NULL
} mp_machine_spi_p_t;
// SoftSPI object.

View File

@ -1,4 +1,7 @@
make -j -C ports/rp2 BOARD=RPI_PICO USER_C_MODULES=../../user_modules/lv_binding_micropython/bindings.cmake
rm -rf ./ports/rp2/build-RPI_PICO/*
make -j -C ports/rp2 BOARD=RPI_PICO USER_C_MODULES=../../user_modules/user.cmake
exit status=$?
# 检查编译是否成功
if [ $? -eq 0 ]; then

View File

@ -27,6 +27,7 @@
#include "py/runtime.h"
#include "py/mphal.h"
#include "py/mperrno.h"
#include "shared/runtime/mpirq.h"
#include "extmod/modmachine.h"
#include "hardware/spi.h"
@ -104,6 +105,8 @@ typedef struct _machine_spi_obj_t
uint32_t baudrate;
int chan_tx;
int chan_rx;
// 这里只为屏幕设置了tx中断用来控制cs引脚和lv.disp_flush_ready()
mp_irq_obj_t *tx_isr_obj;
} machine_spi_obj_t;
static machine_spi_obj_t machine_spi_obj[] = {
@ -122,6 +125,7 @@ static machine_spi_obj_t machine_spi_obj[] = {
// DMA channels, -1 means not claimed
-1,
-1,
NULL
},
{
{&machine_spi_type},
@ -138,6 +142,7 @@ static machine_spi_obj_t machine_spi_obj[] = {
// DMA channels, -1 means not claimed
-1,
-1,
NULL
},
};
@ -314,6 +319,87 @@ static void machine_spi_init(mp_obj_base_t *self_in, size_t n_args, const mp_obj
}
}
static void machine_spi0_tx_irq_handler(void)
{
// currently only used for spi0
machine_spi_obj_t *self = &machine_spi_obj[0];
spi_hw_t *spi_hw = spi_get_hw(self->spi_inst);
// clear the interrupt flag
spi_hw->imsc &= ~(1 << 3);
// 检查是否是发送FIFO中断已屏蔽的中断
// if (spi_hw->mis & (1 << 3)) { // TXMIS=1
// 确认发送真正完成发送FIFO为空且SPI不忙
// mp_raise_msg_varg(&mp_type_AssertionError, MP_ERROR_TEXT("IRQ triggered"));
if ((spi_hw->sr & (1 << 0)) && !(spi_hw->sr & (1 << 4)) && dma_channel_is_busy(self->chan_tx) == false) {
if (self->tx_isr_obj != NULL && self->tx_isr_obj->handler != mp_const_none)
{
mp_irq_handler(self->tx_isr_obj);
}
}
// }
}
static void machine_spi1_tx_irq_handler(void)
{
// currently not used
machine_spi_obj_t *self = &machine_spi_obj[1];
spi_hw_t *spi_hw = spi_get_hw(self->spi_inst);
// clear the interrupt flag
spi_hw->imsc &= ~(1 << 3);
if (spi_hw->mis & (1 << 3)) { // TXMIS=1
// 确认发送真正完成发送FIFO为空且SPI不忙
if ((spi_hw->sr & (1 << 0)) && !(spi_hw->sr & (1 << 4))) {
if (self->tx_isr_obj != NULL && self->tx_isr_obj->handler != mp_const_none)
{
mp_irq_handler(self->tx_isr_obj);
}
}
}
}
static void machine_spi_set_tx_isr(mp_obj_base_t *self_in, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args)
{
machine_spi_obj_t *self = (machine_spi_obj_t *)self_in;
if (self->tx_isr_obj == NULL) {
self->tx_isr_obj = m_new_obj(mp_irq_obj_t);
// MP_STATE_PORT(rp2_uart_irq_obj)[self->uart_id] = self->mp_irq_obj;
}
enum
{
ARG_tx_isr,
};
static const mp_arg_t allowed_args[] = {
{MP_QSTR_tx_isr, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = mp_const_none}},
};
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);
if (n_args < 1) {
mp_raise_ValueError(MP_ERROR_TEXT("missing tx_isr"));
}
mp_obj_t handler = args[ARG_tx_isr].u_obj;
if (handler == mp_const_none || !mp_obj_is_callable(handler)) {
mp_raise_ValueError(MP_ERROR_TEXT("handler must be callable"));
}
if (self->tx_isr_obj == NULL) {
// disable previous irq
}
irq_set_enabled(self->spi_id == 0 ? SPI0_IRQ : SPI1_IRQ, false);
self->tx_isr_obj->handler = handler;
// self->tx_isr_obj->base.type = &mp_type_irq;
// self->tx_isr_obj->methods = &mp_irq_methods;
self->tx_isr_obj->ishard = true;
self->tx_isr_obj->parent = MP_OBJ_FROM_PTR(self);
irq_set_exclusive_handler(self->spi_id == 0 ? SPI0_IRQ : SPI1_IRQ, self->spi_id == 0 ? machine_spi0_tx_irq_handler : machine_spi1_tx_irq_handler);
spi_hw_t *spi_hw = spi_get_hw(self->spi_inst);
spi_hw->imsc |= (1 << 3);
irq_set_enabled(self->spi_id == 0 ? SPI0_IRQ : SPI1_IRQ, true);
}
static void machine_spi_wait_done(mp_obj_base_t *self_in)
{
machine_spi_obj_t *self = (machine_spi_obj_t *)self_in;
@ -332,6 +418,17 @@ static void machine_spi_wait_done(mp_obj_base_t *self_in)
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;
bool write_only = dest == NULL;
const size_t dma_min_size_threshold = 8;
if (len <= dma_min_size_threshold){
// Use software for small transfers
if (write_only) {
spi_write_blocking(self->spi_inst, src, len);
} else {
spi_write_read_blocking(self->spi_inst, src, dest, len);
}
return;
}
// Use DMA for large transfers if channels are available
int chan_tx = self->chan_tx;
int chan_rx = self->chan_rx;
@ -340,13 +437,13 @@ static void machine_spi_transfer(mp_obj_base_t *self_in, size_t len, const uint8
mp_raise_msg_varg(&mp_type_RuntimeError, MP_ERROR_TEXT("Error when using DMA, chan rx: %d, chan tx: %d"), chan_rx, chan_tx);
return;
}
if (chan_rx >= 0)
dma_channel_wait_for_finish_blocking(chan_rx);
if (chan_tx >= 0)
dma_channel_wait_for_finish_blocking(chan_tx);
// if (chan_rx >= 0)
// dma_channel_wait_for_finish_blocking(chan_rx);
// if (chan_tx >= 0)
// dma_channel_wait_for_finish_blocking(chan_tx);
machine_spi_wait_done(self_in);
// bool use_dma = chan_rx >= 0 && chan_tx >= 0;
// note src is guaranteed to be non-NULL
bool write_only = dest == NULL;
uint8_t dev_null;
dma_channel_config c;
@ -413,8 +510,11 @@ static const mp_machine_spi_p_t machine_spi_p = {
.init = machine_spi_init,
.transfer = machine_spi_transfer,
.wait_done = machine_spi_wait_done,
.set_tx_isr = machine_spi_set_tx_isr,
};
MP_DEFINE_CONST_OBJ_TYPE(
machine_spi_type,
MP_QSTR_SPI,