Skip to content
This repository has been archived by the owner on Nov 11, 2024. It is now read-only.

Commit

Permalink
Zephyr I2C port change: implement uPortI2cControllerExchange() so tha…
Browse files Browse the repository at this point in the history
…t STM32 works.

Following the previous commit, which introduced uPortI2cControllerExchange() in the I2C port API with default implementations that fall through to uPortI2cControllerSend()/uPortI2cControllerSendReceive(), this commit implements uPortI2cControllerExchange() properly for Zephyr, such that I2C transactions work with STM32 MCUs as well as nRF52/nRF53.

Test: 6.1 8 13.1.0 13.1.1 15.1 17.1.0 17.1.1 24 26 31 port.gnss
  • Loading branch information
RobMeades committed Feb 26, 2024
1 parent cfc5300 commit 2e1fefc
Show file tree
Hide file tree
Showing 2 changed files with 96 additions and 3 deletions.
96 changes: 96 additions & 0 deletions port/platform/zephyr/src/u_port_i2c.c
Original file line number Diff line number Diff line change
Expand Up @@ -406,6 +406,94 @@ int32_t uPortI2cGetMaxSegmentSize(int32_t handle)
return errorCodeOrMaxSegmentSize;
}

// Send and/or receive over the I2C interface as a controller.
int32_t uPortI2cControllerExchange(int32_t handle, uint16_t address,
const char *pSend, size_t bytesToSend,
char *pReceive, size_t bytesToReceive,
bool noInterveningStop)
{
int32_t errorCodeOrLength = (int32_t) U_ERROR_COMMON_NOT_INITIALISED;
const struct device *pDevice = NULL;
struct i2c_msg message[2];
size_t x;
size_t thisReceiveBytes;

if (gMutex != NULL) {

U_PORT_MUTEX_LOCK(gMutex);

errorCodeOrLength = (int32_t) U_ERROR_COMMON_INVALID_PARAMETER;
if ((handle >= 0) && (handle < sizeof(gI2cData) / sizeof(gI2cData[0])) &&
(gI2cData[handle].pDevice != NULL) &&
((pSend != NULL) || (bytesToSend == 0)) &&
((pReceive != NULL) || (bytesToReceive == 0))) {
pDevice = gI2cData[handle].pDevice;
errorCodeOrLength = 0;
// This is constructed as a do/while loop since
// it is permissible to send zero length data,
// e.g. when polling the bus for a device address
do {
memset(message, 0, sizeof(message));
x = 0;
if (bytesToSend > 0) {
message[x].buf = (uint8_t *) pSend;
message[x].len = (uint32_t) bytesToSend;
if ((gI2cData[handle].maxSegmentSize > 0) &&
(message[x].len > gI2cData[handle].maxSegmentSize)) {
message[x].len = gI2cData[handle].maxSegmentSize;
}
bytesToSend -= message[x].len;
pSend += message[x].len;
message[x].flags = I2C_MSG_WRITE;
if (address > 127) {
message[x].flags |= I2C_MSG_ADDR_10_BITS;
}
if ((pReceive == NULL) && !noInterveningStop) {
// If there's nothing to receive and we are not-not
// going to insert a stop bit then do that
message[x].flags |= I2C_MSG_STOP;
}
x++;
}
thisReceiveBytes = 0;
if (bytesToReceive > 0) {
message[x].buf = (uint8_t *) pReceive;
message[x].len = (uint32_t) bytesToReceive;
if ((gI2cData[handle].maxSegmentSize > 0) &&
(message[x].len > gI2cData[handle].maxSegmentSize)) {
message[x].len = gI2cData[handle].maxSegmentSize;
}
bytesToReceive -= message[x].len;
pReceive += message[x].len;
thisReceiveBytes = message[x].len;
// We're definitely stopping after this message
message[x].flags = I2C_MSG_READ | I2C_MSG_STOP;
if (address > 127) {
message[x].flags |= I2C_MSG_ADDR_10_BITS;
}
// If something was sent, make sure that there is
// a start marker at the front of the message
if (pSend != NULL) {
message[x].flags |= I2C_MSG_RESTART;
}
x++;
}
if (i2c_transfer(pDevice, message, (uint8_t) x, address) == 0) {
errorCodeOrLength += (int32_t) thisReceiveBytes;
} else {
errorCodeOrLength = (int32_t) U_ERROR_COMMON_DEVICE_ERROR;
}
} while ((errorCodeOrLength >= 0) &&
((bytesToSend > 0) || (bytesToReceive > 0)));
}

U_PORT_MUTEX_UNLOCK(gMutex);
}

return errorCodeOrLength;
}

/** /deprecated: please use uPortI2cControllerExchange() instead. */
// Send and/or receive over the I2C interface as a controller.
int32_t uPortI2cControllerSendReceive(int32_t handle, uint16_t address,
const char *pSend, size_t bytesToSend,
Expand Down Expand Up @@ -489,7 +577,15 @@ int32_t uPortI2cControllerSendReceive(int32_t handle, uint16_t address,
return errorCodeOrLength;
}

/** /deprecated: please use uPortI2cControllerExchange() instead. */
// Perform a send over the I2C interface as a controller.
//
// IMPORTANT: if this function is called with noStop set to true, that
// will work for nRF52/nRF53 but it will NOT work for STM32 and may
// not work on other chipsets underneath Zephyr. This is because
// leaving off I2C_MSG_STOP is not guaranteed to work in all cases:
// zome Zephry drivers insist that an I2C transaction ends with a
// stop bit. uPortI2cControllerExchange() should be used instead
int32_t uPortI2cControllerSend(int32_t handle, uint16_t address,
const char *pSend, size_t bytesToSend,
bool noStop)
Expand Down
3 changes: 0 additions & 3 deletions port/test/u_port_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -2744,9 +2744,6 @@ U_PORT_TEST_FUNCTION("[port]", "portI2cRequiresSpecificWiring")
U_PORT_TEST_ASSERT(uPortI2cControllerExchange(gI2cHandle, U_PORT_TEST_I2C_ADDRESS - 1,
gpI2cBuffer, 1, NULL, 0, false) < 0);

// The following should do nothing and return success
U_PORT_TEST_ASSERT(uPortI2cControllerExchange(gI2cHandle, U_PORT_TEST_I2C_ADDRESS - 1,
NULL, 0, NULL, 0, false) == 0);
U_TEST_PRINT_LINE("now using the valid address (0x%02x).", U_PORT_TEST_I2C_ADDRESS);
# if !defined(U_CFG_TEST_USING_NRF5SDK) && !defined(__ZEPHYR__)
// Now do a NULL send which will succeed only if the GNSS device is there;
Expand Down

0 comments on commit 2e1fefc

Please sign in to comment.