0
\$\begingroup\$

I am continuously sending a substantial amount of data between the STM32L432 and a FPGA using SPI in full-duplex mode. I decided to implement DMA on the L432 in order to increase the transfer rate between the two devices.

When I start the communication between the two, the L432 locks up and remains unresponsive. I have a LED in the interrupt service routines for testing purposes which is to be toggled when the ISR is being serviced but the program does not reach that point. I have been trying to solve this problem for days now and have no idea what is causing this to happen.

This is how I set up the SPI and the DMA:

#include "mbed.h"
#include "DMA_SPI.h"

int16_t data_to_transmit[10];
int16_t received_data[10];
DigitalOut myled(LED1);

void SPI_DMA_init() {
    myled = 1;                                                                  //toggle LED on

    //Deinitialise
    SPI_DMA_SLAVE_deinit();
    deinitDMA();

    //Initialise
    initDMA();
    SPI_DMA_SLAVE_init(); 

    //Start DMA communication
    startCommunication();     
}    

/* Starting DMA communication according to STM32L432 Reference Manual p1317-p1318*/
void startCommunication() {

    SET_SPI1_CR2_RXDMAEN_BIT();                                                 //Enable RX DMA buffer
    DMA1_CH3_ENABLE();                                                          //Enable DMA channel 3      
    DMA1_CH2_ENABLE();                                                          //Enable DMA channel 2
    SET_SPI1_CR2_TXDMAEN_BIT();                                                 //Enable TX DMA buffer              
    SPI1_ENABLE();                                                              //SPI module enabled

}


//====================================================DEINITIALISE=========================================================================
void SPI_DMA_SLAVE_deinit() {
    //Disable the clocks
    RCC->AHB2ENR &= ~(RCC_AHB2ENR_GPIOAEN);                                      
    RCC->APB2ENR &=  ~(RCC_APB2ENR_SPI1EN);      

    //Clear pin settings
    GPIOA->MODER&=~((3u<<(2*CS_slave)));                                         //clear GPIOA pin mode (in input mode when reset)

    GPIOA->MODER&=~(                                                             //clear GPIOB
            (3u<<(2*SCK_slave))
            |(3u<<(2*MISO_slave))
            |(3u<<(2*MOSI_slave))
            |0x03
                );

    GPIOA->AFR[0]&=~(                                                            //clear alternate function selector bits
            (0x0f<<(4*SCK_slave))
            |(0x0f<<(4*MISO_slave))
            |(15u<<(4*MOSI_slave))
            );

    //Clear SPI bits
    SPI1_DISABLE();
    CLEAR_SPI1_CR1_MSTR_BIT();
    CLEAR_SPI1_CR1_BR_BITS();
    CLEAR_SPI1_CR1_SSM_BIT();
    CLEAR_SPI1_CR1_SSI_BIT();
    CLEAR_SPI1_CR1_CPOL_BIT();
    CLEAR_SPI1_CR1_CPHA_BIT();


    CLEAR_SPI1_CR2_DS_BITS();
    CLEAR_SPI1_CR2_RXDMAEN_BIT();
    CLEAR_SPI1_CR2_TXDMAEN_BIT();
    CLEAR_SPI1_CR2_RXEIE_BIT();
    CLEAR_SPI1_CR2_TXEIE_BIT();


}

