diff options
author | Damien George <damien.p.george@gmail.com> | 2015-06-10 13:53:00 +0100 |
---|---|---|
committer | Damien George <damien.p.george@gmail.com> | 2015-06-10 14:01:44 +0100 |
commit | 76285469d3e64ffe0312f58fa1f6270f715c842c (patch) | |
tree | a66945f78c5a6998b6dede4085bb09416aaedbbb /stmhal/i2c.c | |
parent | 3d30d605f5286774edb7669f3958628e5c656048 (diff) | |
download | micropython-76285469d3e64ffe0312f58fa1f6270f715c842c.tar.gz micropython-76285469d3e64ffe0312f58fa1f6270f715c842c.zip |
stmhal: Make I2C use DMA when interrupts are enabled.
Diffstat (limited to 'stmhal/i2c.c')
-rw-r--r-- | stmhal/i2c.c | 171 |
1 files changed, 142 insertions, 29 deletions
diff --git a/stmhal/i2c.c b/stmhal/i2c.c index 2d0176124b..3b3c5c4025 100644 --- a/stmhal/i2c.c +++ b/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); |