summaryrefslogtreecommitdiff
path: root/src/drivers/TwiMaster.cpp
diff options
context:
space:
mode:
authorJoaquim <joaquim.org@gmail.com>2021-04-10 19:09:33 +0100
committerJoaquim <joaquim.org@gmail.com>2021-04-10 19:09:33 +0100
commit012c246e401c0745d4b6765217ce7137680070da (patch)
treef5ac127917689dd57a36d7152f44bb923e2a9e9e /src/drivers/TwiMaster.cpp
parenteb769fb60ecb8f96ecf6901082ec3f0610842af8 (diff)
0.16.0 TWI problems fix
More memory for freertos heap and timer stack Fix warning in watchface Fix number of bytes read by cst816 Debug app to show freertos tasks Increased the number of bytes of the twi write buffer
Diffstat (limited to 'src/drivers/TwiMaster.cpp')
-rw-r--r--src/drivers/TwiMaster.cpp203
1 files changed, 160 insertions, 43 deletions
diff --git a/src/drivers/TwiMaster.cpp b/src/drivers/TwiMaster.cpp
index 646823d0..271b714f 100644
--- a/src/drivers/TwiMaster.cpp
+++ b/src/drivers/TwiMaster.cpp
@@ -2,77 +2,167 @@
#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()} {
- ASSERT(mutex != nullptr);
+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);
+
switch(module) {
- case Modules::TWIM1:
+ case Modules::TWIM1: twiBaseAddress = NRF_TWIM1; break;
default:
- twim = NRFX_TWIM_INSTANCE(1);
- break;
+ return;
}
-}
-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);
+ 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;
+ }
+
+ 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);
+ */
xSemaphoreGive(mutex);
+
}
TwiMaster::ErrorCodes TwiMaster::Read(uint8_t deviceAddress, uint8_t registerAddress, uint8_t *data, size_t size) {
xSemaphoreTake(mutex, portMAX_DELAY);
- TwiMaster::ErrorCodes ret;
+ auto ret = Write(deviceAddress, &registerAddress, 1, false);
+ ret = Read(deviceAddress, data, size, true);
+ xSemaphoreGive(mutex);
+ return ret;
+}
+
+TwiMaster::ErrorCodes TwiMaster::Write(uint8_t deviceAddress, uint8_t registerAddress, const uint8_t *data, size_t size) {
+ ASSERT(size <= maxDataSize);
+ xSemaphoreTake(mutex, portMAX_DELAY);
+ internalBuffer[0] = registerAddress;
+ std::memcpy(internalBuffer + 1, data, size);
+ auto ret = Write(deviceAddress, internalBuffer, size + 1, true);
+ xSemaphoreGive(mutex);
+ 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;
- auto err = nrfx_twim_tx(&twim, deviceAddress, &registerAddress, 1, false);
- if(err != 0) {
- return TwiMaster::ErrorCodes::TransactionFailed;
+ 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;
- err = nrfx_twim_rx(&twim, deviceAddress, data, size);
- if(err != 0) {
- return TwiMaster::ErrorCodes::TransactionFailed;
+ 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;
}
- xSemaphoreGive(mutex);
- return TwiMaster::ErrorCodes::NoError;
+ if (twiBaseAddress->EVENTS_ERROR) {
+ twiBaseAddress->EVENTS_ERROR = 0x0UL;
+ }
+ return 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);
- TwiMaster::ErrorCodes ret;
+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;
- internalBuffer[0] = registerAddress;
- std::memcpy(internalBuffer+1, data, size);
- auto err = nrfx_twim_tx(&twim, deviceAddress, internalBuffer , size+1, false);
- if(err != 0){
- return TwiMaster::ErrorCodes::TransactionFailed;
+ 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;
- xSemaphoreGive(mutex);
- return TwiMaster::ErrorCodes::NoError;
+ 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;
}
void TwiMaster::Sleep() {
- nrfx_twim_disable(&twim);
- nrfx_twim_uninit(&twim);
-
+ while(twiBaseAddress->ENABLE != 0) {
+ twiBaseAddress->ENABLE = (TWIM_ENABLE_ENABLE_Disabled << TWIM_ENABLE_ENABLE_Pos);
+ }
nrf_gpio_cfg_default(6);
nrf_gpio_cfg_default(7);
NRF_LOG_INFO("[TWIMASTER] Sleep");
@@ -82,3 +172,30 @@ 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;
+} \ No newline at end of file