Re-enable BLE, BLE status on display and battery level on display.

This commit is contained in:
JF 2020-02-23 16:14:03 +01:00
parent 02772b996f
commit f07ffab4c1
14 changed files with 129 additions and 72 deletions

46
doc/SPI-LCD-driver.md Normal file
View File

@ -0,0 +1,46 @@
# The SPI LCD driver
## Introduction
The LCD controller that drive the display of the Pinetime is the Sitronix ST7789V. This controller is easy to integrate with an MCU thanks to its SPI interface, and has some interesting features like:
- an on-chip display data RAM that can store the whole framebuffer
- partial screen update
- hardware assisted vertical scrolling
- interrupt pin, allowing to drive the display with DMA and IRQ
- ...
When you want to write a device driver for a specific component, its datasheet is your holy bible. This document contains a lot of information about the chip, its specification, characteristics, features and functionalities.
Luckily for us, the datasheet of the ST7789 is great! It contains everything we need to write a nice driver for our beloved Pinetime.
In this document, I'll try to explain the process I've followed to write a device driver for the LCD. There were multiple iterations:
- First, I tried to find the correct initialization sequence so that the controller is configured correctly according to the hardware configuration;
- Then, I tried to display some pixels on the screen;
- Next, I wanted to display squares, colors and text;
- Following, there was a need to make that faster and faster again;
- And finally, I wanted to draw beautiful and useful UIs
I'll describe all these steps in the following chapters.
## The datasheet
As I said in the introduction, the datasheet will be your bedside book during your journey as a device driver designer. You'll read it from the beginning to the end once, twice, maybe ten times. Then, each time you'll want to do something new, you'll reopen the file and search for that specific paragraph or diagram than explains how the controller works so that you can figure out how to use it.
The schematic of your board (the Pinetime schematics in this case) will also be very important, as you'll need to know how the LCD controller is physically connected to the MCU.
How to read the datasheet? I recommand to read it from the beginning to the end (no joke) at least once. You certainly do not need to read everything in details, but it's good to know what information is available and where in the document. It'll be very useful during the developpment phase.
You'll want to read some part with more attention :
- Data color coding in 4-Line Serial Interface : how to send the pixel to be display to the controller
- Display Data Ram : how is the memory organised
- Power On/Off sequence
- System function commands : all the commands you can send to the controller.
## One Pixel at a time
## Bulk transfert
## DMA
## IRQ
## Bare metal integration
Integration customisée dans la lib GFX que j'ai écrite
## Integration with LittleVGL

View File

