DMA interrupts
In the stm32f103c8, each DMA channel can generate 3 types of interrupts:
Data transfer complete
Half data transfer completed
Data transfer error (occurs when a reserved data area is accessed)
For each channel the required interrupts can be enabled individually in the DMA_CCRx channel configuration register. The bits TCIE (transfer complete), HTIE (half buffer transferred) and TEIE (transfer error) are responsible for this:
In addition, there are 2 more special registers, one of which contains information about active interrupt flags of all DMA channels (DMA_ISR register), and the other one can be used to reset the desired interrupt flags of selected channels (DMA_IFCR register):
Using DMA interrupts
Let's move on to practice. As a basis, let's take the code from the article STM32 Programming. Part 10: SPI + DMA, where we sent an array of data to SPI via DMA. SPI initialization is done in the same way, but we will add 3 lines to the end:
Code: Select all
void SPIInit(void)
{
RCC->APB2ENR |= RCC_APB2ENR_SPI1EN; //Enable SPI1 clocking
RCC->APB2ENR |= RCC_APB2ENR_IOPAEN; //enable GPIOA port clocking
RCC->AHBENR |= RCC_AHBENR_DMA1EN; //Enable DMA1 clocking
//Customize GPIO
//PA7 - MOSI
//PA6 - MISO
//PA5 - SCK
//First reset all configuration bits to zeros
GPIOA->CRL &= ~(GPIO_CRL_CNF5_Msk | GPIO_CRL_MODE5_Msk
| GPIO_CRL_CNF6_Msk | GPIO_CRL_MODE6_Msk
| GPIO_CRL_CNF7_Msk | GPIO_CRL_MODE7_Msk);
//Customize
//SCK: MODE5 = 0x03 (11b); CNF5 = 0x02 (10b)
GPIOA->CRL |= (0x02<<GPIO_CRL_CNF5_Pos) | (0x03<<GPIO_CRL_MODE5_Pos);
//MISO: MODE6 = 0x00 (00b); CNF6 = 0x01 (01b)
GPIOA->CRL |= (0x01<<GPIO_CRL_CNF6_Pos) | (0x00<<GPIO_CRL_MODE6_Pos);
//MOSI: MODE7 = 0x03 (11b); CNF7 = 0x02 (10b)
GPIOA->CRL |= (0x02<<GPIO_CRL_CNF7_Pos) | (0x03<<GPIO_CRL_MODE7_Pos);
//Set SPI
SPI1->CR1 = 0<<SPI_CR1_DFF_Pos //Frame size 8 bits
| 0<<SPI_CR1_LSBFIRST_Pos //MSB first
| 1<<SPI_CR1_SSM_Pos //SSS program control
| 1<<SPI_CR1_SSI_Pos //SS in high state
| 0x04<<SPI_CR1_BR_Pos //Baud rate: F_PCLK/32
| 1<<SPI_CR1_MSTR_Pos //Master mode (master)
| 0<<SPI_CR1_CPOL_Pos | 0<<SPI_CR1_CPHA_Pos; // SPI operating mode: 0
SPI1->CR2 |= 1<<SPI_CR2_TXDMAEN_Pos;
SPI1->CR2 |= 1<<SPI_CR2_RXDMAEN_Pos;
SPI1->CR1 |= 1<<SPI_CR1_SPE_Pos; //Enable SPI
DMA1->IFCR = 1<<DMA_IFCR_CTCIF3_Pos; //reset interrupt flag
NVIC_EnableIRQ(DMA1_Channel3_IRQn); //enable interrupts
//from DMA1 channel 3
}
Code: Select all
DMA1->IFCR = 1<<DMA_IFCR_CTCIF3_Pos; //reset the interrupt flag
Code: Select all
NVIC_EnableIRQ(DMA1_Channel3_IRQn); //enable interrupts
//from DMA1 channel 3
Code: Select all
void SPI_Send(uint8_t *data, uint16_t len)
{
//disable the DMA channel after the previous data transfer
DMA1_Channel3->CCR &= ~(1 << DMA_CCR_EN_Pos);
DMA1_Channel3->CPAR = (uint32_t)(&SPI1->DR); //add DR register address to CPAR
DMA1_Channel3->CMAR = (uint32_t)data; //add data address to CMAR register
DMA1_Channel3->CNDTR = len; //number of transmitted data
//DMA channel setup
DMA1_Channel3->CCR = 0 << DMA_CCR_MEM2MEM_Pos //MEM2MEM mode disabled
| 0x00 << DMA_CCR_PL_Pos //priority low
| 0x00 << DMA_CCR_MSIZE_Pos //memory data size 8 bits
| 0x01 << DMA_CCR_PSIZE_Pos //data register size 16 bits
| 1 << DMA_CCR_MINC_Pos // Enable increment of memory address
| 0 << DMA_CCR_PINC_Pos // Peripheral address increment disabled
| 0 << DMA_CCR_CIRC_Pos //ring mode disabled
| 1 << DMA_CCR_DIR_Pos //1 - from memory to periphery
| 1 << DMA_CCR_TCIE_Pos; //Interrupt at transfer completion
DMA1_Channel3->CCR |= 1 << DMA_CCR_EN_Pos; //enable data transfer
}
Code: Select all
1 << DMA_CCR_TCIE_Pos; //Interrupt on completion of transfer
The interrupt handler remains. It will look like this:
Code: Select all
void DMA1_Channel3_IRQHandler(void)
{
DMA1->IFCR = 1<<DMA_IFCR_CTCIF3_Pos; //reset interrupt flag
//add processing code
//...
}
With this line we reset the interrupt flag about the end of data transfer in DMA channel 3. Without this line, the DMA1_Channel3_IRQHandler() interrupt will be called an infinite number of times, which will stall the execution of the main program of the MCU.
To check this main():
Code: Select all
uint8_t data[10];
void main()
{
for(int i=0; i<sizeof(data); i++)
{
data[i] = i+1;
}
SPIInit();
SPI_Send(data, sizeof(data));
for(;;)
{
}
}