cancel
Showing results for 
Search instead for 
Did you mean: 

UART data transmit DMA problems

Bogdan
Senior
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);

}

4 REPLIES 4
stm322399
Senior
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 II
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
Senior
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
Lead
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