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:
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>
|
||||
using namespace Pinetime::Drivers;
|
||||
|
||||
SpiMaster* spiInstance;
|
||||
SpiMaster::SpiMaster(const SpiMaster::SpiModule spi, const SpiMaster::Parameters ¶ms) :
|
||||
spi{spi}, params{params} {
|
||||
spiInstance = this;
|
||||
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
void SpiMaster::irqStarted() {
|
||||
if(busy) {
|
||||
auto s = currentBufferSize;
|
||||
if(s > 0) {
|
||||
auto currentSize = std::min((size_t)255, s);
|
||||
void SpiMaster::OnEndEvent(BufferProvider& provider) {
|
||||
if(!busy) return;
|
||||
|
||||
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.MAXCNT = currentSize;
|
||||
NRF_SPIM0->TXD.LIST = 0;
|
||||
@@ -101,44 +121,19 @@ void SpiMaster::irqStarted() {
|
||||
NRF_SPIM0->RXD.MAXCNT = 0;
|
||||
NRF_SPIM0->RXD.LIST = 0;
|
||||
|
||||
if(repeat == 0)
|
||||
NRF_SPIM0->SHORTS = 0;
|
||||
|
||||
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);
|
||||
NRF_SPIM0->TASKS_START = 1;
|
||||
} else {
|
||||
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;
|
||||
|
||||
while(busy) {
|
||||
@@ -162,16 +157,12 @@ bool SpiMaster::Write(const uint8_t *data, size_t size, size_t r) {
|
||||
|
||||
nrf_gpio_pin_clear(pinCsn);
|
||||
|
||||
currentBufferAddr = bufferAddr = (uint32_t)data;
|
||||
currentBufferSize = bufferSize = size;
|
||||
repeat = r;
|
||||
currentBufferAddr = (uint32_t)data;
|
||||
currentBufferSize = size;
|
||||
busy = true;
|
||||
|
||||
if(repeat > 0)
|
||||
NRF_SPIM0->SHORTS = (1<<17);
|
||||
|
||||
auto currentSize = std::min((size_t)255, bufferSize);
|
||||
NRF_SPIM0->TXD.PTR = bufferAddr;
|
||||
auto currentSize = std::min((size_t)255, (size_t)currentBufferSize);
|
||||
NRF_SPIM0->TXD.PTR = currentBufferAddr;
|
||||
NRF_SPIM0->TXD.MAXCNT = currentSize;
|
||||
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) {
|
||||
while (NRF_SPIM0->EVENTS_END == 0);
|
||||
busy = false;
|
||||
nrf_gpio_pin_set(pinCsn);
|
||||
}
|
||||
|
||||
|
||||
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() {
|
||||
while(NRF_SPIM0->ENABLE != 0) {
|
||||
NRF_SPIM0->ENABLE = (SPIM_ENABLE_ENABLE_Disabled << SPIM_ENABLE_ENABLE_Pos);
|
||||
@@ -216,8 +197,4 @@ void SpiMaster::Wakeup() {
|
||||
Init();
|
||||
}
|
||||
|
||||
void SpiMaster::Wait() {
|
||||
while(busy) {
|
||||
asm("nop");
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -3,6 +3,8 @@
|
||||
#include <cstddef>
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
|
||||
#include "BufferProvider.h"
|
||||
namespace Pinetime {
|
||||
namespace Drivers {
|
||||
class SpiMaster {
|
||||
@@ -23,20 +25,17 @@ namespace Pinetime {
|
||||
|
||||
SpiMaster(const SpiModule spi, const Parameters& params);
|
||||
bool Init();
|
||||
bool Write(const uint8_t* data, size_t size, size_t r = 0);
|
||||
void setup_workaround_for_ftpan_58(NRF_SPIM_Type *spim, uint32_t ppi_channel, uint32_t gpiote_channel);
|
||||
void Wait();
|
||||
bool Write(const uint8_t* data, size_t size);
|
||||
|
||||
void OnStartedEvent(BufferProvider& provider);
|
||||
void OnEndEvent(BufferProvider& provider);
|
||||
|
||||
void Sleep();
|
||||
void Wakeup();
|
||||
|
||||
bool GetStatusEnd();
|
||||
bool GetStatusStarted();
|
||||
|
||||
void irqEnd();
|
||||
void irqStarted();
|
||||
|
||||
private:
|
||||
void setup_workaround_for_ftpan_58(NRF_SPIM_Type *spim, uint32_t ppi_channel, uint32_t gpiote_channel);
|
||||
|
||||
NRF_SPIM_Type * spiBaseAddress;
|
||||
uint8_t pinCsn;
|
||||
|
||||
@@ -44,12 +43,8 @@ namespace Pinetime {
|
||||
SpiMaster::Parameters params;
|
||||
|
||||
volatile bool busy = false;
|
||||
|
||||
uint32_t bufferAddr = 0;
|
||||
volatile uint32_t currentBufferAddr = 0;
|
||||
size_t bufferSize = 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) {
|
||||
spi.Write(data, size, repeat);
|
||||
void St7789::WriteSpi(const uint8_t* data, size_t size) {
|
||||
spi.Write(data, size);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
void St7789::EndDrawBuffer() {
|
||||
spi.Wait();
|
||||
}
|
||||
|
||||
void St7789::NextDrawBuffer(const uint8_t *data, size_t size, size_t repeat) {
|
||||
WriteSpi(data, size, repeat);
|
||||
void St7789::NextDrawBuffer(const uint8_t *data, size_t size) {
|
||||
WriteSpi(data, size);
|
||||
}
|
||||
|
||||
void St7789::HardwareReset() {
|
||||
@@ -178,5 +174,3 @@ void St7789::Wakeup() {
|
||||
NormalModeOn();
|
||||
DisplayOn();
|
||||
}
|
||||
|
||||
|
||||
|
@@ -12,16 +12,13 @@ namespace Pinetime {
|
||||
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 NextDrawBuffer(const uint8_t* data, size_t size, size_t repeat = 0);
|
||||
void EndDrawBuffer();
|
||||
void NextDrawBuffer(const uint8_t* data, size_t size);
|
||||
|
||||
void DisplayOn();
|
||||
void DisplayOff();
|
||||
|
||||
void Sleep();
|
||||
void Wakeup();
|
||||
|
||||
|
||||
private:
|
||||
SpiMaster& spi;
|
||||
uint8_t pinDataCommand;
|
||||
@@ -35,12 +32,9 @@ 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, size_t repeat = 0);
|
||||
void WriteSpi(const uint8_t* data, size_t size);
|
||||
|
||||
enum class Commands : uint8_t {
|
||||
SoftwareReset = 0x01,
|
||||
@@ -62,7 +56,6 @@ namespace Pinetime {
|
||||
static constexpr uint16_t Width = 240;
|
||||
static constexpr uint16_t Height = 240;
|
||||
void RowAddressSet();
|
||||
|
||||
};
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user