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
This commit is contained in:
Joaquim 2021-04-10 19:09:33 +01:00
parent eb769fb60e
commit 012c246e40
14 changed files with 303 additions and 69 deletions

View File

@ -406,6 +406,7 @@ list(APPEND SOURCE_FILES
displayapp/screens/FlashLight.cpp displayapp/screens/FlashLight.cpp
displayapp/screens/List.cpp displayapp/screens/List.cpp
displayapp/screens/BatteryInfo.cpp displayapp/screens/BatteryInfo.cpp
displayapp/screens/Tasks.cpp
## Settings ## Settings
displayapp/screens/settings/QuickSettings.cpp displayapp/screens/settings/QuickSettings.cpp

View File

@ -63,7 +63,7 @@
#define configTICK_RATE_HZ 1024 #define configTICK_RATE_HZ 1024
#define configMAX_PRIORITIES ( 3 ) #define configMAX_PRIORITIES ( 3 )
#define configMINIMAL_STACK_SIZE ( 120 ) #define configMINIMAL_STACK_SIZE ( 120 )
#define configTOTAL_HEAP_SIZE ( 1024*15 ) #define configTOTAL_HEAP_SIZE ( 1024*16 )
#define configMAX_TASK_NAME_LEN ( 4 ) #define configMAX_TASK_NAME_LEN ( 4 )
#define configUSE_16_BIT_TICKS 0 #define configUSE_16_BIT_TICKS 0
#define configIDLE_SHOULD_YIELD 1 #define configIDLE_SHOULD_YIELD 1
@ -96,7 +96,7 @@
#define configUSE_TIMERS 1 #define configUSE_TIMERS 1
#define configTIMER_TASK_PRIORITY ( 0 ) #define configTIMER_TASK_PRIORITY ( 0 )
#define configTIMER_QUEUE_LENGTH 32 #define configTIMER_QUEUE_LENGTH 32
#define configTIMER_TASK_STACK_DEPTH ( 200 ) #define configTIMER_TASK_STACK_DEPTH ( 300 )
/* Tickless Idle configuration. */ /* Tickless Idle configuration. */
#define configEXPECTED_IDLE_TIME_BEFORE_SLEEP 2 #define configEXPECTED_IDLE_TIME_BEFORE_SLEEP 2

View File

@ -4,7 +4,7 @@ namespace Pinetime {
namespace Applications { namespace Applications {
enum class Apps { enum class Apps {
None, Launcher, Clock, SysInfo, FirmwareUpdate, FirmwareValidation, NotificationsPreview, Notifications, FlashLight, BatteryInfo, None, Launcher, Clock, SysInfo, FirmwareUpdate, FirmwareValidation, NotificationsPreview, Notifications, FlashLight, BatteryInfo,
Music, Paint, Paddle, Twos, HeartRate, Navigation, StopWatch, Motion, Music, Paint, Paddle, Twos, HeartRate, Navigation, StopWatch, Motion, Tasks,
QuickSettings, Settings, SettingWatchFace, SettingTimeFormat, SettingDisplay, SettingWakeUp QuickSettings, Settings, SettingWatchFace, SettingTimeFormat, SettingDisplay, SettingWakeUp
}; };
} }

View File

@ -24,6 +24,7 @@
#include "displayapp/screens/Twos.h" #include "displayapp/screens/Twos.h"
#include "displayapp/screens/FlashLight.h" #include "displayapp/screens/FlashLight.h"
#include "displayapp/screens/BatteryInfo.h" #include "displayapp/screens/BatteryInfo.h"
#include "displayapp/screens/Tasks.h"
#include "drivers/Cst816s.h" #include "drivers/Cst816s.h"
#include "drivers/St7789.h" #include "drivers/St7789.h"
#include "drivers/Watchdog.h" #include "drivers/Watchdog.h"
@ -307,6 +308,10 @@ void DisplayApp::LoadApp(Apps app, DisplayApp::FullRefreshDirections direction)
case Apps::Motion: case Apps::Motion:
currentScreen = std::make_unique<Screens::Motion>(this, motionController); currentScreen = std::make_unique<Screens::Motion>(this, motionController);
break; break;
case Apps::Tasks:
currentScreen = std::make_unique<Screens::Tasks>(this);
returnApp(Apps::Launcher, FullRefreshDirections::Down, TouchEvents::SwipeDown);
break;
} }
currentApp = app; currentApp = app;

