From c4e58eaa9820f5e63d9d94cec5ffb403c1dffbe7 Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 11 Nov 2016 17:36:19 +1100 Subject: [PATCH] stmhal/i2c: Add option to I2C to enable/disable use of DMA transfers. New keyword option in constructor and init() method is "dma=". DMA is now disabled by default for I2C transfers because it currently does not handle I2C bus errors very well (eg if slave device doesn't ACK or NACK correctly during a transfer). --- docs/library/pyb.I2C.rst | 5 +++- stmhal/i2c.c | 54 ++++++++++++++++++++++++++-------------- tests/pyb/i2c_error.py | 2 +- 3 files changed, 40 insertions(+), 21 deletions(-) diff --git a/docs/library/pyb.I2C.rst b/docs/library/pyb.I2C.rst index 67131feec0..7400318902 100644 --- a/docs/library/pyb.I2C.rst +++ b/docs/library/pyb.I2C.rst @@ -92,7 +92,7 @@ Methods .. only:: port_pyboard - .. method:: I2C.init(mode, \*, addr=0x12, baudrate=400000, gencall=False) + .. method:: I2C.init(mode, \*, addr=0x12, baudrate=400000, gencall=False, dma=False) Initialise the I2C bus with the given parameters: @@ -100,6 +100,9 @@ Methods - ``addr`` is the 7-bit address (only sensible for a slave) - ``baudrate`` is the SCL clock rate (only sensible for a master) - ``gencall`` is whether to support general call mode + - ``dma`` is whether to allow the use of DMA for the I2C transfers (note + that DMA transfers have more precise timing but currently do not handle bus + errors properly) .. method:: I2C.is_ready(addr) diff --git a/stmhal/i2c.c b/stmhal/i2c.c index 07269bce5e..9f74a2139e 100644 --- a/stmhal/i2c.c +++ b/stmhal/i2c.c @@ -109,6 +109,7 @@ typedef struct _pyb_i2c_obj_t { I2C_HandleTypeDef *i2c; const dma_descr_t *tx_dma_descr; const dma_descr_t *rx_dma_descr; + bool *use_dma; } pyb_i2c_obj_t; #if defined(MICROPY_HW_I2C1_SCL) @@ -121,21 +122,23 @@ I2C_HandleTypeDef I2CHandle2 = {.Instance = NULL}; I2C_HandleTypeDef I2CHandle3 = {.Instance = NULL}; #endif +STATIC bool pyb_i2c_use_dma[3]; + STATIC const pyb_i2c_obj_t pyb_i2c_obj[] = { #if defined(MICROPY_HW_I2C1_SCL) - {{&pyb_i2c_type}, &I2CHandle1, &dma_I2C_1_TX, &dma_I2C_1_RX}, + {{&pyb_i2c_type}, &I2CHandle1, &dma_I2C_1_TX, &dma_I2C_1_RX, &pyb_i2c_use_dma[0]}, #else - {{&pyb_i2c_type}, NULL, NULL, NULL}, + {{&pyb_i2c_type}, NULL, NULL, NULL, NULL}, #endif #if defined(MICROPY_HW_I2C2_SCL) - {{&pyb_i2c_type}, &I2CHandle2, &dma_I2C_2_TX, &dma_I2C_2_RX}, + {{&pyb_i2c_type}, &I2CHandle2, &dma_I2C_2_TX, &dma_I2C_2_RX, &pyb_i2c_use_dma[1]}, #else - {{&pyb_i2c_type}, NULL, NULL, NULL}, + {{&pyb_i2c_type}, NULL, NULL, NULL, NULL}, #endif #if defined(MICROPY_HW_I2C3_SCL) - {{&pyb_i2c_type}, &I2CHandle3, &dma_I2C_3_TX, &dma_I2C_3_RX}, + {{&pyb_i2c_type}, &I2CHandle3, &dma_I2C_3_TX, &dma_I2C_3_RX, &pyb_i2c_use_dma[2]}, #else - {{&pyb_i2c_type}, NULL, NULL, NULL}, + {{&pyb_i2c_type}, NULL, NULL, NULL, NULL}, #endif }; @@ -338,6 +341,7 @@ STATIC mp_obj_t pyb_i2c_init_helper(const pyb_i2c_obj_t *self, mp_uint_t n_args, { MP_QSTR_addr, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0x12} }, { MP_QSTR_baudrate, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = MICROPY_HW_I2C_BAUDRATE_DEFAULT} }, { MP_QSTR_gencall, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} }, + { MP_QSTR_dma, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} }, }; // parse args @@ -362,6 +366,8 @@ STATIC mp_obj_t pyb_i2c_init_helper(const pyb_i2c_obj_t *self, mp_uint_t n_args, init->OwnAddress2 = 0; // unused init->NoStretchMode = I2C_NOSTRETCH_DISABLE; + *self->use_dma = args[4].u_bool; + // init the I2C bus i2c_init(self->i2c); @@ -514,9 +520,11 @@ STATIC mp_obj_t pyb_i2c_send(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_ uint8_t data[1]; pyb_buf_get_for_send(args[0].u_obj, &bufinfo, data); - // if IRQs are enabled then we can use DMA + // if option is set and IRQs are enabled then we can use DMA + bool use_dma = *self->use_dma && query_irq() == IRQ_STATE_ENABLED; + DMA_HandleTypeDef tx_dma; - if (query_irq() == IRQ_STATE_ENABLED) { + if (use_dma) { dma_init(&tx_dma, self->tx_dma_descr, self->i2c); self->i2c->hdmatx = &tx_dma; self->i2c->hdmarx = NULL; @@ -526,19 +534,19 @@ STATIC mp_obj_t pyb_i2c_send(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_ HAL_StatusTypeDef status; if (in_master_mode(self)) { if (args[1].u_int == PYB_I2C_MASTER_ADDRESS) { - if (query_irq() == IRQ_STATE_ENABLED) { + if (use_dma) { dma_deinit(self->tx_dma_descr); } nlr_raise(mp_obj_new_exception_msg(&mp_type_TypeError, "addr argument required")); } mp_uint_t i2c_addr = args[1].u_int << 1; - if (query_irq() == IRQ_STATE_DISABLED) { + if (!use_dma) { status = HAL_I2C_Master_Transmit(self->i2c, i2c_addr, bufinfo.buf, bufinfo.len, args[2].u_int); } else { status = HAL_I2C_Master_Transmit_DMA(self->i2c, i2c_addr, bufinfo.buf, bufinfo.len); } } else { - if (query_irq() == IRQ_STATE_DISABLED) { + if (!use_dma) { status = HAL_I2C_Slave_Transmit(self->i2c, bufinfo.buf, bufinfo.len, args[2].u_int); } else { status = HAL_I2C_Slave_Transmit_DMA(self->i2c, bufinfo.buf, bufinfo.len); @@ -546,7 +554,7 @@ STATIC mp_obj_t pyb_i2c_send(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_ } // if we used DMA, wait for it to finish - if (query_irq() == IRQ_STATE_ENABLED) { + if (use_dma) { if (status == HAL_OK) { status = i2c_wait_dma_finished(self->i2c, args[2].u_int); } @@ -588,9 +596,11 @@ STATIC mp_obj_t pyb_i2c_recv(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_ vstr_t vstr; mp_obj_t o_ret = pyb_buf_get_for_recv(args[0].u_obj, &vstr); - // if IRQs are enabled then we can use DMA + // if option is set and IRQs are enabled then we can use DMA + bool use_dma = *self->use_dma && query_irq() == IRQ_STATE_ENABLED; + DMA_HandleTypeDef rx_dma; - if (query_irq() == IRQ_STATE_ENABLED) { + if (use_dma) { dma_init(&rx_dma, self->rx_dma_descr, self->i2c); self->i2c->hdmatx = NULL; self->i2c->hdmarx = &rx_dma; @@ -603,13 +613,13 @@ STATIC mp_obj_t pyb_i2c_recv(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_ nlr_raise(mp_obj_new_exception_msg(&mp_type_TypeError, "addr argument required")); } mp_uint_t i2c_addr = args[1].u_int << 1; - if (query_irq() == IRQ_STATE_DISABLED) { + if (!use_dma) { status = HAL_I2C_Master_Receive(self->i2c, i2c_addr, (uint8_t*)vstr.buf, vstr.len, args[2].u_int); } else { status = HAL_I2C_Master_Receive_DMA(self->i2c, i2c_addr, (uint8_t*)vstr.buf, vstr.len); } } else { - if (query_irq() == IRQ_STATE_DISABLED) { + if (!use_dma) { status = HAL_I2C_Slave_Receive(self->i2c, (uint8_t*)vstr.buf, vstr.len, args[2].u_int); } else { status = HAL_I2C_Slave_Receive_DMA(self->i2c, (uint8_t*)vstr.buf, vstr.len); @@ -617,7 +627,7 @@ STATIC mp_obj_t pyb_i2c_recv(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_ } // if we used DMA, wait for it to finish - if (query_irq() == IRQ_STATE_ENABLED) { + if (use_dma) { if (status == HAL_OK) { status = i2c_wait_dma_finished(self->i2c, args[2].u_int); } @@ -680,8 +690,11 @@ STATIC mp_obj_t pyb_i2c_mem_read(mp_uint_t n_args, const mp_obj_t *pos_args, mp_ mem_addr_size = I2C_MEMADD_SIZE_16BIT; } + // if option is set and IRQs are enabled then we can use DMA + bool use_dma = *self->use_dma && query_irq() == IRQ_STATE_ENABLED; + HAL_StatusTypeDef status; - if (query_irq() == IRQ_STATE_DISABLED) { + if (!use_dma) { status = HAL_I2C_Mem_Read(self->i2c, i2c_addr, mem_addr, mem_addr_size, (uint8_t*)vstr.buf, vstr.len, args[3].u_int); } else { DMA_HandleTypeDef rx_dma; @@ -744,8 +757,11 @@ STATIC mp_obj_t pyb_i2c_mem_write(mp_uint_t n_args, const mp_obj_t *pos_args, mp mem_addr_size = I2C_MEMADD_SIZE_16BIT; } + // if option is set and IRQs are enabled then we can use DMA + bool use_dma = *self->use_dma && query_irq() == IRQ_STATE_ENABLED; + HAL_StatusTypeDef status; - if (query_irq() == IRQ_STATE_DISABLED) { + if (!use_dma) { status = HAL_I2C_Mem_Write(self->i2c, i2c_addr, mem_addr, mem_addr_size, bufinfo.buf, bufinfo.len, args[3].u_int); } else { DMA_HandleTypeDef tx_dma; diff --git a/tests/pyb/i2c_error.py b/tests/pyb/i2c_error.py index d6c0debe29..3201d6367d 100644 --- a/tests/pyb/i2c_error.py +++ b/tests/pyb/i2c_error.py @@ -7,7 +7,7 @@ from pyb import I2C pyb.Accel() # get I2C bus -i2c = I2C(1, I2C.MASTER) +i2c = I2C(1, I2C.MASTER, dma=True) # test polling mem_read pyb.disable_irq()