void deinitDMA() {
    RCC->AHB1ENR &= ~(RCC_AHB1ENR_DMA1EN);                                      //Disable the DMA1 clock

    //Disable channels
    DMA1_CH2_DISABLE();
    DMA1_CH3_DISABLE();

    CLEAR_DMA1_SPI1RX_CSELR_BITS();                                             //deselect SPI1_Rx on DMA1 Channel 2
    CLEAR_DMA1_SPI1TX_CSELR_BITS();                                             //deselect SPI1_Tx on DMA1 Channel 3


//-----------------------------------------------Receive-----------------------------------------------------                       
   //Clear configuration bits                     
   CLEAR_DMA1_CH2_CCR_DIR_BIT();                                                
   CLEAR_DMA1_CH2_CCR_PSIZE_BITS();
   CLEAR_DMA1_CH2_CCR_MSIZE_BITS();
   CLEAR_DMA1_CH2_CCR_MINC_BIT();
   CLEAR_DMA1_CH2_CCR_PINC_BIT();
   CLEAR_DMA1_CH2_CCR_TCIE_BIT();
   CLEAR_DMA1_CH2_CCR_CIRC_BIT();
   CLEAR_DMA1_CH2_CCR_MEM2MEM_BIT();
   CLEAR_DMA1_CH2_CCR_PL_BITS();
   CLEAR_DMA1_CH2_CCR_TEIE_BIT();

   CLEAR_DMA1_CH2_CNDTR_BITS();
   CLEAR_DMA1_CH2_CPAR_BITS();
   CLEAR_DMA1_CH2_CMAR_BITS();
//-----------------------------------------------Receive-----------------------------------------------------   

//-----------------------------------------------Transmission------------------------------------------------  
    //Clear configuration bits 
    CLEAR_DMA1_CH3_CCR_DIR_BIT();
    CLEAR_DMA1_CH3_CCR_PSIZE_BITS();
    CLEAR_DMA1_CH3_CCR_MSIZE_BITS();
    CLEAR_DMA1_CH3_CCR_MINC_BIT();
    CLEAR_DMA1_CH3_CCR_PINC_BIT();
    CLEAR_DMA1_CH3_CCR_TCIE_BIT();
    CLEAR_DMA1_CH3_CCR_CIRC_BIT();
    CLEAR_DMA1_CH3_CCR_MEM2MEM_BIT();
    CLEAR_DMA1_CH3_CCR_PL_BITS();
    CLEAR_DMA1_CH3_CCR_TEIE_BIT();

    CLEAR_DMA1_CH3_CNDTR_BITS();
    CLEAR_DMA1_CH3_CPAR_BITS();
    CLEAR_DMA1_CH3_CMAR_BITS();

//-----------------------------------------------Transmission------------------------------------------------    

    NVIC->ISER[0]&= ~(1u<<12);                                                   //Disable DMA1 channel 2 interrupt                                                   
    NVIC->ISER[0]&= ~(1u<<13);                                                   //Disable DMA1 channel 3 interrupt

}
//====================================================DEINITIALISE=========================================================================



void SPI_DMA_SLAVE_init() {
    RCC->AHB2ENR|= (RCC_AHB2ENR_GPIOAEN);                                       //GPIO A clock enable       
    RCC->APB2ENR|=RCC_APB2ENR_SPI1EN;                                           //Enable SPI1 Clock

   //SET SCK, MISO, MOSI and CS pins 
    GPIOA->MODER|=(                                                             
            (2u<<(2*SCK_slave))
            |(2u<<(2*MISO_slave))
            |(2u<<(2*MOSI_slave))
            |0x01
                );

    //SET pins to function as SPI pins
    GPIOA->AFR[0]|=(                                                             
            (5u<<(4*SCK_slave))
            |(5u<<(4*MISO_slave))
            |(5u<<(4*MOSI_slave))
                );

    SET_SPI1_CR1_BR_BITS();                                                     //baud rate bits set 1/16 giving 1MHz SCK frequency
    SET_SPI1_CR1_CPOL_BIT();                                                    //CPOL = 1
    SET_SPI1_CR1_CPHA_BIT();                                                    //CPHA = 1                                                  


    SET_SPI1_CR2_DS_BITS();                                                     //Data Size = 16 bits
    SET_SPI1_CR2_RXDMAEN_BIT();                                                 //Rx buffer DMA enable
    SET_SPI1_CR2_TXDMAEN_BIT();                                                 //Tx buffer DMA enable


}



