/* 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