diff --git a/CMakeLists.txt b/CMakeLists.txt index 063decbe..a8d8426b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.10) -project(pinetime VERSION 0.2.0 LANGUAGES C CXX ASM) +project(pinetime VERSION 0.2.1 LANGUAGES C CXX ASM) set(NRF_TARGET "nrf52") diff --git a/src/Components/Gfx/Gfx.cpp b/src/Components/Gfx/Gfx.cpp index 9e680687..0dcb98a6 100644 --- a/src/Components/Gfx/Gfx.cpp +++ b/src/Components/Gfx/Gfx.cpp @@ -1,4 +1,6 @@ #include +#include +#include #include "Gfx.h" #include "../../drivers/St7789.h" using namespace Pinetime::Components; @@ -7,15 +9,38 @@ Gfx::Gfx(Pinetime::Drivers::St7789 &lcd) : lcd{lcd} { } void Gfx::Init() { - lcd.Init(); + } void Gfx::ClearScreen() { - lcd.FillRectangle(0, 0, width, height, 0x0000); + SetBackgroundColor(0x0000); + + state.remainingIterations = 240 + 1; + state.currentIteration = 0; + state.busy = true; + state.action = Action::FillRectangle; + state.taskToNotify = xTaskGetCurrentTaskHandle(); + + lcd.BeginDrawBuffer(0, 0, width, height); + lcd.NextDrawBuffer(reinterpret_cast(buffer), width * 2); + WaitTransfertFinished(); + } -void Gfx::FillRectangle(uint8_t x, uint8_t y, uint8_t width, uint8_t height, uint16_t color) { - lcd.FillRectangle(x, y, width, height, color); +void Gfx::FillRectangle(uint8_t x, uint8_t y, uint8_t w, uint8_t h, uint16_t color) { + SetBackgroundColor(color); + + state.remainingIterations = 240 + 1; + state.currentIteration = 0; + state.busy = true; + state.action = Action::FillRectangle; + state.color = color; + state.taskToNotify = xTaskGetCurrentTaskHandle(); + + lcd.BeginDrawBuffer(x, y, w, h); + lcd.NextDrawBuffer(reinterpret_cast(buffer), width * 2); + + WaitTransfertFinished(); } void Gfx::DrawString(uint8_t x, uint8_t y, uint16_t color, const char *text, const FONT_INFO *p_font, bool wrap) { @@ -56,31 +81,38 @@ 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) { uint8_t char_idx = c - font->startChar; uint16_t bytes_in_line = CEIL_DIV(font->charInfo[char_idx].widthBits, 8); + uint16_t bg = 0x0000; if (c == ' ') { *x += font->height / 2; return; } - // TODO For now, LCD and SPI driver start a new transfer (cs pin + set address windows + write byte) FOR EACH PIXEL! - // This could be improved by setting CS pin, DC pin and address window ONLY ONCE for the whole character - - lcd.BeginDrawBuffer(*x, y, bytes_in_line*8, font->height); - uint16_t bg = 0x0000; - for (uint16_t i = 0; i < font->height; i++) { - for (uint16_t j = 0; j < bytes_in_line; j++) { - for (uint8_t k = 0; k < 8; k++) { - if ((1 << (7 - k)) & - font->data[font->charInfo[char_idx].offset + i * bytes_in_line + j]) { - lcd.NextDrawBuffer(reinterpret_cast(&color), 2); - } - else { - lcd.NextDrawBuffer(reinterpret_cast(&bg), 2); - } + // Build first line + for (uint16_t j = 0; j < bytes_in_line; j++) { + for (uint8_t k = 0; k < 8; k++) { + if ((1 << (7 - k)) & font->data[font->charInfo[char_idx].offset + j]) { + buffer[(j*8)+k] = color; + } + else { + buffer[(j*8)+k] = bg; } } } - lcd.EndDrawBuffer(); + + state.remainingIterations = font->height + 0; + state.currentIteration = 0; + state.busy = true; + state.action = Action::DrawChar; + state.font = const_cast(font); + state.character = c; + state.color = color; + state.taskToNotify = xTaskGetCurrentTaskHandle(); + + lcd.BeginDrawBuffer(*x, y, bytes_in_line*8, font->height); + lcd.NextDrawBuffer(reinterpret_cast(&buffer), bytes_in_line*8*2); + WaitTransfertFinished(); + *x += font->charInfo[char_idx].widthBits + font->spacePixels; } @@ -96,4 +128,59 @@ void Gfx::Wakeup() { lcd.Wakeup(); } +void Gfx::SetBackgroundColor(uint16_t color) { + for(int i = 0; i < width; i++) { + buffer[i] = color; + } +} + +bool Gfx::GetNextBuffer(uint8_t **data, size_t &size) { + if(!state.busy) return false; + state.remainingIterations--; + if (state.remainingIterations == 0) { + state.busy = false; + NotifyEndOfTransfert(state.taskToNotify); + return false; + } + + if(state.action == Action::FillRectangle) { + *data = reinterpret_cast(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(buffer); + size = bytes_in_line*8*2; + } + + state.currentIteration++; + + return true; +} + +void Gfx::NotifyEndOfTransfert(TaskHandle_t task) { + if(task != nullptr) { + BaseType_t xHigherPriorityTaskWoken = pdFALSE; + vTaskNotifyGiveFromISR(task, &xHigherPriorityTaskWoken); + portYIELD_FROM_ISR(xHigherPriorityTaskWoken); + } +} + +void Gfx::WaitTransfertFinished() const { + ulTaskNotifyTake(pdTRUE, 500); +} + diff --git a/src/Components/Gfx/Gfx.h b/src/Components/Gfx/Gfx.h index 9bd07fee..f31b13c0 100644 --- a/src/Components/Gfx/Gfx.h +++ b/src/Components/Gfx/Gfx.h @@ -1,6 +1,9 @@ #pragma once #include #include +#include +#include +#include namespace Pinetime { @@ -8,7 +11,7 @@ namespace Pinetime { class St7789; } namespace Components { - class Gfx { + class Gfx : public Pinetime::Drivers::BufferProvider { public: explicit Gfx(Drivers::St7789& lcd); void Init(); @@ -19,12 +22,34 @@ namespace Pinetime { void Sleep(); void Wakeup(); + bool GetNextBuffer(uint8_t **buffer, size_t &size) override; private: + static constexpr uint8_t width = 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 TaskHandle_t taskToNotify = nullptr; + }; + + volatile State state; + + uint16_t buffer[width]; // 1 line buffer Drivers::St7789& lcd; - const uint8_t width = 240; - const uint8_t height = 240; + void pixel_draw(uint8_t x, uint8_t y, uint16_t color); + void SetBackgroundColor(uint16_t color); + void WaitTransfertFinished() const; + void NotifyEndOfTransfert(TaskHandle_t task); }; } } diff --git a/src/DisplayApp/DisplayApp.cpp b/src/DisplayApp/DisplayApp.cpp index 3b7007af..ca139423 100644 --- a/src/DisplayApp/DisplayApp.cpp +++ b/src/DisplayApp/DisplayApp.cpp @@ -14,25 +14,19 @@ 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::DateTime &dateTimeController) : - spi{Drivers::SpiMaster::SpiModule::SPI0, { - Drivers::SpiMaster::BitOrder::Msb_Lsb, - Drivers::SpiMaster::Modes::Mode3, - Drivers::SpiMaster::Frequencies::Freq8Mhz, - pinSpiSck, - pinSpiMosi, - pinSpiMiso, - pinSpiCsn - }}, - lcd{new Drivers::St7789(spi, pinLcdDataCommand)}, - gfx{new Components::Gfx(*lcd.get()) }, + lcd{lcd}, + gfx{gfx}, + touchPanel{touchPanel}, batteryController{batteryController}, bleController{bleController}, dateTimeController{dateTimeController}, - clockScreen{*(gfx.get())}/*, - messageScreen{*(gfx.get())}*/ { + clockScreen{gfx} { msgQueue = xQueueCreate(queueSize, itemSize); currentScreen = &clockScreen; } @@ -59,12 +53,12 @@ void DisplayApp::InitHw() { nrf_gpio_pin_clear(pinLcdBacklight2); nrf_gpio_pin_clear(pinLcdBacklight3); - spi.Init(); - gfx->Init(); currentScreen->Refresh(true); - touchPanel.Init(); } +uint32_t acc = 0; +uint32_t count = 0; +bool toggle = true; void DisplayApp::Refresh() { TickType_t queueTimeout; switch (state) { @@ -87,16 +81,16 @@ void DisplayApp::Refresh() { nrf_gpio_pin_set(pinLcdBacklight2); vTaskDelay(100); nrf_gpio_pin_set(pinLcdBacklight1); - lcd->DisplayOff(); - lcd->Sleep(); + lcd.DisplayOff(); + lcd.Sleep(); touchPanel.Sleep(); state = States::Idle; break; case Messages::GoToRunning: - lcd->Wakeup(); + lcd.Wakeup(); touchPanel.Wakeup(); - lcd->DisplayOn(); + lcd.DisplayOn(); nrf_gpio_pin_clear(pinLcdBacklight3); nrf_gpio_pin_clear(pinLcdBacklight2); nrf_gpio_pin_clear(pinLcdBacklight1); @@ -124,16 +118,8 @@ void DisplayApp::RunningState() { if(currentScreen != nullptr) { currentScreen->Refresh(false); } - -// if(screenState) { -// currentScreen = &clockScreen; -// } else { -// currentScreen = &messageScreen; -// } -// screenState = !screenState; } - void DisplayApp::IdleState() { } @@ -153,7 +139,7 @@ void DisplayApp::OnTouchEvent() { auto info = touchPanel.GetTouchInfo(); if(info.isTouch) { - lcd->FillRectangle(info.x-10, info.y-10, 20,20, pointColor); + gfx.FillRectangle(info.x-10, info.y-10, 20,20, pointColor); pointColor+=10; } } diff --git a/src/DisplayApp/DisplayApp.h b/src/DisplayApp/DisplayApp.h index 5fb8f6f8..5a5d3ee6 100644 --- a/src/DisplayApp/DisplayApp.h +++ b/src/DisplayApp/DisplayApp.h @@ -23,7 +23,10 @@ namespace Pinetime { public: enum class States {Idle, Running}; 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::DateTime& dateTimeController); void Start(); @@ -33,9 +36,8 @@ namespace Pinetime { TaskHandle_t taskHandle; static void Process(void* instance); void InitHw(); - Pinetime::Drivers::SpiMaster spi; - std::unique_ptr lcd; - std::unique_ptr gfx; + Pinetime::Drivers::St7789& lcd; + Pinetime::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 smallFont {lCD_14ptFontInfo.height, lCD_14ptFontInfo.startChar, lCD_14ptFontInfo.endChar, lCD_14ptFontInfo.spacePixels, lCD_14ptFontInfo.charInfo, lCD_14ptFontInfo.data}; void Refresh(); @@ -52,18 +54,11 @@ namespace Pinetime { Pinetime::Controllers::Ble &bleController; Pinetime::Controllers::DateTime& dateTimeController; - Pinetime::Drivers::Cst816S touchPanel; + Pinetime::Drivers::Cst816S& touchPanel; void OnTouchEvent(); Screens::Clock clockScreen; Screens::Screen* currentScreen = nullptr; -// Screens::Message messageScreen; -// 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 pinLcdBacklight2 = 22; static constexpr uint8_t pinLcdBacklight3 = 23; diff --git a/src/DisplayApp/Screens/Clock.cpp b/src/DisplayApp/Screens/Clock.cpp index 153f4f2e..155cb581 100644 --- a/src/DisplayApp/Screens/Clock.cpp +++ b/src/DisplayApp/Screens/Clock.cpp @@ -9,10 +9,10 @@ using namespace Pinetime::Applications::Screens; void Clock::Refresh(bool fullRefresh) { if(fullRefresh) { gfx.FillRectangle(0,0,240,240,0x0000); - currentChar[0] = 0; - currentChar[1] = 0; - currentChar[2] = 0; - currentChar[3] = 0; + currentChar[0] = 1; + currentChar[1] = 2; + currentChar[2] = 3; + currentChar[3] = 4; auto dummy = currentDateTime.Get(); } diff --git a/src/DisplayApp/Screens/Message.cpp b/src/DisplayApp/Screens/Message.cpp index 2ade4349..121e34b9 100644 --- a/src/DisplayApp/Screens/Message.cpp +++ b/src/DisplayApp/Screens/Message.cpp @@ -9,6 +9,6 @@ using namespace Pinetime::Applications::Screens; void Message::Refresh(bool fullRefresh) { if(fullRefresh) { gfx.FillRectangle(0,0,240,240,0xffff); - gfx.DrawString(120, 10, 0x0000, "COUCOU", &smallFont, false); + gfx.DrawString(120, 10, 0x5555, "COUCOU", &smallFont, false); } } diff --git a/src/drivers/BufferProvider.h b/src/drivers/BufferProvider.h new file mode 100644 index 00000000..50fa253e --- /dev/null +++ b/src/drivers/BufferProvider.h @@ -0,0 +1,11 @@ +#pragma once +#include + +namespace Pinetime { + namespace Drivers { + class BufferProvider { + public: + virtual bool GetNextBuffer(uint8_t** buffer, size_t& size) = 0; + }; + } +} \ No newline at end of file diff --git a/src/drivers/SpiMaster.cpp b/src/drivers/SpiMaster.cpp index 42d3d77b..4a875b9e 100644 --- a/src/drivers/SpiMaster.cpp +++ b/src/drivers/SpiMaster.cpp @@ -1,23 +1,27 @@ #include +#include #include "SpiMaster.h" - +#include using namespace Pinetime::Drivers; SpiMaster::SpiMaster(const SpiMaster::SpiModule spi, const SpiMaster::Parameters ¶ms) : spi{spi}, params{params} { + } bool SpiMaster::Init() { /* Configure GPIO pins used for pselsck, pselmosi, pselmiso and pselss for SPI0 */ + nrf_gpio_pin_set(params.pinSCK); nrf_gpio_cfg_output(params.pinSCK); + nrf_gpio_pin_clear(params.pinMOSI); nrf_gpio_cfg_output(params.pinMOSI); nrf_gpio_cfg_input(params.pinMISO, NRF_GPIO_PIN_NOPULL); nrf_gpio_cfg_output(params.pinCSN); pinCsn = params.pinCSN; switch(spi) { - case SpiModule::SPI0: spiBaseAddress = NRF_SPI0; break; - case SpiModule::SPI1: spiBaseAddress = NRF_SPI1; break; + case SpiModule::SPI0: spiBaseAddress = NRF_SPIM0; break; + case SpiModule::SPI1: spiBaseAddress = NRF_SPIM1; break; default: return false; } @@ -49,51 +53,130 @@ bool SpiMaster::Init() { } spiBaseAddress->CONFIG = regConfig; - spiBaseAddress->EVENTS_READY = 0; - spiBaseAddress->ENABLE = (SPI_ENABLE_ENABLE_Enabled << SPI_ENABLE_ENABLE_Pos); + spiBaseAddress->EVENTS_ENDRX = 0; + spiBaseAddress->EVENTS_ENDTX = 0; + spiBaseAddress->EVENTS_END = 0; + spiBaseAddress->INTENSET = ((unsigned)1 << (unsigned)6); + spiBaseAddress->INTENSET = ((unsigned)1 << (unsigned)1); + spiBaseAddress->INTENSET = ((unsigned)1 << (unsigned)19); + + spiBaseAddress->ENABLE = (SPIM_ENABLE_ENABLE_Enabled << SPIM_ENABLE_ENABLE_Pos); + + NRFX_IRQ_PRIORITY_SET(SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0_IRQn,2); + NRFX_IRQ_ENABLE(SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0_IRQn); return true; } -bool SpiMaster::Write(const uint8_t *data, size_t size) { - volatile uint32_t dummyread; +void SpiMaster::SetupWorkaroundForFtpan58(NRF_SPIM_Type *spim, uint32_t ppi_channel, uint32_t gpiote_channel) { + // Create an event when SCK toggles. + NRF_GPIOTE->CONFIG[gpiote_channel] = (GPIOTE_CONFIG_MODE_Event << GPIOTE_CONFIG_MODE_Pos) | + (spim->PSEL.SCK << GPIOTE_CONFIG_PSEL_Pos) | + (GPIOTE_CONFIG_POLARITY_Toggle << GPIOTE_CONFIG_POLARITY_Pos); + + // Stop the spim instance when SCK toggles. + NRF_PPI->CH[ppi_channel].EEP = (uint32_t) &NRF_GPIOTE->EVENTS_IN[gpiote_channel]; + NRF_PPI->CH[ppi_channel].TEP = (uint32_t) &spim->TASKS_STOP; + NRF_PPI->CHENSET = 1U << ppi_channel; + + // Disable IRQ + spim->INTENCLR = (1<<6); + spim->INTENCLR = (1<<1); + spim->INTENCLR = (1<<19); +} + +void SpiMaster::DisableWorkaroundForFtpan58(NRF_SPIM_Type *spim, uint32_t ppi_channel, uint32_t gpiote_channel) { + NRF_GPIOTE->CONFIG[gpiote_channel] = 0; + NRF_PPI->CH[ppi_channel].EEP = 0; + NRF_PPI->CH[ppi_channel].TEP = 0; + NRF_PPI->CHENSET = ppi_channel; + spim->INTENSET = (1<<6); + spim->INTENSET = (1<<1); + spim->INTENSET = (1<<19); +} + +void SpiMaster::OnEndEvent(BufferProvider& provider) { + if(!busy) return; + + auto s = currentBufferSize; + if(s > 0) { + auto currentSize = std::min((size_t) 255, s); + PrepareTx(currentBufferAddr, currentSize); + currentBufferAddr += currentSize; + currentBufferSize -= currentSize; + + spiBaseAddress->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); + PrepareTx(currentBufferAddr, currentSize); + currentBufferAddr += currentSize; + currentBufferSize -= currentSize; + + spiBaseAddress->TASKS_START = 1; + } else { + busy = false; + nrf_gpio_pin_set(pinCsn); + } + } +} + +void SpiMaster::OnStartedEvent(BufferProvider& provider) { + if(!busy) return; +} + +void SpiMaster::PrepareTx(const volatile uint32_t bufferAddress, const volatile size_t size) { + spiBaseAddress->TXD.PTR = bufferAddress; + spiBaseAddress->TXD.MAXCNT = size; + spiBaseAddress->TXD.LIST = 0; + spiBaseAddress->RXD.PTR = 0; + spiBaseAddress->RXD.MAXCNT = 0; + spiBaseAddress->RXD.LIST = 0; + spiBaseAddress->EVENTS_END = 0; +} + +bool SpiMaster::Write(const uint8_t *data, size_t size) { if(data == nullptr) return false; - /* enable slave (slave select active low) */ - nrf_gpio_pin_clear(pinCsn); - - spiBaseAddress->EVENTS_READY = 0; - - spiBaseAddress->TXD = (uint32_t)*data++; - - while(--size) - { - spiBaseAddress->TXD = (uint32_t)*data++; - - /* Wait for the transaction complete or timeout (about 10ms - 20 ms) */ - while (spiBaseAddress->EVENTS_READY == 0); - - /* clear the event to be ready to receive next messages */ - spiBaseAddress->EVENTS_READY = 0; - - dummyread = spiBaseAddress->RXD; + while(busy) { + asm("nop"); } - /* Wait for the transaction complete or timeout (about 10ms - 20 ms) */ - while (spiBaseAddress->EVENTS_READY == 0); + if(size == 1) { + SetupWorkaroundForFtpan58(spiBaseAddress, 0,0); + } else { + DisableWorkaroundForFtpan58(spiBaseAddress, 0, 0); + } - dummyread = spiBaseAddress->RXD; + nrf_gpio_pin_clear(pinCsn); - /* disable slave (slave select active low) */ - nrf_gpio_pin_set(pinCsn); + currentBufferAddr = (uint32_t)data; + currentBufferSize = size; + busy = true; + + auto currentSize = std::min((size_t)255, (size_t)currentBufferSize); + PrepareTx(currentBufferAddr, currentSize); + currentBufferSize -= currentSize; + currentBufferAddr += currentSize; + spiBaseAddress->TASKS_START = 1; + + if(size == 1) { + while (spiBaseAddress->EVENTS_END == 0); + busy = false; + } return true; } void SpiMaster::Sleep() { - while(NRF_SPI0->ENABLE != 0) { - NRF_SPI0->ENABLE = (SPIM_ENABLE_ENABLE_Disabled << SPIM_ENABLE_ENABLE_Pos); + while(spiBaseAddress->ENABLE != 0) { + spiBaseAddress->ENABLE = (SPIM_ENABLE_ENABLE_Disabled << SPIM_ENABLE_ENABLE_Pos); } nrf_gpio_cfg_default(params.pinSCK); nrf_gpio_cfg_default(params.pinMOSI); @@ -104,3 +187,5 @@ void SpiMaster::Sleep() { void SpiMaster::Wakeup() { Init(); } + + diff --git a/src/drivers/SpiMaster.h b/src/drivers/SpiMaster.h index 073501a8..60013242 100644 --- a/src/drivers/SpiMaster.h +++ b/src/drivers/SpiMaster.h @@ -2,7 +2,9 @@ #include #include #include +#include +#include "BufferProvider.h" namespace Pinetime { namespace Drivers { class SpiMaster { @@ -25,15 +27,26 @@ namespace Pinetime { bool Init(); bool Write(const uint8_t* data, size_t size); + void OnStartedEvent(BufferProvider& provider); + void OnEndEvent(BufferProvider& provider); + void Sleep(); void Wakeup(); private: - NRF_SPI_Type * spiBaseAddress; + void SetupWorkaroundForFtpan58(NRF_SPIM_Type *spim, uint32_t ppi_channel, uint32_t gpiote_channel); + void DisableWorkaroundForFtpan58(NRF_SPIM_Type *spim, uint32_t ppi_channel, uint32_t gpiote_channel); + void PrepareTx(const volatile uint32_t bufferAddress, const volatile size_t size); + + NRF_SPIM_Type * spiBaseAddress; uint8_t pinCsn; SpiMaster::SpiModule spi; SpiMaster::Parameters params; + + volatile bool busy = false; + volatile uint32_t currentBufferAddr = 0; + volatile size_t currentBufferSize = 0; }; } } diff --git a/src/drivers/St7789.cpp b/src/drivers/St7789.cpp index ac813064..39595dc9 100644 --- a/src/drivers/St7789.cpp +++ b/src/drivers/St7789.cpp @@ -38,7 +38,7 @@ void St7789::WriteData(uint8_t data) { void St7789::WriteSpi(const uint8_t* data, size_t size) { - spi.Write(data, size); + spi.Write(data, size); } void St7789::SoftwareReset() { @@ -95,20 +95,6 @@ void St7789::DisplayOn() { WriteCommand(static_cast(Commands::DisplayOn)); } -void St7789::FillRectangle(uint16_t x, uint16_t y, uint16_t width, uint16_t height, uint16_t color) { - BeginDrawBuffer(x, y, width, height); - - uint32_t c = color + (color << 16); - uint8_t w = width/2; - - for(y=height+ST7789_ROW_OFFSET; y>ST7789_ROW_OFFSET; y--) { - for(x=w; x>0; x--) { - NextDrawBuffer(reinterpret_cast(&c), 4); - } - } - EndDrawBuffer(); -} - void St7789::SetAddrWindow(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1) { WriteCommand(static_cast(Commands::ColumnAddressSet)); WriteData(x0 >> 8); @@ -156,11 +142,8 @@ void St7789::BeginDrawBuffer(uint16_t x, uint16_t y, uint16_t width, uint16_t he nrf_gpio_pin_set(pinDataCommand); } -void St7789::EndDrawBuffer() { -} - void St7789::NextDrawBuffer(const uint8_t *data, size_t size) { - spi.Write(data, size); + WriteSpi(data, size); } void St7789::HardwareReset() { @@ -191,5 +174,3 @@ void St7789::Wakeup() { NormalModeOn(); DisplayOn(); } - - diff --git a/src/drivers/St7789.h b/src/drivers/St7789.h index a32a96f9..9ecf9f27 100644 --- a/src/drivers/St7789.h +++ b/src/drivers/St7789.h @@ -10,19 +10,15 @@ namespace Pinetime { void Init(); void Uninit(); void DrawPixel(uint16_t x, uint16_t y, uint32_t color); - void FillRectangle(uint16_t x, uint16_t y, uint16_t width, uint16_t height, uint16_t color); void BeginDrawBuffer(uint16_t x, uint16_t y, uint16_t width, uint16_t height); void NextDrawBuffer(const uint8_t* data, size_t size); - void EndDrawBuffer(); void DisplayOn(); void DisplayOff(); void Sleep(); void Wakeup(); - - private: SpiMaster& spi; uint8_t pinDataCommand; @@ -36,10 +32,7 @@ namespace Pinetime { void DisplayInversionOn(); void NormalModeOn(); void WriteToRam(); - - void SetAddrWindow(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1); - void WriteCommand(uint8_t cmd); void WriteSpi(const uint8_t* data, size_t size); @@ -63,7 +56,6 @@ namespace Pinetime { static constexpr uint16_t Width = 240; static constexpr uint16_t Height = 240; void RowAddressSet(); - }; } } diff --git a/src/main.cpp b/src/main.cpp index 062a36e4..13dddca6 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -15,6 +15,10 @@ #include "BLE/BleManager.h" #include "Components/Battery/BatteryController.h" #include "Components/Ble/BleController.h" +#include "../drivers/Cst816s.h" +#include +#include +#include #if NRF_LOG_ENABLED #include "Logging/NrfLogger.h" @@ -24,6 +28,18 @@ Pinetime::Logging::NrfLogger logger; Pinetime::Logging::DummyLogger logger; #endif +std::unique_ptr spi; +std::unique_ptr lcd; +std::unique_ptr gfx; +std::unique_ptr 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 displayApp; TaskHandle_t systemThread; bool isSleeping = false; @@ -85,9 +101,29 @@ void SystemTask(void *) { APP_GPIOTE_INIT(2); bool erase_bonds=false; 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(); - batteryController.Init(); batteryController.Update(); displayApp->PushMessage(Pinetime::Applications::DisplayApp::Messages::UpdateBatteryLevel); @@ -158,8 +194,22 @@ void OnNewTime(current_time_char_t* currentTime) { dayOfWeek, hour, minute, second, nrf_rtc_counter_get(portNRF_RTC_REG)); } +void SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0_IRQHandler(void) { + if(((NRF_SPIM0->INTENSET & (1<<6)) != 0) && NRF_SPIM0->EVENTS_END == 1) { + NRF_SPIM0->EVENTS_END = 0; + spi->OnEndEvent(*gfx); + } + + if(((NRF_SPIM0->INTENSET & (1<<19)) != 0) && NRF_SPIM0->EVENTS_STARTED == 1) { + NRF_SPIM0->EVENTS_STARTED = 0; + spi->OnStartedEvent(*gfx); + } + + if(((NRF_SPIM0->INTENSET & (1<<1)) != 0) && NRF_SPIM0->EVENTS_STOPPED == 1) { + NRF_SPIM0->EVENTS_STOPPED = 0; + } +} int main(void) { - displayApp.reset(new Pinetime::Applications::DisplayApp(batteryController, bleController, dateTimeController)); logger.Init(); nrf_drv_clock_init();