/* Arduino SdSpi Library
* Copyright (C) 2013 by William Greiman
*
* STM32F1 code for Maple and Maple Mini support, 2015 by Victor Perez
*
* This file is part of the Arduino SdSpi Library
*
* This Library is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This Library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with the Arduino SdSpi Library. If not, see
* .
*/
#if defined(__STM32F1__)
#include "SdSpi.h"
#include
/** Use STM32 DMAC if nonzero */
#define USE_STM32F1_DMAC 1
/** Time in ms for DMA receive timeout */
#define STM32F1_DMA_TIMEOUT 100
/** DMAC receive channel */
#define SPI1_DMAC_RX_CH DMA_CH2
/** DMAC transmit channel */
#define SPI1_DMAC_TX_CH DMA_CH3
volatile bool SPI_DMA_TX_Active = false;
volatile bool SPI_DMA_RX_Active = false;
/** ISR for DMA TX event. */
inline void SPI_DMA_TX_Event() {
SPI_DMA_TX_Active = false;
dma_disable(DMA1, SPI_DMAC_TX_CH);
}
/** ISR for DMA RX event. */
inline void SPI_DMA_RX_Event() {
SPI_DMA_RX_Active = false;
dma_disable(DMA1, SPI1_DMAC_RX_CH);
}
//------------------------------------------------------------------------------
/** Disable DMA Channel. */
static void dmac_channel_disable(dma_channel ul_num) {
dma_disable(DMA1, ul_num);
}
/** Enable DMA Channel. */
static void dmac_channel_enable(dma_channel ul_num) {
dma_enable(DMA1, ul_num);
}
//------------------------------------------------------------------------------
void SdSpi::begin(uint8_t chipSelectPin) {
pinMode(chipSelectPin, OUTPUT);
digitalWrite(chipSelectPin, HIGH);
SPI.begin();
}
//------------------------------------------------------------------------------
// start RX DMA
static void spiDmaRX(uint8_t* dst, uint16_t count) {
// spi_rx_dma_enable(SPI1);
if (count < 1) return;
dma_setup_transfer(DMA1, SPI1_DMAC_RX_CH, &SPI1->regs->DR, DMA_SIZE_8BITS,
dst, DMA_SIZE_8BITS, (DMA_MINC_MODE | DMA_TRNS_CMPLT));
dma_set_num_transfers(DMA1, SPI1_DMAC_RX_CH, count); // 2 bytes per pixel
SPI_DMA_RX_Active = true;
dma_enable(DMA1, SPI1_DMAC_RX_CH);
}
//------------------------------------------------------------------------------
// start TX DMA
static void spiDmaTX(const uint8_t* src, uint16_t count) {
if (count < 1) return;
static uint8_t ff = 0XFF;
if (!src) {
src = &ff;
dma_setup_transfer(DMA1, SPI1_DMAC_TX_CH, &SPI1->regs->DR, DMA_SIZE_8BITS,
const_cast(src), DMA_SIZE_8BITS,
(DMA_FROM_MEM | DMA_TRNS_CMPLT));
} else {
dma_setup_transfer(DMA1, SPI1_DMAC_TX_CH, &SPI1->regs->DR, DMA_SIZE_8BITS,
const_cast(src), DMA_SIZE_8BITS,
(DMA_MINC_MODE | DMA_FROM_MEM | DMA_TRNS_CMPLT));
}
dma_set_num_transfers(DMA1, SPI1_DMAC_TX_CH, count); // 2 bytes per pixel
SPI_DMA_TX_Active = true;
dma_enable(DMA1, SPI1_DMAC_TX_CH);
}
//------------------------------------------------------------------------------
// initialize SPI controller STM32F1
void SdSpi::beginTransaction(uint8_t sckDivisor) {
#if ENABLE_SPI_TRANSACTIONS
SPI.beginTransaction(SPISettings());
#endif // ENABLE_SPI_TRANSACTIONS
if (sckDivisor < SPI_CLOCK_DIV2 || sckDivisor > SPI_CLOCK_DIV256) {
sckDivisor = SPI_CLOCK_DIV2; // may not be needed, testing.
}
SPI.setClockDivider(sckDivisor);
SPI.setBitOrder(MSBFIRST);
SPI.setDataMode(SPI_MODE0);
#if USE_STM32F1_DMAC
dma_init(DMA1);
dma_attach_interrupt(DMA1, SPI1_DMAC_TX_CH, SPI_DMA_TX_Event);
dma_attach_interrupt(DMA1, SPI1_DMAC_RX_CH, SPI_DMA_RX_Event);
spi_tx_dma_enable(SPI1);
spi_rx_dma_enable(SPI1);
#endif // USE_STM32F1_DMAC
}
//------------------------------------------------------------------------------
void SdSpi::endTransaction() {
#if ENABLE_SPI_TRANSACTIONS
SPI.endTransaction();
#endif // ENABLE_SPI_TRANSACTIONS
}
//------------------------------------------------------------------------------
// STM32
static inline uint8_t spiTransfer(uint8_t b) {
return SPI.transfer(b);
}
//------------------------------------------------------------------------------
// should be valid for STM32
/** SPI receive a byte */
uint8_t SdSpi::receive() {
return spiTransfer(0xFF);
}
//------------------------------------------------------------------------------
/** SPI receive multiple bytes */
// check and finish.
uint8_t SdSpi::receive(uint8_t* buf, size_t n) {
int rtn = 0;
#if USE_STM32F1_DMAC
spiDmaRX(buf, n);
spiDmaTX(0, n);
uint32_t m = millis();
while (SPI_DMA_RX_Active) {
if ((millis() - m) > STM32F1_DMA_TIMEOUT) {
dmac_channel_disable(SPI_DMAC_RX_CH);
dmac_channel_disable(SPI_DMAC_TX_CH);
rtn = 2;
break;
}
}
#else // USE_STM32F1_DMAC
for (size_t i = 0; i < n; i++) {
buf[i] = SPI.transfer(0xFF);
}
#endif // USE_STM32F1_DMAC
return rtn;
}
//------------------------------------------------------------------------------
/** SPI send a byte */
void SdSpi::send(uint8_t b) {
spiTransfer(b);
}
//------------------------------------------------------------------------------
void SdSpi::send(const uint8_t* buf , size_t n) {
#if USE_STM32F1_DMAC
spiDmaTX(buf, n);
while (SPI_DMA_TX_Active) {}
#else // #if USE_STM32F1_DMAC
SPI.write(buf, n);
#endif // #if USE_STM32F1_DMAC
// leave RX register empty
// while (spi_is_rx_nonempty(SPI1))
uint8_t b = spi_rx_reg(SPI1);
}
#endif // USE_NATIVE_STM32F1_SPI