Skip to main content
Bogdan
Senior
March 6, 2018
Question

UART data transmit DMA problems

  • March 6, 2018
  • 4 replies
  • 1928 views
Posted on March 06, 2018 at 16:22

Hello,

i am working on a application where i try to measure a motor speed and current,  and then send the data to the PC using serial uart.

The device used is a STM32F427

For controlling the motor i used a timer1 6pwm channels  triggered by timer2  hall xor event.

The motor runs with no problems.

For uart transition i use UART3  with DMA TX

- Timer2 CC1 IRQ event occurs at every change in the state of the 3 hall sensors, here i store the CNT value to the rpm variable.

- in the main program loop i check if a previously DMA transfer has ben made ( flag = 0?)

- if yes, then i  set the flag  , and read the current value and store to a  variable called current , and convert the data to string using sprintf in the following format    1234,5678\r\n (11 chars in lenght)

- after i converted the data, i enable the DMA USART TX request

- when the DMA TC completes, i copy the previously converted data to a temporary string buffer for sending out to the USART, afterwards the DMA TX is disabled

The problem i am facing is when displaying the data on the PC i get very often garbage data.

I have attached a small terminal data showing with red marking the garbage, and a simplified version of the code

How to overcome this?

0690X00000609xBQAQ.png

char tmp[2]='''';

char Buffer[20]='''';

uint16_t buflen=11;

void DMA1_Stream3_IRQHandler(void)

{

    if (DMA_GetITStatus(DMA1_Stream3, DMA_IT_TCIF3))

   {

        DMA_ClearITPendingBit(DMA1_Stream3, DMA_IT_TCIF3);

  USART_DMACmd(USART3, USART_DMAReq_Tx, DISABLE); // disable DMA USART TX request

         for(int k=0; k<buflen; k++)

                              tmp[k]=Buffer[k];

         flag=0; // set

    }

}

void TIM2_IRQHandler()

{

      if (TIM_GetITStatus(TIM2, TIM_IT_CC1) != RESET)

     {     

           TIM_ClearITPendingBit(TIM2, TIM_IT_CC1);

           rpm = calculate_rpm();  // store the time between hall sensor state change

      }

}

int main(void){

init_usart_with_dma();

while(1){

         if(flag==0){

                           flag=1; // set the DMA flag

                        // read external current sensor

                          GPIOA->BSRRH = GPIO_Pin_3; // set CS Low

                          SPI1->DR = 0x00;/// write data to be transmitted to the SPI data register

                          while( !(SPI1->SR & SPI_I2S_FLAG_RXNE) ); // wait until receive complete

                           while( SPI1->SR & SPI_I2S_FLAG_BSY ); // wait until SPI is not busy anymore

                           current =SPI1->DR; // return received data from SPI data register

                           GPIOA->BSRRL = GPIO_Pin_3; // set CS High

                           sprintf(Buffer,''%04d,%04d\r\n'',current,rpm); // 12chars - convert uint to string

                            USART_DMACmd(USART3, USART_DMAReq_Tx, ENABLE); // re-enable DMA  USART TX request

                     }

     }

}

void init_usart_with_dma(void)

{

DMA_InitTypeDef DMA_InitStructure;

USART_InitTypeDef USART_InitStructure;

GPIO_InitTypeDef GPIO_InitStruct2;

// USART 3 TX - is tied to DMA1 Stream3

//PB 10 - TX

//PB 11 - RX

GPIO_InitStruct2.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11; // Pins 10 (TX) and 11 (RX) are used

GPIO_InitStruct2.GPIO_Mode = GPIO_Mode_AF; // the pins are configured as alternate function so the USART peripheral has access to them

GPIO_InitStruct2.GPIO_Speed = GPIO_Speed_50MHz; // this defines the IO speed and has nothing to do with the baudrate!

GPIO_InitStruct2.GPIO_OType = GPIO_OType_PP; // this defines the output type as push pull mode (as opposed to open drain)

GPIO_InitStruct2.GPIO_PuPd = GPIO_PuPd_UP; // this activates the pullup resistors on the IO pins

GPIO_Init(GPIOB, &GPIO_InitStruct2);

GPIO_PinAFConfig(GPIOB, GPIO_PinSource10, GPIO_AF_USART3); //

GPIO_PinAFConfig(GPIOB, GPIO_PinSource11, GPIO_AF_USART3);

RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE);

RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1, ENABLE);

USART_InitStructure.USART_BaudRate = 38400;// 115200;

USART_InitStructure.USART_WordLength = USART_WordLength_8b;

USART_InitStructure.USART_StopBits = USART_StopBits_1;

USART_InitStructure.USART_Parity = USART_Parity_No;

USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;

USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;

USART_Init(USART3, &USART_InitStructure);

USART_ITConfig(USART3, USART_IT_RXNE, ENABLE);

USART_Cmd(USART3, ENABLE);

//++++++++++++++++ SETUP DMA ++++++++++++++++++++++++//

NVIC_InitStructure.NVIC_IRQChannel = DMA1_Stream3_IRQn;

NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;

NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;

NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;

NVIC_Init(&NVIC_InitStructure);

DMA_DeInit(DMA1_Stream3);

DMA_InitStructure.DMA_Channel = DMA_Channel_4;

DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral; // Transmit

DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)tmp;

DMA_InitStructure.DMA_BufferSize = (uint16_t)sizeof(tmp) - 1;

DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&USART3->DR;

DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;

DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;

DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;

DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;

DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;

DMA_InitStructure.DMA_Priority = DMA_Priority_High;

DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Enable;

DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;

DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;

DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;

DMA_Init(DMA1_Stream3, &DMA_InitStructure);

// Enable the USART Tx DMA request

USART_DMACmd(USART3, USART_DMAReq_Tx, ENABLE);

// Enable DMA Stream Transfer Complete interrupt

DMA_ITConfig(DMA1_Stream3, DMA_IT_TC, ENABLE);

// Enable the DMA RX Stream

