Better integration of SPI with DMA and IRQ. Using only 'End' IRQ. Perf could be improved by using 'Started' IRQ to prepare the next buffer while the current one is beeing sent.
This commit is contained in:
parent
eb7a1b3ac9
commit
5fa4f5abe0
|
@ -7,23 +7,33 @@ Gfx::Gfx(Pinetime::Drivers::St7789 &lcd) : lcd{lcd} {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Gfx::Init() {
|
void Gfx::Init() {
|
||||||
lcd.Init();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Gfx::ClearScreen() {
|
void Gfx::ClearScreen() {
|
||||||
SetBackgroundColor(0x0000);
|
SetBackgroundColor(0x0000);
|
||||||
|
|
||||||
|
state.remainingIterations = 240 + 1;
|
||||||
|
state.currentIteration = 0;
|
||||||
|
state.busy = true;
|
||||||
|
state.action = Action::FillRectangle;
|
||||||
|
|
||||||
lcd.BeginDrawBuffer(0, 0, width, height);
|
lcd.BeginDrawBuffer(0, 0, width, height);
|
||||||
for(int i = 0; i < height; i++) {
|
lcd.NextDrawBuffer(reinterpret_cast<const uint8_t *>(buffer), width * 2);
|
||||||
lcd.NextDrawBuffer(reinterpret_cast<const uint8_t *>(buffer), width * 2);
|
while(state.busy) {} // TODO wait on an event/queue/... instead of polling
|
||||||
}
|
|
||||||
lcd.EndDrawBuffer();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Gfx::FillRectangle(uint8_t x, uint8_t y, uint8_t width, uint8_t height, uint16_t color) {
|
void Gfx::FillRectangle(uint8_t x, uint8_t y, uint8_t w, uint8_t h, uint16_t color) {
|
||||||
SetBackgroundColor(color);
|
SetBackgroundColor(color);
|
||||||
lcd.BeginDrawBuffer(0, 0, width, height);
|
|
||||||
lcd.NextDrawBuffer(reinterpret_cast<const uint8_t *>(buffer), width * 2, 240);
|
state.remainingIterations = 240 + 1;
|
||||||
lcd.EndDrawBuffer();
|
state.currentIteration = 0;
|
||||||
|
state.busy = true;
|
||||||
|
state.action = Action::FillRectangle;
|
||||||
|
|
||||||
|
lcd.BeginDrawBuffer(x, y, w, h);
|
||||||
|
lcd.NextDrawBuffer(reinterpret_cast<const uint8_t *>(buffer), width * 2);
|
||||||
|
while(state.busy) {} // TODO wait on an event/queue/... instead of polling
|
||||||
}
|
}
|
||||||
|
|
||||||
void Gfx::DrawString(uint8_t x, uint8_t y, uint16_t color, const char *text, const FONT_INFO *p_font, bool wrap) {
|
void Gfx::DrawString(uint8_t x, uint8_t y, uint16_t color, const char *text, const FONT_INFO *p_font, bool wrap) {
|
||||||
|
@ -64,31 +74,37 @@ void Gfx::DrawString(uint8_t x, uint8_t y, uint16_t color, const char *text, con
|
||||||
void Gfx::DrawChar(const FONT_INFO *font, uint8_t c, uint8_t *x, uint8_t y, uint16_t color) {
|
void Gfx::DrawChar(const FONT_INFO *font, uint8_t c, uint8_t *x, uint8_t y, uint16_t color) {
|
||||||
uint8_t char_idx = c - font->startChar;
|
uint8_t char_idx = c - font->startChar;
|
||||||
uint16_t bytes_in_line = CEIL_DIV(font->charInfo[char_idx].widthBits, 8);
|
uint16_t bytes_in_line = CEIL_DIV(font->charInfo[char_idx].widthBits, 8);
|
||||||
|
uint16_t bg = 0x0000;
|
||||||
|
|
||||||
if (c == ' ') {
|
if (c == ' ') {
|
||||||
*x += font->height / 2;
|
*x += font->height / 2;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO For now, LCD and SPI driver start a new transfer (cs pin + set address windows + write byte) FOR EACH PIXEL!
|
// Build first line
|
||||||
// This could be improved by setting CS pin, DC pin and address window ONLY ONCE for the whole character
|
for (uint16_t j = 0; j < bytes_in_line; j++) {
|
||||||
|
for (uint8_t k = 0; k < 8; k++) {
|
||||||
lcd.BeginDrawBuffer(*x, y, bytes_in_line*8, font->height);
|
if ((1 << (7 - k)) & font->data[font->charInfo[char_idx].offset + j]) {
|
||||||
uint16_t bg = 0x0000;
|
buffer[(j*8)+k] = color;
|
||||||
for (uint16_t i = 0; i < font->height; i++) {
|
}
|
||||||
for (uint16_t j = 0; j < bytes_in_line; j++) {
|
else {
|
||||||
for (uint8_t k = 0; k < 8; k++) {
|
buffer[(j*8)+k] = bg;
|
||||||
if ((1 << (7 - k)) & font->data[font->charInfo[char_idx].offset + i * bytes_in_line + j]) {
|
|
||||||
buffer[(j*8)+k] = color;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
buffer[(j*8)+k] = bg;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
lcd.NextDrawBuffer(reinterpret_cast<uint8_t *>(&buffer), bytes_in_line*8*2);
|
|
||||||
}
|
}
|
||||||
lcd.EndDrawBuffer();
|
|
||||||
|
state.remainingIterations = font->height + 0;
|
||||||
|
state.currentIteration = 0;
|
||||||
|
state.busy = true;
|
||||||
|
state.action = Action::DrawChar;
|
||||||
|
state.font = const_cast<FONT_INFO *>(font);
|
||||||
|
state.character = c;
|
||||||
|
state.color = color;
|
||||||
|
|
||||||
|
lcd.BeginDrawBuffer(*x, y, bytes_in_line*8, font->height);
|
||||||
|
lcd.NextDrawBuffer(reinterpret_cast<const uint8_t *>(&buffer), bytes_in_line*8*2);
|
||||||
|
while(state.busy) {} // TODO wait on an event/queue/... instead of polling
|
||||||
|
|
||||||
*x += font->charInfo[char_idx].widthBits + font->spacePixels;
|
*x += font->charInfo[char_idx].widthBits + font->spacePixels;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -110,4 +126,40 @@ void Gfx::SetBackgroundColor(uint16_t color) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Gfx::GetNextBuffer(uint8_t **data, size_t &size) {
|
||||||
|
if(!state.busy) return false;
|
||||||
|
state.remainingIterations--;
|
||||||
|
if (state.remainingIterations == 0) {
|
||||||
|
state.busy = false;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(state.action == Action::FillRectangle) {
|
||||||
|
*data = reinterpret_cast<uint8_t *>(buffer);
|
||||||
|
size = width * 2;
|
||||||
|
} else if(state.action == Action::DrawChar) {
|
||||||
|
uint16_t bg = 0x0000;
|
||||||
|
uint8_t char_idx = state.character - state.font->startChar;
|
||||||
|
uint16_t bytes_in_line = CEIL_DIV(state.font->charInfo[char_idx].widthBits, 8);
|
||||||
|
|
||||||
|
for (uint16_t j = 0; j < bytes_in_line; j++) {
|
||||||
|
for (uint8_t k = 0; k < 8; k++) {
|
||||||
|
if ((1 << (7 - k)) & state.font->data[state.font->charInfo[char_idx].offset + ((state.currentIteration+1) * bytes_in_line) + j]) {
|
||||||
|
buffer[(j*8)+k] = state.color;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
buffer[(j*8)+k] = bg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*data = reinterpret_cast<uint8_t *>(buffer);
|
||||||
|
size = bytes_in_line*8*2;
|
||||||
|
}
|
||||||
|
|
||||||
|
state.currentIteration++;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <nrf_font.h>
|
#include <nrf_font.h>
|
||||||
|
#include <drivers/BufferProvider.h>
|
||||||
|
|
||||||
|
|
||||||
namespace Pinetime {
|
namespace Pinetime {
|
||||||
|
@ -8,7 +9,7 @@ namespace Pinetime {
|
||||||
class St7789;
|
class St7789;
|
||||||
}
|
}
|
||||||
namespace Components {
|
namespace Components {
|
||||||
class Gfx {
|
class Gfx : public Pinetime::Drivers::BufferProvider {
|
||||||
public:
|
public:
|
||||||
explicit Gfx(Drivers::St7789& lcd);
|
explicit Gfx(Drivers::St7789& lcd);
|
||||||
void Init();
|
void Init();
|
||||||
|
@ -19,11 +20,26 @@ namespace Pinetime {
|
||||||
|
|
||||||
void Sleep();
|
void Sleep();
|
||||||
void Wakeup();
|
void Wakeup();
|
||||||
|
bool GetNextBuffer(uint8_t **buffer, size_t &size) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static constexpr uint8_t width = 240;
|
static constexpr uint8_t width = 240;
|
||||||
static constexpr uint8_t height = 240;
|
static constexpr uint8_t height = 240;
|
||||||
|
|
||||||
|
enum class Action { None, FillRectangle, DrawChar};
|
||||||
|
struct State {
|
||||||
|
State() : busy{false}, action{Action::None}, remainingIterations{0}, currentIteration{0} {}
|
||||||
|
volatile bool busy;
|
||||||
|
volatile Action action;
|
||||||
|
volatile uint16_t remainingIterations;
|
||||||
|
volatile uint16_t currentIteration;
|
||||||
|
volatile FONT_INFO *font;
|
||||||
|
volatile uint16_t color;
|
||||||
|
volatile uint8_t character;
|
||||||
|
};
|
||||||
|
|
||||||
|
volatile State state;
|
||||||
|
|
||||||
uint16_t buffer[width]; // 1 line buffer
|
uint16_t buffer[width]; // 1 line buffer
|
||||||
Drivers::St7789& lcd;
|
Drivers::St7789& lcd;
|
||||||
|
|
||||||
|
|
|
@ -14,25 +14,20 @@
|
||||||
|
|
||||||
using namespace Pinetime::Applications;
|
using namespace Pinetime::Applications;
|
||||||
|
|
||||||
DisplayApp::DisplayApp(Controllers::Battery &batteryController,
|
DisplayApp::DisplayApp(Pinetime::Drivers::St7789& lcd,
|
||||||
|
Pinetime::Components::Gfx& gfx,
|
||||||
|
Pinetime::Drivers::Cst816S& touchPanel,
|
||||||
|
Controllers::Battery &batteryController,
|
||||||
Controllers::Ble &bleController,
|
Controllers::Ble &bleController,
|
||||||
Controllers::DateTime &dateTimeController) :
|
Controllers::DateTime &dateTimeController) :
|
||||||
spi{Drivers::SpiMaster::SpiModule::SPI0, {
|
lcd{lcd},
|
||||||
Drivers::SpiMaster::BitOrder::Msb_Lsb,
|
gfx{gfx},
|
||||||
Drivers::SpiMaster::Modes::Mode3,
|
touchPanel{touchPanel},
|
||||||
Drivers::SpiMaster::Frequencies::Freq8Mhz,
|
|
||||||
pinSpiSck,
|
|
||||||
pinSpiMosi,
|
|
||||||
pinSpiMiso,
|
|
||||||
pinSpiCsn
|
|
||||||
}},
|
|
||||||
lcd{new Drivers::St7789(spi, pinLcdDataCommand)},
|
|
||||||
gfx{new Components::Gfx(*lcd.get()) },
|
|
||||||
batteryController{batteryController},
|
batteryController{batteryController},
|
||||||
bleController{bleController},
|
bleController{bleController},
|
||||||
dateTimeController{dateTimeController},
|
dateTimeController{dateTimeController},
|
||||||
clockScreen{*(gfx.get())},
|
clockScreen{gfx},
|
||||||
messageScreen{*(gfx.get())} {
|
messageScreen{gfx} {
|
||||||
msgQueue = xQueueCreate(queueSize, itemSize);
|
msgQueue = xQueueCreate(queueSize, itemSize);
|
||||||
currentScreen = &clockScreen;
|
currentScreen = &clockScreen;
|
||||||
}
|
}
|
||||||
|
@ -59,22 +54,19 @@ void DisplayApp::InitHw() {
|
||||||
nrf_gpio_pin_clear(pinLcdBacklight2);
|
nrf_gpio_pin_clear(pinLcdBacklight2);
|
||||||
nrf_gpio_pin_clear(pinLcdBacklight3);
|
nrf_gpio_pin_clear(pinLcdBacklight3);
|
||||||
|
|
||||||
spi.Init();
|
|
||||||
gfx->Init();
|
|
||||||
currentScreen->Refresh(true);
|
currentScreen->Refresh(true);
|
||||||
touchPanel.Init();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t acc = 0;
|
uint32_t acc = 0;
|
||||||
uint32_t count = 0;
|
uint32_t count = 0;
|
||||||
bool toggle = true;
|
bool toggle = true;
|
||||||
void DisplayApp::Refresh() {
|
void DisplayApp::Refresh() {
|
||||||
|
#if 0
|
||||||
uint32_t before = nrf_rtc_counter_get(portNRF_RTC_REG);
|
uint32_t before = nrf_rtc_counter_get(portNRF_RTC_REG);
|
||||||
if(toggle) {
|
if(toggle) {
|
||||||
gfx->FillRectangle(0,0,240,240,0x0000);
|
gfx.FillRectangle(0,0,240,240,0x0000);
|
||||||
} else {
|
} else {
|
||||||
gfx->FillRectangle(0,0,240,240,0xffff);
|
gfx.FillRectangle(0,0,240,240,0xffff);
|
||||||
}
|
}
|
||||||
uint32_t after = nrf_rtc_counter_get(portNRF_RTC_REG);
|
uint32_t after = nrf_rtc_counter_get(portNRF_RTC_REG);
|
||||||
|
|
||||||
|
@ -85,8 +77,9 @@ void DisplayApp::Refresh() {
|
||||||
}
|
}
|
||||||
count++;
|
count++;
|
||||||
toggle = !toggle;
|
toggle = !toggle;
|
||||||
|
#endif
|
||||||
|
|
||||||
#if 0
|
#if 1
|
||||||
TickType_t queueTimeout;
|
TickType_t queueTimeout;
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case States::Idle:
|
case States::Idle:
|
||||||
|
@ -108,16 +101,16 @@ void DisplayApp::Refresh() {
|
||||||
nrf_gpio_pin_set(pinLcdBacklight2);
|
nrf_gpio_pin_set(pinLcdBacklight2);
|
||||||
vTaskDelay(100);
|
vTaskDelay(100);
|
||||||
nrf_gpio_pin_set(pinLcdBacklight1);
|
nrf_gpio_pin_set(pinLcdBacklight1);
|
||||||
lcd->DisplayOff();
|
lcd.DisplayOff();
|
||||||
lcd->Sleep();
|
lcd.Sleep();
|
||||||
touchPanel.Sleep();
|
touchPanel.Sleep();
|
||||||
state = States::Idle;
|
state = States::Idle;
|
||||||
break;
|
break;
|
||||||
case Messages::GoToRunning:
|
case Messages::GoToRunning:
|
||||||
lcd->Wakeup();
|
lcd.Wakeup();
|
||||||
touchPanel.Wakeup();
|
touchPanel.Wakeup();
|
||||||
|
|
||||||
lcd->DisplayOn();
|
lcd.DisplayOn();
|
||||||
nrf_gpio_pin_clear(pinLcdBacklight3);
|
nrf_gpio_pin_clear(pinLcdBacklight3);
|
||||||
nrf_gpio_pin_clear(pinLcdBacklight2);
|
nrf_gpio_pin_clear(pinLcdBacklight2);
|
||||||
nrf_gpio_pin_clear(pinLcdBacklight1);
|
nrf_gpio_pin_clear(pinLcdBacklight1);
|
||||||
|
@ -179,7 +172,7 @@ void DisplayApp::OnTouchEvent() {
|
||||||
auto info = touchPanel.GetTouchInfo();
|
auto info = touchPanel.GetTouchInfo();
|
||||||
|
|
||||||
if(info.isTouch) {
|
if(info.isTouch) {
|
||||||
gfx->FillRectangle(info.x-10, info.y-10, 20,20, pointColor);
|
gfx.FillRectangle(info.x-10, info.y-10, 20,20, pointColor);
|
||||||
pointColor+=10;
|
pointColor+=10;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,7 +23,10 @@ namespace Pinetime {
|
||||||
public:
|
public:
|
||||||
enum class States {Idle, Running};
|
enum class States {Idle, Running};
|
||||||
enum class Messages : uint8_t {GoToSleep, GoToRunning, UpdateDateTime, UpdateBleConnection, UpdateBatteryLevel, TouchEvent} ;
|
enum class Messages : uint8_t {GoToSleep, GoToRunning, UpdateDateTime, UpdateBleConnection, UpdateBatteryLevel, TouchEvent} ;
|
||||||
DisplayApp(Controllers::Battery &batteryController,
|
DisplayApp(Pinetime::Drivers::St7789& lcd,
|
||||||
|
Pinetime::Components::Gfx& gfx,
|
||||||
|
Pinetime::Drivers::Cst816S&,
|
||||||
|
Controllers::Battery &batteryController,
|
||||||
Controllers::Ble &bleController,
|
Controllers::Ble &bleController,
|
||||||
Controllers::DateTime& dateTimeController);
|
Controllers::DateTime& dateTimeController);
|
||||||
void Start();
|
void Start();
|
||||||
|
@ -33,9 +36,8 @@ namespace Pinetime {
|
||||||
TaskHandle_t taskHandle;
|
TaskHandle_t taskHandle;
|
||||||
static void Process(void* instance);
|
static void Process(void* instance);
|
||||||
void InitHw();
|
void InitHw();
|
||||||
Pinetime::Drivers::SpiMaster spi;
|
Pinetime::Drivers::St7789& lcd;
|
||||||
std::unique_ptr<Drivers::St7789> lcd;
|
Pinetime::Components::Gfx& gfx;
|
||||||
std::unique_ptr<Components::Gfx> gfx;
|
|
||||||
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 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};
|
const FONT_INFO smallFont {lCD_14ptFontInfo.height, lCD_14ptFontInfo.startChar, lCD_14ptFontInfo.endChar, lCD_14ptFontInfo.spacePixels, lCD_14ptFontInfo.charInfo, lCD_14ptFontInfo.data};
|
||||||
void Refresh();
|
void Refresh();
|
||||||
|
@ -52,18 +54,13 @@ namespace Pinetime {
|
||||||
Pinetime::Controllers::Ble &bleController;
|
Pinetime::Controllers::Ble &bleController;
|
||||||
Pinetime::Controllers::DateTime& dateTimeController;
|
Pinetime::Controllers::DateTime& dateTimeController;
|
||||||
|
|
||||||
Pinetime::Drivers::Cst816S touchPanel;
|
Pinetime::Drivers::Cst816S& touchPanel;
|
||||||
void OnTouchEvent();
|
void OnTouchEvent();
|
||||||
|
|
||||||
Screens::Clock clockScreen;
|
Screens::Clock clockScreen;
|
||||||
Screens::Screen* currentScreen = nullptr;
|
Screens::Screen* currentScreen = nullptr;
|
||||||
Screens::Message messageScreen;
|
Screens::Message messageScreen;
|
||||||
bool screenState = false;
|
bool screenState = false;
|
||||||
static constexpr uint8_t pinSpiSck = 2;
|
|
||||||
static constexpr uint8_t pinSpiMosi = 3;
|
|
||||||
static constexpr uint8_t pinSpiMiso = 4;
|
|
||||||
static constexpr uint8_t pinSpiCsn = 25;
|
|
||||||
static constexpr uint8_t pinLcdDataCommand = 18;
|
|
||||||
static constexpr uint8_t pinLcdBacklight1 = 14;
|
static constexpr uint8_t pinLcdBacklight1 = 14;
|
||||||
static constexpr uint8_t pinLcdBacklight2 = 22;
|
static constexpr uint8_t pinLcdBacklight2 = 22;
|
||||||
static constexpr uint8_t pinLcdBacklight3 = 23;
|
static constexpr uint8_t pinLcdBacklight3 = 23;
|
||||||
|
|
11
src/drivers/BufferProvider.h
Normal file
11
src/drivers/BufferProvider.h
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
#pragma once
|
||||||
|
#include <cstddef>
|
||||||
|
|
||||||
|
namespace Pinetime {
|
||||||
|
namespace Drivers {
|
||||||
|
class BufferProvider {
|
||||||
|
public:
|
||||||
|
virtual bool GetNextBuffer(uint8_t** buffer, size_t& size) = 0;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,10 +4,9 @@
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
using namespace Pinetime::Drivers;
|
using namespace Pinetime::Drivers;
|
||||||
|
|
||||||
SpiMaster* spiInstance;
|
|
||||||
SpiMaster::SpiMaster(const SpiMaster::SpiModule spi, const SpiMaster::Parameters ¶ms) :
|
SpiMaster::SpiMaster(const SpiMaster::SpiModule spi, const SpiMaster::Parameters ¶ms) :
|
||||||
spi{spi}, params{params} {
|
spi{spi}, params{params} {
|
||||||
spiInstance = this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SpiMaster::Init() {
|
bool SpiMaster::Init() {
|
||||||
|
@ -84,12 +83,33 @@ void SpiMaster::setup_workaround_for_ftpan_58(NRF_SPIM_Type *spim, uint32_t ppi_
|
||||||
NRF_PPI->CHENSET = 1U << ppi_channel;
|
NRF_PPI->CHENSET = 1U << ppi_channel;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SpiMaster::irqStarted() {
|
void SpiMaster::OnEndEvent(BufferProvider& provider) {
|
||||||
if(busy) {
|
if(!busy) return;
|
||||||
auto s = currentBufferSize;
|
|
||||||
if(s > 0) {
|
|
||||||
auto currentSize = std::min((size_t)255, s);
|
|
||||||
|
|
||||||
|
auto s = currentBufferSize;
|
||||||
|
if(s > 0) {
|
||||||
|
auto currentSize = std::min((size_t) 255, s);
|
||||||
|
|
||||||
|
NRF_SPIM0->TXD.PTR = (uint32_t) currentBufferAddr;
|
||||||
|
NRF_SPIM0->TXD.MAXCNT = currentSize;
|
||||||
|
NRF_SPIM0->TXD.LIST = 0;
|
||||||
|
|
||||||
|
currentBufferAddr += currentSize;
|
||||||
|
currentBufferSize -= currentSize;
|
||||||
|
|
||||||
|
NRF_SPIM0->RXD.PTR = (uint32_t) 0;
|
||||||
|
NRF_SPIM0->RXD.MAXCNT = 0;
|
||||||
|
NRF_SPIM0->RXD.LIST = 0;
|
||||||
|
|
||||||
|
NRF_SPIM0->TASKS_START = 1;
|
||||||
|
} else {
|
||||||
|
uint8_t* buffer = nullptr;
|
||||||
|
size_t size = 0;
|
||||||
|
if(provider.GetNextBuffer(&buffer, size)) {
|
||||||
|
currentBufferAddr = (uint32_t) buffer;
|
||||||
|
currentBufferSize = size;
|
||||||
|
auto s = currentBufferSize;
|
||||||
|
auto currentSize = std::min((size_t)255, s);
|
||||||
NRF_SPIM0->TXD.PTR = (uint32_t) currentBufferAddr;
|
NRF_SPIM0->TXD.PTR = (uint32_t) currentBufferAddr;
|
||||||
NRF_SPIM0->TXD.MAXCNT = currentSize;
|
NRF_SPIM0->TXD.MAXCNT = currentSize;
|
||||||
NRF_SPIM0->TXD.LIST = 0;
|
NRF_SPIM0->TXD.LIST = 0;
|
||||||
|
@ -101,44 +121,19 @@ void SpiMaster::irqStarted() {
|
||||||
NRF_SPIM0->RXD.MAXCNT = 0;
|
NRF_SPIM0->RXD.MAXCNT = 0;
|
||||||
NRF_SPIM0->RXD.LIST = 0;
|
NRF_SPIM0->RXD.LIST = 0;
|
||||||
|
|
||||||
if(repeat == 0)
|
NRF_SPIM0->TASKS_START = 1;
|
||||||
NRF_SPIM0->SHORTS = 0;
|
} else {
|
||||||
|
|
||||||
return;
|
|
||||||
}else {
|
|
||||||
if(repeat > 0) {
|
|
||||||
repeat = repeat -1;
|
|
||||||
|
|
||||||
currentBufferAddr = bufferAddr;
|
|
||||||
currentBufferSize = bufferSize;
|
|
||||||
s = currentBufferSize;
|
|
||||||
auto currentSize = std::min((size_t)255, s);
|
|
||||||
NRF_SPIM0->TXD.PTR = (uint32_t) currentBufferAddr;
|
|
||||||
NRF_SPIM0->TXD.MAXCNT = currentSize;
|
|
||||||
NRF_SPIM0->TXD.LIST = 0;
|
|
||||||
|
|
||||||
currentBufferAddr += currentSize;
|
|
||||||
currentBufferSize -= currentSize;
|
|
||||||
|
|
||||||
NRF_SPIM0->RXD.PTR = (uint32_t) 0;
|
|
||||||
NRF_SPIM0->RXD.MAXCNT = 0;
|
|
||||||
NRF_SPIM0->RXD.LIST = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SpiMaster::irqEnd() {
|
|
||||||
if(busy) {
|
|
||||||
if(repeat == 0 && currentBufferSize == 0) {
|
|
||||||
nrf_gpio_pin_set(pinCsn);
|
|
||||||
busy = false;
|
busy = false;
|
||||||
|
nrf_gpio_pin_set(pinCsn);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SpiMaster::OnStartedEvent(BufferProvider& provider) {
|
||||||
|
if(!busy) return;
|
||||||
|
}
|
||||||
|
|
||||||
bool SpiMaster::Write(const uint8_t *data, size_t size, size_t r) {
|
bool SpiMaster::Write(const uint8_t *data, size_t size) {
|
||||||
if(data == nullptr) return false;
|
if(data == nullptr) return false;
|
||||||
|
|
||||||
while(busy) {
|
while(busy) {
|
||||||
|
@ -162,16 +157,12 @@ bool SpiMaster::Write(const uint8_t *data, size_t size, size_t r) {
|
||||||
|
|
||||||
nrf_gpio_pin_clear(pinCsn);
|
nrf_gpio_pin_clear(pinCsn);
|
||||||
|
|
||||||
currentBufferAddr = bufferAddr = (uint32_t)data;
|
currentBufferAddr = (uint32_t)data;
|
||||||
currentBufferSize = bufferSize = size;
|
currentBufferSize = size;
|
||||||
repeat = r;
|
|
||||||
busy = true;
|
busy = true;
|
||||||
|
|
||||||
if(repeat > 0)
|
auto currentSize = std::min((size_t)255, (size_t)currentBufferSize);
|
||||||
NRF_SPIM0->SHORTS = (1<<17);
|
NRF_SPIM0->TXD.PTR = currentBufferAddr;
|
||||||
|
|
||||||
auto currentSize = std::min((size_t)255, bufferSize);
|
|
||||||
NRF_SPIM0->TXD.PTR = bufferAddr;
|
|
||||||
NRF_SPIM0->TXD.MAXCNT = currentSize;
|
NRF_SPIM0->TXD.MAXCNT = currentSize;
|
||||||
NRF_SPIM0->TXD.LIST = 0;
|
NRF_SPIM0->TXD.LIST = 0;
|
||||||
|
|
||||||
|
@ -187,21 +178,11 @@ bool SpiMaster::Write(const uint8_t *data, size_t size, size_t r) {
|
||||||
if(size == 1) {
|
if(size == 1) {
|
||||||
while (NRF_SPIM0->EVENTS_END == 0);
|
while (NRF_SPIM0->EVENTS_END == 0);
|
||||||
busy = false;
|
busy = false;
|
||||||
nrf_gpio_pin_set(pinCsn);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SpiMaster::GetStatusEnd() {
|
|
||||||
return (bool)*(volatile uint32_t *)((uint8_t *)spiBaseAddress + (uint32_t)NRF_SPIM_EVENT_END);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool SpiMaster::GetStatusStarted() {
|
|
||||||
return (bool)*(volatile uint32_t *)((uint8_t *)spiBaseAddress + (uint32_t)NRF_SPIM_EVENT_STARTED);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SpiMaster::Sleep() {
|
void SpiMaster::Sleep() {
|
||||||
while(NRF_SPIM0->ENABLE != 0) {
|
while(NRF_SPIM0->ENABLE != 0) {
|
||||||
NRF_SPIM0->ENABLE = (SPIM_ENABLE_ENABLE_Disabled << SPIM_ENABLE_ENABLE_Pos);
|
NRF_SPIM0->ENABLE = (SPIM_ENABLE_ENABLE_Disabled << SPIM_ENABLE_ENABLE_Pos);
|
||||||
|
@ -216,8 +197,4 @@ void SpiMaster::Wakeup() {
|
||||||
Init();
|
Init();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SpiMaster::Wait() {
|
|
||||||
while(busy) {
|
|
||||||
asm("nop");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -3,6 +3,8 @@
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
|
|
||||||
|
#include "BufferProvider.h"
|
||||||
namespace Pinetime {
|
namespace Pinetime {
|
||||||
namespace Drivers {
|
namespace Drivers {
|
||||||
class SpiMaster {
|
class SpiMaster {
|
||||||
|
@ -23,20 +25,17 @@ namespace Pinetime {
|
||||||
|
|
||||||
SpiMaster(const SpiModule spi, const Parameters& params);
|
SpiMaster(const SpiModule spi, const Parameters& params);
|
||||||
bool Init();
|
bool Init();
|
||||||
bool Write(const uint8_t* data, size_t size, size_t r = 0);
|
bool Write(const uint8_t* data, size_t size);
|
||||||
void setup_workaround_for_ftpan_58(NRF_SPIM_Type *spim, uint32_t ppi_channel, uint32_t gpiote_channel);
|
|
||||||
void Wait();
|
void OnStartedEvent(BufferProvider& provider);
|
||||||
|
void OnEndEvent(BufferProvider& provider);
|
||||||
|
|
||||||
void Sleep();
|
void Sleep();
|
||||||
void Wakeup();
|
void Wakeup();
|
||||||
|
|
||||||
bool GetStatusEnd();
|
|
||||||
bool GetStatusStarted();
|
|
||||||
|
|
||||||
void irqEnd();
|
|
||||||
void irqStarted();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void setup_workaround_for_ftpan_58(NRF_SPIM_Type *spim, uint32_t ppi_channel, uint32_t gpiote_channel);
|
||||||
|
|
||||||
NRF_SPIM_Type * spiBaseAddress;
|
NRF_SPIM_Type * spiBaseAddress;
|
||||||
uint8_t pinCsn;
|
uint8_t pinCsn;
|
||||||
|
|
||||||
|
@ -44,12 +43,8 @@ namespace Pinetime {
|
||||||
SpiMaster::Parameters params;
|
SpiMaster::Parameters params;
|
||||||
|
|
||||||
volatile bool busy = false;
|
volatile bool busy = false;
|
||||||
|
|
||||||
uint32_t bufferAddr = 0;
|
|
||||||
volatile uint32_t currentBufferAddr = 0;
|
volatile uint32_t currentBufferAddr = 0;
|
||||||
size_t bufferSize = 0;
|
|
||||||
volatile size_t currentBufferSize = 0;
|
volatile size_t currentBufferSize = 0;
|
||||||
volatile uint32_t repeat = 0;
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,8 +37,8 @@ void St7789::WriteData(uint8_t data) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void St7789::WriteSpi(const uint8_t* data, size_t size, size_t repeat) {
|
void St7789::WriteSpi(const uint8_t* data, size_t size) {
|
||||||
spi.Write(data, size, repeat);
|
spi.Write(data, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
void St7789::SoftwareReset() {
|
void St7789::SoftwareReset() {
|
||||||
|
@ -142,12 +142,8 @@ void St7789::BeginDrawBuffer(uint16_t x, uint16_t y, uint16_t width, uint16_t he
|
||||||
nrf_gpio_pin_set(pinDataCommand);
|
nrf_gpio_pin_set(pinDataCommand);
|
||||||
}
|
}
|
||||||
|
|
||||||
void St7789::EndDrawBuffer() {
|
void St7789::NextDrawBuffer(const uint8_t *data, size_t size) {
|
||||||
spi.Wait();
|
WriteSpi(data, size);
|
||||||
}
|
|
||||||
|
|
||||||
void St7789::NextDrawBuffer(const uint8_t *data, size_t size, size_t repeat) {
|
|
||||||
WriteSpi(data, size, repeat);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void St7789::HardwareReset() {
|
void St7789::HardwareReset() {
|
||||||
|
@ -178,5 +174,3 @@ void St7789::Wakeup() {
|
||||||
NormalModeOn();
|
NormalModeOn();
|
||||||
DisplayOn();
|
DisplayOn();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -12,16 +12,13 @@ namespace Pinetime {
|
||||||
void DrawPixel(uint16_t x, uint16_t y, uint32_t color);
|
void DrawPixel(uint16_t x, uint16_t y, uint32_t color);
|
||||||
|
|
||||||
void BeginDrawBuffer(uint16_t x, uint16_t y, uint16_t width, uint16_t height);
|
void BeginDrawBuffer(uint16_t x, uint16_t y, uint16_t width, uint16_t height);
|
||||||
void NextDrawBuffer(const uint8_t* data, size_t size, size_t repeat = 0);
|
void NextDrawBuffer(const uint8_t* data, size_t size);
|
||||||
void EndDrawBuffer();
|
|
||||||
|
|
||||||
void DisplayOn();
|
void DisplayOn();
|
||||||
void DisplayOff();
|
void DisplayOff();
|
||||||
|
|
||||||
void Sleep();
|
void Sleep();
|
||||||
void Wakeup();
|
void Wakeup();
|
||||||
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
SpiMaster& spi;
|
SpiMaster& spi;
|
||||||
uint8_t pinDataCommand;
|
uint8_t pinDataCommand;
|
||||||
|
@ -35,12 +32,9 @@ namespace Pinetime {
|
||||||
void DisplayInversionOn();
|
void DisplayInversionOn();
|
||||||
void NormalModeOn();
|
void NormalModeOn();
|
||||||
void WriteToRam();
|
void WriteToRam();
|
||||||
|
|
||||||
|
|
||||||
void SetAddrWindow(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1);
|
void SetAddrWindow(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1);
|
||||||
|
|
||||||
void WriteCommand(uint8_t cmd);
|
void WriteCommand(uint8_t cmd);
|
||||||
void WriteSpi(const uint8_t* data, size_t size, size_t repeat = 0);
|
void WriteSpi(const uint8_t* data, size_t size);
|
||||||
|
|
||||||
enum class Commands : uint8_t {
|
enum class Commands : uint8_t {
|
||||||
SoftwareReset = 0x01,
|
SoftwareReset = 0x01,
|
||||||
|
@ -62,7 +56,6 @@ namespace Pinetime {
|
||||||
static constexpr uint16_t Width = 240;
|
static constexpr uint16_t Width = 240;
|
||||||
static constexpr uint16_t Height = 240;
|
static constexpr uint16_t Height = 240;
|
||||||
void RowAddressSet();
|
void RowAddressSet();
|
||||||
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
47
src/main.cpp
47
src/main.cpp
|
@ -15,6 +15,10 @@
|
||||||
#include "BLE/BleManager.h"
|
#include "BLE/BleManager.h"
|
||||||
#include "Components/Battery/BatteryController.h"
|
#include "Components/Battery/BatteryController.h"
|
||||||
#include "Components/Ble/BleController.h"
|
#include "Components/Ble/BleController.h"
|
||||||
|
#include "../drivers/Cst816s.h"
|
||||||
|
#include <drivers/St7789.h>
|
||||||
|
#include <drivers/SpiMaster.h>
|
||||||
|
#include <Components/Gfx/Gfx.h>
|
||||||
|
|
||||||
#if NRF_LOG_ENABLED
|
#if NRF_LOG_ENABLED
|
||||||
#include "Logging/NrfLogger.h"
|
#include "Logging/NrfLogger.h"
|
||||||
|
@ -24,6 +28,18 @@ Pinetime::Logging::NrfLogger logger;
|
||||||
Pinetime::Logging::DummyLogger logger;
|
Pinetime::Logging::DummyLogger logger;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
std::unique_ptr<Pinetime::Drivers::SpiMaster> spi;
|
||||||
|
std::unique_ptr<Pinetime::Drivers::St7789> lcd;
|
||||||
|
std::unique_ptr<Pinetime::Components::Gfx> gfx;
|
||||||
|
std::unique_ptr<Pinetime::Drivers::Cst816S> touchPanel;
|
||||||
|
|
||||||
|
static constexpr uint8_t pinSpiSck = 2;
|
||||||
|
static constexpr uint8_t pinSpiMosi = 3;
|
||||||
|
static constexpr uint8_t pinSpiMiso = 4;
|
||||||
|
static constexpr uint8_t pinSpiCsn = 25;
|
||||||
|
static constexpr uint8_t pinLcdDataCommand = 18;
|
||||||
|
|
||||||
|
|
||||||
std::unique_ptr<Pinetime::Applications::DisplayApp> displayApp;
|
std::unique_ptr<Pinetime::Applications::DisplayApp> displayApp;
|
||||||
TaskHandle_t systemThread;
|
TaskHandle_t systemThread;
|
||||||
bool isSleeping = false;
|
bool isSleeping = false;
|
||||||
|
@ -85,9 +101,29 @@ void SystemTask(void *) {
|
||||||
APP_GPIOTE_INIT(2);
|
APP_GPIOTE_INIT(2);
|
||||||
bool erase_bonds=false;
|
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.reset(new Pinetime::Drivers::SpiMaster {Pinetime::Drivers::SpiMaster::SpiModule::SPI0, {
|
||||||
|
Pinetime::Drivers::SpiMaster::BitOrder::Msb_Lsb,
|
||||||
|
Pinetime::Drivers::SpiMaster::Modes::Mode3,
|
||||||
|
Pinetime::Drivers::SpiMaster::Frequencies::Freq8Mhz,
|
||||||
|
pinSpiSck,
|
||||||
|
pinSpiMosi,
|
||||||
|
pinSpiMiso,
|
||||||
|
pinSpiCsn
|
||||||
|
}});
|
||||||
|
|
||||||
|
lcd.reset(new Pinetime::Drivers::St7789(*spi, pinLcdDataCommand));
|
||||||
|
gfx.reset(new Pinetime::Components::Gfx(*lcd));
|
||||||
|
touchPanel.reset(new Pinetime::Drivers::Cst816S());
|
||||||
|
|
||||||
|
spi->Init();
|
||||||
|
lcd->Init();
|
||||||
|
touchPanel->Init();
|
||||||
|
batteryController.Init();
|
||||||
|
|
||||||
|
displayApp.reset(new Pinetime::Applications::DisplayApp(*lcd, *gfx, *touchPanel, batteryController, bleController, dateTimeController));
|
||||||
displayApp->Start();
|
displayApp->Start();
|
||||||
|
|
||||||
batteryController.Init();
|
|
||||||
batteryController.Update();
|
batteryController.Update();
|
||||||
displayApp->PushMessage(Pinetime::Applications::DisplayApp::Messages::UpdateBatteryLevel);
|
displayApp->PushMessage(Pinetime::Applications::DisplayApp::Messages::UpdateBatteryLevel);
|
||||||
|
|
||||||
|
@ -158,27 +194,22 @@ void OnNewTime(current_time_char_t* currentTime) {
|
||||||
dayOfWeek, hour, minute, second, nrf_rtc_counter_get(portNRF_RTC_REG));
|
dayOfWeek, hour, minute, second, nrf_rtc_counter_get(portNRF_RTC_REG));
|
||||||
}
|
}
|
||||||
|
|
||||||
extern Pinetime::Drivers::SpiMaster* spiInstance;
|
|
||||||
void SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0_IRQHandler(void) {
|
void SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0_IRQHandler(void) {
|
||||||
if(((NRF_SPIM0->INTENSET & (1<<6)) != 0) && NRF_SPIM0->EVENTS_END == 1) {
|
if(((NRF_SPIM0->INTENSET & (1<<6)) != 0) && NRF_SPIM0->EVENTS_END == 1) {
|
||||||
NRF_SPIM0->EVENTS_END = 0;
|
NRF_SPIM0->EVENTS_END = 0;
|
||||||
spiInstance->irqEnd();
|
spi->OnEndEvent(*gfx);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(((NRF_SPIM0->INTENSET & (1<<19)) != 0) && NRF_SPIM0->EVENTS_STARTED == 1) {
|
if(((NRF_SPIM0->INTENSET & (1<<19)) != 0) && NRF_SPIM0->EVENTS_STARTED == 1) {
|
||||||
NRF_SPIM0->EVENTS_STARTED = 0;
|
NRF_SPIM0->EVENTS_STARTED = 0;
|
||||||
spiInstance->irqStarted();
|
spi->OnStartedEvent(*gfx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if(((NRF_SPIM0->INTENSET & (1<<1)) != 0) && NRF_SPIM0->EVENTS_STOPPED == 1) {
|
if(((NRF_SPIM0->INTENSET & (1<<1)) != 0) && NRF_SPIM0->EVENTS_STOPPED == 1) {
|
||||||
NRF_SPIM0->EVENTS_STOPPED = 0;
|
NRF_SPIM0->EVENTS_STOPPED = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
int main(void) {
|
int main(void) {
|
||||||
displayApp.reset(new Pinetime::Applications::DisplayApp(batteryController, bleController, dateTimeController));
|
|
||||||
logger.Init();
|
logger.Init();
|
||||||
nrf_drv_clock_init();
|
nrf_drv_clock_init();
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue