0
votes

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?

1
TL;DR, but as a note, the DMA has to be enabled in the peripheral before the actual transfer is started. One aspect is that you cannot (easily) mix CPU and DMA transfers. (That is also true for e.g. the USART).too honest for this site
I'm assuming you didn't put all the code here, but in the question, you are not calling SPI_Config where you setup your DMA. If this is actually the case, that is probably why it doesn't work.rjp
@rjp I'm using SPI_Config to set up the SPI including the DMA dedicated to to SPI transmission. But I'm enabling the DMA with Analog_Out_Config function. I have done that before for other parts of the system such as ADC and it's never been a problem.judoka_acl
@rjp yes you are right there I didn't post part of the code from the Analog_Out_Config function. After the comment of Olaf I think now that it might be relevant to the problem.judoka_acl

1 Answers

0
votes

You have your DMA configuration structure setting DMA_Mode to DMA_NORMAL. It likely should be set to DMA_PFCTRL which is peripheral flow control. This will cause the DMA to wait for the signal from the peripheral (which you have configured) rather than run continuously. I would expect, though, that without this setting, you would get one or two words coming out the SPI bus, as SPI_DR should get all the memory setup in the DMA transfer dumped on it consecutively (as it is shifting out the first word).

Another thing to check is that none of your peripherals (SPI1, DMA2) are in reset. I see that you disable the clock, but I don't remember if ST also takes the peripheral out of reset on that call.

Note: I used the STM32F2xx DMA peripheral as a reference, but the STM32F4xx peripherals tend to be supersets. It also looks like you're using a different version of the ST Peripheral Library than what I used as a reference.