DMA_Cmd(DMA1_Stream3, ENABLE);

}

    This topic has been closed for replies.

    4 replies

    stm322399
    Senior
    March 6, 2018
    Posted on March 06, 2018 at 20:15

    IMHO the DMA is perfectly doing its job. To make sure, replace rpm as sprintf argument with a dummy incrementing value, so you will easily sort out which of DMA or calculate_rpm() is wrong.

    Alan Chambers
    Associate III
    March 6, 2018
    Posted on March 06, 2018 at 23:07

    char tmp[2]='';

    char Buffer[20]='';

    Is that just a typo in your forum post? tmp looks too small if I have understood how you are using it.

    Bogdan
    BogdanAuthor
    Senior
    March 7, 2018
    Posted on March 07, 2018 at 07:49

    Hello,

    regarding tmp[2]  ,  this is 1 char + space for NULL ,  it is  used to send one char on every dma transfer

    i have tried to use a fixed value for rpm in the sprintf function, it seems stable.

    After doing more debugging, i have moved most of the code inside the timer2 irq handler,   now my DMA irq code looks like this

    The DMA now works with the Buffer,  i have removed tmp

    The DMA when finishes the transfer, sets the flag to 0,    normaly when the CC1 IRQ is triggering again, it will check for that flag,  if the flag is reset, will process the new data, and prepare for a new DMA trigger,  if not it will move on with the rest of the tasks

    void DMA1_Stream3_IRQHandler(void)

    {     

           if (DMA_GetITStatus(DMA1_Stream3, DMA_IT_TCIF3))

           {

              DMA_ClearITPendingBit(DMA1_Stream3, DMA_IT_TCIF3);

             USART_DMACmd(USART3, USART_DMAReq_Tx, DISABLE);

    flag=0;  

           }

    }

    It seems my problem is in the timer2 irq handler.

    To sumarize my settings 

    - timer2 has a input clock (PCLK)  of  50 MHZ

    - timer2 prescaler is set to 699  ( 700-1)

    -  period set to 65535-1

    - motor RPM  min 50,  max 5000

    so timer2 is ticking with a speed of  (1/50MHZ) * 699 = 13.98 uS 

    It will overflow at 13.98us x 65534 , which is 0.91 seconds, seems long enough 

    At low speed i should have a time between IRQ calls of  6.9 mS

    At high speed i should have a time between IRQ calls of 0.666 ms

    Who is storing the number of ticks? TIM2->CNT or TIM2->CCR1 ?

    char Buffer[11+1] = ''; // holding buffer for DMA -> USART TX ,  11 chars + room for NULL

    uint32_t t_sum,t,current;

    void TIM2_IRQHandler()

    {

    if (TIM_GetITStatus(TIM2, TIM_IT_CC1) != RESET)

    {

           TIM_ClearITPendingBit(TIM2, TIM_IT_CC1);

             GPIOB->BSRRL = GPIO_Pin_12; // set Hi  for logic analyzer probe

           current = (GPIO_ReadInputData(GPIOA)& 0x0007); // read current position of the rotor HALL sensor

             

               if(current !=last)

                  { 

                      last = current;

                      pdata = current;

                   }

                 else

                 { 

                      return;

                  }   

                 UH = FORWARD_TABLE_DATASHEET[pdata][0];

                  UL = FORWARD_TABLE_DATASHEET[pdata][1];

                  VH = FORWARD_TABLE_DATASHEET[pdata][2];

                  VL = FORWARD_TABLE_DATASHEET[pdata][3];

                  WH = FORWARD_TABLE_DATASHEET[pdata][4];

                  WL = FORWARD_TABLE_DATASHEET[pdata][5];

                   comutate_phases(); // Switch ON/OFF corespondent FET transistors

                    if(flag==0){

                              

                               GPIOE->BSRRL = GPIO_Pin_12; // set Hi  for logic analyzer probe

                                   

                               flag=1;

                                  GPIOA->BSRRH = GPIO_Pin_3; // set CS4 Low

                                  SPI1->DR = 0x00;/// write data to be transmitted to the SPI data register

                                  while( !(SPI1->SR & SPI_I2S_FLAG_RXNE) ); // wait until receive complete

                                  while( SPI1->SR & SPI_I2S_FLAG_BSY ); // wait until SPI is not busy anymore

                                 current =SPI1->DR; // return received data from SPI data register

                                  GPIOA->BSRRL = GPIO_Pin_3; // set CS4 High

                                 // calculate RPM from ticks

                                  t_sum = (uint16_t)TIM2->CCR1;

                                  t = (uint32_t)(t_sum *14);

                                  t= t * 18;

                                  t = t/1000;

                                  t= 60000/t;

                                 sprintf(Buffer,'%04d,%04d\r\n',current,t); // 11 chars - sprintf takes arround 60us to execute, its enough time between irq calls

                                 USART_DMACmd(USART3, USART_DMAReq_Tx, ENABLE); 

                                 GPIOE->BSRRH = GPIO_Pin_12; // set J17 Lo

                     }

           }

    }

    T J
    Senior III
    March 7, 2018
    Posted on March 07, 2018 at 07:51

    This code is a little incorrect;

    EDIT:

    I wrote the basis of this code some time ago.  // but some important steps have been moved around

    :(

    Your Code:

    while(1){

             if(flag==0){

                               flag=1; // set the DMA flag

                            // read external current sensor

                              GPIOA->BSRRH = GPIO_Pin_3; // set CS Low

                              SPI1->DR = 0x00;/// write data to be transmitted to the SPI data register

                              while( !(SPI1->SR & SPI_I2S_FLAG_RXNE) ); // wait until receive complete

                               while( SPI1->SR & SPI_I2S_FLAG_BSY ); // wait until SPI is not busy anymore

                               current =SPI1->DR; // return received data from SPI data register

                               GPIOA->BSRRL = GPIO_Pin_3; // set CS High

                               sprintf(Buffer,'%04d,%04d\r\n',current,rpm); // 12chars - convert uint to string

                                USART_DMACmd(USART3, USART_DMAReq_Tx, ENABLE); // re-enable DMA  USART TX request

                         }

         }

    }

    My code:

    you should first

    wait for TxEmpty

           while( SPI1->SR & SPI_I2S_FLAG_BSY ); // wait until SPI is not busy anymore

    remove any old Rx data 

          while( !(SPI1->SR & SPI_I2S_FLAG_RXNE) )

             ;  // dumping old rx data here

    send your byte/s

                              SPI1->DR = 0x00;/// write data to be transmitted to the SPI data register

    wait for it to be sent totally

                               while( SPI1->SR & SPI_I2S_FLAG_BSY )

                                  ; // wait until SPI is not busy anymore

    Your byte should be ready to read;

                               current =SPI1->DR; // return received data from SPI data register