I have a STM32f405 and the task is to transmit data over the SPI and save processor time with the DMA. The used SPI is SPI1 with pins PA4 to PA7. I've chosen for DMA the 3-rd stream from DMA2 channel 3. The idea is to activate the CS signal and store some data in the memory, which then to be transferred automatically by the DMA and once that's done DMA should trigger an interrupt handler to deactivate the CS. Here is the code:
static void SPI_Config(void) {
GPIO_InitTypeDef GPIO_InitStructure;
SPI_InitTypeDef SPI_InitStructure;
DMA_InitTypeDef DMA_Init_Structure;
NVIC_InitTypeDef NVIC_InitStructure;
/* Enable the SPI clock */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1,ENABLE);
/* Enable GPIO clocks */
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
/* Enable DMA clock */
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);
/* SPI GPIO Configuration --------------------------------------------------*/
/* GPIO Deinitialisation */
GPIO_DeInit(GPIOA);
/* Connect SPI pins to AF5 */
// GPIO_PinAFConfig(GPIOA, GPIO_PinSource4, GPIO_AF_SPI1); //SS
GPIO_PinAFConfig(GPIOA, GPIO_PinSource5, GPIO_AF_SPI1); //SCK
GPIO_PinAFConfig(GPIOA, GPIO_PinSource6, GPIO_AF_SPI1); //MISO
GPIO_PinAFConfig(GPIOA, GPIO_PinSource7, GPIO_AF_SPI1); //MOSI
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; //SCK
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6; //MISO
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7; //MOSI
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4; //SS
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOA, &GPIO_InitStructure);
//DMA Globul Interrupt
NVIC_InitStructure.NVIC_IRQChannel = DMA2_Stream3_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
//DMA Configuration
DMA_DeInit(DMA2_Stream3);
DMA_Cmd(DMA2_Stream3, DISABLE);
while (DMA1_Stream0->CR & DMA_SxCR_EN);
DMA_Init_Structure.DMA_BufferSize = 0;
DMA_Init_Structure.DMA_Channel = DMA_Channel_3;
DMA_Init_Structure.DMA_DIR = DMA_DIR_MemoryToPeripheral;
DMA_Init_Structure.DMA_FIFOMode = DMA_FIFOMode_Disable;
DMA_Init_Structure.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull;
DMA_Init_Structure.DMA_Memory0BaseAddr = (uint32_t)(&spi_tx_val);
DMA_Init_Structure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
DMA_Init_Structure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
DMA_Init_Structure.DMA_MemoryInc = DMA_MemoryInc_Disable;
DMA_Init_Structure.DMA_Mode = DMA_Mode_Circular;
DMA_Init_Structure.DMA_PeripheralBaseAddr = (uint32_t) (&(SPI1->DR));
DMA_Init_Structure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
DMA_Init_Structure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
DMA_Init_Structure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_Init_Structure.DMA_Priority = DMA_Priority_High;
DMA_Init(DMA2_Stream3,&DMA_Init_Structure);
//SPI Configuration
SPI_I2S_DeInit(SPI1);
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256;
SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;
SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low; //AD5620 doku page 18 falling edge of SCLK
SPI_InitStructure.SPI_CRCPolynomial = 0; //x_8+x_2+x_1+1 in python hex(2**8+2**2+2+1)
SPI_InitStructure.SPI_DataSize = SPI_DataSize_16b; //AD5620 input register is 16 bit
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
SPI_Init(SPI1, &SPI_InitStructure);
}
int8_t Analog_Out_Config(uint32_t target_reg_val) {
uint16_t power_on_status;
target_reg_val = target_reg_val;
SPI_Config();
// SPI_Cmd(SPI1, ENABLE);
// power_on_status=PowerOn_AD5750_OutDriver();
// if(power_on_status) {
//enable dma interrupt
// SPI_Cmd(SPI1, DISABLE);
DMA_ITConfig(DMA2_Stream3,DMA_IT_TC,ENABLE);
DMA_ClearFlag(DMA2_Stream3, DMA_FLAG_FEIF3|DMA_FLAG_DMEIF3|DMA_FLAG_TEIF3|DMA_FLAG_HTIF3|DMA_FLAG_TCIF3);
DMA_Cmd(DMA2_Stream3, ENABLE);
SPI_I2S_DMACmd(SPI1,SPI_I2S_DMAReq_Tx, ENABLE);
SPI_Cmd(SPI1, ENABLE);
return power_on_status&0x07;
// }else {
// return -1;
// }
}
void Analog_Output(uint32_t measured_reg_val) {
val=0x7ff;
ACTIVATE_CS_DAC();
spi_tx_val=val;
}
void DMA2_Stream3_IRQHandler(void) {
if(DMA_GetITStatus(DMA2_Stream3,DMA_IT_TCIF3)!=RESET) {
DMA_ClearITPendingBit(DMA2_Stream3,DMA_IT_TCIF0|DMA_IT_HTIF0);
DEACTIVATE_CS_DAC();
}
}
int main(void)
{
target_reg_val=14;
measured_reg_val=12;
Analog_Out_Config(target_reg_val);
while (1)
{
for(val=-target_reg_val;val<target_reg_val;val++) {
Analog_Output(val);
for(i=0;i<1000;i++);
}
}
}
With the debugger I've found out that the DMA2_Stream3_IRQHandler is never activated. According to the reference manual DMA should transfer the data when TXE flag from the SPI_DR register is 1, which was so. Also the flag TXDMAEN from the SPI_CR2 was set. I checked the DMA S3CR register too and the flags TCIE and EN were set as well. In addition the function DMA2_Stream3_IRQHandler in visible to the main function. Still DMA2_Stream3_IRQHandler was never activated.
UPDATE: when I manually reset the EN bit of DMA2_S3CR register then the DMA2_Stream3_IRQHandler is triggered. According to the reference manual this bit is cleared by the hardware:
- on a DMA end of transfer (stream ready to be configured)
- if a transfer error occurs on the AHB master buses
- when the FIFO threshold on memory AHB port is not compatible with the size of the burst
I've also changed the SPI_Config and the Analog_Out_Config, but still without intervention with the debugger DMA2_Stream3_IRQHandler is never triggered. It looks like that the DMA is not triggering the transfer and cannot end it for some reason. How can I find out if DMA has trigger any transfer?
SPI_Config
where you setup your DMA. If this is actually the case, that is probably why it doesn't work. – rjp