stmhal: Make I2C use DMA when interrupts are enabled.
This commit is contained in:
parent
3d30d605f5
commit
76285469d3
171
stmhal/i2c.c
171
stmhal/i2c.c
@ -29,9 +29,11 @@
|
||||
|
||||
#include "py/nlr.h"
|
||||
#include "py/runtime.h"
|
||||
#include "irq.h"
|
||||
#include "pin.h"
|
||||
#include "genhdr/pins.h"
|
||||
#include "bufhelper.h"
|
||||
#include "dma.h"
|
||||
#include "i2c.h"
|
||||
#include MICROPY_HAL_H
|
||||
|
||||
@ -84,9 +86,26 @@
|
||||
/// # starting at address 2 in the slave
|
||||
/// i2c.mem_write('abc', 0x42, 2, timeout=1000)
|
||||
|
||||
// Possible DMA configurations for I2C busses:
|
||||
// I2C1_TX: DMA1_Stream6.CHANNEL_1 or DMA1_Stream7.CHANNEL_1
|
||||
// I2C1_RX: DMA1_Stream0.CHANNEL_1 or DMA1_Stream5.CHANNEL_1
|
||||
// I2C2_TX: DMA1_Stream7.CHANNEL_7
|
||||
// I2C2_RX: DMA1_Stream2.CHANNEL_7 or DMA1_Stream3.CHANNEL_7
|
||||
// I2C3_TX: DMA1_Stream4.CHANNEL_3
|
||||
// I2C3_RX: DMA1_Stream2.CHANNEL_3
|
||||
|
||||
#define PYB_I2C_MASTER (0)
|
||||
#define PYB_I2C_SLAVE (1)
|
||||
|
||||
typedef struct _pyb_i2c_obj_t {
|
||||
mp_obj_base_t base;
|
||||
I2C_HandleTypeDef *i2c;
|
||||
DMA_Stream_TypeDef *tx_dma_stream;
|
||||
uint32_t tx_dma_channel;
|
||||
DMA_Stream_TypeDef *rx_dma_stream;
|
||||
uint32_t rx_dma_channel;
|
||||
} pyb_i2c_obj_t;
|
||||
|
||||
#if defined(MICROPY_HW_I2C1_SCL)
|
||||
I2C_HandleTypeDef I2CHandle1 = {.Instance = NULL};
|
||||
#endif
|
||||
@ -97,6 +116,24 @@ I2C_HandleTypeDef I2CHandle2 = {.Instance = NULL};
|
||||
I2C_HandleTypeDef I2CHandle3 = {.Instance = NULL};
|
||||
#endif
|
||||
|
||||
STATIC const pyb_i2c_obj_t pyb_i2c_obj[] = {
|
||||
#if defined(MICROPY_HW_I2C1_SCL)
|
||||
{{&pyb_i2c_type}, &I2CHandle1, DMA1_Stream7, DMA_CHANNEL_1, DMA1_Stream0, DMA_CHANNEL_1},
|
||||
#else
|
||||
{{&pyb_i2c_type}, NULL, NULL, 0, NULL, 0},
|
||||
#endif
|
||||
#if defined(MICROPY_HW_I2C2_SCL)
|
||||
{{&pyb_i2c_type}, &I2CHandle2, DMA1_Stream7, DMA_CHANNEL_7, DMA1_Stream2, DMA_CHANNEL_7},
|
||||
#else
|
||||
{{&pyb_i2c_type}, NULL, NULL, 0, NULL, 0},
|
||||
#endif
|
||||
#if defined(MICROPY_HW_I2C3_SCL)
|
||||
{{&pyb_i2c_type}, &I2CHandle3, DMA1_Stream4, DMA_CHANNEL_3, DMA1_Stream2, DMA_CHANNEL_3},
|
||||
#else
|
||||
{{&pyb_i2c_type}, NULL, NULL, 0, NULL, 0},
|
||||
#endif
|
||||
};
|
||||
|
||||
void i2c_init0(void) {
|
||||
// reset the I2C1 handles
|
||||
#if defined(MICROPY_HW_I2C1_SCL)
|
||||
@ -120,10 +157,13 @@ void i2c_init(I2C_HandleTypeDef *i2c) {
|
||||
GPIO_InitStructure.Speed = GPIO_SPEED_FAST;
|
||||
GPIO_InitStructure.Pull = GPIO_NOPULL; // have external pull-up resistors on both lines
|
||||
|
||||
const pyb_i2c_obj_t *self;
|
||||
const pin_obj_t *pins[2];
|
||||
|
||||
if (0) {
|
||||
#if defined(MICROPY_HW_I2C1_SCL)
|
||||
} else if (i2c == &I2CHandle1) {
|
||||
self = &pyb_i2c_obj[0];
|
||||
pins[0] = &MICROPY_HW_I2C1_SCL;
|
||||
pins[1] = &MICROPY_HW_I2C1_SDA;
|
||||
GPIO_InitStructure.Alternate = GPIO_AF4_I2C1;
|
||||
@ -131,6 +171,7 @@ void i2c_init(I2C_HandleTypeDef *i2c) {
|
||||
#endif
|
||||
#if defined(MICROPY_HW_I2C2_SCL)
|
||||
} else if (i2c == &I2CHandle2) {
|
||||
self = &pyb_i2c_obj[1];
|
||||
pins[0] = &MICROPY_HW_I2C2_SCL;
|
||||
pins[1] = &MICROPY_HW_I2C2_SDA;
|
||||
GPIO_InitStructure.Alternate = GPIO_AF4_I2C2;
|
||||
@ -138,6 +179,7 @@ void i2c_init(I2C_HandleTypeDef *i2c) {
|
||||
#endif
|
||||
#if defined(MICROPY_HW_I2C3_SCL)
|
||||
} else if (i2c == &I2CHandle3) {
|
||||
self = &pyb_i2c_obj[2];
|
||||
pins[0] = &MICROPY_HW_I2C3_SCL;
|
||||
pins[1] = &MICROPY_HW_I2C3_SDA;
|
||||
GPIO_InitStructure.Alternate = GPIO_AF4_I2C3;
|
||||
@ -162,6 +204,10 @@ void i2c_init(I2C_HandleTypeDef *i2c) {
|
||||
printf("OSError: HAL_I2C_Init failed\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// invalidate the DMA channels so they are initialised on first use
|
||||
dma_invalidate_channel(self->tx_dma_stream, self->tx_dma_channel);
|
||||
dma_invalidate_channel(self->rx_dma_stream, self->rx_dma_channel);
|
||||
}
|
||||
|
||||
void i2c_deinit(I2C_HandleTypeDef *i2c) {
|
||||
@ -188,34 +234,24 @@ void i2c_deinit(I2C_HandleTypeDef *i2c) {
|
||||
}
|
||||
}
|
||||
|
||||
STATIC HAL_StatusTypeDef i2c_wait_dma_finished(I2C_HandleTypeDef *i2c, uint32_t timeout) {
|
||||
// Note: we can't use WFI to idle in this loop because the DMA completion
|
||||
// interrupt may occur before the WFI. Hence we miss it and have to wait
|
||||
// until the next sys-tick (up to 1ms).
|
||||
uint32_t start = HAL_GetTick();
|
||||
while (HAL_I2C_GetState(i2c) != HAL_I2C_STATE_READY) {
|
||||
if (HAL_GetTick() - start >= timeout) {
|
||||
return HAL_TIMEOUT;
|
||||
}
|
||||
}
|
||||
return HAL_OK;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
/* Micro Python bindings */
|
||||
|
||||
typedef struct _pyb_i2c_obj_t {
|
||||
mp_obj_base_t base;
|
||||
I2C_HandleTypeDef *i2c;
|
||||
} pyb_i2c_obj_t;
|
||||
|
||||
STATIC inline bool in_master_mode(pyb_i2c_obj_t *self) { return self->i2c->Init.OwnAddress1 == PYB_I2C_MASTER_ADDRESS; }
|
||||
|
||||
STATIC const pyb_i2c_obj_t pyb_i2c_obj[] = {
|
||||
#if defined(MICROPY_HW_I2C1_SCL)
|
||||
{{&pyb_i2c_type}, &I2CHandle1},
|
||||
#else
|
||||
{{&pyb_i2c_type}, NULL},
|
||||
#endif
|
||||
#if defined(MICROPY_HW_I2C2_SCL)
|
||||
{{&pyb_i2c_type}, &I2CHandle2},
|
||||
#else
|
||||
{{&pyb_i2c_type}, NULL},
|
||||
#endif
|
||||
#if defined(MICROPY_HW_I2C3_SCL)
|
||||
{{&pyb_i2c_type}, &I2CHandle3},
|
||||
#else
|
||||
{{&pyb_i2c_type}, NULL},
|
||||
#endif
|
||||
};
|
||||
|
||||
STATIC void pyb_i2c_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
|
||||
pyb_i2c_obj_t *self = self_in;
|
||||
|
||||
@ -435,16 +471,43 @@ STATIC mp_obj_t pyb_i2c_send(mp_uint_t n_args, const mp_obj_t *args, mp_map_t *k
|
||||
uint8_t data[1];
|
||||
pyb_buf_get_for_send(vals[0].u_obj, &bufinfo, data);
|
||||
|
||||
// if IRQs are enabled then we can use DMA
|
||||
DMA_HandleTypeDef tx_dma;
|
||||
if (query_irq() == IRQ_STATE_ENABLED) {
|
||||
dma_init(&tx_dma, self->tx_dma_stream, self->tx_dma_channel, DMA_MEMORY_TO_PERIPH, self->i2c);
|
||||
self->i2c->hdmatx = &tx_dma;
|
||||
self->i2c->hdmarx = NULL;
|
||||
}
|
||||
|
||||
// send the data
|
||||
HAL_StatusTypeDef status;
|
||||
if (in_master_mode(self)) {
|
||||
if (vals[1].u_int == PYB_I2C_MASTER_ADDRESS) {
|
||||
if (query_irq() == IRQ_STATE_ENABLED) {
|
||||
dma_deinit(&tx_dma);
|
||||
}
|
||||
nlr_raise(mp_obj_new_exception_msg(&mp_type_TypeError, "addr argument required"));
|
||||
}
|
||||
mp_uint_t i2c_addr = vals[1].u_int << 1;
|
||||
status = HAL_I2C_Master_Transmit(self->i2c, i2c_addr, bufinfo.buf, bufinfo.len, vals[2].u_int);
|
||||
if (query_irq() == IRQ_STATE_DISABLED) {
|
||||
status = HAL_I2C_Master_Transmit(self->i2c, i2c_addr, bufinfo.buf, bufinfo.len, vals[2].u_int);
|
||||
} else {
|
||||
status = HAL_I2C_Master_Transmit_DMA(self->i2c, i2c_addr, bufinfo.buf, bufinfo.len);
|
||||
}
|
||||
} else {
|
||||
status = HAL_I2C_Slave_Transmit(self->i2c, bufinfo.buf, bufinfo.len, vals[2].u_int);
|
||||
if (query_irq() == IRQ_STATE_DISABLED) {
|
||||
status = HAL_I2C_Slave_Transmit(self->i2c, bufinfo.buf, bufinfo.len, vals[2].u_int);
|
||||
} else {
|
||||
status = HAL_I2C_Slave_Transmit_DMA(self->i2c, bufinfo.buf, bufinfo.len);
|
||||
}
|
||||
}
|
||||
|
||||
// if we used DMA, wait for it to finish
|
||||
if (query_irq() == IRQ_STATE_ENABLED) {
|
||||
if (status == HAL_OK) {
|
||||
status = i2c_wait_dma_finished(self->i2c, vals[2].u_int);
|
||||
}
|
||||
dma_deinit(&tx_dma);
|
||||
}
|
||||
|
||||
if (status != HAL_OK) {
|
||||
@ -484,6 +547,14 @@ STATIC mp_obj_t pyb_i2c_recv(mp_uint_t n_args, const mp_obj_t *args, mp_map_t *k
|
||||
vstr_t vstr;
|
||||
mp_obj_t o_ret = pyb_buf_get_for_recv(vals[0].u_obj, &vstr);
|
||||
|
||||
// if IRQs are enabled then we can use DMA
|
||||
DMA_HandleTypeDef rx_dma;
|
||||
if (query_irq() == IRQ_STATE_ENABLED) {
|
||||
dma_init(&rx_dma, self->rx_dma_stream, self->rx_dma_channel, DMA_PERIPH_TO_MEMORY, self->i2c);
|
||||
self->i2c->hdmatx = NULL;
|
||||
self->i2c->hdmarx = &rx_dma;
|
||||
}
|
||||
|
||||
// receive the data
|
||||
HAL_StatusTypeDef status;
|
||||
if (in_master_mode(self)) {
|
||||
@ -491,9 +562,25 @@ STATIC mp_obj_t pyb_i2c_recv(mp_uint_t n_args, const mp_obj_t *args, mp_map_t *k
|
||||
nlr_raise(mp_obj_new_exception_msg(&mp_type_TypeError, "addr argument required"));
|
||||
}
|
||||
mp_uint_t i2c_addr = vals[1].u_int << 1;
|
||||
status = HAL_I2C_Master_Receive(self->i2c, i2c_addr, (uint8_t*)vstr.buf, vstr.len, vals[2].u_int);
|
||||
if (query_irq() == IRQ_STATE_DISABLED) {
|
||||
status = HAL_I2C_Master_Receive(self->i2c, i2c_addr, (uint8_t*)vstr.buf, vstr.len, vals[2].u_int);
|
||||
} else {
|
||||
status = HAL_I2C_Master_Receive_DMA(self->i2c, i2c_addr, (uint8_t*)vstr.buf, vstr.len);
|
||||
}
|
||||
} else {
|
||||
status = HAL_I2C_Slave_Receive(self->i2c, (uint8_t*)vstr.buf, vstr.len, vals[2].u_int);
|
||||
if (query_irq() == IRQ_STATE_DISABLED) {
|
||||
status = HAL_I2C_Slave_Receive(self->i2c, (uint8_t*)vstr.buf, vstr.len, vals[2].u_int);
|
||||
} else {
|
||||
status = HAL_I2C_Slave_Receive_DMA(self->i2c, (uint8_t*)vstr.buf, vstr.len);
|
||||
}
|
||||
}
|
||||
|
||||
// if we used DMA, wait for it to finish
|
||||
if (query_irq() == IRQ_STATE_ENABLED) {
|
||||
if (status == HAL_OK) {
|
||||
status = i2c_wait_dma_finished(self->i2c, vals[2].u_int);
|
||||
}
|
||||
dma_deinit(&rx_dma);
|
||||
}
|
||||
|
||||
if (status != HAL_OK) {
|
||||
@ -554,7 +641,20 @@ STATIC mp_obj_t pyb_i2c_mem_read(mp_uint_t n_args, const mp_obj_t *args, mp_map_
|
||||
mem_addr_size = I2C_MEMADD_SIZE_16BIT;
|
||||
}
|
||||
|
||||
HAL_StatusTypeDef status = HAL_I2C_Mem_Read(self->i2c, i2c_addr, mem_addr, mem_addr_size, (uint8_t*)vstr.buf, vstr.len, vals[3].u_int);
|
||||
HAL_StatusTypeDef status;
|
||||
if (query_irq() == IRQ_STATE_DISABLED) {
|
||||
status = HAL_I2C_Mem_Read(self->i2c, i2c_addr, mem_addr, mem_addr_size, (uint8_t*)vstr.buf, vstr.len, vals[3].u_int);
|
||||
} else {
|
||||
DMA_HandleTypeDef rx_dma;
|
||||
dma_init(&rx_dma, self->rx_dma_stream, self->rx_dma_channel, DMA_PERIPH_TO_MEMORY, self->i2c);
|
||||
self->i2c->hdmatx = NULL;
|
||||
self->i2c->hdmarx = &rx_dma;
|
||||
status = HAL_I2C_Mem_Read_DMA(self->i2c, i2c_addr, mem_addr, mem_addr_size, (uint8_t*)vstr.buf, vstr.len);
|
||||
if (status == HAL_OK) {
|
||||
status = i2c_wait_dma_finished(self->i2c, vals[3].u_int);
|
||||
}
|
||||
dma_deinit(&rx_dma);
|
||||
}
|
||||
|
||||
if (status != HAL_OK) {
|
||||
mp_hal_raise(status);
|
||||
@ -606,7 +706,20 @@ STATIC mp_obj_t pyb_i2c_mem_write(mp_uint_t n_args, const mp_obj_t *args, mp_map
|
||||
mem_addr_size = I2C_MEMADD_SIZE_16BIT;
|
||||
}
|
||||
|
||||
HAL_StatusTypeDef status = HAL_I2C_Mem_Write(self->i2c, i2c_addr, mem_addr, mem_addr_size, bufinfo.buf, bufinfo.len, vals[3].u_int);
|
||||
HAL_StatusTypeDef status;
|
||||
if (query_irq() == IRQ_STATE_DISABLED) {
|
||||
status = HAL_I2C_Mem_Write(self->i2c, i2c_addr, mem_addr, mem_addr_size, bufinfo.buf, bufinfo.len, vals[3].u_int);
|
||||
} else {
|
||||
DMA_HandleTypeDef tx_dma;
|
||||
dma_init(&tx_dma, self->tx_dma_stream, self->tx_dma_channel, DMA_MEMORY_TO_PERIPH, self->i2c);
|
||||
self->i2c->hdmatx = &tx_dma;
|
||||
self->i2c->hdmarx = NULL;
|
||||
status = HAL_I2C_Mem_Write_DMA(self->i2c, i2c_addr, mem_addr, mem_addr_size, bufinfo.buf, bufinfo.len);
|
||||
if (status == HAL_OK) {
|
||||
status = i2c_wait_dma_finished(self->i2c, vals[3].u_int);
|
||||
}
|
||||
dma_deinit(&tx_dma);
|
||||
}
|
||||
|
||||
if (status != HAL_OK) {
|
||||
mp_hal_raise(status);
|
||||
|
Loading…
Reference in New Issue
Block a user