View File

@ -246,10 +246,10 @@ static void basic_init(void)
lv_style_set_border_color(&style_table_cell, LV_STATE_DEFAULT, LV_PINETIME_GRAY); lv_style_set_border_color(&style_table_cell, LV_STATE_DEFAULT, LV_PINETIME_GRAY);
lv_style_set_border_width(&style_table_cell, LV_STATE_DEFAULT, 1); lv_style_set_border_width(&style_table_cell, LV_STATE_DEFAULT, 1);
lv_style_set_border_side(&style_table_cell, LV_STATE_DEFAULT, LV_BORDER_SIDE_FULL); lv_style_set_border_side(&style_table_cell, LV_STATE_DEFAULT, LV_BORDER_SIDE_FULL);
lv_style_set_pad_left(&style_table_cell, LV_STATE_DEFAULT, 12); lv_style_set_pad_left(&style_table_cell, LV_STATE_DEFAULT, 5);
lv_style_set_pad_right(&style_table_cell, LV_STATE_DEFAULT, 12); lv_style_set_pad_right(&style_table_cell, LV_STATE_DEFAULT, 5);
lv_style_set_pad_top(&style_table_cell, LV_STATE_DEFAULT, 12); lv_style_set_pad_top(&style_table_cell, LV_STATE_DEFAULT, 2);
lv_style_set_pad_bottom(&style_table_cell, LV_STATE_DEFAULT, 12); lv_style_set_pad_bottom(&style_table_cell, LV_STATE_DEFAULT, 2);
style_init_reset(&style_pad_small); style_init_reset(&style_pad_small);
lv_style_int_t pad_small_value = 10; lv_style_int_t pad_small_value = 10;
@ -356,6 +356,7 @@ static void theme_apply(lv_obj_t * obj, lv_theme_style_t name)
lv_obj_clean_style_list(obj, LV_OBJ_PART_MAIN); lv_obj_clean_style_list(obj, LV_OBJ_PART_MAIN);
list = lv_obj_get_style_list(obj, LV_OBJ_PART_MAIN); list = lv_obj_get_style_list(obj, LV_OBJ_PART_MAIN);
_lv_style_list_add_style(list, &style_bg); _lv_style_list_add_style(list, &style_bg);
_lv_style_list_add_style(list, &style_label_white);
break; break;
case LV_THEME_OBJ: case LV_THEME_OBJ:
@ -499,6 +500,7 @@ static void theme_apply(lv_obj_t * obj, lv_theme_style_t name)
for(; idx <= LV_TABLE_CELL_STYLE_CNT; idx ++) { for(; idx <= LV_TABLE_CELL_STYLE_CNT; idx ++) {
list = lv_obj_get_style_list(obj, idx); list = lv_obj_get_style_list(obj, idx);
_lv_style_list_add_style(list, &style_table_cell); _lv_style_list_add_style(list, &style_table_cell);
_lv_style_list_add_style(list, &style_label_white);
} }
break; break;

View File

@ -49,7 +49,7 @@ std::unique_ptr<Screen> ApplicationList::CreateScreen1() {
{Symbols::map, Apps::Navigation}, {Symbols::map, Apps::Navigation},
{Symbols::shoe, Apps::Motion}, {Symbols::shoe, Apps::Motion},
{Symbols::heartBeat, Apps::HeartRate}, {Symbols::heartBeat, Apps::HeartRate},
{"", Apps::None}, {"Tasks", Apps::Tasks},
} }
}; };

View File

