summaryrefslogtreecommitdiffstatshomepage
path: root/stmhal/i2c.c
diff options
context:
space:
mode:
authorDamien George <damien.p.george@gmail.com>2015-06-10 13:53:00 +0100
committerDamien George <damien.p.george@gmail.com>2015-06-10 14:01:44 +0100
commit76285469d3e64ffe0312f58fa1f6270f715c842c (patch)
treea66945f78c5a6998b6dede4085bb09416aaedbbb /stmhal/i2c.c
parent3d30d605f5286774edb7669f3958628e5c656048 (diff)
downloadmicropython-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.c171
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);