STM32: I2C unitary functions for IP V2

STM32 supported targets have 2 possible versions of I2C.
This patch makes the start / stop / read and write byte work ok for IP V2.
This was not working before and does not seem to be widely used.
pull/3488/head
Laurent MEUNIER 2016-12-15 14:51:14 +01:00
parent 20eb127f80
commit 20c9af8bec
6 changed files with 143 additions and 58 deletions

View File

@ -98,6 +98,7 @@ struct i2c_s {
IRQn_Type error_i2cIRQ;
uint32_t XferOperation;
volatile uint8_t event;
volatile int pending_start;
#if DEVICE_I2CSLAVE
uint8_t slave;
volatile uint8_t pending_slave_tx_master_rx;

View File

@ -98,6 +98,7 @@ struct i2c_s {
IRQn_Type error_i2cIRQ;
uint32_t XferOperation;
volatile uint8_t event;
volatile int pending_start;
#if DEVICE_I2CSLAVE
uint8_t slave;
volatile uint8_t pending_slave_tx_master_rx;

View File

@ -98,6 +98,7 @@ struct i2c_s {
IRQn_Type error_i2cIRQ;
uint32_t XferOperation;
volatile uint8_t event;
volatile int pending_start;
#if DEVICE_I2CSLAVE
uint8_t slave;
volatile uint8_t pending_slave_tx_master_rx;

View File

@ -98,6 +98,7 @@ struct i2c_s {
IRQn_Type error_i2cIRQ;
uint32_t XferOperation;
volatile uint8_t event;
volatile int pending_start;
#if DEVICE_I2CSLAVE
uint8_t slave;
volatile uint8_t pending_slave_tx_master_rx;

View File

@ -98,6 +98,7 @@ struct i2c_s {
IRQn_Type error_i2cIRQ;
uint32_t XferOperation;
volatile uint8_t event;
volatile int pending_start;
#if DEVICE_I2CSLAVE
uint8_t slave;
volatile uint8_t pending_slave_tx_master_rx;

View File

@ -341,6 +341,9 @@ void i2c_init(i2c_t *obj, PinName sda, PinName scl) {
// I2C Xfer operation init
obj_s->event = 0;
obj_s->XferOperation = I2C_FIRST_AND_LAST_FRAME;
#ifdef I2C_IP_VERSION_V2
obj_s->pending_start = 0;
#endif
}
void i2c_frequency(i2c_t *obj, int hz)
@ -440,6 +443,14 @@ i2c_t *get_i2c_obj(I2C_HandleTypeDef *hi2c){
return (obj);
}
void i2c_reset(i2c_t *obj) {
struct i2c_s *obj_s = I2C_S(obj);
/* As recommended in i2c_api.h, mainly send stop */
i2c_stop(obj);
/* then re-init */
i2c_init(obj, obj_s->sda, obj_s->scl);
}
/*
* UNITARY APIS.
* For very basic operations, direct registers access is needed
@ -514,7 +525,7 @@ int i2c_byte_read(i2c_t *obj, int last) {
timeout = FLAG_TIMEOUT;
while (__HAL_I2C_GET_FLAG(handle, I2C_FLAG_RXNE) == RESET) {
if ((timeout--) == 0) {
return -1;
return 2;
}
}
@ -535,7 +546,7 @@ int i2c_byte_write(i2c_t *obj, int data) {
(__HAL_I2C_GET_FLAG(handle, I2C_FLAG_BTF) == RESET) &&
(__HAL_I2C_GET_FLAG(handle, I2C_FLAG_ADDR) == RESET)) {
if ((timeout--) == 0) {
return 0;
return 2;
}
}
@ -548,92 +559,161 @@ int i2c_byte_write(i2c_t *obj, int data) {
}
#endif //I2C_IP_VERSION_V1
#ifdef I2C_IP_VERSION_V2
int i2c_start(i2c_t *obj) {
struct i2c_s *obj_s = I2C_S(obj);
I2C_HandleTypeDef *handle = &(obj_s->handle);
I2C_TypeDef *i2c = (I2C_TypeDef *)obj_s->i2c;
int timeout;
// Clear Acknowledge failure flag
__HAL_I2C_CLEAR_FLAG(handle, I2C_FLAG_AF);
// Wait the STOP condition has been previously correctly sent
timeout = FLAG_TIMEOUT;
while ((i2c->CR2 & I2C_CR2_STOP) == I2C_CR2_STOP){
if ((timeout--) == 0) {
return 1;
}
}
// Generate the START condition
i2c->CR2 |= I2C_CR2_START;
// Wait the START condition has been correctly sent
timeout = FLAG_TIMEOUT;
while (__HAL_I2C_GET_FLAG(handle, I2C_FLAG_BUSY) == RESET) {
if ((timeout--) == 0) {
return 1;
}
}
/* This I2C IP doesn't */
obj_s->pending_start = 1;
return 0;
}
int i2c_stop(i2c_t *obj) {
struct i2c_s *obj_s = I2C_S(obj);
I2C_TypeDef *i2c = (I2C_TypeDef *)obj_s->i2c;
I2C_HandleTypeDef *handle = &(obj_s->handle);
int timeout = FLAG_TIMEOUT;
#if DEVICE_I2CSLAVE
if (obj_s->slave) {
/* re-init slave when stop is requested */
i2c_init(obj, obj_s->sda, obj_s->scl);
return 0;
}
#endif
// Disable reload mode
handle->Instance->CR2 &= (uint32_t)~I2C_CR2_RELOAD;
// Generate the STOP condition
i2c->CR2 |= I2C_CR2_STOP;
handle->Instance->CR2 |= I2C_CR2_STOP;
timeout = FLAG_TIMEOUT;
while (!__HAL_I2C_GET_FLAG(handle, I2C_FLAG_STOPF)) {
if ((timeout--) == 0) {
return I2C_ERROR_BUS_BUSY;
}
}
/* Clear STOP Flag */
__HAL_I2C_CLEAR_FLAG(handle, I2C_FLAG_STOPF);
/* Erase slave address, this wiil be used as a marker
* to know when we need to prepare next start */
handle->Instance->CR2 &= ~I2C_CR2_SADD;
/* In case of mixed usage of the APIs (unitary + SYNC)
* re-inti HAL state */
if (obj_s->XferOperation != I2C_FIRST_AND_LAST_FRAME) {
i2c_init(obj, obj_s->sda, obj_s->scl);
}
return 0;
}
int i2c_byte_read(i2c_t *obj, int last) {
struct i2c_s *obj_s = I2C_S(obj);
I2C_TypeDef *i2c = (I2C_TypeDef *)obj_s->i2c;
I2C_HandleTypeDef *handle = &(obj_s->handle);
int timeout;
// Wait until the byte is received
timeout = FLAG_TIMEOUT;
while (__HAL_I2C_GET_FLAG(handle, I2C_FLAG_RXNE) == RESET) {
if ((timeout--) == 0) {
return -1;
int timeout = FLAG_TIMEOUT;
uint32_t tmpreg = handle->Instance->CR2;
char data;
#if DEVICE_I2CSLAVE
if (obj_s->slave) {
return i2c_slave_read(obj, &data, 1);
}
#endif
/* Then send data when there's room in the TX fifo */
if ((tmpreg & I2C_CR2_RELOAD) != 0) {
while (!__HAL_I2C_GET_FLAG(handle, I2C_FLAG_TCR)) {
if ((timeout--) == 0) {
printf("timeout in byte_read\r\n");
//return 2;
}
}
}
return (int)i2c->RXDR;
/* Enable reload mode as we don't know how many bytes will eb sent */
handle->Instance->CR2 |= I2C_CR2_RELOAD;
/* Set transfer size to 1 */
handle->Instance->CR2 |= (I2C_CR2_NBYTES & (1 << 16));
/* Set the prepared configuration */
handle->Instance->CR2 = tmpreg;
timeout = FLAG_TIMEOUT;
while (!__HAL_I2C_GET_FLAG(handle, I2C_FLAG_RXNE)) {
if ((timeout--) == 0) {
return 2;
}
}
/* Then Get Byte */
data = handle->Instance->RXDR;
if (last) {
/* Disable Address Acknowledge */
handle->Instance->CR2 |= I2C_CR2_NACK;
}
return data;
}
int i2c_byte_write(i2c_t *obj, int data) {
struct i2c_s *obj_s = I2C_S(obj);
I2C_TypeDef *i2c = (I2C_TypeDef *)obj_s->i2c;
I2C_HandleTypeDef *handle = &(obj_s->handle);
int timeout;
// Wait until the previous byte is transmitted
timeout = FLAG_TIMEOUT;
while (__HAL_I2C_GET_FLAG(handle, I2C_FLAG_TXIS) == RESET) {
if ((timeout--) == 0) {
return 0;
}
int timeout = FLAG_TIMEOUT;
uint32_t tmpreg = handle->Instance->CR2;
#if DEVICE_I2CSLAVE
if (obj_s->slave) {
return i2c_slave_write(obj, (char *) &data, 1);
}
#endif
if (obj_s->pending_start) {
obj_s->pending_start = 0;
//* First byte after the start is the address */
tmpreg |= (uint32_t)((uint32_t)data & I2C_CR2_SADD);
if (data & 0x01) {
tmpreg |= I2C_CR2_START | I2C_CR2_RD_WRN;
} else {
tmpreg |= I2C_CR2_START;
tmpreg &= ~I2C_CR2_RD_WRN;
}
/* Disable reload first to use it later */
tmpreg &= ~I2C_CR2_RELOAD;
/* Disable Autoend */
tmpreg &= ~I2C_CR2_AUTOEND;
/* Do not set any transfer size for now */
tmpreg |= (I2C_CR2_NBYTES & (1 << 16));
/* Set the prepared configuration */
handle->Instance->CR2 = tmpreg;
} else {
/* Set the prepared configuration */
tmpreg = handle->Instance->CR2;
i2c->TXDR = (uint8_t)data;
/* Then send data when there's room in the TX fifo */
if ((tmpreg & I2C_CR2_RELOAD) != 0) {
while (!__HAL_I2C_GET_FLAG(handle, I2C_FLAG_TCR)) {
if ((timeout--) == 0) {
printf("timeout in byte_write\r\n");
//return 2;
}
}
}
/* Enable reload mode as we don't know how many bytes will eb sent */
tmpreg |= I2C_CR2_RELOAD;
/* Set transfer size to 1 */
tmpreg |= (I2C_CR2_NBYTES & (1 << 16));
/* Set the prepared configuration */
handle->Instance->CR2 = tmpreg;
/* Prepare next write */
timeout = FLAG_TIMEOUT;
while (!__HAL_I2C_GET_FLAG(handle, I2C_FLAG_TXE)) {
if ((timeout--) == 0) {
return 2;
}
}
/* Write byte */
handle->Instance->TXDR = data;
}
return 1;
}
#endif //I2C_IP_VERSION_V2
void i2c_reset(i2c_t *obj) {
struct i2c_s *obj_s = I2C_S(obj);
/* As recommended in i2c_api.h, mainly send stop */
i2c_stop(obj);
/* then re-init */
i2c_init(obj, obj_s->sda, obj_s->scl);
}
/*
* SYNC APIS
*/