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:
@@ -7,23 +7,33 @@ Gfx::Gfx(Pinetime::Drivers::St7789 &lcd) : lcd{lcd} {
|
||||
}
|
||||
|
||||
void Gfx::Init() {
|
||||
lcd.Init();
|
||||
|
||||
}
|
||||
|
||||
void Gfx::ClearScreen() {
|
||||
SetBackgroundColor(0x0000);
|
||||
|
||||
state.remainingIterations = 240 + 1;
|
||||
state.currentIteration = 0;
|
||||
state.busy = true;
|
||||
state.action = Action::FillRectangle;
|
||||
|
||||
lcd.BeginDrawBuffer(0, 0, width, height);
|
||||
for(int i = 0; i < height; i++) {
|
||||
lcd.NextDrawBuffer(reinterpret_cast<const uint8_t *>(buffer), width * 2);
|
||||
}
|
||||
lcd.EndDrawBuffer();
|
||||
lcd.NextDrawBuffer(reinterpret_cast<const uint8_t *>(buffer), width * 2);
|
||||
while(state.busy) {} // TODO wait on an event/queue/... instead of polling
|
||||
}
|
||||
|
||||
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);
|
||||
lcd.BeginDrawBuffer(0, 0, width, height);
|
||||
lcd.NextDrawBuffer(reinterpret_cast<const uint8_t *>(buffer), width * 2, 240);
|
||||
lcd.EndDrawBuffer();
|
||||
|
||||
state.remainingIterations = 240 + 1;
|
||||
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) {
|
||||
@@ -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) {
|
||||
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]) {
|
||||
buffer[(j*8)+k] = color;
|
||||
}
|
||||
else {
|
||||
buffer[(j*8)+k] = bg;
|
||||
}
|
||||
// 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.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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
#include <cstdint>
|
||||
#include <nrf_font.h>
|
||||
#include <drivers/BufferProvider.h>
|
||||
|
||||
|
||||
namespace Pinetime {
|
||||
@@ -8,7 +9,7 @@ namespace Pinetime {
|
||||
class St7789;
|
||||
}
|
||||
namespace Components {
|
||||
class Gfx {
|
||||
class Gfx : public Pinetime::Drivers::BufferProvider {
|
||||
public:
|
||||
explicit Gfx(Drivers::St7789& lcd);
|
||||
void Init();
|
||||
@@ -19,11 +20,26 @@ 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 State state;
|
||||
|
||||
uint16_t buffer[width]; // 1 line buffer
|
||||
Drivers::St7789& lcd;
|
||||
|
||||
|
Reference in New Issue
Block a user