@ -0,0 +1,77 @@
#include <FreeRTOS.h>
#include <task.h>
#include "Tasks.h"
#include <lvgl/lvgl.h>
#include "../DisplayApp.h"
#include <string>
#include <algorithm>
using namespace Pinetime::Applications::Screens;
static void lv_update_task(struct _lv_task_t *task) {
auto user_data = static_cast<Tasks *>(task->user_data);
user_data->UpdateScreen();
}
Tasks::Tasks(
Pinetime::Applications::DisplayApp *app) :
Screen(app)
{
table = lv_table_create(lv_scr_act(), NULL);
lv_table_set_col_cnt(table, 3);
lv_table_set_row_cnt(table, 8);
//lv_obj_align(table, NULL, LV_ALIGN_CENTER, 0, 0);
lv_obj_set_size(table, 240, 240);
lv_obj_set_pos(table, 0, 0);
/*lv_table_set_cell_type(table, 0, 0, 1);
lv_table_set_cell_type(table, 0, 1, 1);
lv_table_set_cell_type(table, 0, 2, 1);
lv_table_set_cell_type(table, 0, 3, 1);*/
lv_table_set_cell_value(table, 0, 0, "#");
lv_table_set_col_width(table, 0, 50);
lv_table_set_cell_value(table, 0, 1, "Task");
lv_table_set_col_width(table, 1, 80);
lv_table_set_cell_value(table, 0, 2, "Free");
lv_table_set_col_width(table, 2, 80);
lv_obj_t * backgroundLabel = lv_label_create(lv_scr_act(), nullptr);
lv_label_set_long_mode(backgroundLabel, LV_LABEL_LONG_CROP);
lv_obj_set_size(backgroundLabel, 240, 240);
lv_obj_set_pos(backgroundLabel, 0, 0);
lv_label_set_text_static(backgroundLabel, "");
UpdateScreen();
taskUpdate = lv_task_create(lv_update_task, 100000, LV_TASK_PRIO_LOW, this);
}
Tasks::~Tasks() {
lv_task_del(taskUpdate);
lv_obj_clean(lv_scr_act());
}
bool sortById(const TaskStatus_t &lhs, const TaskStatus_t &rhs) { return lhs.xTaskNumber < rhs.xTaskNumber; }
void Tasks::UpdateScreen() {
auto nb = uxTaskGetSystemState(tasksStatus, 7, nullptr);
std::sort(tasksStatus, tasksStatus + nb, sortById);
for (uint8_t i = 0; i < nb; i++) {
lv_table_set_cell_value(table, i + 1, 0, std::to_string(tasksStatus[i].xTaskNumber).c_str());
lv_table_set_cell_value(table, i + 1, 1, tasksStatus[i].pcTaskName);
if (tasksStatus[i].usStackHighWaterMark < 20) {
std::string str1 = std::to_string(tasksStatus[i].usStackHighWaterMark) + " low";
lv_table_set_cell_value(table, i + 1, 2, str1.c_str());
} else {
lv_table_set_cell_value(table, i + 1, 2, std::to_string(tasksStatus[i].usStackHighWaterMark).c_str());
}
}
}
bool Tasks::Refresh() {
return running;
}

View File

@ -0,0 +1,31 @@
#pragma once
#include <FreeRTOS.h>
#include <task.h>
#include <cstdint>
#include <lvgl/lvgl.h>
#include <timers.h>
#include "Screen.h"
namespace Pinetime {
namespace Applications {
namespace Screens {
class Tasks : public Screen{
public:
Tasks(DisplayApp* app);
~Tasks() override;
bool Refresh() override;
void UpdateScreen();
private:
mutable TaskStatus_t tasksStatus[7];
lv_task_t* taskUpdate;
lv_obj_t * table;
};
}
}
}

View File

