#include "spi_dma.h" #include "ch32v003fun.h" #include "debug.h" #define RX_Channel DMA1_Channel2 #define TX_Channel DMA1_Channel3 static const uint8_t tx_dummy_byte = 0xFF; static uint8_t rx_dummy; void spi_select(void) { GPIOA->BCR = (1 << 4); // Set PA4 (CS) low } void spi_unselect(void) { GPIOA->BSHR = (1 << 4); // Set PA4 (CS) high } // SPI DMA void spidma_read_buffer(uint8_t* buf, uint16_t len) { // tx TX_Channel->MADDR = (uint32_t)&tx_dummy_byte; TX_Channel->CNTR = len; TX_Channel->CFGR &= ~DMA_MemoryInc_Enable; // rx RX_Channel->MADDR = (uint32_t)buf; RX_Channel->CNTR = len; RX_Channel->CFGR |= DMA_MemoryInc_Enable; // enable RX_Channel->CFGR |= DMA_CFGR1_EN; TX_Channel->CFGR |= DMA_CFGR1_EN; while (!(DMA1->INTFR & DMA1_FLAG_TC2) && !(DMA1->INTFR & DMA1_FLAG_TC3)); /** * In transmission mode, when the DMA has written all the data to be * transmitted (flag TCIF is set in the DMA_ISR register), the BSY flag can be * monitored to ensure that the SPI communication is complete. This is * required to avoid corrupting the last transmission before disabling the SPI * or entering the Stop mode. The software must first wait until TXE=1 and * then until BSY=0. */ while (!(SPI1->STATR & SPI_STATR_TXE)); while (SPI1->STATR & SPI_I2S_FLAG_BSY); // clear intfr DMA1->INTFCR |= DMA1_FLAG_TC2 | DMA1_FLAG_TC3; RX_Channel->CFGR &= ~DMA_CFGR1_EN; TX_Channel->CFGR &= ~DMA_CFGR1_EN; } void spidma_write_buffer(uint8_t* buf, uint16_t len) { // tx TX_Channel->MADDR = (uint32_t)buf; TX_Channel->CNTR = len; TX_Channel->CFGR |= DMA_MemoryInc_Enable; // rx RX_Channel->MADDR = (uint32_t)&rx_dummy; RX_Channel->CNTR = len; RX_Channel->CFGR &= ~DMA_MemoryInc_Enable; // enable TX_Channel->CFGR |= DMA_CFGR1_EN; RX_Channel->CFGR |= DMA_CFGR1_EN; while (!(DMA1->INTFR & DMA1_FLAG_TC2) && !(DMA1->INTFR & DMA1_FLAG_TC3)); // wait for SPI to complete: TXE = 1, then BSY = 0 while (!(SPI1->STATR & SPI_STATR_TXE)); while (SPI1->STATR & SPI_I2S_FLAG_BSY); // clear intfr DMA1->INTFCR |= DMA1_FLAG_TC2 | DMA1_FLAG_TC3; RX_Channel->CFGR &= ~DMA_CFGR1_EN; TX_Channel->CFGR &= ~DMA_CFGR1_EN; } void spidma_init(void) { // Enable clock for GPIOA and SPI1 RCC->APB2PCENR |= RCC_APB2Periph_GPIOA | RCC_APB2Periph_SPI1; // Enable clock for DMA1 RCC->AHBPCENR |= RCC_AHBPeriph_DMA1; // SPI1 Pin Configuration // CS on PA4, 10MHz Output, push-pull GPIOA->CFGLR &= ~(0xf << (4 * 4)); GPIOA->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_PP) << (4 * 4); // SCK on PA5, 10MHz Output, alt func, push-pull GPIOA->CFGLR &= ~(0xf << (4 * 5)); GPIOA->CFGLR |= (GPIO_Speed_50MHz | GPIO_CNF_OUT_PP_AF) << (4 * 5); // MISO on PA6, 10MHz input, floating GPIOA->CFGLR &= ~(0xf << (4 * 6)); GPIOA->CFGLR |= (GPIO_CNF_IN_FLOATING) << (4 * 6); // MOSI on PA7, 10MHz Output, alt func, push-pull GPIOA->CFGLR &= ~(0xf << (4 * 7)); GPIOA->CFGLR |= (GPIO_Speed_50MHz | GPIO_CNF_OUT_PP_AF) << (4 * 7); // Reset and Configure SPI1 SPI1->CTLR1 = 0; // Clear CTLR1 initially SPI1->CTLR1 = SPI_Mode_Master | // Master mode SPI_Direction_2Lines_FullDuplex | // Full duplex SPI_DataSize_8b | // 8-bit data frame format SPI_CPHA_1Edge | // Clock polarity SPI_CPOL_Low | // Clock phase SPI_BaudRatePrescaler_64 | // Baud rate prescaler SPI_NSS_Soft; // Software NSS management // Enable TX and RX DMA SPI1->CTLR2 = SPI_CTLR2_TXDMAEN | SPI_CTLR2_RXDMAEN; SPI1->I2SCFGR &= SPI_Mode_Select; // Disable I2S mode SPI1->CTLR1 |= SPI_CTLR1_SPE; // Enable SPI // DMA setup TX_Channel->PADDR = (uint32_t)&SPI1->DATAR; // TX Channel TX_Channel->CFGR = DMA_M2M_Disable | DMA_Priority_VeryHigh | DMA_MemoryDataSize_Byte | DMA_PeripheralDataSize_Byte | DMA_MemoryInc_Enable | DMA_PeripheralInc_Disable | DMA_Mode_Normal | DMA_DIR_PeripheralDST; RX_Channel->PADDR = (uint32_t)&SPI1->DATAR; // RX Channel RX_Channel->CFGR = DMA_M2M_Disable | DMA_Priority_VeryHigh | DMA_MemoryDataSize_Byte | DMA_PeripheralDataSize_Byte | DMA_MemoryInc_Enable | DMA_PeripheralInc_Disable | DMA_Mode_Normal | DMA_DIR_PeripheralSRC; }