stmhal: Make I2C use DMA when interrupts are enabled.

This commit is contained in:
Damien George 2015-06-10 13:53:00 +01:00
parent 3d30d605f5
commit 76285469d3
1 changed files with 142 additions and 29 deletions

View File

@ -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);