i2c_master: minor, improve register read consistency

This commit is contained in:
King Kévin 2020-02-11 12:20:13 +01:00
parent 4ddf1562fc
commit 25ce80b73a
2 changed files with 60 additions and 37 deletions

View File

@ -15,7 +15,7 @@
/** library to communicate using I²C as master (code)
* @file
* @author King Kévin <kingkevin@cuvoodoo.info>
* @date 2017-2019
* @date 2017-2020
* @note peripherals used: I2C
*/
@ -298,6 +298,7 @@ enum i2c_master_rc i2c_master_start(uint32_t i2c)
bool retry = true; // retry after reset if first try failed
enum i2c_master_rc to_return; // return code
uint16_t sr1; // read register once, since reading/writing other registers or other events clears some flags
try:
to_return = I2C_MASTER_RC_NONE; // return code
// send (re-)start condition
@ -313,20 +314,22 @@ try:
i2c_send_start(i2c); // send start condition to start transaction
bool timeout = false; // remember if the timeout has been reached
systick_counter_enable(); // start timer
while ((I2C_CR1(i2c) & I2C_CR1_START) && !(I2C_SR1(i2c) & (I2C_SR1_BERR | I2C_SR1_ARLO)) && !timeout) { // wait until start condition has been accepted and cleared
while ((I2C_CR1(i2c) & I2C_CR1_START) && !((sr1 = I2C_SR1(i2c)) & (I2C_SR1_BERR | I2C_SR1_ARLO)) && !timeout) { // wait until start condition has been accepted and cleared
timeout |= systick_get_countflag(); // verify if timeout has been reached
}
if (I2C_SR1(i2c) & (I2C_SR1_BERR | I2C_SR1_ARLO)) {
sr1 = I2C_SR1(i2c); // be sure to get the current value
if (sr1 & (I2C_SR1_BERR | I2C_SR1_ARLO)) {
to_return = I2C_MASTER_RC_BUS_ERROR;
}
while (!(I2C_SR1(i2c) & I2C_SR1_SB) && !(I2C_SR1(i2c) & (I2C_SR1_BERR | I2C_SR1_ARLO)) && !timeout && I2C_MASTER_RC_NONE == to_return) { // wait until start condition is transmitted
while (!((sr1 = I2C_SR1(i2c)) & (I2C_SR1_SB | I2C_SR1_BERR | I2C_SR1_ARLO)) && !timeout && I2C_MASTER_RC_NONE == to_return) { // wait until start condition is transmitted
timeout |= systick_get_countflag(); // verify if timeout has been reached
}
if (I2C_SR1(i2c) & (I2C_SR1_BERR|I2C_SR1_ARLO)) {
sr1 = I2C_SR1(i2c); // be sure to get the current value
if (sr1 & (I2C_SR1_BERR|I2C_SR1_ARLO)) {
to_return = I2C_MASTER_RC_BUS_ERROR;
} else if (!(I2C_SR1(i2c) & I2C_SR1_SB)) { // the start bit has not been set although we the peripheral is not busy anymore
} else if (!(sr1 & I2C_SR1_SB)) { // the start bit has not been set although we the peripheral is not busy anymore
to_return = I2C_MASTER_RC_BUS_ERROR;
} else if (!(I2C_SR2(i2c) & I2C_SR2_MSL)) { // verify if in master mode
} else if (!(sr1 & I2C_SR2_MSL)) { // verify if in master mode
to_return = I2C_MASTER_RC_NOT_MASTER;
} else if (timeout) { // timeout has been reached, i.e. the peripheral hangs
to_return = I2C_MASTER_RC_NOT_MASTER;
@ -347,43 +350,47 @@ enum i2c_master_rc i2c_master_select_slave(uint32_t i2c, uint16_t slave, bool ad
cm3_assert(I2C1 == i2c || I2C2 == i2c);
enum i2c_master_rc rc = I2C_MASTER_RC_NONE; // to store I²C return codes
if (!(I2C_SR1(i2c) & I2C_SR1_SB)) { // start condition has not been sent
uint16_t sr1, sr2; // read register once, since reading/writing other registers or other events clears some flags
if (!((sr1 = I2C_SR1(i2c)) & I2C_SR1_SB)) { // start condition has not been sent
rc = i2c_master_start(i2c); // send start condition
if (I2C_MASTER_RC_NONE != rc) {
return rc;
}
}
if (!(I2C_SR2(i2c) & I2C_SR2_MSL)) { // I²C device is not in master mode
if (!((sr2 = I2C_SR2(i2c)) & I2C_SR2_MSL)) { // I²C device is not in master mode
return I2C_MASTER_RC_NOT_MASTER;
}
// select slave
I2C_SR1(i2c) &= ~(I2C_SR1_AF); // clear acknowledgement failure
if (!address_10bit) { // 7-bit address
I2C_SR1(i2c) &= ~(I2C_SR1_AF); // clear acknowledgement failure
i2c_send_7bit_address(i2c, slave, write ? I2C_WRITE : I2C_READ); // select slave, with read/write flag
while (!(I2C_SR1(i2c) & (I2C_SR1_ADDR | I2C_SR1_AF)) && !(I2C_SR1(i2c) & (I2C_SR1_BERR | I2C_SR1_ARLO))); // wait until address is transmitted
if (I2C_SR1(i2c) & (I2C_SR1_BERR | I2C_SR1_ARLO)) {
while (!((sr1 = I2C_SR1(i2c)) & (I2C_SR1_ADDR | I2C_SR1_AF | I2C_SR1_BERR | I2C_SR1_ARLO))); // wait until address is transmitted
if (sr1 & (I2C_SR1_BERR | I2C_SR1_ARLO)) {
return I2C_MASTER_RC_BUS_ERROR;
}
if (I2C_SR1(i2c) & I2C_SR1_AF) { // address has not been acknowledged
if (sr1 & I2C_SR1_AF) { // address has not been acknowledged
return I2C_MASTER_RC_NAK;
}
} else { // 10-bit address
// send first part of address
I2C_SR1(i2c) &= ~(I2C_SR1_AF); // clear acknowledgement failure
I2C_DR(i2c) = 11110000 | (((slave >> 8 ) & 0x3) << 1); // send first header (11110xx0, where xx are 2 MSb of slave address)
while (!(I2C_SR1(i2c) & (I2C_SR1_ADD10 | I2C_SR1_AF)) && !(I2C_SR1(i2c) & (I2C_SR1_BERR | I2C_SR1_ARLO))); // wait until first part of address is transmitted
if (I2C_SR1(i2c) & I2C_SR1_AF) { // address has not been acknowledged
while (!((sr1 = I2C_SR1(i2c)) & (I2C_SR1_ADD10 | I2C_SR1_AF | I2C_SR1_BERR | I2C_SR1_ARLO))); // wait until first part of address is transmitted
if (sr1 & (I2C_SR1_BERR | I2C_SR1_ARLO)) {
return I2C_MASTER_RC_BUS_ERROR;
}
if (sr1 & I2C_SR1_AF) { // address has not been acknowledged
return I2C_MASTER_RC_NAK;
}
// send second part of address
I2C_SR1(i2c) &= ~(I2C_SR1_AF); // clear acknowledgement failure
I2C_DR(i2c) = (slave & 0xff); // send remaining of address
while (!(I2C_SR1(i2c) & (I2C_SR1_ADDR|I2C_SR1_AF)) && !(I2C_SR1(i2c) & (I2C_SR1_BERR | I2C_SR1_ARLO))); // wait until remaining part of address is transmitted
if (I2C_SR1(i2c) & (I2C_SR1_BERR | I2C_SR1_ARLO)) {
while (!((sr1 = I2C_SR1(i2c)) & (I2C_SR1_ADDR | I2C_SR1_AF | I2C_SR1_BERR | I2C_SR1_ARLO))); // wait until remaining part of address is transmitted
if (sr1 & (I2C_SR1_BERR | I2C_SR1_ARLO)) {
return I2C_MASTER_RC_BUS_ERROR;
}
if (I2C_SR1(i2c) & I2C_SR1_AF) { // address has not been acknowledged
if (sr1 & I2C_SR1_AF) { // address has not been acknowledged
return I2C_MASTER_RC_NAK;
}
// go into receive mode if necessary
@ -395,11 +402,11 @@ enum i2c_master_rc i2c_master_select_slave(uint32_t i2c, uint16_t slave, bool ad
// send first part of address with receive flag
I2C_SR1(i2c) &= ~(I2C_SR1_AF); // clear acknowledgement failure
I2C_DR(i2c) = 11110001 | (((slave >> 8) & 0x3) << 1); // send header (11110xx1, where xx are 2 MSb of slave address)
while (!(I2C_SR1(i2c) & (I2C_SR1_ADDR | I2C_SR1_AF)) && !(I2C_SR1(i2c) & (I2C_SR1_BERR | I2C_SR1_ARLO))); // wait until remaining part of address is transmitted
if (I2C_SR1(i2c) & (I2C_SR1_BERR | I2C_SR1_ARLO)) {
while (!((sr1 = I2C_SR1(i2c)) & (I2C_SR1_ADDR | I2C_SR1_AF | I2C_SR1_BERR | I2C_SR1_ARLO))); // wait until remaining part of address is transmitted
if (sr1 & (I2C_SR1_BERR | I2C_SR1_ARLO)) {
return I2C_MASTER_RC_BUS_ERROR;
}
if (I2C_SR1(i2c) & I2C_SR1_AF) { // address has not been acknowledged
if (sr1 & I2C_SR1_AF) { // address has not been acknowledged
return I2C_MASTER_RC_NAK;
}
}
@ -416,6 +423,16 @@ enum i2c_master_rc i2c_master_read(uint32_t i2c, uint8_t* data, size_t data_size
if (NULL == data || 0 == data_size) { // no data to read
return I2C_MASTER_RC_NONE;
}
// I²C start condition check
uint16_t sr1 = I2C_SR1(i2c); // read once
if (!(sr1 & I2C_SR1_ADDR)) { // no slave have been selected
return I2C_MASTER_RC_NOT_READY;
}
if (sr1 & I2C_SR1_AF) { // check if the previous transaction went well
return I2C_MASTER_RC_NOT_READY;
}
if (1 == data_size) {
i2c_nack_current(i2c); // [N]ACK current byte
i2c_disable_ack(i2c); // NACK after first byte
@ -426,16 +443,13 @@ enum i2c_master_rc i2c_master_read(uint32_t i2c, uint8_t* data, size_t data_size
i2c_nack_current(i2c); // ACK current byte
i2c_enable_ack(i2c); // NAK after next byte
}
// reading SR2 will also also clear ADDR and start the transaction
if (!(I2C_SR2(i2c) & I2C_SR2_MSL)) { // I²C device is not master
uint16_t sr2 = I2C_SR2(i2c); // reading SR2 will also also clear ADDR in SR1 and start the transaction
if (!(sr2 & I2C_SR2_MSL)) { // I²C device is not master
return I2C_MASTER_RC_NOT_MASTER;
}
if ((I2C_SR2(i2c) & I2C_SR2_TRA)) { // I²C device not in receiver mode
if ((sr2 & I2C_SR2_TRA)) { // I²C device not in receiver mode
return I2C_MASTER_RC_NOT_RECEIVE;
}
if (I2C_SR1(i2c) & I2C_SR1_AF) { // check if the previous transaction went well
return I2C_MASTER_RC_NOT_READY;
}
// read data
for (size_t i = 0; i < data_size; i++) { // read bytes
@ -448,8 +462,8 @@ enum i2c_master_rc i2c_master_read(uint32_t i2c, uint8_t* data, size_t data_size
} else {
i2c_enable_ack(i2c); // ACK received byte to continue slave transmission
}
while (!(I2C_SR1(i2c) & I2C_SR1_RxNE) && !(I2C_SR1(i2c) & (I2C_SR1_BERR | I2C_SR1_ARLO))); // wait until byte has been received
if (I2C_SR1(i2c) & (I2C_SR1_BERR|I2C_SR1_ARLO)) {
while (!((sr1 = I2C_SR1(i2c)) & (I2C_SR1_RxNE | I2C_SR1_BERR | I2C_SR1_ARLO))); // wait until byte has been received
if (sr1 & (I2C_SR1_BERR|I2C_SR1_ARLO)) {
return I2C_MASTER_RC_BUS_ERROR;
}
data[i] = i2c_get_data(i2c); // read received byte
@ -466,15 +480,24 @@ enum i2c_master_rc i2c_master_write(uint32_t i2c, const uint8_t* data, size_t da
if (NULL == data || 0 == data_size) { // no data to write
return I2C_MASTER_RC_NONE;
}
if (!(I2C_SR2(i2c) & I2C_SR2_MSL)) { // I²C device is not master
// I²C start condition check
uint16_t sr1 = I2C_SR1(i2c); // read once
if (!(sr1 & I2C_SR1_ADDR)) { // no slave have been selected
return I2C_MASTER_RC_NOT_READY;
}
if (sr1 & I2C_SR1_AF) { // check if the previous transaction went well
return I2C_MASTER_RC_NOT_READY;
}
// master check
uint16_t sr2 = I2C_SR2(i2c); // reading SR2 will also also clear ADDR in SR1 and start the transaction
if (!(sr2 & I2C_SR2_MSL)) { // I²C device is not master
return I2C_MASTER_RC_NOT_MASTER;
}
if (!(I2C_SR2(i2c) & I2C_SR2_TRA)) { // I²C device not in transmitter mode
if (!(sr2 & I2C_SR2_TRA)) { // I²C device not in transmitter mode
return I2C_MASTER_RC_NOT_TRANSMIT;
}
if (I2C_SR1(i2c) & I2C_SR1_AF) { // check if the previous transaction went well
return I2C_MASTER_RC_NOT_READY;
}
// write data
for (size_t i = 0; i < data_size; i++) { // write bytes

View File

@ -15,8 +15,8 @@
/** library to communicate using I²C as master (API)
* @file
* @author King Kévin <kingkevin@cuvoodoo.info>
* @date 2017-2019
* @note peripherals used: I²C
* @date 2017-2020
* @note peripherals used: I2C
*/
#pragma once