mirror of
https://github.com/betaflight/betaflight.git
synced 2025-07-12 19:10:32 +03:00
Add support for SPI DMA (#14483)
This commit is contained in:
parent
aeda0905c5
commit
9fed1f9ebb
5 changed files with 112 additions and 36 deletions
|
@ -1830,7 +1830,7 @@ case MSP_NAME:
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < 8; i++) {
|
for (int i = 0; i < 8; i++) {
|
||||||
for (unsigned j; j < ARRAYLEN(gyroDeviceConfig(i)->customAlignment.raw); j++) {
|
for (unsigned j = 0; j < ARRAYLEN(gyroDeviceConfig(i)->customAlignment.raw); j++) {
|
||||||
sbufWriteU16(dst, i < GYRO_COUNT ? gyroDeviceConfig(i)->customAlignment.raw[j] : 0);
|
sbufWriteU16(dst, i < GYRO_COUNT ? gyroDeviceConfig(i)->customAlignment.raw[j] : 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,7 +33,7 @@
|
||||||
#include "platform.h"
|
#include "platform.h"
|
||||||
|
|
||||||
#ifdef USE_SPI
|
#ifdef USE_SPI
|
||||||
#define TESTING_NO_DMA 1
|
//#define TESTING_NO_DMA 1
|
||||||
|
|
||||||
#include "common/maths.h"
|
#include "common/maths.h"
|
||||||
#include "drivers/bus.h"
|
#include "drivers/bus.h"
|
||||||
|
@ -43,6 +43,7 @@
|
||||||
#include "drivers/io.h"
|
#include "drivers/io.h"
|
||||||
#include "drivers/io_def.h"
|
#include "drivers/io_def.h"
|
||||||
#include "drivers/io_impl.h"
|
#include "drivers/io_impl.h"
|
||||||
|
#include "drivers/nvic.h"
|
||||||
|
|
||||||
#include "hardware/spi.h"
|
#include "hardware/spi.h"
|
||||||
#include "hardware/gpio.h"
|
#include "hardware/gpio.h"
|
||||||
|
@ -126,6 +127,8 @@ const spiHardware_t spiHardware[] = {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
extern busDevice_t spiBusDevice[SPIDEV_COUNT];
|
||||||
|
|
||||||
void spiPinConfigure(const struct spiPinConfig_s *pConfig)
|
void spiPinConfigure(const struct spiPinConfig_s *pConfig)
|
||||||
{
|
{
|
||||||
for (size_t hwindex = 0 ; hwindex < ARRAYLEN(spiHardware) ; hwindex++) {
|
for (size_t hwindex = 0 ; hwindex < ARRAYLEN(spiHardware) ; hwindex++) {
|
||||||
|
@ -242,11 +245,84 @@ void spiInitDevice(SPIDevice device)
|
||||||
gpio_set_function(IO_PINBYTAG(spi->sck), GPIO_FUNC_SPI);
|
gpio_set_function(IO_PINBYTAG(spi->sck), GPIO_FUNC_SPI);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void spiInternalStopDMA (const extDevice_t *dev)
|
||||||
|
{
|
||||||
|
dmaChannelDescriptor_t *dmaRx = dev->bus->dmaRx;
|
||||||
|
dmaChannelDescriptor_t *dmaTx = dev->bus->dmaTx;
|
||||||
|
|
||||||
|
if (dmaRx && dma_channel_is_busy(dmaRx->channel)) {
|
||||||
|
// Abort active DMA - this should never happen
|
||||||
|
dma_channel_abort(dmaRx->channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dmaTx && dma_channel_is_busy(dmaTx->channel)) {
|
||||||
|
// Abort active DMA - this should never happen as Tx should complete before Rx
|
||||||
|
dma_channel_abort(dmaTx->channel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Interrupt handler for SPI receive DMA completion
|
||||||
|
FAST_IRQ_HANDLER static void spiRxIrqHandler(dmaChannelDescriptor_t* descriptor)
|
||||||
|
{
|
||||||
|
const extDevice_t *dev = (const extDevice_t *)descriptor->userParam;
|
||||||
|
|
||||||
|
if (!dev) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
busDevice_t *bus = dev->bus;
|
||||||
|
|
||||||
|
if (bus->curSegment->negateCS) {
|
||||||
|
// Negate Chip Select
|
||||||
|
IOHi(dev->busType_u.spi.csnPin);
|
||||||
|
}
|
||||||
|
|
||||||
|
spiInternalStopDMA(dev);
|
||||||
|
|
||||||
|
spiIrqHandler(dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
extern dmaChannelDescriptor_t dmaDescriptors[];
|
||||||
|
|
||||||
void spiInitBusDMA(void)
|
void spiInitBusDMA(void)
|
||||||
{
|
{
|
||||||
//TODO: implement
|
for (uint32_t device = 0; device < SPIDEV_COUNT; device++) {
|
||||||
// if required to set up mappings of peripherals to DMA instances?
|
busDevice_t *bus = &spiBusDevice[device];
|
||||||
// can just start off with dma_claim_unused_channel in spiInternalInitStream?
|
int32_t channel_tx;
|
||||||
|
int32_t channel_rx;
|
||||||
|
|
||||||
|
if (bus->busType != BUS_TYPE_SPI) {
|
||||||
|
// This bus is not in use
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
channel_tx = dma_claim_unused_channel(true);
|
||||||
|
if (channel_tx == -1) {
|
||||||
|
// no more available channels so give up
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
channel_rx = dma_claim_unused_channel(true);
|
||||||
|
if (channel_rx == -1) {
|
||||||
|
// no more available channels so give up, first releasing the one
|
||||||
|
// channel we did claim
|
||||||
|
dma_channel_unclaim(channel_tx);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bus->dmaTx = &dmaDescriptors[DMA_CHANNEL_TO_INDEX(channel_tx)];
|
||||||
|
bus->dmaTx->channel = channel_tx;
|
||||||
|
|
||||||
|
bus->dmaRx = &dmaDescriptors[DMA_CHANNEL_TO_INDEX(channel_rx)];
|
||||||
|
bus->dmaRx->channel = channel_rx;
|
||||||
|
|
||||||
|
// The transaction concludes when the data has been received which will be after transmission is complete
|
||||||
|
dmaSetHandler(DMA_CHANNEL_TO_IDENTIFIER(bus->dmaRx->channel), spiRxIrqHandler, NVIC_PRIO_SPI_DMA, 0);
|
||||||
|
|
||||||
|
// We got the required resources, so we can use DMA on this bus
|
||||||
|
bus->useDMA = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void spiInternalResetStream(dmaChannelDescriptor_t *descriptor)
|
void spiInternalResetStream(dmaChannelDescriptor_t *descriptor)
|
||||||
|
@ -271,26 +347,26 @@ void spiInternalInitStream(const extDevice_t *dev, bool preInit)
|
||||||
#else
|
#else
|
||||||
UNUSED(preInit);
|
UNUSED(preInit);
|
||||||
|
|
||||||
int dma_tx = dma_claim_unused_channel(true);
|
busDevice_t *bus = dev->bus;
|
||||||
int dma_rx = dma_claim_unused_channel(true);
|
|
||||||
|
|
||||||
dev->bus->dmaTx->channel = dma_tx;
|
volatile busSegment_t *segment = bus->curSegment;
|
||||||
dev->bus->dmaRx->channel = dma_rx;
|
|
||||||
dev->bus->dmaTx->irqHandlerCallback = NULL;
|
|
||||||
dev->bus->dmaRx->irqHandlerCallback = spiInternalResetStream; // TODO: implement - correct callback
|
|
||||||
|
|
||||||
const spiDevice_t *spi = &spiDevice[spiDeviceByInstance(dev->bus->busType_u.spi.instance)];
|
const spiDevice_t *spi = &spiDevice[spiDeviceByInstance(dev->bus->busType_u.spi.instance)];
|
||||||
dma_channel_config config = dma_channel_get_default_config(dma_tx);
|
dma_channel_config config = dma_channel_get_default_config(dev->bus->dmaTx->channel);
|
||||||
channel_config_set_transfer_data_size(&config, DMA_SIZE_8);
|
channel_config_set_transfer_data_size(&config, DMA_SIZE_8);
|
||||||
|
channel_config_set_read_increment(&config, true);
|
||||||
|
channel_config_set_write_increment(&config, false);
|
||||||
channel_config_set_dreq(&config, spi_get_dreq(SPI_INST(spi->dev), true));
|
channel_config_set_dreq(&config, spi_get_dreq(SPI_INST(spi->dev), true));
|
||||||
|
|
||||||
dma_channel_configure(dma_tx, &config, &spi_get_hw(SPI_INST(spi->dev))->dr, dev->txBuf, 0, false);
|
dma_channel_configure(dev->bus->dmaTx->channel, &config, &spi_get_hw(SPI_INST(spi->dev))->dr, segment->u.buffers.txData, 0, false);
|
||||||
|
|
||||||
config = dma_channel_get_default_config(dma_rx);
|
config = dma_channel_get_default_config(dev->bus->dmaRx->channel);
|
||||||
channel_config_set_transfer_data_size(&config, DMA_SIZE_8);
|
channel_config_set_transfer_data_size(&config, DMA_SIZE_8);
|
||||||
|
channel_config_set_read_increment(&config, false);
|
||||||
|
channel_config_set_write_increment(&config, true);
|
||||||
channel_config_set_dreq(&config, spi_get_dreq(SPI_INST(spi->dev), false));
|
channel_config_set_dreq(&config, spi_get_dreq(SPI_INST(spi->dev), false));
|
||||||
|
|
||||||
dma_channel_configure(dma_rx, &config, dev->rxBuf, &spi_get_hw(SPI_INST(spi->dev))->dr, 0, false);
|
dma_channel_configure(dev->bus->dmaRx->channel, &config, segment->u.buffers.rxData, &spi_get_hw(SPI_INST(spi->dev))->dr, 0, false);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -300,12 +376,13 @@ void spiInternalStartDMA(const extDevice_t *dev)
|
||||||
#ifndef USE_DMA
|
#ifndef USE_DMA
|
||||||
UNUSED(dev);
|
UNUSED(dev);
|
||||||
#else
|
#else
|
||||||
|
dev->bus->dmaRx->userParam = (uint32_t)dev;
|
||||||
|
|
||||||
// TODO check correct, was len + 1 now len
|
// TODO check correct, was len + 1 now len
|
||||||
dma_channel_set_trans_count(dev->bus->dmaTx->channel, dev->bus->curSegment->len, false);
|
dma_channel_set_trans_count(dev->bus->dmaTx->channel, dev->bus->curSegment->len, false);
|
||||||
dma_channel_set_trans_count(dev->bus->dmaRx->channel, dev->bus->curSegment->len, false);
|
dma_channel_set_trans_count(dev->bus->dmaRx->channel, dev->bus->curSegment->len, false);
|
||||||
|
|
||||||
dma_channel_start(dev->bus->dmaTx->channel);
|
dma_start_channel_mask((1 << dev->bus->dmaTx->channel) | (1 << dev->bus->dmaRx->channel));
|
||||||
dma_channel_start(dev->bus->dmaRx->channel);
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -34,9 +34,6 @@
|
||||||
#include "hardware/dma.h"
|
#include "hardware/dma.h"
|
||||||
#include "hardware/irq.h"
|
#include "hardware/irq.h"
|
||||||
|
|
||||||
volatile bool dma_irq0_handler_registered = false;
|
|
||||||
volatile bool dma_irq1_handler_registered = false;
|
|
||||||
|
|
||||||
dmaChannelDescriptor_t dmaDescriptors[DMA_LAST_HANDLER] = {
|
dmaChannelDescriptor_t dmaDescriptors[DMA_LAST_HANDLER] = {
|
||||||
DEFINE_DMA_CHANNEL(DMA_CH0_HANDLER),
|
DEFINE_DMA_CHANNEL(DMA_CH0_HANDLER),
|
||||||
DEFINE_DMA_CHANNEL(DMA_CH1_HANDLER),
|
DEFINE_DMA_CHANNEL(DMA_CH1_HANDLER),
|
||||||
|
@ -103,7 +100,7 @@ dmaIdentifier_e dmaGetFreeIdentifier(void)
|
||||||
|
|
||||||
void dmaSetHandler(dmaIdentifier_e identifier, dmaCallbackHandlerFuncPtr callback, uint32_t priority, uint32_t userParam)
|
void dmaSetHandler(dmaIdentifier_e identifier, dmaCallbackHandlerFuncPtr callback, uint32_t priority, uint32_t userParam)
|
||||||
{
|
{
|
||||||
if (identifier < DMA_CH0_HANDLER || identifier > DMA_LAST_HANDLER) {
|
if (identifier < DMA_FIRST_HANDLER || identifier > DMA_LAST_HANDLER) {
|
||||||
return; // Invalid identifier
|
return; // Invalid identifier
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -129,22 +126,24 @@ void dmaSetHandler(dmaIdentifier_e identifier, dmaCallbackHandlerFuncPtr callbac
|
||||||
|
|
||||||
const int index = DMA_IDENTIFIER_TO_INDEX(identifier);
|
const int index = DMA_IDENTIFIER_TO_INDEX(identifier);
|
||||||
const uint32_t channel = dmaDescriptors[index].channel;
|
const uint32_t channel = dmaDescriptors[index].channel;
|
||||||
|
static bool dma_irqN_handler_registered[2];
|
||||||
|
|
||||||
|
if (!dma_irqN_handler_registered[core]) {
|
||||||
|
// Register the DMA IRQ handler if needed
|
||||||
|
dmaDescriptors[index].irqN = core ? DMA_IRQ_1_IRQn : DMA_IRQ_0_IRQn;
|
||||||
|
irq_handler_t irq_handler = core ? dma_irq1_handler : dma_irq0_handler;
|
||||||
|
|
||||||
|
|
||||||
|
irq_set_exclusive_handler(dmaDescriptors[index].irqN, irq_handler);
|
||||||
|
irq_set_enabled(dmaDescriptors[index].irqN, true);
|
||||||
|
|
||||||
|
dma_irqN_handler_registered[core] = true;
|
||||||
|
}
|
||||||
|
|
||||||
if (core) {
|
if (core) {
|
||||||
// Core 1 uses DMA IRQ1
|
dma_channel_set_irq1_enabled(channel, true);
|
||||||
if (!dma_irq1_handler_registered) {
|
|
||||||
irq_set_exclusive_handler(DMA_IRQ_1, dma_irq1_handler);
|
|
||||||
irq_set_enabled(DMA_IRQ_1, true);
|
|
||||||
dma_channel_set_irq1_enabled(channel, true);
|
|
||||||
dma_irq1_handler_registered = true;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// Core 0 uses DMA IRQ0
|
dma_channel_set_irq0_enabled(channel, true);
|
||||||
if (!dma_irq0_handler_registered) {
|
|
||||||
irq_set_exclusive_handler(DMA_IRQ_0, dma_irq0_handler);
|
|
||||||
irq_set_enabled(DMA_IRQ_0, true);
|
|
||||||
dma_channel_set_irq0_enabled(channel, true);
|
|
||||||
dma_irq0_handler_registered = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
dmaDescriptors[index].irqHandlerCallback = callback;
|
dmaDescriptors[index].irqHandlerCallback = callback;
|
||||||
|
|
|
@ -61,7 +61,6 @@ typedef enum {DISABLE = 0, ENABLE = !DISABLE} FunctionalState;
|
||||||
//#define SystemCoreClock
|
//#define SystemCoreClock
|
||||||
//#define EXTI_TypeDef
|
//#define EXTI_TypeDef
|
||||||
//#define EXTI_InitTypeDef
|
//#define EXTI_InitTypeDef
|
||||||
//#define IRQn_Type void*
|
|
||||||
|
|
||||||
// We have to use SPI0_Type (or void) because config will pass in SPI0, SPI1,
|
// We have to use SPI0_Type (or void) because config will pass in SPI0, SPI1,
|
||||||
// which are defined in pico-sdk as SPI0_Type*.
|
// which are defined in pico-sdk as SPI0_Type*.
|
||||||
|
|
|
@ -53,6 +53,7 @@
|
||||||
#define USE_SPI
|
#define USE_SPI
|
||||||
#define USE_SPI_DEVICE_0
|
#define USE_SPI_DEVICE_0
|
||||||
#define USE_SPI_DEVICE_1
|
#define USE_SPI_DEVICE_1
|
||||||
|
#define USE_SPI_DMA_ENABLE_LATE
|
||||||
|
|
||||||
#define USE_I2C
|
#define USE_I2C
|
||||||
#define USE_I2C_DEVICE_0
|
#define USE_I2C_DEVICE_0
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue