#include "spi_dma.h" #include "ch32v003fun.h" #include "debug.h" volatile transfer_state_t tx_state = IDLE; volatile transfer_state_t rx_state = IDLE; static const uint8_t tx_dummy_byte = 0xFF; static uint8_t rx_dummy; // Static RX dummy buffer static inline void wait_for_transfer_complete(void) { while (tx_state != TX_DONE || rx_state != RX_DONE); } // static inline void spi_wait_not_busy(void) { // while ((SPI1->STATR & SPI_STATR_BSY) != 0); // } void set_transfer_states(void) { tx_state = TRANSMITTING; rx_state = RECEIVING; } void clear_transfer_states(void) { tx_state = IDLE; rx_state = IDLE; } void DMA1_HandleIrq(uint32_t channel_interrupt, volatile transfer_state_t* state, transfer_state_t active_state, transfer_state_t done_state) { if (DMA1->INTFR & channel_interrupt) { // check if DMA interrupt occurred DMA1->INTFCR = channel_interrupt; // clear the interrupt flag if (*state == active_state) { *state = done_state; } } } void DMA1_Channel3_IRQHandler(void) __attribute__((interrupt)); void DMA1_Channel2_IRQHandler(void) __attribute__((interrupt)); void DMA1_Channel3_IRQHandler(void) { DMA1_HandleIrq(DMA1_IT_TC3, &tx_state, TRANSMITTING, TX_DONE); } void DMA1_Channel2_IRQHandler(void) { DMA1_HandleIrq(DMA1_IT_TC2, &rx_state, RECEIVING, RX_DONE); } void configure_dma(DMA_Channel_TypeDef* tx_channel, uint32_t tx_addr, DMA_Channel_TypeDef* rx_channel, uint32_t rx_addr, int rx_circular, uint16_t len) { // disable DMA channels tx_channel->CFGR &= ~DMA_CFGR1_EN; rx_channel->CFGR &= ~DMA_CFGR1_EN; // set memory addresses and transfer count tx_channel->MADDR = tx_addr; tx_channel->CNTR = len; rx_channel->MADDR = rx_addr; rx_channel->CNTR = len; // set or clear the circular mode for RX rx_channel->CFGR = (rx_channel->CFGR & ~DMA_CFGR1_CIRC) | (rx_circular ? DMA_CFGR1_CIRC : 0); // enable DMA channels tx_channel->CFGR |= DMA_CFGR1_EN; rx_channel->CFGR |= DMA_CFGR1_EN; } void spidma_read_buffer(uint8_t* buf, uint16_t len) { set_transfer_states(); configure_dma(DMA1_Channel3, (uint32_t)&tx_dummy_byte, DMA1_Channel2, (uint32_t)buf, 0, len); wait_for_transfer_complete(); clear_transfer_states(); } void spidma_write_buffer(uint8_t* buf, uint16_t len) { set_transfer_states(); configure_dma(DMA1_Channel3, (uint32_t)buf, DMA1_Channel2, (uint32_t)rx_dummy, 1, len); wait_for_transfer_complete(); clear_transfer_states(); } uint8_t spi_transfer(uint8_t data) { while (!(SPI1->STATR & SPI_STATR_TXE)); SPI1->DATAR = data; while (!(SPI1->STATR & SPI_STATR_RXNE)); return SPI1->DATAR; } uint8_t spi_read_byte() { return spi_transfer(tx_dummy_byte); } void spi_write_byte(uint8_t byte) { spi_transfer(byte); } void spi_select(void) { GPIOA->BCR = (1 << 4); // Set PA4 (CS) low } void spi_unselect(void) { GPIOA->BSHR = (1 << 4); // Set PA4 (CS) high } void init_spidma(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, open-drain GPIOA->CFGLR &= ~(0xf << (4 * 4)); GPIOA->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_OD) << (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); // MOSI on PA7, 10MHz input, floating GPIOA->CFGLR &= ~(0xf << (4 * 6)); GPIOA->CFGLR |= (GPIO_CNF_IN_FLOATING) << (4 * 6); // MISO on PA6, 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_CPOL_Low | // Clock polarity SPI_CPHA_1Edge | // Clock phase SPI_BaudRatePrescaler_8 | // 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 DMA1_Channel3->PADDR = (uint32_t)&SPI1->DATAR; // TX Channel DMA1_Channel3->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 | DMA_IT_TC; DMA1_Channel2->PADDR = (uint32_t)&SPI1->DATAR; // RX Channel DMA1_Channel2->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 | DMA_IT_TC; NVIC_SetPriority(DMA1_Channel2_IRQn, 0); NVIC_SetPriority(DMA1_Channel3_IRQn, 0); NVIC_EnableIRQ(DMA1_Channel3_IRQn); NVIC_EnableIRQ(DMA1_Channel2_IRQn); }