InfiniTime/src/drivers/SpiMaster.cpp
Jean-François Milants 63e0c4f4ef Application selection at build time
A list of "user applications" is built at compile time. It contains all the info needed to create the application at runtime (ptr to a create() function) and to display the app in the application menu. All applications declare a TypeTrait with these information.
When a new app must be loaded, DisplayApp first check if this app is a System app (in which case it creates it like it did before). If it's not a System app, it looks for the app in the list of User applications and creates it if it found it.
Those changes allow to more easily add new app and to select which app must be built into the firmware.
Switch to C++20 (and fix a few issues in SpiMaster.cpp and Watchdog.cpp.
2023-11-19 21:13:55 +01:00

297 lines
7.9 KiB
C++

#include "drivers/SpiMaster.h"
#include <hal/nrf_gpio.h>
#include <hal/nrf_spim.h>
#include <nrfx_log.h>
#include <algorithm>
using namespace Pinetime::Drivers;
SpiMaster::SpiMaster(const SpiMaster::SpiModule spi, const SpiMaster::Parameters& params) : spi {spi}, params {params} {
}
bool SpiMaster::Init() {
if (mutex == nullptr) {
mutex = xSemaphoreCreateBinary();
ASSERT(mutex != nullptr);
}
/* 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_SPIM0;
break;
case SpiModule::SPI1:
spiBaseAddress = NRF_SPIM1;
break;
default:
return false;
}
/* Configure pins, frequency and mode */
spiBaseAddress->PSELSCK = params.pinSCK;
spiBaseAddress->PSELMOSI = params.pinMOSI;
spiBaseAddress->PSELMISO = params.pinMISO;
uint32_t frequency;
switch (params.Frequency) {
case Frequencies::Freq8Mhz:
frequency = 0x80000000;
break;
default:
return false;
}
spiBaseAddress->FREQUENCY = frequency;
uint32_t regConfig = 0;
switch (params.bitOrder) {
case BitOrder::Msb_Lsb:
break;
case BitOrder::Lsb_Msb:
regConfig = 1;
break;
default:
return false;
}
switch (params.mode) {
case Modes::Mode0:
break;
case Modes::Mode1:
regConfig |= (0x01 << 1);
break;
case Modes::Mode2:
regConfig |= (0x02 << 1);
break;
case Modes::Mode3:
regConfig |= (0x03 << 1);
break;
default:
return false;
}
spiBaseAddress->CONFIG = regConfig;
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);
xSemaphoreGive(mutex);
return true;
}
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;
spiBaseAddress->EVENTS_END = 0;
// 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;
spiBaseAddress->EVENTS_END = 0;
spim->INTENSET = (1 << 6);
spim->INTENSET = (1 << 1);
spim->INTENSET = (1 << 19);
}
void SpiMaster::OnEndEvent() {
if (currentBufferAddr == 0) {
return;
}
auto s = currentBufferSize;
if (s > 0) {
auto currentSize = std::min((size_t) 255, s);
PrepareTx(currentBufferAddr, currentSize);
currentBufferAddr = currentBufferAddr + currentSize;
currentBufferSize = currentBufferSize - currentSize;
spiBaseAddress->TASKS_START = 1;
} else {
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
if (taskToNotify != nullptr) {
vTaskNotifyGiveFromISR(taskToNotify, &xHigherPriorityTaskWoken);
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}
nrf_gpio_pin_set(this->pinCsn);
currentBufferAddr = 0;
BaseType_t xHigherPriorityTaskWoken2 = pdFALSE;
xSemaphoreGiveFromISR(mutex, &xHigherPriorityTaskWoken2);
portYIELD_FROM_ISR(xHigherPriorityTaskWoken | xHigherPriorityTaskWoken2);
}
}
void SpiMaster::OnStartedEvent() {
}
void SpiMaster::PrepareTx(const uint32_t bufferAddress, const 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;
}
void SpiMaster::PrepareRx(const uint32_t bufferAddress, const size_t size) {
spiBaseAddress->TXD.PTR = 0;
spiBaseAddress->TXD.MAXCNT = 0;
spiBaseAddress->TXD.LIST = 0;
spiBaseAddress->RXD.PTR = bufferAddress;
spiBaseAddress->RXD.MAXCNT = size;
spiBaseAddress->RXD.LIST = 0;
spiBaseAddress->EVENTS_END = 0;
}
bool SpiMaster::Write(uint8_t pinCsn, const uint8_t* data, size_t size) {
if (data == nullptr)
return false;
auto ok = xSemaphoreTake(mutex, portMAX_DELAY);
ASSERT(ok == true);
taskToNotify = xTaskGetCurrentTaskHandle();
this->pinCsn = pinCsn;
if (size == 1) {
SetupWorkaroundForFtpan58(spiBaseAddress, 0, 0);
} else {
DisableWorkaroundForFtpan58(spiBaseAddress, 0, 0);
}
nrf_gpio_pin_clear(this->pinCsn);
currentBufferAddr = (uint32_t) data;
currentBufferSize = size;
auto currentSize = std::min((size_t) 255, (size_t) currentBufferSize);
PrepareTx(currentBufferAddr, currentSize);
currentBufferSize = currentBufferSize - currentSize;
currentBufferAddr = currentBufferAddr + currentSize;
spiBaseAddress->TASKS_START = 1;
if (size == 1) {
while (spiBaseAddress->EVENTS_END == 0)
;
nrf_gpio_pin_set(this->pinCsn);
currentBufferAddr = 0;
DisableWorkaroundForFtpan58(spiBaseAddress, 0, 0);
xSemaphoreGive(mutex);
}
return true;
}
bool SpiMaster::Read(uint8_t pinCsn, uint8_t* cmd, size_t cmdSize, uint8_t* data, size_t dataSize) {
xSemaphoreTake(mutex, portMAX_DELAY);
taskToNotify = nullptr;
this->pinCsn = pinCsn;
DisableWorkaroundForFtpan58(spiBaseAddress, 0, 0);
spiBaseAddress->INTENCLR = (1 << 6);
spiBaseAddress->INTENCLR = (1 << 1);
spiBaseAddress->INTENCLR = (1 << 19);
nrf_gpio_pin_clear(this->pinCsn);
currentBufferAddr = 0;
currentBufferSize = 0;
PrepareTx((uint32_t) cmd, cmdSize);
spiBaseAddress->TASKS_START = 1;
while (spiBaseAddress->EVENTS_END == 0)
;
PrepareRx((uint32_t) data, dataSize);
spiBaseAddress->TASKS_START = 1;
while (spiBaseAddress->EVENTS_END == 0)
;
nrf_gpio_pin_set(this->pinCsn);
xSemaphoreGive(mutex);
return true;
}
void SpiMaster::Sleep() {
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);
nrf_gpio_cfg_default(params.pinMISO);
NRF_LOG_INFO("[SPIMASTER] sleep")
}
void SpiMaster::Wakeup() {
Init();
NRF_LOG_INFO("[SPIMASTER] Wakeup");
}
bool SpiMaster::WriteCmdAndBuffer(uint8_t pinCsn, const uint8_t* cmd, size_t cmdSize, const uint8_t* data, size_t dataSize) {
xSemaphoreTake(mutex, portMAX_DELAY);
taskToNotify = nullptr;
this->pinCsn = pinCsn;
DisableWorkaroundForFtpan58(spiBaseAddress, 0, 0);
spiBaseAddress->INTENCLR = (1 << 6);
spiBaseAddress->INTENCLR = (1 << 1);
spiBaseAddress->INTENCLR = (1 << 19);
nrf_gpio_pin_clear(this->pinCsn);
currentBufferAddr = 0;
currentBufferSize = 0;
PrepareTx((uint32_t) cmd, cmdSize);
spiBaseAddress->TASKS_START = 1;
while (spiBaseAddress->EVENTS_END == 0)
;
PrepareTx((uint32_t) data, dataSize);
spiBaseAddress->TASKS_START = 1;
while (spiBaseAddress->EVENTS_END == 0)
;
nrf_gpio_pin_set(this->pinCsn);
xSemaphoreGive(mutex);
return true;
}