@ -227,12 +227,10 @@ bool WatchFaceDigital::Refresh() {
heartbeat = heartRateController.HeartRate(); heartbeat = heartRateController.HeartRate();
heartbeatRunning = heartRateController.State() != Controllers::HeartRateController::States::Stopped; heartbeatRunning = heartRateController.State() != Controllers::HeartRateController::States::Stopped;
if(heartbeat.IsUpdated() || heartbeatRunning.IsUpdated()) { if(heartbeat.IsUpdated() || heartbeatRunning.IsUpdated()) {
char heartbeatBuffer[4];
if(heartbeatRunning.Get()) if(heartbeatRunning.Get())
sprintf(heartbeatBuffer, "%d", heartbeat.Get()); lv_label_set_text_fmt(heartbeatValue, "%d", heartbeat.Get());
else else
sprintf(heartbeatBuffer, "---"); lv_label_set_text_static(heartbeatValue, "---");
lv_label_set_text(heartbeatValue, heartbeatBuffer); lv_label_set_text(heartbeatValue, heartbeatBuffer);
lv_obj_align(heartbeatIcon, lv_scr_act(), LV_ALIGN_IN_BOTTOM_LEFT, 5, -2); lv_obj_align(heartbeatIcon, lv_scr_act(), LV_ALIGN_IN_BOTTOM_LEFT, 5, -2);
lv_obj_align(heartbeatValue, heartbeatIcon, LV_ALIGN_OUT_RIGHT_MID, 5, 0); lv_obj_align(heartbeatValue, heartbeatIcon, LV_ALIGN_OUT_RIGHT_MID, 5, 0);
@ -242,11 +240,7 @@ bool WatchFaceDigital::Refresh() {
stepCount = motionController.NbSteps(); stepCount = motionController.NbSteps();
motionSensorOk = motionController.IsSensorOk(); motionSensorOk = motionController.IsSensorOk();
if(stepCount.IsUpdated() || motionSensorOk.IsUpdated()) { if(stepCount.IsUpdated() || motionSensorOk.IsUpdated()) {
char stepBuffer[5]; lv_label_set_text_fmt(stepValue, "%lu", stepCount.Get());
if(motionSensorOk.Get())
sprintf(stepBuffer, "%lu", stepCount.Get());
else
sprintf(stepBuffer, "---", stepCount.Get());
lv_label_set_text(stepValue, stepBuffer); lv_label_set_text(stepValue, stepBuffer);
lv_obj_align(stepValue, lv_scr_act(), LV_ALIGN_IN_BOTTOM_RIGHT, -5, -2); lv_obj_align(stepValue, lv_scr_act(), LV_ALIGN_IN_BOTTOM_RIGHT, -5, -2);
lv_obj_align(stepIcon, stepValue, LV_ALIGN_OUT_LEFT_MID, -5, 0); lv_obj_align(stepIcon, stepValue, LV_ALIGN_OUT_LEFT_MID, -5, 0);

View File

@ -31,7 +31,7 @@ Bma421::Bma421(TwiMaster& twiMaster, uint8_t twiAddress) : twiMaster{twiMaster},
bma.variant = BMA42X_VARIANT; bma.variant = BMA42X_VARIANT;
bma.intf_ptr = this; bma.intf_ptr = this;
bma.delay_us = user_delay; bma.delay_us = user_delay;
bma.read_write_len = 8; bma.read_write_len = 16;
} }
void Bma421::Init() { void Bma421::Init() {

View File

@ -31,6 +31,7 @@ void Cst816S::Init() {
twiMaster.Read(twiAddress, 0x15, &dummy, 1); twiMaster.Read(twiAddress, 0x15, &dummy, 1);
vTaskDelay(5); vTaskDelay(5);
twiMaster.Read(twiAddress, 0xa7, &dummy, 1); twiMaster.Read(twiAddress, 0xa7, &dummy, 1);
vTaskDelay(5);
/* /*
[2] EnConLR - Continuous operation can slide around [2] EnConLR - Continuous operation can slide around

View File

@ -53,7 +53,7 @@ namespace Pinetime {
static constexpr uint8_t touchStep = 6; static constexpr uint8_t touchStep = 6;
static constexpr uint8_t gestureIndex = 1; static constexpr uint8_t gestureIndex = 1;
uint8_t touchData[63]; uint8_t touchData[10];
TwiMaster& twiMaster; TwiMaster& twiMaster;
uint8_t twiAddress; uint8_t twiAddress;
}; };

View File

@ -2,77 +2,167 @@
#include <cstring> #include <cstring>
#include <hal/nrf_gpio.h> #include <hal/nrf_gpio.h>
#include <nrfx_log.h> #include <nrfx_log.h>
#include <nrfx_twim.h>
#include <nrf_drv_twi.h>
using namespace Pinetime::Drivers; using namespace Pinetime::Drivers;
// TODO use shortcut to automatically send STOP when receive LastTX, for example // TODO use shortcut to automatically send STOP when receive LastTX, for example
// TODO use DMA/IRQ // TODO use DMA/IRQ
TwiMaster::TwiMaster(const Modules module, const Parameters& params) : module{module}, params{params}, mutex{xSemaphoreCreateBinary()} { TwiMaster::TwiMaster(const Modules module, const Parameters& params) : module{module}, params{params} {
ASSERT(mutex != nullptr); mutex = xSemaphoreCreateBinary();
switch(module) {
case Modules::TWIM1:
default:
twim = NRFX_TWIM_INSTANCE(1);
break;
}
} }
void TwiMaster::Init() { void TwiMaster::Init() {
nrfx_twim_config_t config; NRF_GPIO->PIN_CNF[params.pinScl] = ((uint32_t)GPIO_PIN_CNF_DIR_Input << GPIO_PIN_CNF_DIR_Pos)
config.frequency = static_cast<nrf_twim_frequency_t>(params.frequency); | ((uint32_t)GPIO_PIN_CNF_INPUT_Connect << GPIO_PIN_CNF_INPUT_Pos)
config.hold_bus_uninit = false; | ((uint32_t)GPIO_PIN_CNF_PULL_Pullup << GPIO_PIN_CNF_PULL_Pos)
config.interrupt_priority = 0; | ((uint32_t)GPIO_PIN_CNF_DRIVE_S0D1 << GPIO_PIN_CNF_DRIVE_Pos)
config.scl = params.pinScl; | ((uint32_t)GPIO_PIN_CNF_SENSE_Disabled << GPIO_PIN_CNF_SENSE_Pos);
config.sda = params.pinSda;
nrfx_twim_init(&twim, NRF_GPIO->PIN_CNF[params.pinSda] = ((uint32_t)GPIO_PIN_CNF_DIR_Input << GPIO_PIN_CNF_DIR_Pos)
&config, | ((uint32_t)GPIO_PIN_CNF_INPUT_Connect << GPIO_PIN_CNF_INPUT_Pos)
nullptr, | ((uint32_t)GPIO_PIN_CNF_PULL_Pullup << GPIO_PIN_CNF_PULL_Pos)
nullptr); | ((uint32_t)GPIO_PIN_CNF_DRIVE_S0D1 << GPIO_PIN_CNF_DRIVE_Pos)
nrfx_twim_enable(&twim); | ((uint32_t)GPIO_PIN_CNF_SENSE_Disabled << GPIO_PIN_CNF_SENSE_Pos);
switch(module) {
case Modules::TWIM1: twiBaseAddress = NRF_TWIM1; break;
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;
}
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); xSemaphoreGive(mutex);
} }
TwiMaster::ErrorCodes TwiMaster::Read(uint8_t deviceAddress, uint8_t registerAddress, uint8_t *data, size_t size) { TwiMaster::ErrorCodes TwiMaster::Read(uint8_t deviceAddress, uint8_t registerAddress, uint8_t *data, size_t size) {
xSemaphoreTake(mutex, portMAX_DELAY); xSemaphoreTake(mutex, portMAX_DELAY);
TwiMaster::ErrorCodes ret; auto ret = Write(deviceAddress, &registerAddress, 1, false);
ret = Read(deviceAddress, data, size, true);
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); 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) { TwiMaster::ErrorCodes TwiMaster::Write(uint8_t deviceAddress, uint8_t registerAddress, const uint8_t *data, size_t size) {
ASSERT(size <= maxDataSize); ASSERT(size <= maxDataSize);
xSemaphoreTake(mutex, portMAX_DELAY); xSemaphoreTake(mutex, portMAX_DELAY);
TwiMaster::ErrorCodes ret;
internalBuffer[0] = registerAddress; internalBuffer[0] = registerAddress;
std::memcpy(internalBuffer+1, data, size); std::memcpy(internalBuffer + 1, data, size);
auto err = nrfx_twim_tx(&twim, deviceAddress, internalBuffer , size+1, false); auto ret = Write(deviceAddress, internalBuffer, size + 1, true);
if(err != 0){ xSemaphoreGive(mutex);
return TwiMaster::ErrorCodes::TransactionFailed; 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;
} }
xSemaphoreGive(mutex); if (twiBaseAddress->EVENTS_ERROR) {
return TwiMaster::ErrorCodes::NoError; 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;
} }
void TwiMaster::Sleep() { void TwiMaster::Sleep() {
nrfx_twim_disable(&twim); while(twiBaseAddress->ENABLE != 0) {
nrfx_twim_uninit(&twim); twiBaseAddress->ENABLE = (TWIM_ENABLE_ENABLE_Disabled << TWIM_ENABLE_ENABLE_Pos);
}
nrf_gpio_cfg_default(6); nrf_gpio_cfg_default(6);
nrf_gpio_cfg_default(7); nrf_gpio_cfg_default(7);
NRF_LOG_INFO("[TWIMASTER] Sleep"); NRF_LOG_INFO("[TWIMASTER] Sleep");
@ -82,3 +172,30 @@ void TwiMaster::Wakeup() {
Init(); Init();
NRF_LOG_INFO("[TWIMASTER] Wakeup"); 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;
}

View File

@ -3,13 +3,13 @@
#include <semphr.h> #include <semphr.h>
#include <drivers/include/nrfx_twi.h> // NRF_TWIM_Type #include <drivers/include/nrfx_twi.h> // NRF_TWIM_Type
#include <cstdint> #include <cstdint>
#include <nrfx_twim.h>
namespace Pinetime { namespace Pinetime {
namespace Drivers { namespace Drivers {
class TwiMaster { class TwiMaster {
public: public:
enum class Modules { TWIM1 }; enum class Modules { TWIM1 };
enum class Frequencies {Khz100, Khz250, Khz400};
enum class ErrorCodes {NoError, TransactionFailed}; enum class ErrorCodes {NoError, TransactionFailed};
struct Parameters { struct Parameters {
uint32_t frequency; uint32_t frequency;
@ -27,13 +27,19 @@ namespace Pinetime {
void Wakeup(); void Wakeup();
private: private:
nrfx_twim_t twim;
ErrorCodes Read(uint8_t deviceAddress, uint8_t* buffer, size_t size, bool stop);
ErrorCodes Write(uint8_t deviceAddress, const uint8_t* data, size_t size, bool stop);
void FixHwFreezed();
NRF_TWIM_Type* twiBaseAddress;
SemaphoreHandle_t mutex;
const Modules module; const Modules module;
const Parameters params; const Parameters params;
SemaphoreHandle_t mutex; static constexpr uint8_t maxDataSize{16};
static constexpr uint8_t maxDataSize{8};
static constexpr uint8_t registerSize{1}; static constexpr uint8_t registerSize{1};
uint8_t internalBuffer[maxDataSize + registerSize]; uint8_t internalBuffer[maxDataSize + registerSize];
uint32_t txStartedCycleCount = 0;
static constexpr uint32_t HwFreezedDelay{161000};
}; };
} }
} }