stmhal/i2c: Add option to I2C to enable/disable use of DMA transfers.
New keyword option in constructor and init() method is "dma=<bool>". 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).
This commit is contained in:
parent
e6da6a720f
commit
c4e58eaa98
|
@ -92,7 +92,7 @@ Methods
|
||||||
|
|
||||||
.. only:: port_pyboard
|
.. 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:
|
Initialise the I2C bus with the given parameters:
|
||||||
|
|
||||||
|
@ -100,6 +100,9 @@ Methods
|
||||||
- ``addr`` is the 7-bit address (only sensible for a slave)
|
- ``addr`` is the 7-bit address (only sensible for a slave)
|
||||||
- ``baudrate`` is the SCL clock rate (only sensible for a master)
|
- ``baudrate`` is the SCL clock rate (only sensible for a master)
|
||||||
- ``gencall`` is whether to support general call mode
|
- ``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)
|
.. method:: I2C.is_ready(addr)
|
||||||
|
|
||||||
|
|
54
stmhal/i2c.c
54
stmhal/i2c.c
|
@ -109,6 +109,7 @@ typedef struct _pyb_i2c_obj_t {
|
||||||
I2C_HandleTypeDef *i2c;
|
I2C_HandleTypeDef *i2c;
|
||||||
const dma_descr_t *tx_dma_descr;
|
const dma_descr_t *tx_dma_descr;
|
||||||
const dma_descr_t *rx_dma_descr;
|
const dma_descr_t *rx_dma_descr;
|
||||||
|
bool *use_dma;
|
||||||
} pyb_i2c_obj_t;
|
} pyb_i2c_obj_t;
|
||||||
|
|
||||||
#if defined(MICROPY_HW_I2C1_SCL)
|
#if defined(MICROPY_HW_I2C1_SCL)
|
||||||
|
@ -121,21 +122,23 @@ I2C_HandleTypeDef I2CHandle2 = {.Instance = NULL};
|
||||||
I2C_HandleTypeDef I2CHandle3 = {.Instance = NULL};
|
I2C_HandleTypeDef I2CHandle3 = {.Instance = NULL};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
STATIC bool pyb_i2c_use_dma[3];
|
||||||
|
|
||||||
STATIC const pyb_i2c_obj_t pyb_i2c_obj[] = {
|
STATIC const pyb_i2c_obj_t pyb_i2c_obj[] = {
|
||||||
#if defined(MICROPY_HW_I2C1_SCL)
|
#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
|
#else
|
||||||
{{&pyb_i2c_type}, NULL, NULL, NULL},
|
{{&pyb_i2c_type}, NULL, NULL, NULL, NULL},
|
||||||
#endif
|
#endif
|
||||||
#if defined(MICROPY_HW_I2C2_SCL)
|
#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
|
#else
|
||||||
{{&pyb_i2c_type}, NULL, NULL, NULL},
|
{{&pyb_i2c_type}, NULL, NULL, NULL, NULL},
|
||||||
#endif
|
#endif
|
||||||
#if defined(MICROPY_HW_I2C3_SCL)
|
#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
|
#else
|
||||||
{{&pyb_i2c_type}, NULL, NULL, NULL},
|
{{&pyb_i2c_type}, NULL, NULL, NULL, NULL},
|
||||||
#endif
|
#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_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_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_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
|
// 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->OwnAddress2 = 0; // unused
|
||||||
init->NoStretchMode = I2C_NOSTRETCH_DISABLE;
|
init->NoStretchMode = I2C_NOSTRETCH_DISABLE;
|
||||||
|
|
||||||
|
*self->use_dma = args[4].u_bool;
|
||||||
|
|
||||||
// init the I2C bus
|
// init the I2C bus
|
||||||
i2c_init(self->i2c);
|
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];
|
uint8_t data[1];
|
||||||
pyb_buf_get_for_send(args[0].u_obj, &bufinfo, data);
|
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;
|
DMA_HandleTypeDef tx_dma;
|
||||||
if (query_irq() == IRQ_STATE_ENABLED) {
|
if (use_dma) {
|
||||||
dma_init(&tx_dma, self->tx_dma_descr, self->i2c);
|
dma_init(&tx_dma, self->tx_dma_descr, self->i2c);
|
||||||
self->i2c->hdmatx = &tx_dma;
|
self->i2c->hdmatx = &tx_dma;
|
||||||
self->i2c->hdmarx = NULL;
|
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;
|
HAL_StatusTypeDef status;
|
||||||
if (in_master_mode(self)) {
|
if (in_master_mode(self)) {
|
||||||
if (args[1].u_int == PYB_I2C_MASTER_ADDRESS) {
|
if (args[1].u_int == PYB_I2C_MASTER_ADDRESS) {
|
||||||
if (query_irq() == IRQ_STATE_ENABLED) {
|
if (use_dma) {
|
||||||
dma_deinit(self->tx_dma_descr);
|
dma_deinit(self->tx_dma_descr);
|
||||||
}
|
}
|
||||||
nlr_raise(mp_obj_new_exception_msg(&mp_type_TypeError, "addr argument required"));
|
nlr_raise(mp_obj_new_exception_msg(&mp_type_TypeError, "addr argument required"));
|
||||||
}
|
}
|
||||||
mp_uint_t i2c_addr = args[1].u_int << 1;
|
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);
|
status = HAL_I2C_Master_Transmit(self->i2c, i2c_addr, bufinfo.buf, bufinfo.len, args[2].u_int);
|
||||||
} else {
|
} else {
|
||||||
status = HAL_I2C_Master_Transmit_DMA(self->i2c, i2c_addr, bufinfo.buf, bufinfo.len);
|
status = HAL_I2C_Master_Transmit_DMA(self->i2c, i2c_addr, bufinfo.buf, bufinfo.len);
|
||||||
}
|
}
|
||||||
} else {
|
} 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);
|
status = HAL_I2C_Slave_Transmit(self->i2c, bufinfo.buf, bufinfo.len, args[2].u_int);
|
||||||
} else {
|
} else {
|
||||||
status = HAL_I2C_Slave_Transmit_DMA(self->i2c, bufinfo.buf, bufinfo.len);
|
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 we used DMA, wait for it to finish
|
||||||
if (query_irq() == IRQ_STATE_ENABLED) {
|
if (use_dma) {
|
||||||
if (status == HAL_OK) {
|
if (status == HAL_OK) {
|
||||||
status = i2c_wait_dma_finished(self->i2c, args[2].u_int);
|
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;
|
vstr_t vstr;
|
||||||
mp_obj_t o_ret = pyb_buf_get_for_recv(args[0].u_obj, &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;
|
DMA_HandleTypeDef rx_dma;
|
||||||
if (query_irq() == IRQ_STATE_ENABLED) {
|
if (use_dma) {
|
||||||
dma_init(&rx_dma, self->rx_dma_descr, self->i2c);
|
dma_init(&rx_dma, self->rx_dma_descr, self->i2c);
|
||||||
self->i2c->hdmatx = NULL;
|
self->i2c->hdmatx = NULL;
|
||||||
self->i2c->hdmarx = &rx_dma;
|
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"));
|
nlr_raise(mp_obj_new_exception_msg(&mp_type_TypeError, "addr argument required"));
|
||||||
}
|
}
|
||||||
mp_uint_t i2c_addr = args[1].u_int << 1;
|
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);
|
status = HAL_I2C_Master_Receive(self->i2c, i2c_addr, (uint8_t*)vstr.buf, vstr.len, args[2].u_int);
|
||||||
} else {
|
} else {
|
||||||
status = HAL_I2C_Master_Receive_DMA(self->i2c, i2c_addr, (uint8_t*)vstr.buf, vstr.len);
|
status = HAL_I2C_Master_Receive_DMA(self->i2c, i2c_addr, (uint8_t*)vstr.buf, vstr.len);
|
||||||
}
|
}
|
||||||
} else {
|
} 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);
|
status = HAL_I2C_Slave_Receive(self->i2c, (uint8_t*)vstr.buf, vstr.len, args[2].u_int);
|
||||||
} else {
|
} else {
|
||||||
status = HAL_I2C_Slave_Receive_DMA(self->i2c, (uint8_t*)vstr.buf, vstr.len);
|
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 we used DMA, wait for it to finish
|
||||||
if (query_irq() == IRQ_STATE_ENABLED) {
|
if (use_dma) {
|
||||||
if (status == HAL_OK) {
|
if (status == HAL_OK) {
|
||||||
status = i2c_wait_dma_finished(self->i2c, args[2].u_int);
|
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;
|
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;
|
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);
|
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 {
|
} else {
|
||||||
DMA_HandleTypeDef rx_dma;
|
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;
|
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;
|
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);
|
status = HAL_I2C_Mem_Write(self->i2c, i2c_addr, mem_addr, mem_addr_size, bufinfo.buf, bufinfo.len, args[3].u_int);
|
||||||
} else {
|
} else {
|
||||||
DMA_HandleTypeDef tx_dma;
|
DMA_HandleTypeDef tx_dma;
|
||||||
|
|
|
@ -7,7 +7,7 @@ from pyb import I2C
|
||||||
pyb.Accel()
|
pyb.Accel()
|
||||||
|
|
||||||
# get I2C bus
|
# get I2C bus
|
||||||
i2c = I2C(1, I2C.MASTER)
|
i2c = I2C(1, I2C.MASTER, dma=True)
|
||||||
|
|
||||||
# test polling mem_read
|
# test polling mem_read
|
||||||
pyb.disable_irq()
|
pyb.disable_irq()
|
||||||
|
|
Loading…
Reference in New Issue