From 12e1e841ea217ef3eb7ae9c0542ecd57827fec4a Mon Sep 17 00:00:00 2001 From: fauxpark Date: Fri, 3 Sep 2021 16:58:41 +1000 Subject: [PATCH 1/7] i2c_master: Add support for reading/writing to 16-bit registers --- docs/i2c_driver.md | 48 ++++++++++++++++++++++- platforms/avr/drivers/i2c_master.c | 53 ++++++++++++++++++++++++++ platforms/avr/drivers/i2c_master.h | 2 + platforms/chibios/drivers/i2c_master.c | 22 +++++++++++ platforms/chibios/drivers/i2c_master.h | 2 + 5 files changed, 125 insertions(+), 2 deletions(-) diff --git a/docs/i2c_driver.md b/docs/i2c_driver.md index 3ec34a0f87f2..335123de23f7 100644 --- a/docs/i2c_driver.md +++ b/docs/i2c_driver.md @@ -190,7 +190,7 @@ Receive multiple bytes from the selected SPI device. ### `i2c_status_t i2c_writeReg(uint8_t devaddr, uint8_t regaddr, uint8_t* data, uint16_t length, uint16_t timeout)` -Writes to a register on the I2C device. +Writes to an 8-bit register on the I2C device. #### Arguments @@ -211,9 +211,32 @@ Writes to a register on the I2C device. --- +### `i2c_status_t i2c_writeReg16(uint8_t devaddr, uint16_t regaddr, uint8_t* data, uint16_t length, uint16_t timeout)` + +Writes to a 16-bit register on the I2C device. + +#### Arguments + + - `uint8_t devaddr` + The 7-bit I2C address of the device. + - `uint16_t regaddr` + The register address to write to. + - `uint8_t *data` + A pointer to the data to transmit. + - `uint16_t length` + The number of bytes to write. Take care not to overrun the length of `data`. + - `uint16_t timeout` + The time in milliseconds to wait for a response from the target device. + +#### Return Value + +`I2C_STATUS_TIMEOUT` if the timeout period elapses, `I2C_STATUS_ERROR` if some other error occurs, otherwise `I2C_STATUS_SUCCESS`. + +--- + ### `i2c_status_t i2c_readReg(uint8_t devaddr, uint8_t regaddr, uint8_t* data, uint16_t length, uint16_t timeout)` -Reads from a register on the I2C device. +Reads from an 8-bit register on the I2C device. #### Arguments @@ -232,6 +255,27 @@ Reads from a register on the I2C device. --- +### `i2c_status_t i2c_readReg16(uint8_t devaddr, uint16_t regaddr, uint8_t* data, uint16_t length, uint16_t timeout)` + +Reads from a 16-bit register on the I2C device. + +#### Arguments + + - `uint8_t devaddr` + The 7-bit I2C address of the device. + - `uint16_t regaddr` + The register address to read from. + - `uint16_t length` + The number of bytes to read. Take care not to overrun the length of `data`. + - `uint16_t timeout` + The time in milliseconds to wait for a response from the target device. + +#### Return Value + +`I2C_STATUS_TIMEOUT` if the timeout period elapses, `I2C_STATUS_ERROR` if some other error occurs, otherwise `I2C_STATUS_SUCCESS`. + +--- + ### `i2c_status_t i2c_stop(void)` Stop the current I2C transaction. diff --git a/platforms/avr/drivers/i2c_master.c b/platforms/avr/drivers/i2c_master.c index 2773e00778ef..9725548e0b90 100644 --- a/platforms/avr/drivers/i2c_master.c +++ b/platforms/avr/drivers/i2c_master.c @@ -202,6 +202,22 @@ i2c_status_t i2c_writeReg(uint8_t devaddr, uint8_t regaddr, const uint8_t* data, return status; } +i2c_status_t i2c_writeReg16(uint8_t devaddr, uint16_t regaddr, const uint8_t* data, uint16_t length, uint16_t timeout) { + i2c_status_t status = i2c_start(devaddr | 0x00, timeout); + if (status >= 0) { + i2c_write(regaddr >> 8, timeout); + status = i2c_write(regaddr & 0xFF, timeout); + + for (uint16_t i = 0; i < length && status >= 0; i++) { + status = i2c_write(data[i], timeout); + } + } + + i2c_stop(); + + return status; +} + i2c_status_t i2c_readReg(uint8_t devaddr, uint8_t regaddr, uint8_t* data, uint16_t length, uint16_t timeout) { i2c_status_t status = i2c_start(devaddr, timeout); if (status < 0) { @@ -235,6 +251,43 @@ i2c_status_t i2c_readReg(uint8_t devaddr, uint8_t regaddr, uint8_t* data, uint16 return (status < 0) ? status : I2C_STATUS_SUCCESS; } +i2c_status_t i2c_readReg16(uint8_t devaddr, uint16_t regaddr, uint8_t* data, uint16_t length, uint16_t timeout) { + i2c_status_t status = i2c_start(devaddr, timeout); + if (status < 0) { + goto error; + } + + status = i2c_write(regaddr >> 8, timeout); + if (status < 0) { + goto error; + } + status = i2c_write(regaddr & 0xFF, timeout); + if (status < 0) { + goto error; + } + + status = i2c_start(devaddr | 0x01, timeout); + + for (uint16_t i = 0; i < (length - 1) && status >= 0; i++) { + status = i2c_read_ack(timeout); + if (status >= 0) { + data[i] = status; + } + } + + if (status >= 0) { + status = i2c_read_nack(timeout); + if (status >= 0) { + data[(length - 1)] = status; + } + } + +error: + i2c_stop(); + + return (status < 0) ? status : I2C_STATUS_SUCCESS; +} + void i2c_stop(void) { // transmit STOP condition TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWSTO); diff --git a/platforms/avr/drivers/i2c_master.h b/platforms/avr/drivers/i2c_master.h index e5af73364be3..2d95846db518 100644 --- a/platforms/avr/drivers/i2c_master.h +++ b/platforms/avr/drivers/i2c_master.h @@ -39,5 +39,7 @@ int16_t i2c_read_nack(uint16_t timeout); i2c_status_t i2c_transmit(uint8_t address, const uint8_t* data, uint16_t length, uint16_t timeout); i2c_status_t i2c_receive(uint8_t address, uint8_t* data, uint16_t length, uint16_t timeout); i2c_status_t i2c_writeReg(uint8_t devaddr, uint8_t regaddr, const uint8_t* data, uint16_t length, uint16_t timeout); +i2c_status_t i2c_writeReg16(uint8_t devaddr, uint16_t regaddr, const uint8_t* data, uint16_t length, uint16_t timeout); i2c_status_t i2c_readReg(uint8_t devaddr, uint8_t regaddr, uint8_t* data, uint16_t length, uint16_t timeout); +i2c_status_t i2c_readReg16(uint8_t devaddr, uint16_t regaddr, uint8_t* data, uint16_t length, uint16_t timeout); void i2c_stop(void); diff --git a/platforms/chibios/drivers/i2c_master.c b/platforms/chibios/drivers/i2c_master.c index fc4bb2ab3701..18122947e1ac 100644 --- a/platforms/chibios/drivers/i2c_master.c +++ b/platforms/chibios/drivers/i2c_master.c @@ -111,6 +111,21 @@ i2c_status_t i2c_writeReg(uint8_t devaddr, uint8_t regaddr, const uint8_t* data, return chibios_to_qmk(&status); } +i2c_status_t i2c_writeReg16(uint8_t devaddr, uint16_t regaddr, const uint8_t* data, uint16_t length, uint16_t timeout) { + i2c_address = devaddr; + i2cStart(&I2C_DRIVER, &i2cconfig); + + uint8_t complete_packet[length + 2]; + for (uint8_t i = 0; i < length; i++) { + complete_packet[i + 2] = data[i]; + } + complete_packet[0] = regaddr >> 8; + 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); +} + 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); @@ -118,4 +133,11 @@ i2c_status_t i2c_readReg(uint8_t devaddr, uint8_t regaddr, uint8_t* data, uint16 return chibios_to_qmk(&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); + msg_t status = i2cMasterTransmitTimeout(&I2C_DRIVER, (i2c_address >> 1), ®addr, 2, data, length, TIME_MS2I(timeout)); + return chibios_to_qmk(&status); +} + void i2c_stop(void) { i2cStop(&I2C_DRIVER); } diff --git a/platforms/chibios/drivers/i2c_master.h b/platforms/chibios/drivers/i2c_master.h index c68109acbd3f..8c8026ad1eca 100644 --- a/platforms/chibios/drivers/i2c_master.h +++ b/platforms/chibios/drivers/i2c_master.h @@ -109,5 +109,7 @@ i2c_status_t i2c_start(uint8_t address); i2c_status_t i2c_transmit(uint8_t address, const uint8_t* data, uint16_t length, uint16_t timeout); i2c_status_t i2c_receive(uint8_t address, uint8_t* data, uint16_t length, uint16_t timeout); i2c_status_t i2c_writeReg(uint8_t devaddr, uint8_t regaddr, const uint8_t* data, uint16_t length, uint16_t timeout); +i2c_status_t i2c_writeReg16(uint8_t devaddr, uint16_t regaddr, const uint8_t* data, uint16_t length, uint16_t timeout); i2c_status_t i2c_readReg(uint8_t devaddr, uint8_t regaddr, uint8_t* data, uint16_t length, uint16_t timeout); +i2c_status_t i2c_readReg16(uint8_t devaddr, uint16_t regaddr, uint8_t* data, uint16_t length, uint16_t timeout); void i2c_stop(void); From 633186239c4cb757dc122c0821bbe6cedb889fde Mon Sep 17 00:00:00 2001 From: fauxpark Date: Fri, 10 Sep 2021 17:47:39 +1000 Subject: [PATCH 2/7] Check status of first i2c_write() in AVR writeReg16() --- platforms/avr/drivers/i2c_master.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/platforms/avr/drivers/i2c_master.c b/platforms/avr/drivers/i2c_master.c index 9725548e0b90..111b55d6b0d3 100644 --- a/platforms/avr/drivers/i2c_master.c +++ b/platforms/avr/drivers/i2c_master.c @@ -205,11 +205,14 @@ i2c_status_t i2c_writeReg(uint8_t devaddr, uint8_t regaddr, const uint8_t* data, i2c_status_t i2c_writeReg16(uint8_t devaddr, uint16_t regaddr, const uint8_t* data, uint16_t length, uint16_t timeout) { i2c_status_t status = i2c_start(devaddr | 0x00, timeout); if (status >= 0) { - i2c_write(regaddr >> 8, timeout); - status = i2c_write(regaddr & 0xFF, timeout); + status = i2c_write(regaddr >> 8, timeout); - for (uint16_t i = 0; i < length && status >= 0; i++) { - status = i2c_write(data[i], timeout); + if (status >= 0) { + status = i2c_write(regaddr & 0xFF, timeout); + + for (uint16_t i = 0; i < length && status >= 0; i++) { + status = i2c_write(data[i], timeout); + } } } From 7dd3684d888ce492d21d403712d15ceac1fa6278 Mon Sep 17 00:00:00 2001 From: fauxpark Date: Fri, 10 Sep 2021 17:56:33 +1000 Subject: [PATCH 3/7] ARM readReg16() - transmit register in big-endian order --- platforms/chibios/drivers/i2c_master.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/platforms/chibios/drivers/i2c_master.c b/platforms/chibios/drivers/i2c_master.c index 18122947e1ac..62716d0caffa 100644 --- a/platforms/chibios/drivers/i2c_master.c +++ b/platforms/chibios/drivers/i2c_master.c @@ -136,7 +136,11 @@ i2c_status_t i2c_readReg(uint8_t devaddr, uint8_t regaddr, uint8_t* data, uint16 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); - msg_t status = i2cMasterTransmitTimeout(&I2C_DRIVER, (i2c_address >> 1), ®addr, 2, data, length, TIME_MS2I(timeout)); + uint8_t register_packet[2] = { + regaddr >> 8, + regaddr & 0xFF + }; + msg_t status = i2cMasterTransmitTimeout(&I2C_DRIVER, (i2c_address >> 1), ®ister_packet, 2, data, length, TIME_MS2I(timeout)); return chibios_to_qmk(&status); } From 9bbcb38beb866b0567602a0ae258d189d2e23830 Mon Sep 17 00:00:00 2001 From: fauxpark Date: Fri, 10 Sep 2021 17:59:16 +1000 Subject: [PATCH 4/7] Format pass --- platforms/chibios/drivers/i2c_master.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/platforms/chibios/drivers/i2c_master.c b/platforms/chibios/drivers/i2c_master.c index 62716d0caffa..57f8a487b5e9 100644 --- a/platforms/chibios/drivers/i2c_master.c +++ b/platforms/chibios/drivers/i2c_master.c @@ -136,11 +136,8 @@ i2c_status_t i2c_readReg(uint8_t devaddr, uint8_t regaddr, uint8_t* data, uint16 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), ®ister_packet, 2, data, length, TIME_MS2I(timeout)); + uint8_t register_packet[2] = {regaddr >> 8, regaddr & 0xFF}; + msg_t status = i2cMasterTransmitTimeout(&I2C_DRIVER, (i2c_address >> 1), ®ister_packet, 2, data, length, TIME_MS2I(timeout)); return chibios_to_qmk(&status); } From 52b9b2230d484750148377b0503ff27fd5adaf4c Mon Sep 17 00:00:00 2001 From: fauxpark Date: Fri, 10 Sep 2021 18:01:22 +1000 Subject: [PATCH 5/7] Now you're just being pedantic, clang-format --- platforms/chibios/drivers/i2c_master.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platforms/chibios/drivers/i2c_master.c b/platforms/chibios/drivers/i2c_master.c index 57f8a487b5e9..99af2c3c54e4 100644 --- a/platforms/chibios/drivers/i2c_master.c +++ b/platforms/chibios/drivers/i2c_master.c @@ -137,7 +137,7 @@ i2c_status_t i2c_readReg16(uint8_t devaddr, uint16_t regaddr, uint8_t* data, uin 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), ®ister_packet, 2, data, length, TIME_MS2I(timeout)); + msg_t status = i2cMasterTransmitTimeout(&I2C_DRIVER, (i2c_address >> 1), ®ister_packet, 2, data, length, TIME_MS2I(timeout)); return chibios_to_qmk(&status); } From e81ca7fe359aa1cc6cb49f693ef0c4356447a448 Mon Sep 17 00:00:00 2001 From: fauxpark Date: Fri, 10 Sep 2021 18:16:00 +1000 Subject: [PATCH 6/7] Clarify description of read/writeReg functions --- docs/i2c_driver.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/i2c_driver.md b/docs/i2c_driver.md index 335123de23f7..32c46db9bfd1 100644 --- a/docs/i2c_driver.md +++ b/docs/i2c_driver.md @@ -190,7 +190,7 @@ Receive multiple bytes from the selected SPI device. ### `i2c_status_t i2c_writeReg(uint8_t devaddr, uint8_t regaddr, uint8_t* data, uint16_t length, uint16_t timeout)` -Writes to an 8-bit register on the I2C device. +Writes to a register with an 8-bit address on the I2C device. #### Arguments @@ -213,7 +213,7 @@ Writes to an 8-bit register on the I2C device. ### `i2c_status_t i2c_writeReg16(uint8_t devaddr, uint16_t regaddr, uint8_t* data, uint16_t length, uint16_t timeout)` -Writes to a 16-bit register on the I2C device. +Writes to a register with a 16-bit address (big endian) on the I2C device. #### Arguments @@ -236,7 +236,7 @@ Writes to a 16-bit register on the I2C device. ### `i2c_status_t i2c_readReg(uint8_t devaddr, uint8_t regaddr, uint8_t* data, uint16_t length, uint16_t timeout)` -Reads from an 8-bit register on the I2C device. +Reads from a register with an 8-bit address on the I2C device. #### Arguments @@ -257,7 +257,7 @@ Reads from an 8-bit register on the I2C device. ### `i2c_status_t i2c_readReg16(uint8_t devaddr, uint16_t regaddr, uint8_t* data, uint16_t length, uint16_t timeout)` -Reads from a 16-bit register on the I2C device. +Reads from a register with a 16-bit address (big endian) on the I2C device. #### Arguments From 9aadf842dfa1852d860c307d90ac0ce96a4c8863 Mon Sep 17 00:00:00 2001 From: fauxpark Date: Thu, 30 Sep 2021 20:46:13 +1000 Subject: [PATCH 7/7] Fix counter type --- platforms/chibios/drivers/i2c_master.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/platforms/chibios/drivers/i2c_master.c b/platforms/chibios/drivers/i2c_master.c index 99af2c3c54e4..571260b3e3fa 100644 --- a/platforms/chibios/drivers/i2c_master.c +++ b/platforms/chibios/drivers/i2c_master.c @@ -102,7 +102,7 @@ i2c_status_t i2c_writeReg(uint8_t devaddr, uint8_t regaddr, const uint8_t* data, i2cStart(&I2C_DRIVER, &i2cconfig); uint8_t complete_packet[length + 1]; - for (uint8_t i = 0; i < length; i++) { + for (uint16_t i = 0; i < length; i++) { complete_packet[i + 1] = data[i]; } complete_packet[0] = regaddr; @@ -116,7 +116,7 @@ i2c_status_t i2c_writeReg16(uint8_t devaddr, uint16_t regaddr, const uint8_t* da i2cStart(&I2C_DRIVER, &i2cconfig); uint8_t complete_packet[length + 2]; - for (uint8_t i = 0; i < length; i++) { + for (uint16_t i = 0; i < length; i++) { complete_packet[i + 2] = data[i]; } complete_packet[0] = regaddr >> 8;