Skip to content

Commit 9ac4be8

Browse files
committed
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.
1 parent 1d7576d commit 9ac4be8

7 files changed

Lines changed: 66 additions & 206 deletions

File tree

src/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ set(SDK_SOURCE_FILES
8383
"${NRF5_SDK_PATH}/external/fprintf/nrf_fprintf_format.c"
8484

8585
# TWI
86-
"${NRF5_SDK_PATH}/modules/nrfx/drivers/src/nrfx_twi.c"
86+
"${NRF5_SDK_PATH}/modules/nrfx/drivers/src/nrfx_twim.c"
8787

8888
# GPIOTE
8989
"${NRF5_SDK_PATH}/components/libraries/gpiote/app_gpiote.c"

src/drivers/Bma421.cpp

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,9 @@ Bma421::Bma421(TwiMaster& twiMaster, uint8_t twiAddress) : twiMaster{twiMaster},
3535
}
3636

3737
void Bma421::Init() {
38-
auto ret = bma4_soft_reset(&bma);
39-
if(ret != BMA4_OK) return;
38+
if(not isResetOk) return; // Call SoftReset (and reset TWI device) first!
4039

41-
nrf_delay_ms(1);
42-
43-
ret = bma423_init(&bma);
40+
auto ret = bma423_init(&bma);
4441
if(ret != BMA4_OK) return;
4542

4643
ret = bma423_write_config_file(&bma);
@@ -109,3 +106,11 @@ bool Bma421::IsOk() const {
109106
void Bma421::ResetStepCounter() {
110107
bma423_reset_step_counter(&bma);
111108
}
109+
110+
void Bma421::SoftReset() {
111+
auto ret = bma4_soft_reset(&bma);
112+
if(ret == BMA4_OK) {
113+
isResetOk = true;
114+
nrf_delay_ms(1);
115+
}
116+
}

src/drivers/Bma421.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ namespace Pinetime {
1818
Bma421(Bma421&&) = delete;
1919
Bma421& operator=(Bma421&&) = delete;
2020

21+
/// The chip freezes the TWI bus after the softreset operation. Softreset is separated from the
22+
/// Init() method to allow the caller to uninit and then reinit the TWI device after the softreset.
23+
void SoftReset();
2124
void Init();
2225
Values Process();
2326
void ResetStepCounter();
@@ -34,6 +37,7 @@ namespace Pinetime {
3437
uint8_t deviceAddress = 0x18;
3538
struct bma4_dev bma;
3639
bool isOk = false;
40+
bool isResetOk = false;
3741
};
3842
}
3943
}

src/drivers/TwiMaster.cpp

Lines changed: 40 additions & 185 deletions
Original file line numberDiff line numberDiff line change
@@ -2,195 +2,77 @@
22
#include <cstring>
33
#include <hal/nrf_gpio.h>
44
#include <nrfx_log.h>
5-
5+
#include <nrfx_twim.h>
6+
#include <nrf_drv_twi.h>
67
using namespace Pinetime::Drivers;
78

89
// TODO use shortcut to automatically send STOP when receive LastTX, for example
910
// TODO use DMA/IRQ
1011

11-
TwiMaster::TwiMaster(const Modules module, const Parameters& params) : module{module}, params{params} {
12-
mutex = xSemaphoreCreateBinary();
13-
}
14-
15-
void TwiMaster::Init() {
16-
NRF_GPIO->PIN_CNF[params.pinScl] = ((uint32_t)GPIO_PIN_CNF_DIR_Input << GPIO_PIN_CNF_DIR_Pos)
17-
| ((uint32_t)GPIO_PIN_CNF_INPUT_Connect << GPIO_PIN_CNF_INPUT_Pos)
18-
| ((uint32_t)GPIO_PIN_CNF_PULL_Pullup << GPIO_PIN_CNF_PULL_Pos)
19-
| ((uint32_t)GPIO_PIN_CNF_DRIVE_S0D1 << GPIO_PIN_CNF_DRIVE_Pos)
20-
| ((uint32_t)GPIO_PIN_CNF_SENSE_Disabled << GPIO_PIN_CNF_SENSE_Pos);
21-
22-
NRF_GPIO->PIN_CNF[params.pinSda] = ((uint32_t)GPIO_PIN_CNF_DIR_Input << GPIO_PIN_CNF_DIR_Pos)
23-
| ((uint32_t)GPIO_PIN_CNF_INPUT_Connect << GPIO_PIN_CNF_INPUT_Pos)
24-
| ((uint32_t)GPIO_PIN_CNF_PULL_Pullup << GPIO_PIN_CNF_PULL_Pos)
25-
| ((uint32_t)GPIO_PIN_CNF_DRIVE_S0D1 << GPIO_PIN_CNF_DRIVE_Pos)
26-
| ((uint32_t)GPIO_PIN_CNF_SENSE_Disabled << GPIO_PIN_CNF_SENSE_Pos);
27-
12+
TwiMaster::TwiMaster(const Modules module, const Parameters& params) : module{module}, params{params}, mutex{xSemaphoreCreateBinary()} {
13+
ASSERT(mutex != nullptr);
2814
switch(module) {
29-
case Modules::TWIM1: twiBaseAddress = NRF_TWIM1; break;
15+
case Modules::TWIM1:
3016
default:
31-
return;
32-
}
33-
34-
switch(static_cast<Frequencies>(params.frequency)) {
35-
case Frequencies::Khz100 : twiBaseAddress->FREQUENCY = TWIM_FREQUENCY_FREQUENCY_K100; break;
36-
case Frequencies::Khz250 : twiBaseAddress->FREQUENCY = TWIM_FREQUENCY_FREQUENCY_K250; break;
37-
case Frequencies::Khz400 : twiBaseAddress->FREQUENCY = TWIM_FREQUENCY_FREQUENCY_K400; break;
17+
twim = NRFX_TWIM_INSTANCE(1);
18+
break;
3819
}
20+
}
3921

40-
twiBaseAddress->PSEL.SCL = params.pinScl;
41-
twiBaseAddress->PSEL.SDA = params.pinSda;
42-
twiBaseAddress->EVENTS_LASTRX = 0;
43-
twiBaseAddress->EVENTS_STOPPED = 0;
44-
twiBaseAddress->EVENTS_LASTTX = 0;
45-
twiBaseAddress->EVENTS_ERROR = 0;
46-
twiBaseAddress->EVENTS_RXSTARTED = 0;
47-
twiBaseAddress->EVENTS_SUSPENDED = 0;
48-
twiBaseAddress->EVENTS_TXSTARTED = 0;
49-
50-
twiBaseAddress->ENABLE = (TWIM_ENABLE_ENABLE_Enabled << TWIM_ENABLE_ENABLE_Pos);
51-
52-
53-
/* // IRQ
54-
NVIC_ClearPendingIRQ(_IRQn);
55-
NVIC_SetPriority(_IRQn, 2);
56-
NVIC_EnableIRQ(_IRQn);
57-
*/
22+
void TwiMaster::Init() {
23+
nrfx_twim_config_t config;
24+
config.frequency = static_cast<nrf_twim_frequency_t>(params.frequency);
25+
config.hold_bus_uninit = false;
26+
config.interrupt_priority = 0;
27+
config.scl = params.pinScl;
28+
config.sda = params.pinSda;
29+
nrfx_twim_init(&twim,
30+
&config,
31+
nullptr,
32+
nullptr);
33+
nrfx_twim_enable(&twim);
5834

5935
xSemaphoreGive(mutex);
60-
6136
}
6237

6338
TwiMaster::ErrorCodes TwiMaster::Read(uint8_t deviceAddress, uint8_t registerAddress, uint8_t *data, size_t size) {
6439
xSemaphoreTake(mutex, portMAX_DELAY);
65-
auto ret = ReadWithRetry(deviceAddress, registerAddress, data, size);
40+
TwiMaster::ErrorCodes ret;
41+
42+
auto err = nrfx_twim_tx(&twim, deviceAddress, &registerAddress, 1, false);
43+
if(err != 0) {
44+
return TwiMaster::ErrorCodes::TransactionFailed;
45+
}
46+
47+
err = nrfx_twim_rx(&twim, deviceAddress, data, size);
48+
if(err != 0) {
49+
return TwiMaster::ErrorCodes::TransactionFailed;
50+
}
6651
xSemaphoreGive(mutex);
6752

68-
return ret;
53+
return TwiMaster::ErrorCodes::NoError;
6954
}
7055

7156
TwiMaster::ErrorCodes TwiMaster::Write(uint8_t deviceAddress, uint8_t registerAddress, const uint8_t *data, size_t size) {
7257
ASSERT(size <= maxDataSize);
7358
xSemaphoreTake(mutex, portMAX_DELAY);
74-
75-
auto ret = WriteWithRetry(deviceAddress, registerAddress, data, size);
76-
xSemaphoreGive(mutex);
77-
return ret;
78-
}
79-
80-
/* Execute a read transaction (composed of a write and a read operation). If one of these opeartion fails,
81-
* it's retried once. If it fails again, an error is returned */
82-
TwiMaster::ErrorCodes TwiMaster::ReadWithRetry(uint8_t deviceAddress, uint8_t registerAddress, uint8_t *data, size_t size) {
8359
TwiMaster::ErrorCodes ret;
84-
ret = Write(deviceAddress, &registerAddress, 1, false);
85-
if(ret != ErrorCodes::NoError)
86-
ret = Write(deviceAddress, &registerAddress, 1, false);
87-
88-
if(ret != ErrorCodes::NoError) return ret;
8960

90-
ret = Read(deviceAddress, data, size, true);
91-
if(ret != ErrorCodes::NoError)
92-
ret = Read(deviceAddress, data, size, true);
93-
94-
return ret;
95-
}
96-
97-
/* Execute a write transaction. If it fails, it is retried once. If it fails again, an error is returned. */
98-
TwiMaster::ErrorCodes TwiMaster::WriteWithRetry(uint8_t deviceAddress, uint8_t registerAddress, const uint8_t *data, size_t size) {
9961
internalBuffer[0] = registerAddress;
10062
std::memcpy(internalBuffer+1, data, size);
101-
auto ret = Write(deviceAddress, internalBuffer, size+1, true);
102-
if(ret != ErrorCodes::NoError)
103-
ret = Write(deviceAddress, internalBuffer, size+1, true);
104-
105-
return ret;
106-
}
107-
108-
109-
TwiMaster::ErrorCodes TwiMaster::Read(uint8_t deviceAddress, uint8_t *buffer, size_t size, bool stop) {
110-
twiBaseAddress->ADDRESS = deviceAddress;
111-
twiBaseAddress->TASKS_RESUME = 0x1UL;
112-
twiBaseAddress->RXD.PTR = (uint32_t)buffer;
113-
twiBaseAddress->RXD.MAXCNT = size;
114-
115-
twiBaseAddress->TASKS_STARTRX = 1;
116-
117-
while(!twiBaseAddress->EVENTS_RXSTARTED && !twiBaseAddress->EVENTS_ERROR);
118-
twiBaseAddress->EVENTS_RXSTARTED = 0x0UL;
119-
120-
txStartedCycleCount = DWT->CYCCNT;
121-
uint32_t currentCycleCount;
122-
while(!twiBaseAddress->EVENTS_LASTRX && !twiBaseAddress->EVENTS_ERROR) {
123-
currentCycleCount = DWT->CYCCNT;
124-
if ((currentCycleCount-txStartedCycleCount) > HwFreezedDelay) {
125-
FixHwFreezed();
126-
return ErrorCodes::TransactionFailed;
127-
}
128-
}
129-
twiBaseAddress->EVENTS_LASTRX = 0x0UL;
130-
131-
if (stop || twiBaseAddress->EVENTS_ERROR) {
132-
twiBaseAddress->TASKS_STOP = 0x1UL;
133-
while(!twiBaseAddress->EVENTS_STOPPED);
134-
twiBaseAddress->EVENTS_STOPPED = 0x0UL;
135-
}
136-
else {
137-
twiBaseAddress->TASKS_SUSPEND = 0x1UL;
138-
while(!twiBaseAddress->EVENTS_SUSPENDED);
139-
twiBaseAddress->EVENTS_SUSPENDED = 0x0UL;
63+
auto err = nrfx_twim_tx(&twim, deviceAddress, internalBuffer , size+1, false);
64+
if(err != 0){
65+
return TwiMaster::ErrorCodes::TransactionFailed;
14066
}
14167

142-
if (twiBaseAddress->EVENTS_ERROR) {
143-
twiBaseAddress->EVENTS_ERROR = 0x0UL;
144-
}
145-
return ErrorCodes::NoError;
146-
}
147-
148-
TwiMaster::ErrorCodes TwiMaster::Write(uint8_t deviceAddress, const uint8_t *data, size_t size, bool stop) {
149-
twiBaseAddress->ADDRESS = deviceAddress;
150-
twiBaseAddress->TASKS_RESUME = 0x1UL;
151-
twiBaseAddress->TXD.PTR = (uint32_t)data;
152-
twiBaseAddress->TXD.MAXCNT = size;
153-
154-
twiBaseAddress->TASKS_STARTTX = 1;
155-
156-
while(!twiBaseAddress->EVENTS_TXSTARTED && !twiBaseAddress->EVENTS_ERROR);
157-
twiBaseAddress->EVENTS_TXSTARTED = 0x0UL;
158-
159-
txStartedCycleCount = DWT->CYCCNT;
160-
uint32_t currentCycleCount;
161-
while(!twiBaseAddress->EVENTS_LASTTX && !twiBaseAddress->EVENTS_ERROR) {
162-
currentCycleCount = DWT->CYCCNT;
163-
if ((currentCycleCount-txStartedCycleCount) > HwFreezedDelay) {
164-
FixHwFreezed();
165-
return ErrorCodes::TransactionFailed;
166-
}
167-
}
168-
twiBaseAddress->EVENTS_LASTTX = 0x0UL;
169-
170-
if (stop || twiBaseAddress->EVENTS_ERROR) {
171-
twiBaseAddress->TASKS_STOP = 0x1UL;
172-
while(!twiBaseAddress->EVENTS_STOPPED);
173-
twiBaseAddress->EVENTS_STOPPED = 0x0UL;
174-
}
175-
else {
176-
twiBaseAddress->TASKS_SUSPEND = 0x1UL;
177-
while(!twiBaseAddress->EVENTS_SUSPENDED);
178-
twiBaseAddress->EVENTS_SUSPENDED = 0x0UL;
179-
}
180-
181-
if (twiBaseAddress->EVENTS_ERROR) {
182-
twiBaseAddress->EVENTS_ERROR = 0x0UL;
183-
uint32_t error = twiBaseAddress->ERRORSRC;
184-
twiBaseAddress->ERRORSRC = error;
185-
}
186-
187-
return ErrorCodes::NoError;
68+
xSemaphoreGive(mutex);
69+
return TwiMaster::ErrorCodes::NoError;
18870
}
18971

19072
void TwiMaster::Sleep() {
191-
while(twiBaseAddress->ENABLE != 0) {
192-
twiBaseAddress->ENABLE = (TWIM_ENABLE_ENABLE_Disabled << TWIM_ENABLE_ENABLE_Pos);
193-
}
73+
nrfx_twim_disable(&twim);
74+
nrfx_twim_uninit(&twim);
75+
19476
nrf_gpio_cfg_default(6);
19577
nrf_gpio_cfg_default(7);
19678
NRF_LOG_INFO("[TWIMASTER] Sleep");
@@ -200,30 +82,3 @@ void TwiMaster::Wakeup() {
20082
Init();
20183
NRF_LOG_INFO("[TWIMASTER] Wakeup");
20284
}
203-
204-
/* Sometimes, the TWIM device just freeze and never set the event EVENTS_LASTTX.
205-
* This method disable and re-enable the peripheral so that it works again.
206-
* This is just a workaround, and it would be better if we could find a way to prevent
207-
* this issue from happening.
208-
* */
209-
void TwiMaster::FixHwFreezed() {
210-
NRF_LOG_INFO("I2C device frozen, reinitializing it!");
211-
// Disable I²C
212-
uint32_t twi_state = NRF_TWI1->ENABLE;
213-
twiBaseAddress->ENABLE = TWIM_ENABLE_ENABLE_Disabled << TWI_ENABLE_ENABLE_Pos;
214-
215-
NRF_GPIO->PIN_CNF[params.pinScl] = ((uint32_t)GPIO_PIN_CNF_DIR_Input << GPIO_PIN_CNF_DIR_Pos)
216-
| ((uint32_t)GPIO_PIN_CNF_INPUT_Connect << GPIO_PIN_CNF_INPUT_Pos)
217-
| ((uint32_t)GPIO_PIN_CNF_PULL_Pullup << GPIO_PIN_CNF_PULL_Pos)
218-
| ((uint32_t)GPIO_PIN_CNF_DRIVE_S0S1 << GPIO_PIN_CNF_DRIVE_Pos)
219-
| ((uint32_t)GPIO_PIN_CNF_SENSE_Disabled << GPIO_PIN_CNF_SENSE_Pos);
220-
221-
NRF_GPIO->PIN_CNF[params.pinSda] = ((uint32_t)GPIO_PIN_CNF_DIR_Input << GPIO_PIN_CNF_DIR_Pos)
222-
| ((uint32_t)GPIO_PIN_CNF_INPUT_Connect << GPIO_PIN_CNF_INPUT_Pos)
223-
| ((uint32_t)GPIO_PIN_CNF_PULL_Pullup << GPIO_PIN_CNF_PULL_Pos)
224-
| ((uint32_t)GPIO_PIN_CNF_DRIVE_S0S1 << GPIO_PIN_CNF_DRIVE_Pos)
225-
| ((uint32_t)GPIO_PIN_CNF_SENSE_Disabled << GPIO_PIN_CNF_SENSE_Pos);
226-
227-
// Re-enable I²C
228-
twiBaseAddress->ENABLE = twi_state;
229-
}

src/drivers/TwiMaster.h

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,13 @@
33
#include <semphr.h>
44
#include <drivers/include/nrfx_twi.h> // NRF_TWIM_Type
55
#include <cstdint>
6+
#include <nrfx_twim.h>
67

78
namespace Pinetime {
89
namespace Drivers {
910
class TwiMaster {
1011
public:
1112
enum class Modules { TWIM1 };
12-
enum class Frequencies {Khz100, Khz250, Khz400};
1313
enum class ErrorCodes {NoError, TransactionFailed};
1414
struct Parameters {
1515
uint32_t frequency;
@@ -27,21 +27,13 @@ namespace Pinetime {
2727
void Wakeup();
2828

2929
private:
30-
ErrorCodes ReadWithRetry(uint8_t deviceAddress, uint8_t registerAddress, uint8_t* buffer, size_t size);
31-
ErrorCodes WriteWithRetry(uint8_t deviceAddress, uint8_t registerAddress, const uint8_t* data, size_t size);
32-
33-
ErrorCodes Read(uint8_t deviceAddress, uint8_t* buffer, size_t size, bool stop);
34-
ErrorCodes Write(uint8_t deviceAddress, const uint8_t* data, size_t size, bool stop);
35-
void FixHwFreezed();
36-
NRF_TWIM_Type* twiBaseAddress;
37-
SemaphoreHandle_t mutex;
30+
nrfx_twim_t twim;
3831
const Modules module;
3932
const Parameters params;
33+
SemaphoreHandle_t mutex;
4034
static constexpr uint8_t maxDataSize{8};
4135
static constexpr uint8_t registerSize{1};
4236
uint8_t internalBuffer[maxDataSize + registerSize];
43-
uint32_t txStartedCycleCount = 0;
44-
static constexpr uint32_t HwFreezedDelay{161000};
4537
};
4638
}
4739
}

src/sdk_config.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4992,7 +4992,7 @@
49924992
// <e> NRFX_TWIM_ENABLED - nrfx_twim - TWIM peripheral driver
49934993
//==========================================================
49944994
#ifndef NRFX_TWIM_ENABLED
4995-
#define NRFX_TWIM_ENABLED 0
4995+
#define NRFX_TWIM_ENABLED 1
49964996
#endif
49974997
// <q> NRFX_TWIM0_ENABLED - Enable TWIM0 instance
49984998

@@ -5005,7 +5005,7 @@
50055005

50065006

50075007
#ifndef NRFX_TWIM1_ENABLED
5008-
#define NRFX_TWIM1_ENABLED 0
5008+
#define NRFX_TWIM1_ENABLED 1
50095009
#endif
50105010

50115011
// <o> NRFX_TWIM_DEFAULT_CONFIG_FREQUENCY - Frequency

src/systemtask/SystemTask.cpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,10 +84,14 @@ void SystemTask::Work() {
8484
touchPanel.Init();
8585
batteryController.Init();
8686
motorController.Init();
87-
motionSensor.Init();
87+
motionSensor.SoftReset();
8888

89-
settingsController.Init();
89+
// Reset the TWI device because the motion sensor chip most probably crashed it...
90+
twiMaster.Sleep();
91+
twiMaster.Init();
9092

93+
motionSensor.Init();
94+
settingsController.Init();
9195

9296
displayApp = std::make_unique<Pinetime::Applications::DisplayApp>(lcd, lvgl, touchPanel, batteryController, bleController,
9397
dateTimeController, watchdogView, *this, notificationManager,

0 commit comments

Comments
 (0)