| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266 |
- // Driver and interface for accessing SD card in SPI mode
- // Used on AzulSCSI v1.0.
- #include "AzulSCSI_platform.h"
- #include "AzulSCSI_log.h"
- #include "gd32f20x_spi.h"
- #include "gd32f20x_dma.h"
- #include <SdFat.h>
- #ifndef SD_USE_SDIO
- class GD32SPIDriver : public SdSpiBaseClass
- {
- public:
- void begin(SdSpiConfig config) {
- rcu_periph_clock_enable(RCU_SPI0);
- rcu_periph_clock_enable(RCU_DMA0);
- dma_parameter_struct rx_dma_config =
- {
- .periph_addr = (uint32_t)&SPI_DATA(SD_SPI),
- .periph_width = DMA_PERIPHERAL_WIDTH_8BIT,
- .memory_addr = 0, // Set before transfer
- .memory_width = DMA_MEMORY_WIDTH_8BIT,
- .number = 0, // Set before transfer
- .priority = DMA_PRIORITY_ULTRA_HIGH,
- .periph_inc = DMA_PERIPH_INCREASE_DISABLE,
- .memory_inc = DMA_MEMORY_INCREASE_ENABLE,
- .direction = DMA_PERIPHERAL_TO_MEMORY
- };
- dma_init(DMA0, SD_SPI_RX_DMA_CHANNEL, &rx_dma_config);
- dma_parameter_struct tx_dma_config =
- {
- .periph_addr = (uint32_t)&SPI_DATA(SD_SPI),
- .periph_width = DMA_PERIPHERAL_WIDTH_8BIT,
- .memory_addr = 0, // Set before transfer
- .memory_width = DMA_MEMORY_WIDTH_8BIT,
- .number = 0, // Set before transfer
- .priority = DMA_PRIORITY_HIGH,
- .periph_inc = DMA_PERIPH_INCREASE_DISABLE,
- .memory_inc = DMA_MEMORY_INCREASE_ENABLE,
- .direction = DMA_MEMORY_TO_PERIPHERAL
- };
- dma_init(DMA0, SD_SPI_TX_DMA_CHANNEL, &tx_dma_config);
- }
-
- void activate() {
- spi_parameter_struct config = {
- SPI_MASTER,
- SPI_TRANSMODE_FULLDUPLEX,
- SPI_FRAMESIZE_8BIT,
- SPI_NSS_SOFT,
- SPI_ENDIAN_MSB,
- SPI_CK_PL_LOW_PH_1EDGE,
- SPI_PSC_256
- };
- // Select closest available divider based on system frequency
- int divider = (SystemCoreClock + m_sckfreq / 2) / m_sckfreq;
- if (divider <= 2)
- config.prescale = SPI_PSC_2;
- else if (divider <= 4)
- config.prescale = SPI_PSC_4;
- else if (divider <= 8)
- config.prescale = SPI_PSC_8;
- else if (divider <= 16)
- config.prescale = SPI_PSC_16;
- else if (divider <= 32)
- config.prescale = SPI_PSC_32;
- else if (divider <= 64)
- config.prescale = SPI_PSC_64;
- else if (divider <= 128)
- config.prescale = SPI_PSC_128;
- else
- config.prescale = SPI_PSC_256;
- spi_init(SD_SPI, &config);
- spi_enable(SD_SPI);
- }
-
- void deactivate() {
- spi_disable(SD_SPI);
- }
- void wait_idle() {
- while (!(SPI_STAT(SD_SPI) & SPI_STAT_TBE));
- while (SPI_STAT(SD_SPI) & SPI_STAT_TRANS);
- }
- // Single byte receive
- uint8_t receive() {
- // Wait for idle and clear RX buffer
- wait_idle();
- (void)SPI_DATA(SD_SPI);
- // Send dummy byte and wait for receive
- SPI_DATA(SD_SPI) = 0xFF;
- while (!(SPI_STAT(SD_SPI) & SPI_STAT_RBNE));
- return SPI_DATA(SD_SPI);
- }
- // Single byte send
- void send(uint8_t data) {
- SPI_DATA(SD_SPI) = data;
- wait_idle();
- }
- // Multiple byte receive
- uint8_t receive(uint8_t* buf, size_t count)
- {
- // Wait for idle and clear RX buffer
- wait_idle();
- (void)SPI_DATA(SD_SPI);
- // Check if this is part of callback streaming request
- bool stream = false;
- if (m_stream_callback && buf == m_stream_buffer + m_stream_count)
- {
- stream = true;
- }
- else if (m_stream_callback)
- {
- azdbg("Stream buffer mismatch: ", (uint32_t)buf, " vs. ", (uint32_t)(m_stream_buffer + m_stream_count));
- }
- // Use DMA to stream dummy TX data and store RX data
- uint8_t tx_data = 0xFF;
- DMA_INTC(DMA0) = DMA_FLAG_ADD(DMA_FLAG_FTF | DMA_FLAG_ERR, SD_SPI_RX_DMA_CHANNEL);
- DMA_INTC(DMA0) = DMA_FLAG_ADD(DMA_FLAG_FTF | DMA_FLAG_ERR, SD_SPI_TX_DMA_CHANNEL);
- DMA_CHMADDR(DMA0, SD_SPI_RX_DMA_CHANNEL) = (uint32_t)buf;
- DMA_CHMADDR(DMA0, SD_SPI_TX_DMA_CHANNEL) = (uint32_t)&tx_data;
- DMA_CHCTL(DMA0, SD_SPI_TX_DMA_CHANNEL) &= ~DMA_CHXCTL_MNAGA; // No memory increment for TX
- DMA_CHCNT(DMA0, SD_SPI_RX_DMA_CHANNEL) = count;
- DMA_CHCNT(DMA0, SD_SPI_TX_DMA_CHANNEL) = count;
- DMA_CHCTL(DMA0, SD_SPI_RX_DMA_CHANNEL) |= DMA_CHXCTL_CHEN;
- DMA_CHCTL(DMA0, SD_SPI_TX_DMA_CHANNEL) |= DMA_CHXCTL_CHEN;
- SPI_CTL1(SD_SPI) |= SPI_CTL1_DMAREN | SPI_CTL1_DMATEN;
-
- uint32_t start = millis();
- while (!(DMA_INTF(DMA0) & DMA_FLAG_ADD(DMA_FLAG_FTF | DMA_FLAG_ERR, SD_SPI_RX_DMA_CHANNEL)))
- {
- if (millis() - start > 500)
- {
- azlog("ERROR: SPI DMA receive of ", (int)count, " bytes timeouted");
- return 1;
- }
- if (stream)
- {
- uint32_t complete = m_stream_count + (count - DMA_CHCNT(DMA0, SD_SPI_RX_DMA_CHANNEL));
- m_stream_callback(complete);
- }
- }
- if (DMA_INTF(DMA0) & DMA_FLAG_ADD(DMA_FLAG_ERR, SD_SPI_RX_DMA_CHANNEL))
- {
- azlog("ERROR: SPI DMA receive set DMA_FLAG_ERR");
- }
- SPI_CTL1(SD_SPI) &= ~(SPI_CTL1_DMAREN | SPI_CTL1_DMATEN);
- DMA_CHCTL(DMA0, SD_SPI_RX_DMA_CHANNEL) &= ~DMA_CHXCTL_CHEN;
- DMA_CHCTL(DMA0, SD_SPI_TX_DMA_CHANNEL) &= ~DMA_CHXCTL_CHEN;
- if (stream)
- {
- m_stream_count += count;
- }
- return 0;
- }
- // Multiple byte send
- void send(const uint8_t* buf, size_t count) {
- // Check if this is part of callback streaming request
- bool stream = false;
- if (m_stream_callback && buf == m_stream_buffer + m_stream_count)
- {
- stream = true;
- }
- else if (m_stream_callback)
- {
- azdbg("Stream buffer mismatch: ", (uint32_t)buf, " vs. ", (uint32_t)(m_stream_buffer + m_stream_count));
- }
- // Use DMA to stream TX data
- DMA_INTC(DMA0) = DMA_FLAG_ADD(DMA_FLAG_FTF | DMA_FLAG_ERR, SD_SPI_TX_DMA_CHANNEL);
- DMA_CHMADDR(DMA0, SD_SPI_TX_DMA_CHANNEL) = (uint32_t)buf;
- DMA_CHCTL(DMA0, SD_SPI_TX_DMA_CHANNEL) |= DMA_CHXCTL_MNAGA; // Memory increment for TX
- DMA_CHCNT(DMA0, SD_SPI_TX_DMA_CHANNEL) = count;
- DMA_CHCTL(DMA0, SD_SPI_TX_DMA_CHANNEL) |= DMA_CHXCTL_CHEN;
- SPI_CTL1(SD_SPI) |= SPI_CTL1_DMATEN;
-
- uint32_t start = millis();
- while (!(DMA_INTF(DMA0) & DMA_FLAG_ADD(DMA_FLAG_FTF | DMA_FLAG_ERR, SD_SPI_TX_DMA_CHANNEL)))
- {
- if (millis() - start > 500)
- {
- azlog("ERROR: SPI DMA transmit of ", (int)count, " bytes timeouted");
- return;
- }
- if (stream)
- {
- uint32_t complete = m_stream_count + (count - DMA_CHCNT(DMA0, SD_SPI_TX_DMA_CHANNEL));
- m_stream_callback(complete);
- }
- }
- if (DMA_INTF(DMA0) & DMA_FLAG_ADD(DMA_FLAG_ERR, SD_SPI_TX_DMA_CHANNEL))
- {
- azlog("ERROR: SPI DMA transmit set DMA_FLAG_ERR");
- }
- wait_idle();
- SPI_CTL1(SD_SPI) &= ~(SPI_CTL1_DMAREN | SPI_CTL1_DMATEN);
- DMA_CHCTL(DMA0, SD_SPI_TX_DMA_CHANNEL) &= ~DMA_CHXCTL_CHEN;
- if (stream)
- {
- m_stream_count += count;
- }
- }
- void setSckSpeed(uint32_t maxSck) {
- m_sckfreq = maxSck;
- }
- void set_sd_callback(sd_callback_t func, const uint8_t *buffer)
- {
- m_stream_buffer = buffer;
- m_stream_count = 0;
- m_stream_callback = func;
- }
- private:
- uint32_t m_sckfreq;
- const uint8_t *m_stream_buffer;
- uint32_t m_stream_count;
- sd_callback_t m_stream_callback;
- };
- void sdCsInit(SdCsPin_t pin)
- {
- }
- void sdCsWrite(SdCsPin_t pin, bool level)
- {
- if (level)
- GPIO_BOP(SD_PORT) = SD_CS_PIN;
- else
- GPIO_BC(SD_PORT) = SD_CS_PIN;
- }
- GD32SPIDriver g_sd_spi_port;
- SdSpiConfig g_sd_spi_config(0, DEDICATED_SPI, SD_SCK_MHZ(30), &g_sd_spi_port);
- void azplatform_set_sd_callback(sd_callback_t func, const uint8_t *buffer)
- {
- g_sd_spi_port.set_sd_callback(func, buffer);
- }
- #endif
|