@ -1,4 +1,5 @@
#pragma once
#include <ble/ble_services/ble_cts_c/ble_cts_c.h>
#ifdef __cplusplus
extern "C" {

View File

@ -158,7 +158,7 @@ list(APPEND SOURCE_FILES
DisplayApp/DisplayApp.cpp
DisplayApp/Screens/Screen.cpp
DisplayApp/Screens/Clock.cpp
# DisplayApp/Screens/Message.cpp
DisplayApp/Screens/Message.cpp
DisplayApp/Screens/Tile.cpp
# DisplayApp/Screens/Tab.cpp
main.cpp
@ -189,7 +189,7 @@ set(INCLUDE_FILES
DisplayApp/DisplayApp.h
DisplayApp/Screens/Screen.h
DisplayApp/Screens/Clock.h
# DisplayApp/Screens/Message.h
DisplayApp/Screens/Message.h
DisplayApp/Screens/Tile.h
# DisplayApp/Screens/Tab.h
drivers/St7789.h

View File

@ -4,17 +4,14 @@
#include <libraries/log/nrf_log.h>
#include <boards.h>
#include <nrf_font.h>
#include <hal/nrf_rtc.h>
#include "Components/Gfx/Gfx.h"
#include <queue.h>
#include <Components/DateTime/DateTimeController.h>
#include <drivers/Cst816s.h>
#include <chrono>
#include <string>
#include <lvgl/lvgl.h>
#include <DisplayApp/Screens/Tile.h>
#include <DisplayApp/Screens/Message.h>
#include "../SystemTask/SystemTask.h"
//#include <DisplayApp/Screens/Tab.h>
using namespace Pinetime::Applications;
@ -31,7 +28,7 @@ DisplayApp::DisplayApp(Pinetime::Drivers::St7789& lcd,
batteryController{batteryController},
bleController{bleController},
dateTimeController{dateTimeController},
currentScreen{new Screens::Clock(this, dateTimeController) },
currentScreen{new Screens::Clock(this, dateTimeController, batteryController, bleController) },
systemTask{systemTask} {
msgQueue = xQueueCreate(queueSize, itemSize);
}
@ -126,13 +123,13 @@ void DisplayApp::Refresh() {
void DisplayApp::RunningState() {
// clockScreen.SetCurrentDateTime(dateTimeController.CurrentDateTime());
if(!currentScreen->Refresh(true)) {
if(!currentScreen->Refresh()) {
currentScreen.reset(nullptr);
switch(nextApp) {
case Apps::None:
case Apps::Launcher: currentScreen.reset(new Screens::Tile(this)); break;
case Apps::Clock: currentScreen.reset(new Screens::Clock(this, dateTimeController)); break;
// case Apps::Test: currentScreen.reset(new Screens::Message(this)); break;
case Apps::Clock: currentScreen.reset(new Screens::Clock(this, dateTimeController, batteryController, bleController)); break;
case Apps::Test: currentScreen.reset(new Screens::Message(this)); break;
}
nextApp = Apps::None;
}
@ -158,7 +155,7 @@ void DisplayApp::OnTouchEvent() {
// auto info = touchPanel.GetTouchInfo();
//
// if(info.isTouch) {
// gfx.FillRectangle(info.x-10, info.y-10, 20,20, pointColor);
// lcd.DrawPixel(info.x, info.y, pointColor);
// pointColor+=10;
// }
}

View File

@ -14,7 +14,6 @@
#include "LittleVgl.h"
#include <date/date.h>
#include <DisplayApp/Screens/Clock.h>
//#include <DisplayApp/Screens/Message.h>
namespace Pinetime {

View File

@ -15,7 +15,11 @@ static void event_handler(lv_obj_t * obj, lv_event_t event) {
screen->OnObjectEvent(obj, event);
}
Clock::Clock(DisplayApp* app, Controllers::DateTime& dateTimeController) : Screen(app), currentDateTime{{}}, version {{}}, dateTimeController{dateTimeController} {
Clock::Clock(DisplayApp* app,
Controllers::DateTime& dateTimeController,
Controllers::Battery& batteryController,
Controllers::Ble& bleController) : Screen(app), currentDateTime{{}}, version {{}},
dateTimeController{dateTimeController}, batteryController{batteryController}, bleController{bleController} {
displayedChar[0] = 0;
displayedChar[1] = 0;
displayedChar[2] = 0;
@ -65,12 +69,9 @@ Clock::~Clock() {
lv_obj_clean(lv_scr_act());
}
bool Clock::Refresh(bool fullRefresh) {
if(fullRefresh) {
auto currentDateTime = dateTimeController.CurrentDateTime();
}
if (fullRefresh || batteryPercentRemaining.IsUpdated()) {
bool Clock::Refresh() {
batteryPercentRemaining = batteryController.PercentRemaining();
if (batteryPercentRemaining.IsUpdated()) {
char batteryChar[11];
auto newBatteryValue = batteryPercentRemaining.Get();
newBatteryValue = (newBatteryValue > 100) ? 100 : newBatteryValue;
@ -80,8 +81,9 @@ bool Clock::Refresh(bool fullRefresh) {
lv_label_set_text(label_battery, batteryChar);
}
if (fullRefresh || bleState.IsUpdated()) {
if(bleState.Get() == BleConnectionStates::Connected) {
bleState = bleController.IsConnected();
if (bleState.IsUpdated()) {
if(bleState.Get() == true) {
lv_obj_set_hidden(label_ble, false);
lv_label_set_text(label_ble, "BLE");
} else {
@ -91,7 +93,7 @@ bool Clock::Refresh(bool fullRefresh) {
currentDateTime = dateTimeController.CurrentDateTime();
if(fullRefresh || currentDateTime.IsUpdated()) {
if(currentDateTime.IsUpdated()) {
auto newDateTime = currentDateTime.Get();
auto dp = date::floor<date::days>(newDateTime);
@ -138,7 +140,7 @@ bool Clock::Refresh(bool fullRefresh) {
}
}
if(fullRefresh || version.IsUpdated()) {
if(version.IsUpdated()) {
auto dummy = version.Get();
char versionStr[20];
sprintf(versionStr, "VERSION: %d.%d.%d", Version::Major(), Version::Minor(), Version::Patch());

View File

@ -7,6 +7,8 @@
#include <bits/unique_ptr.h>
#include <libs/lvgl/src/lv_core/lv_style.h>
#include <libs/lvgl/src/lv_core/lv_obj.h>
#include <Components/Battery/BatteryController.h>
#include <Components/Ble/BleController.h>
#include "../Fonts/lcdfont14.h"
#include "../Fonts/lcdfont70.h"
#include "../../Version.h"
@ -24,8 +26,10 @@ namespace Pinetime {
T& Get() { this->isUpdated = false; return value; }
DirtyValue& operator=(const T& other) {
this->value = other;
this->isUpdated = true;
if (this->value != other) {
this->value = other;
this->isUpdated = true;
}
return *this;
}
private:
@ -34,17 +38,15 @@ namespace Pinetime {
};
class Clock : public Screen{
public:
enum class BleConnectionStates{ NotConnected, Connected};
Clock(DisplayApp* app, Controllers::DateTime& dateTimeController);
Clock(DisplayApp* app,
Controllers::DateTime& dateTimeController,
Controllers::Battery& batteryController,
Controllers::Ble& bleController);
~Clock() override;
bool Refresh(bool fullRefresh) override;
bool Refresh() override;
bool OnButtonPushed() override;
void SetBatteryPercentRemaining(uint8_t percent) { batteryPercentRemaining = percent; }
void SetBleConnectionState(BleConnectionStates state) { bleState = state; }
void SetCurrentDateTime(const std::chrono::time_point<std::chrono::system_clock, std::chrono::milliseconds>& tp) { currentDateTime = tp;}
void OnObjectEvent(lv_obj_t *pObj, lv_event_t i);
private:
static const char* MonthToString(Pinetime::Controllers::DateTime::Months month);
@ -60,7 +62,7 @@ namespace Pinetime {
uint8_t currentDay = 0;
DirtyValue<uint8_t> batteryPercentRemaining {0};
DirtyValue<BleConnectionStates> bleState {BleConnectionStates::NotConnected};
DirtyValue<bool> bleState {false};
DirtyValue<std::chrono::time_point<std::chrono::system_clock, std::chrono::milliseconds>> currentDateTime;
DirtyValue<Pinetime::Version> version;
@ -75,6 +77,8 @@ namespace Pinetime {
lv_obj_t* backgroundLabel;
Controllers::DateTime& dateTimeController;
Controllers::Battery& batteryController;
Controllers::Ble& bleController;
bool running = true;

View File

@ -19,7 +19,7 @@ static void event_handler(lv_obj_t * obj, lv_event_t event) {
screen->OnObjectEvent(obj, event);
}
Message::Message(DisplayApp* app, Pinetime::Components::Gfx &gfx) : Screen(app, gfx) {
Message::Message(DisplayApp* app) : Screen(app) {
backgroundLabel = lv_label_create(lv_scr_act(), NULL);
backgroundLabel->user_data = this;
@ -55,11 +55,13 @@ Message::~Message() {
lv_obj_clean(lv_scr_act());
}
void Message::Refresh(bool fullRefresh) {
bool Message::Refresh() {
if(previousClickCount != clickCount) {
lv_label_set_text_fmt(labelClick, "%d", clickCount);
previousClickCount = clickCount;
}
return running;
}
void Message::OnObjectEvent(lv_obj_t *obj, lv_event_t event) {
@ -79,3 +81,8 @@ void Message::OnObjectEvent(lv_obj_t *obj, lv_event_t event) {
NRF_LOG_INFO("Toggled");
}
}
bool Message::OnButtonPushed() {
running = false;
return true;
}

View File

@ -15,15 +15,13 @@ namespace Pinetime {
namespace Screens {
class Message : public Screen{
public:
explicit Message(DisplayApp* app, Components::Gfx& gfx);
explicit Message(DisplayApp* app);
~Message() override;
void Refresh(bool fullRefresh) override;
bool Refresh() override;
bool OnButtonPushed();
void OnObjectEvent(lv_obj_t* obj, lv_event_t event);
void OnButtonPushed() override { nextScreen = Screen::NextScreen::Menu; }
private:
const FONT_INFO largeFont {lCD_70ptFontInfo.height, lCD_70ptFontInfo.startChar, lCD_70ptFontInfo.endChar, lCD_70ptFontInfo.spacePixels, lCD_70ptFontInfo.charInfo, lCD_70ptFontInfo.data};
const FONT_INFO smallFont {lCD_14ptFontInfo.height, lCD_14ptFontInfo.startChar, lCD_14ptFontInfo.endChar, lCD_14ptFontInfo.spacePixels, lCD_14ptFontInfo.charInfo, lCD_14ptFontInfo.data};
lv_style_t* labelStyle;
lv_obj_t * label;
@ -33,6 +31,7 @@ namespace Pinetime {
uint32_t clickCount = 0 ;
uint32_t previousClickCount = 0;
bool running = true;
};
}
}

View File

@ -1,20 +1,16 @@
#pragma once
#include <Components/Gfx/Gfx.h>
namespace Pinetime {
namespace Applications {
class DisplayApp;
namespace Screens {
class Screen {
public:
enum class NextScreen {None, Clock, Menu, App};
Screen(DisplayApp* app) : app{app} {}
virtual ~Screen() = default;
// Return false if the app can be closed, true if it must continue to run
virtual bool Refresh(bool fullRefresh) = 0;
virtual bool Refresh() = 0;
// Return false if the button hasn't been handled by the app, true if it has been handled
virtual bool OnButtonPushed() { return false; }

View File

@ -1,14 +1,8 @@
#include <cstdio>
#include <libs/date/includes/date/date.h>
#include <Components/DateTime/DateTimeController.h>
#include <Version.h>
#include <libs/lvgl/src/lv_core/lv_obj.h>
#include <libs/lvgl/src/lv_font/lv_font.h>
#include <libs/lvgl/lvgl.h>
#include <libraries/log/nrf_log.h>
#include "Tile.h"
#include <DisplayApp/DisplayApp.h>
#include <libs/lvgl/src/lv_core/lv_style.h>
using namespace Pinetime::Applications::Screens;
@ -17,7 +11,9 @@ extern lv_font_t jetbrains_mono_bold_20;
static void event_handler(lv_obj_t * obj, lv_event_t event) {
Tile* screen = static_cast<Tile *>(obj->user_data);
screen->OnObjectEvent(obj, event);
uint32_t* eventDataPtr = (uint32_t*) lv_event_get_data();
uint32_t eventData = *eventDataPtr;
screen->OnObjectEvent(obj, event, eventData);
}
static const char * btnm_map1[] = {"App1", "App2", "App3", "\n", "App4", "App5", "App11", ""};
@ -104,19 +100,28 @@ Tile::~Tile() {
lv_obj_clean(lv_scr_act());
}
bool Tile::Refresh(bool fullRefresh) {
bool Tile::Refresh() {
return running;
}
void Tile::OnObjectEvent(lv_obj_t *obj, lv_event_t event) {
void Tile::OnObjectEvent(lv_obj_t *obj, lv_event_t event, uint32_t buttonId) {
auto* tile = static_cast<Tile*>(obj->user_data);
if(event == LV_EVENT_CLICKED) {
if(event == LV_EVENT_VALUE_CHANGED) {
switch(buttonId) {
case 0:
case 1:
case 2:
tile->StartClockApp();
break;
case 3:
case 4:
case 5:
tile->StartTestApp();
tile->StartApp();
break;
}
clickCount++;
}
else if(event == LV_EVENT_VALUE_CHANGED) {
}
}
bool Tile::OnButtonPushed() {
@ -125,7 +130,12 @@ bool Tile::OnButtonPushed() {
return true;
}
void Tile::StartApp() {
void Tile::StartClockApp() {
app->StartApp(DisplayApp::Apps::Clock);
running = false;
}
void Tile::StartTestApp() {
app->StartApp(DisplayApp::Apps::Test);
running = false;
}

View File

@ -18,10 +18,10 @@ namespace Pinetime {
explicit Tile(DisplayApp* app);
~Tile() override;
bool Refresh(bool fullRefresh) override;
bool Refresh() override;
bool OnButtonPushed() override;
void OnObjectEvent(lv_obj_t* obj, lv_event_t event);
void OnObjectEvent(lv_obj_t* obj, lv_event_t event, uint32_t buttonId);
private:
@ -50,7 +50,8 @@ namespace Pinetime {
uint32_t clickCount = 0 ;
uint32_t previousClickCount = 0;
void StartApp();
void StartClockApp();
void StartTestApp();
bool running = true;
};
}

View File

@ -3,6 +3,8 @@
#include <drivers/Cst816s.h>
#include <DisplayApp/LittleVgl.h>
#include <hal/nrf_rtc.h>
#include <BLE/BleManager.h>
#include <softdevice/common/nrf_sdh_freertos.h>
#include "SystemTask.h"
#include "../main.h"
using namespace Pinetime::System;
@ -16,7 +18,7 @@ SystemTask::SystemTask(Pinetime::Drivers::SpiMaster &spi, Pinetime::Drivers::St7
}
void SystemTask::Start() {
if (pdPASS != xTaskCreate(SystemTask::Process, "MAIN", 256, this, 0, &taskHandle))
if (pdPASS != xTaskCreate(SystemTask::Process, "MAIN", 350, this, 0, &taskHandle))
APP_ERROR_HANDLER(NRF_ERROR_NO_MEM);
}
@ -29,7 +31,7 @@ void SystemTask::Process(void *instance) {
void SystemTask::Work() {
APP_GPIOTE_INIT(2);
bool erase_bonds=false;
// nrf_sdh_freertos_init(ble_manager_start_advertising, &erase_bonds);
nrf_sdh_freertos_init(ble_manager_start_advertising, &erase_bonds);
spi.Init();
lcd.Init();

View File

@ -1,25 +1,19 @@
#include <FreeRTOS.h>
#include <task.h>
#include <libraries/bsp/bsp.h>
#include <legacy/nrf_drv_clock.h>
#include <libraries/timer/app_timer.h>
#include <libraries/gpiote/app_gpiote.h>
#include <DisplayApp/DisplayApp.h>
#include <softdevice/common/nrf_sdh.h>
#include <softdevice/common/nrf_sdh_freertos.h>
#include <hal/nrf_rtc.h>
#include <timers.h>
#include <libraries/log/nrf_log.h>
#include <ble/ble_services/ble_cts_c/ble_cts_c.h>
#include <Components/DateTime/DateTimeController.h>
#include "BLE/BleManager.h"
#include "Components/Battery/BatteryController.h"
#include "Components/Ble/BleController.h"
#include "../drivers/Cst816s.h"
#include <drivers/St7789.h>
#include <drivers/SpiMaster.h>
#include <lvgl/lvgl.h>
#include <DisplayApp/LittleVgl.h>
#include <SystemTask/SystemTask.h>
@ -134,12 +128,11 @@ int main(void) {
systemTask.reset(new Pinetime::System::SystemTask(*spi, *lcd, *touchPanel, *lvgl, batteryController, bleController, dateTimeController));
systemTask->Start();
/*
ble_manager_init();
ble_manager_set_new_time_callback(OnNewTime);
ble_manager_set_ble_connection_callback(OnBleConnection);
ble_manager_set_ble_disconnection_callback(OnBleDisconnection);
*/
vTaskStartScheduler();
for (;;) {