feat: SPI DMA
Squashed commit of the following: commit c3e1f696b4fbafb6dd30c6934e4c7c181562e055 Author: kuwoyuki <kuwoyuki@cock.li> Date: Sat Oct 12 20:43:12 2024 +0600 chore: spi dma commit d1e7df60be3e06ed85ce8639516e299085d3c72b Author: kuwoyuki <kuwoyuki@cock.li> Date: Sat Oct 12 15:14:11 2024 +0600 static dummies commit 81682428b471f825e3350e37aa74c373b46d4fef Author: kuwoyuki <kuwoyuki@cock.li> Date: Sat Oct 12 14:45:39 2024 +0600 dma works? commit 150c97a4b566712a502d8a2861a2dc0864324d61 Author: kuwoyuki <kuwoyuki@cock.li> Date: Sat Oct 12 02:07:39 2024 +0600 dma?
This commit is contained in:
163
src/spi_dma.c
Normal file
163
src/spi_dma.c
Normal file
@@ -0,0 +1,163 @@
|
||||
#include "spi_dma.h"
|
||||
|
||||
#include "ch32v003fun.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_EnableIRQ(DMA1_Channel3_IRQn);
|
||||
NVIC_EnableIRQ(DMA1_Channel2_IRQn);
|
||||
}
|
||||
Reference in New Issue
Block a user