STM32: I2C: Change the master sync implementation to use ITs

With this new implementation, as in slave implementaiton, we use the
interrupts instead of accessing to registers continuously.

This has 2 main advantages:
- this shall improve performances overall and allows for sleep
time in the future
- this also removes some direct registers access from this
layer of code and makes it more generic among families
pull/3238/head
Laurent MEUNIER 2016-10-20 13:43:52 +02:00
parent ec95aa5701
commit a50dc77c60
2 changed files with 68 additions and 86 deletions

View File

@ -95,6 +95,7 @@ struct i2c_s {
PinName scl; PinName scl;
IRQn_Type event_i2cIRQ; IRQn_Type event_i2cIRQ;
IRQn_Type error_i2cIRQ; IRQn_Type error_i2cIRQ;
uint8_t XferOperation;
volatile uint8_t event; volatile uint8_t event;
#if DEVICE_I2CSLAVE #if DEVICE_I2CSLAVE
uint8_t slave; uint8_t slave;
@ -105,7 +106,6 @@ struct i2c_s {
uint32_t address; uint32_t address;
uint8_t stop; uint8_t stop;
uint8_t available_events; uint8_t available_events;
uint8_t XferOperation;
#endif #endif
}; };

View File

@ -37,11 +37,6 @@
#include "pinmap.h" #include "pinmap.h"
#include "PeripheralPins.h" #include "PeripheralPins.h"
/* Timeout values for flags and events waiting loops. These timeouts are
not based on accurate values, they just guarantee that the application will
not remain stuck if the I2C communication is corrupted. */
#define FLAG_TIMEOUT ((int)0x1000)
#define LONG_TIMEOUT ((int)0x8000)
/* Timeout values are based on core clock and I2C clock. /* Timeout values are based on core clock and I2C clock.
The BYTE_TIMEOUT is computed as twice the number of cycles it would The BYTE_TIMEOUT is computed as twice the number of cycles it would
take to send 10 bits over I2C. Most Flags should take less than that. take to send 10 bits over I2C. Most Flags should take less than that.
@ -260,10 +255,9 @@ void i2c_init(i2c_t *obj, PinName sda, PinName scl) {
obj_s->pending_slave_rx_maxter_tx = 0; obj_s->pending_slave_rx_maxter_tx = 0;
#endif #endif
#if DEVICE_I2C_ASYNCH
// I2C Xfer operation init // I2C Xfer operation init
obj_s->event = 0;
obj_s->XferOperation = I2C_FIRST_AND_LAST_FRAME; obj_s->XferOperation = I2C_FIRST_AND_LAST_FRAME;
#endif
/* Activate default IRQ handlers for sync mode /* Activate default IRQ handlers for sync mode
* which would be overwritten in async mode * which would be overwritten in async mode
@ -354,95 +348,83 @@ inline int i2c_stop(i2c_t *obj) {
} }
int i2c_read(i2c_t *obj, int address, char *data, int length, int stop) { int i2c_read(i2c_t *obj, int address, char *data, int length, int stop) {
int timeout;
int count;
int value;
struct i2c_s *obj_s = I2C_S(obj); struct i2c_s *obj_s = I2C_S(obj);
I2C_HandleTypeDef *handle = &(obj_s->handle); I2C_HandleTypeDef *handle = &(obj_s->handle);
int count = 0, ret = 0;
uint32_t timeout = 0;
i2c_start(obj); if ((obj_s->XferOperation == I2C_FIRST_AND_LAST_FRAME) ||
(obj_s->XferOperation == I2C_LAST_FRAME)) {
if (stop)
obj_s->XferOperation = I2C_FIRST_AND_LAST_FRAME;
else
obj_s->XferOperation = I2C_FIRST_FRAME;
} else if ((obj_s->XferOperation == I2C_FIRST_FRAME) ||
(obj_s->XferOperation == I2C_NEXT_FRAME)) {
if (stop)
obj_s->XferOperation = I2C_LAST_FRAME;
else
obj_s->XferOperation = I2C_NEXT_FRAME;
}
// Wait until SB flag is set obj_s->event = 0;
timeout = FLAG_TIMEOUT; ret = HAL_I2C_Master_Sequential_Receive_IT(handle, address, (uint8_t *) data, length, obj_s->XferOperation);
while (__HAL_I2C_GET_FLAG(handle, I2C_FLAG_SB) == RESET) {
timeout--; if(ret == HAL_OK) {
if (timeout == 0) { timeout = BYTE_TIMEOUT_US * length;
return -1; /* transfer started : wait completion or timeout */
while(!(obj_s->event & I2C_EVENT_ALL) && (--timeout != 0)) {
wait_us(1);
}
if((timeout == 0) || (obj_s->event != I2C_EVENT_TRANSFER_COMPLETE)) {
/* re-init IP to try and get back in a working state */
i2c_init(obj, obj_s->sda, obj_s->scl);
} else {
count = length;
} }
} }
handle->Instance->DR = __HAL_I2C_7BIT_ADD_READ(address); return count;
// Wait address is acknowledged
timeout = FLAG_TIMEOUT;
while (__HAL_I2C_GET_FLAG(handle, I2C_FLAG_ADDR) == RESET) {
timeout--;
if (timeout == 0) {
return -1;
}
}
__HAL_I2C_CLEAR_ADDRFLAG(handle);
// Read all bytes except last one
for (count = 0; count < (length - 1); count++) {
value = i2c_byte_read(obj, 0);
data[count] = (char)value;
}
// If not repeated start, send stop.
// Warning: must be done BEFORE the data is read.
if (stop) {
i2c_stop(obj);
}
// Read the last byte
value = i2c_byte_read(obj, 1);
data[count] = (char)value;
return length;
} }
int i2c_write(i2c_t *obj, int address, const char *data, int length, int stop) { int i2c_write(i2c_t *obj, int address, const char *data, int length, int stop) {
int timeout;
int count;
struct i2c_s *obj_s = I2C_S(obj); struct i2c_s *obj_s = I2C_S(obj);
I2C_HandleTypeDef *handle = &(obj_s->handle); I2C_HandleTypeDef *handle = &(obj_s->handle);
int count = 0, ret = 0;
uint32_t timeout = 0;
i2c_start(obj); if ((obj_s->XferOperation == I2C_FIRST_AND_LAST_FRAME) ||
(obj_s->XferOperation == I2C_LAST_FRAME)) {
// Wait until SB flag is set if (stop)
timeout = FLAG_TIMEOUT; obj_s->XferOperation = I2C_FIRST_AND_LAST_FRAME;
while (__HAL_I2C_GET_FLAG(handle, I2C_FLAG_SB) == RESET) { else
timeout--; obj_s->XferOperation = I2C_FIRST_FRAME;
if (timeout == 0) { } else if ((obj_s->XferOperation == I2C_FIRST_FRAME) ||
return -1; (obj_s->XferOperation == I2C_NEXT_FRAME)) {
} if (stop)
obj_s->XferOperation = I2C_LAST_FRAME;
else
obj_s->XferOperation = I2C_NEXT_FRAME;
} }
handle->Instance->DR = __HAL_I2C_7BIT_ADD_WRITE(address); obj_s->event = 0;
// Wait address is acknowledged ret = HAL_I2C_Master_Sequential_Transmit_IT(handle, address, (uint8_t *) data, length, obj_s->XferOperation);
timeout = FLAG_TIMEOUT;
while (__HAL_I2C_GET_FLAG(handle, I2C_FLAG_ADDR) == RESET) { if(ret == HAL_OK) {
timeout--; timeout = BYTE_TIMEOUT_US * length;
if (timeout == 0) { /* transfer started : wait completion or timeout */
return -1; while(!(obj_s->event & I2C_EVENT_ALL) && (--timeout != 0)) {
wait_us(1);
} }
}
__HAL_I2C_CLEAR_ADDRFLAG(handle);
for (count = 0; count < length; count++) { if((timeout == 0) || (obj_s->event != I2C_EVENT_TRANSFER_COMPLETE)) {
if (i2c_byte_write(obj, data[count]) != 1) { /* re-init IP to try and get back in a working state */
i2c_stop(obj); i2c_init(obj, obj_s->sda, obj_s->scl);
return -1; } else {
} count = length;
} }
// If not repeated start, send stop.
if (stop) {
i2c_stop(obj);
} }
return count; return count;
@ -673,26 +655,25 @@ int i2c_slave_write(i2c_t *obj, const char *data, int length) {
#endif // DEVICE_I2CSLAVE #endif // DEVICE_I2CSLAVE
#if DEVICE_I2C_ASYNCH
void HAL_I2C_MasterTxCpltCallback(I2C_HandleTypeDef *hi2c){ void HAL_I2C_MasterTxCpltCallback(I2C_HandleTypeDef *hi2c){
/* Get object ptr based on handler ptr */ /* Get object ptr based on handler ptr */
i2c_t *obj = get_i2c_obj(hi2c); i2c_t *obj = get_i2c_obj(hi2c);
struct i2c_s *obj_s = I2C_S(obj); struct i2c_s *obj_s = I2C_S(obj);
#if DEVICE_I2C_ASYNCH
/* Handle potential Tx/Rx use case */ /* Handle potential Tx/Rx use case */
if ((obj->tx_buff.length) && (obj->rx_buff.length)) { if ((obj->tx_buff.length) && (obj->rx_buff.length)) {
if (obj_s->stop) {
if (obj_s->stop) {
obj_s->XferOperation = I2C_LAST_FRAME; obj_s->XferOperation = I2C_LAST_FRAME;
} } else {
else {
obj_s->XferOperation = I2C_NEXT_FRAME; obj_s->XferOperation = I2C_NEXT_FRAME;
} }
HAL_I2C_Master_Sequential_Receive_IT(hi2c, obj_s->address, (uint8_t*)obj->rx_buff.buffer , obj->rx_buff.length, obj_s->XferOperation); HAL_I2C_Master_Sequential_Receive_IT(hi2c, obj_s->address, (uint8_t*)obj->rx_buff.buffer , obj->rx_buff.length, obj_s->XferOperation);
} }
else { else
#endif
{
/* Set event flag */ /* Set event flag */
obj_s->event = I2C_EVENT_TRANSFER_COMPLETE; obj_s->event = I2C_EVENT_TRANSFER_COMPLETE;
} }
@ -720,6 +701,7 @@ void HAL_I2C_ErrorCallback(I2C_HandleTypeDef *hi2c){
obj_s->event = I2C_EVENT_ERROR; obj_s->event = I2C_EVENT_ERROR;
} }
#if DEVICE_I2C_ASYNCH
void HAL_I2C_AbortCpltCallback(I2C_HandleTypeDef *hi2c){ void HAL_I2C_AbortCpltCallback(I2C_HandleTypeDef *hi2c){
/* Get object ptr based on handler ptr */ /* Get object ptr based on handler ptr */
i2c_t *obj = get_i2c_obj(hi2c); i2c_t *obj = get_i2c_obj(hi2c);