summaryrefslogtreecommitdiff
path: root/src/drivers/TwiMaster.cpp
diff options
context:
space:
mode:
authorJean-François Milants <jf@codingfield.com>2021-04-08 18:07:24 (GMT)
committerJean-François Milants <jf@codingfield.com>2021-04-08 18:07:24 (GMT)
commit9ac4be8b759bb2cedeb999ce5e87d983261beded (patch)
tree2a5fabd9b7505976c20b5512f9335e75290bdd86 /src/drivers/TwiMaster.cpp
parent1d7576de64a33837434e6f414a74ae7dbe929196 (diff)
TwiMaster is now based on the NRFX TWI driver, as it handles more edge cases and workarounds for errors on the bus.
Reset the TWI bus after the soft-reset of the motion sensor to workaround issues on the TWI bus.
Diffstat (limited to 'src/drivers/TwiMaster.cpp')
-rw-r--r--src/drivers/TwiMaster.cpp225
1 files changed, 40 insertions, 185 deletions
diff --git a/src/drivers/TwiMaster.cpp b/src/drivers/TwiMaster.cpp
index 6a063ec..646823d 100644
--- a/src/drivers/TwiMaster.cpp
+++ b/src/drivers/TwiMaster.cpp
@@ -2,195 +2,77 @@
#include <cstring>
#include <hal/nrf_gpio.h>
#include <nrfx_log.h>
-
+#include <nrfx_twim.h>
+#include <nrf_drv_twi.h>
using namespace Pinetime::Drivers;
// TODO use shortcut to automatically send STOP when receive LastTX, for example
// TODO use DMA/IRQ
-TwiMaster::TwiMaster(const Modules module, const Parameters& params) : module{module}, params{params} {
- mutex = xSemaphoreCreateBinary();
-}
-
-void TwiMaster::Init() {
- NRF_GPIO->PIN_CNF[params.pinScl] = ((uint32_t)GPIO_PIN_CNF_DIR_Input << GPIO_PIN_CNF_DIR_Pos)
- | ((uint32_t)GPIO_PIN_CNF_INPUT_Connect << GPIO_PIN_CNF_INPUT_Pos)
- | ((uint32_t)GPIO_PIN_CNF_PULL_Pullup << GPIO_PIN_CNF_PULL_Pos)
- | ((uint32_t)GPIO_PIN_CNF_DRIVE_S0D1 << GPIO_PIN_CNF_DRIVE_Pos)
- | ((uint32_t)GPIO_PIN_CNF_SENSE_Disabled << GPIO_PIN_CNF_SENSE_Pos);
-
- NRF_GPIO->PIN_CNF[params.pinSda] = ((uint32_t)GPIO_PIN_CNF_DIR_Input << GPIO_PIN_CNF_DIR_Pos)
- | ((uint32_t)GPIO_PIN_CNF_INPUT_Connect << GPIO_PIN_CNF_INPUT_Pos)
- | ((uint32_t)GPIO_PIN_CNF_PULL_Pullup << GPIO_PIN_CNF_PULL_Pos)
- | ((uint32_t)GPIO_PIN_CNF_DRIVE_S0D1 << GPIO_PIN_CNF_DRIVE_Pos)
- | ((uint32_t)GPIO_PIN_CNF_SENSE_Disabled << GPIO_PIN_CNF_SENSE_Pos);
-
+TwiMaster::TwiMaster(const Modules module, const Parameters& params) : module{module}, params{params}, mutex{xSemaphoreCreateBinary()} {
+ ASSERT(mutex != nullptr);
switch(module) {
- case Modules::TWIM1: twiBaseAddress = NRF_TWIM1; break;
+ case Modules::TWIM1:
default:
- return;
- }
-
- switch(static_cast<Frequencies>(params.frequency)) {
- case Frequencies::Khz100 : twiBaseAddress->FREQUENCY = TWIM_FREQUENCY_FREQUENCY_K100; break;
- case Frequencies::Khz250 : twiBaseAddress->FREQUENCY = TWIM_FREQUENCY_FREQUENCY_K250; break;
- case Frequencies::Khz400 : twiBaseAddress->FREQUENCY = TWIM_FREQUENCY_FREQUENCY_K400; break;
+ twim = NRFX_TWIM_INSTANCE(1);
+ break;
}
+}
- twiBaseAddress->PSEL.SCL = params.pinScl;
- twiBaseAddress->PSEL.SDA = params.pinSda;
- twiBaseAddress->EVENTS_LASTRX = 0;
- twiBaseAddress->EVENTS_STOPPED = 0;
- twiBaseAddress->EVENTS_LASTTX = 0;
- twiBaseAddress->EVENTS_ERROR = 0;
- twiBaseAddress->EVENTS_RXSTARTED = 0;
- twiBaseAddress->EVENTS_SUSPENDED = 0;
- twiBaseAddress->EVENTS_TXSTARTED = 0;
-
- twiBaseAddress->ENABLE = (TWIM_ENABLE_ENABLE_Enabled << TWIM_ENABLE_ENABLE_Pos);
-
-
- /* // IRQ
- NVIC_ClearPendingIRQ(_IRQn);
- NVIC_SetPriority(_IRQn, 2);
- NVIC_EnableIRQ(_IRQn);
- */
+void TwiMaster::Init() {
+ nrfx_twim_config_t config;
+ config.frequency = static_cast<nrf_twim_frequency_t>(params.frequency);
+ config.hold_bus_uninit = false;
+ config.interrupt_priority = 0;
+ config.scl = params.pinScl;
+ config.sda = params.pinSda;
+ nrfx_twim_init(&twim,
+ &config,
+ nullptr,
+ nullptr);
+ nrfx_twim_enable(&twim);
xSemaphoreGive(mutex);
-
}
TwiMaster::ErrorCodes TwiMaster::Read(uint8_t deviceAddress, uint8_t registerAddress, uint8_t *data, size_t size) {
xSemaphoreTake(mutex, portMAX_DELAY);
- auto ret = ReadWithRetry(deviceAddress, registerAddress, data, size);
+ TwiMaster::ErrorCodes ret;
+
+ auto err = nrfx_twim_tx(&twim, deviceAddress, &registerAddress, 1, false);
+ if(err != 0) {
+ return TwiMaster::ErrorCodes::TransactionFailed;
+ }
+
+ err = nrfx_twim_rx(&twim, deviceAddress, data, size);
+ if(err != 0) {
+ return TwiMaster::ErrorCodes::TransactionFailed;
+ }
xSemaphoreGive(mutex);
- return ret;
+ return TwiMaster::ErrorCodes::NoError;
}
TwiMaster::ErrorCodes TwiMaster::Write(uint8_t deviceAddress, uint8_t registerAddress, const uint8_t *data, size_t size) {
ASSERT(size <= maxDataSize);
xSemaphoreTake(mutex, portMAX_DELAY);
-
- auto ret = WriteWithRetry(deviceAddress, registerAddress, data, size);
- xSemaphoreGive(mutex);
- return ret;
-}
-
-/* Execute a read transaction (composed of a write and a read operation). If one of these opeartion fails,
- * it's retried once. If it fails again, an error is returned */
-TwiMaster::ErrorCodes TwiMaster::ReadWithRetry(uint8_t deviceAddress, uint8_t registerAddress, uint8_t *data, size_t size) {
TwiMaster::ErrorCodes ret;
- ret = Write(deviceAddress, &registerAddress, 1, false);
- if(ret != ErrorCodes::NoError)
- ret = Write(deviceAddress, &registerAddress, 1, false);
-
- if(ret != ErrorCodes::NoError) return ret;
- ret = Read(deviceAddress, data, size, true);
- if(ret != ErrorCodes::NoError)
- ret = Read(deviceAddress, data, size, true);
-
- return ret;
-}
-
-/* Execute a write transaction. If it fails, it is retried once. If it fails again, an error is returned. */
-TwiMaster::ErrorCodes TwiMaster::WriteWithRetry(uint8_t deviceAddress, uint8_t registerAddress, const uint8_t *data, size_t size) {
internalBuffer[0] = registerAddress;
std::memcpy(internalBuffer+1, data, size);
- auto ret = Write(deviceAddress, internalBuffer, size+1, true);
- if(ret != ErrorCodes::NoError)
- ret = Write(deviceAddress, internalBuffer, size+1, true);
-
- return ret;
-}
-
-
-TwiMaster::ErrorCodes TwiMaster::Read(uint8_t deviceAddress, uint8_t *buffer, size_t size, bool stop) {
- twiBaseAddress->ADDRESS = deviceAddress;
- twiBaseAddress->TASKS_RESUME = 0x1UL;
- twiBaseAddress->RXD.PTR = (uint32_t)buffer;
- twiBaseAddress->RXD.MAXCNT = size;
-
- twiBaseAddress->TASKS_STARTRX = 1;
-
- while(!twiBaseAddress->EVENTS_RXSTARTED && !twiBaseAddress->EVENTS_ERROR);
- twiBaseAddress->EVENTS_RXSTARTED = 0x0UL;
-
- txStartedCycleCount = DWT->CYCCNT;
- uint32_t currentCycleCount;
- while(!twiBaseAddress->EVENTS_LASTRX && !twiBaseAddress->EVENTS_ERROR) {
- currentCycleCount = DWT->CYCCNT;
- if ((currentCycleCount-txStartedCycleCount) > HwFreezedDelay) {
- FixHwFreezed();
- return ErrorCodes::TransactionFailed;
- }
- }
- twiBaseAddress->EVENTS_LASTRX = 0x0UL;
-
- if (stop || twiBaseAddress->EVENTS_ERROR) {
- twiBaseAddress->TASKS_STOP = 0x1UL;
- while(!twiBaseAddress->EVENTS_STOPPED);
- twiBaseAddress->EVENTS_STOPPED = 0x0UL;
- }
- else {
- twiBaseAddress->TASKS_SUSPEND = 0x1UL;
- while(!twiBaseAddress->EVENTS_SUSPENDED);
- twiBaseAddress->EVENTS_SUSPENDED = 0x0UL;
+ auto err = nrfx_twim_tx(&twim, deviceAddress, internalBuffer , size+1, false);
+ if(err != 0){
+ return TwiMaster::ErrorCodes::TransactionFailed;
}
- if (twiBaseAddress->EVENTS_ERROR) {
- twiBaseAddress->EVENTS_ERROR = 0x0UL;
- }
- return ErrorCodes::NoError;
-}
-
-TwiMaster::ErrorCodes TwiMaster::Write(uint8_t deviceAddress, const uint8_t *data, size_t size, bool stop) {
- twiBaseAddress->ADDRESS = deviceAddress;
- twiBaseAddress->TASKS_RESUME = 0x1UL;
- twiBaseAddress->TXD.PTR = (uint32_t)data;
- twiBaseAddress->TXD.MAXCNT = size;
-
- twiBaseAddress->TASKS_STARTTX = 1;
-
- while(!twiBaseAddress->EVENTS_TXSTARTED && !twiBaseAddress->EVENTS_ERROR);
- twiBaseAddress->EVENTS_TXSTARTED = 0x0UL;
-
- txStartedCycleCount = DWT->CYCCNT;
- uint32_t currentCycleCount;
- while(!twiBaseAddress->EVENTS_LASTTX && !twiBaseAddress->EVENTS_ERROR) {
- currentCycleCount = DWT->CYCCNT;
- if ((currentCycleCount-txStartedCycleCount) > HwFreezedDelay) {
- FixHwFreezed();
- return ErrorCodes::TransactionFailed;
- }
- }
- twiBaseAddress->EVENTS_LASTTX = 0x0UL;
-
- if (stop || twiBaseAddress->EVENTS_ERROR) {
- twiBaseAddress->TASKS_STOP = 0x1UL;
- while(!twiBaseAddress->EVENTS_STOPPED);
- twiBaseAddress->EVENTS_STOPPED = 0x0UL;
- }
- else {
- twiBaseAddress->TASKS_SUSPEND = 0x1UL;
- while(!twiBaseAddress->EVENTS_SUSPENDED);
- twiBaseAddress->EVENTS_SUSPENDED = 0x0UL;
- }
-
- if (twiBaseAddress->EVENTS_ERROR) {
- twiBaseAddress->EVENTS_ERROR = 0x0UL;
- uint32_t error = twiBaseAddress->ERRORSRC;
- twiBaseAddress->ERRORSRC = error;
- }
-
- return ErrorCodes::NoError;
+ xSemaphoreGive(mutex);
+ return TwiMaster::ErrorCodes::NoError;
}
void TwiMaster::Sleep() {
- while(twiBaseAddress->ENABLE != 0) {
- twiBaseAddress->ENABLE = (TWIM_ENABLE_ENABLE_Disabled << TWIM_ENABLE_ENABLE_Pos);
- }
+ nrfx_twim_disable(&twim);
+ nrfx_twim_uninit(&twim);
+
nrf_gpio_cfg_default(6);
nrf_gpio_cfg_default(7);
NRF_LOG_INFO("[TWIMASTER] Sleep");
@@ -200,30 +82,3 @@ void TwiMaster::Wakeup() {
Init();
NRF_LOG_INFO("[TWIMASTER] Wakeup");
}
-
-/* Sometimes, the TWIM device just freeze and never set the event EVENTS_LASTTX.
- * This method disable and re-enable the peripheral so that it works again.
- * This is just a workaround, and it would be better if we could find a way to prevent
- * this issue from happening.
- * */
-void TwiMaster::FixHwFreezed() {
- NRF_LOG_INFO("I2C device frozen, reinitializing it!");
- // Disable I²C
- uint32_t twi_state = NRF_TWI1->ENABLE;
- twiBaseAddress->ENABLE = TWIM_ENABLE_ENABLE_Disabled << TWI_ENABLE_ENABLE_Pos;
-
- NRF_GPIO->PIN_CNF[params.pinScl] = ((uint32_t)GPIO_PIN_CNF_DIR_Input << GPIO_PIN_CNF_DIR_Pos)
- | ((uint32_t)GPIO_PIN_CNF_INPUT_Connect << GPIO_PIN_CNF_INPUT_Pos)
- | ((uint32_t)GPIO_PIN_CNF_PULL_Pullup << GPIO_PIN_CNF_PULL_Pos)
- | ((uint32_t)GPIO_PIN_CNF_DRIVE_S0S1 << GPIO_PIN_CNF_DRIVE_Pos)
- | ((uint32_t)GPIO_PIN_CNF_SENSE_Disabled << GPIO_PIN_CNF_SENSE_Pos);
-
- NRF_GPIO->PIN_CNF[params.pinSda] = ((uint32_t)GPIO_PIN_CNF_DIR_Input << GPIO_PIN_CNF_DIR_Pos)
- | ((uint32_t)GPIO_PIN_CNF_INPUT_Connect << GPIO_PIN_CNF_INPUT_Pos)
- | ((uint32_t)GPIO_PIN_CNF_PULL_Pullup << GPIO_PIN_CNF_PULL_Pos)
- | ((uint32_t)GPIO_PIN_CNF_DRIVE_S0S1 << GPIO_PIN_CNF_DRIVE_Pos)
- | ((uint32_t)GPIO_PIN_CNF_SENSE_Disabled << GPIO_PIN_CNF_SENSE_Pos);
-
- // Re-enable I²C
- twiBaseAddress->ENABLE = twi_state;
-}