Skip to content

Commit

Permalink
Chibios: Stop I2C peripheral on transaction error (#17798)
Browse files Browse the repository at this point in the history
From the ChibiOS HAL I2C driver pages:

After a timeout the driver must be stopped and restarted because the bus is in
an uncertain state.

This commit does that stopping explicitly on any error that occurred, not only
timeouts. As all the i2c functions restart the peripheral if necessary it is
safe to do so.

Co-authored-by: Dasky <32983009+daskygit@users.noreply.github.com>

Co-authored-by: Dasky <32983009+daskygit@users.noreply.github.com>
  • Loading branch information
KarlK90 and daskygit authored Jul 26, 2022
1 parent 2899e69 commit 083b420
Showing 1 changed file with 24 additions and 15 deletions.
39 changes: 24 additions & 15 deletions platforms/chibios/drivers/i2c_master.c
Original file line number Diff line number Diff line change
Expand Up @@ -107,16 +107,25 @@ static const I2CConfig i2cconfig = {
#endif
};

static i2c_status_t chibios_to_qmk(const msg_t* status) {
switch (*status) {
case I2C_NO_ERROR:
return I2C_STATUS_SUCCESS;
case I2C_TIMEOUT:
return I2C_STATUS_TIMEOUT;
// I2C_BUS_ERROR, I2C_ARBITRATION_LOST, I2C_ACK_FAILURE, I2C_OVERRUN, I2C_PEC_ERROR, I2C_SMB_ALERT
default:
return I2C_STATUS_ERROR;
/**
* @brief Handles any I2C error condition by stopping the I2C peripheral and
* aborting any ongoing transactions. Furthermore ChibiOS status codes are
* converted into QMK codes.
*
* @param status ChibiOS specific I2C status code
* @return i2c_status_t QMK specific I2C status code
*/
static i2c_status_t i2c_epilogue(const msg_t status) {
if (status == I2C_NO_ERROR) {
return I2C_STATUS_SUCCESS;
}

// From ChibiOS HAL: "After a timeout the driver must be stopped and
// restarted because the bus is in an uncertain state." We also issue that
// hard stop in case of any error.
i2c_stop();

return status == I2C_TIMEOUT ? I2C_STATUS_TIMEOUT : I2C_STATUS_ERROR;
}

__attribute__((weak)) void i2c_init(void) {
Expand Down Expand Up @@ -149,14 +158,14 @@ i2c_status_t i2c_transmit(uint8_t address, const uint8_t* data, uint16_t length,
i2c_address = address;
i2cStart(&I2C_DRIVER, &i2cconfig);
msg_t status = i2cMasterTransmitTimeout(&I2C_DRIVER, (i2c_address >> 1), data, length, 0, 0, TIME_MS2I(timeout));
return chibios_to_qmk(&status);
return i2c_epilogue(status);
}

i2c_status_t i2c_receive(uint8_t address, uint8_t* data, uint16_t length, uint16_t timeout) {
i2c_address = address;
i2cStart(&I2C_DRIVER, &i2cconfig);
msg_t status = i2cMasterReceiveTimeout(&I2C_DRIVER, (i2c_address >> 1), data, length, TIME_MS2I(timeout));
return chibios_to_qmk(&status);
return i2c_epilogue(status);
}

i2c_status_t i2c_writeReg(uint8_t devaddr, uint8_t regaddr, const uint8_t* data, uint16_t length, uint16_t timeout) {
Expand All @@ -170,7 +179,7 @@ i2c_status_t i2c_writeReg(uint8_t devaddr, uint8_t regaddr, const uint8_t* data,
complete_packet[0] = regaddr;

msg_t status = i2cMasterTransmitTimeout(&I2C_DRIVER, (i2c_address >> 1), complete_packet, length + 1, 0, 0, TIME_MS2I(timeout));
return chibios_to_qmk(&status);
return i2c_epilogue(status);
}

i2c_status_t i2c_writeReg16(uint8_t devaddr, uint16_t regaddr, const uint8_t* data, uint16_t length, uint16_t timeout) {
Expand All @@ -185,22 +194,22 @@ i2c_status_t i2c_writeReg16(uint8_t devaddr, uint16_t regaddr, const uint8_t* da
complete_packet[1] = regaddr & 0xFF;

msg_t status = i2cMasterTransmitTimeout(&I2C_DRIVER, (i2c_address >> 1), complete_packet, length + 2, 0, 0, TIME_MS2I(timeout));
return chibios_to_qmk(&status);
return i2c_epilogue(status);
}

i2c_status_t i2c_readReg(uint8_t devaddr, uint8_t regaddr, uint8_t* data, uint16_t length, uint16_t timeout) {
i2c_address = devaddr;
i2cStart(&I2C_DRIVER, &i2cconfig);
msg_t status = i2cMasterTransmitTimeout(&I2C_DRIVER, (i2c_address >> 1), &regaddr, 1, data, length, TIME_MS2I(timeout));
return chibios_to_qmk(&status);
return i2c_epilogue(status);
}

i2c_status_t i2c_readReg16(uint8_t devaddr, uint16_t regaddr, uint8_t* data, uint16_t length, uint16_t timeout) {
i2c_address = devaddr;
i2cStart(&I2C_DRIVER, &i2cconfig);
uint8_t register_packet[2] = {regaddr >> 8, regaddr & 0xFF};
msg_t status = i2cMasterTransmitTimeout(&I2C_DRIVER, (i2c_address >> 1), register_packet, 2, data, length, TIME_MS2I(timeout));
return chibios_to_qmk(&status);
return i2c_epilogue(status);
}

void i2c_stop(void) {
Expand Down

0 comments on commit 083b420

Please sign in to comment.