void initDMA() {
    RCC->AHB1ENR|= (RCC_AHB1ENR_DMA1EN);                                        //Enable the DMA1 clock

    DMA1_CH2_DISABLE();                                                         //Disable DMA channel 2
    DMA1_CH3_DISABLE();                                                         //Disable DMA channel 3

    SET_DMA1_SPI1RX_CSELR_BITS();                                               //Select SPI1_Rx on DMA1 Channel 2
    SET_DMA1_SPI1TX_CSELR_BITS();                                               //Select SPI1_Tx on DMA1 Channel 3

//-----------------------------------------------Receive-----------------------------------------------------                         
   CLEAR_DMA1_CH2_CCR_DIR_BIT();                                                //Peripheral->Memory
   SET_DMA1_CH2_CCR_PSIZE_BITS();                                               //16 bits
   SET_DMA1_CH2_CCR_MSIZE_BITS();                                               //16 bits
   SET_DMA1_CH2_CCR_MINC_BIT();                                                 //Memory increment mode
   SET_DMA1_CH2_CCR_PINC_BIT();                                                 //Peripheral increment mode
   SET_DMA1_CH2_CCR_TCIE_BIT();                                                 //Transfer complete interrupt enable
   SET_DMA1_CH2_CCR_CIRC_BIT();                                                 //Circular Buffer mode
   SET_DMA1_CH2_CCR_PL_BITS();                                                  //Priority Level = Highest

    DMA1_Channel2->CNDTR = 10;                                                  //number of data to transfer from the peripheral to memory.
    DMA1_Channel2->CPAR = (int32_t)&SPI1->DR;                                   //Source Adddress = SPI data register
    DMA1_Channel2->CMAR = (int32_t)received_data;                               //Destination address = received_data array
//-----------------------------------------------Receive-----------------------------------------------------   

//-----------------------------------------------Transmission------------------------------------------------  

   SET_DMA1_CH3_CCR_DIR_BIT();                                                  //Memory->Peripheral
   SET_DMA1_CH3_CCR_PSIZE_BITS();                                               //16 bits
   SET_DMA1_CH3_CCR_MSIZE_BITS();                                               //16 bits
   SET_DMA1_CH3_CCR_MINC_BIT();                                                 //Memory increment mode
   SET_DMA1_CH3_CCR_PINC_BIT();                                                 //Peripheral increment mode
   SET_DMA1_CH3_CCR_TCIE_BIT();                                                 //Transfer complete interrupt enable
   SET_DMA1_CH3_CCR_CIRC_BIT();                                                 //Circular Buffer mode
   SET_DMA1_CH3_CCR_PL_BITS();                                                  //Priority Level = Highest

    DMA1_Channel3->CNDTR = 10;                                                  //number of data to transfer from memory to the peripheral
    DMA1_Channel3->CPAR = (int32_t)&SPI1->DR;                                   //Destination address = SPI data register
    DMA1_Channel3->CMAR = (int32_t)data_to_transmit;                            //Source address = data_to_transmit
//-----------------------------------------------Transmission------------------------------------------------    


    NVIC->ISER[0]|= (1u<<12);                                                   //Enable DMA1 channel 2 interrupt                                                   
    NVIC->ISER[0]|= (1u<<13);                                                   //Enable DMA1 channel 3 interrupt
    NVIC_EnableIRQ(DMA1_Channel2_IRQn);
    NVIC_EnableIRQ(DMA1_Channel3_IRQn);

}


//Interrupt Handler for DMA1 Channel 2
void DMA1_Channel2_IRQHandler(void) {
     myled = 0;                                                                 //Toggle LED off
     CLEAR_DMA1_CH2_IFCR_GFLAG();                                               //Clear Global Interrupt flag
}


//Interrupt Handler for DMA1 Channel 3
void DMA1_Channel3_IRQHandler(void) {
     myled = 0;  
     CLEAR_DMA1_CH3_IFCR_GFLAG();                                               //Clear Global Interrupt flag             
}

Is there anything that I am missing?

\$\endgroup\$
  • 1
    \$\begingroup\$ Would suggest you move from LEDs to toggling GPIOs and get that and the SPI itself on a cheap USB based logic analyzer. Then run it all with the debugger connected and a breakpoint in the hardfault hander, and if still inconclusive stop it with the debugger and see where it is... \$\endgroup\$ – Chris Stratton Mar 4 at 22:32
  • \$\begingroup\$ Also, really look at the behind the scenes code... there is a some eye opening stuff deep in the internals of mbed that may prove quite a bit different than what you might imagine from the outside. \$\endgroup\$ – Chris Stratton Mar 4 at 22:33
  • \$\begingroup\$ Not sure if only problem, but at your deinit functions you disable clock first and then you try clear settings. \$\endgroup\$ – Rokta Mar 5 at 10:10
  • \$\begingroup\$ @Rokta You are correct, I have changed my code to disable the clock after I clear the bits. \$\endgroup\$ – Zed Mar 8 at 1:03
0
\$\begingroup\$

I found the solution to this problem. Because I am using a C++, I needed to use extern "C" on the interrupt handlers according to this post. My interrupt handlers are now written like this:

extern "C" void DMA1_Channel2_IRQHandler(void) {
         myled = 0;                                                                 //Toggle LED off
         CLEAR_DMA1_CH2_IFCR_GFLAG();                                               //Clear Global Interrupt flag
}
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.