517 views
in System Integration by

Hello,

I'm implementing UART reading from a device based on STM32F469

I'm using DMA and the peripheral works, when the DMA buffer is full, the call to HAL_UART_RxCpltCallback  sends back to have a feedback and it doesn't miss a char.

So i know i have the data in the buffer available.

I made a flag to stop writing on that buffer when the message is not still processed by the GUI.

Following the Device integration Example, i made a init function for the UART thred to continously print whats on the buffer, this init is called in the Init() of the Device instance in Embedded Wizard.

void Uart_Thread_Init( void )
{
  /* check for initialization */
  if ( DeviceInitialized )
    return;
/*init delle funzioni*/

DeviceInitialized = 1;

#if EW_USE_OPERATING_SYSTEM == 1

  /* create and start the worker thread to process UART data */
 WorkerThread = EwBspOsThreadCreate( UartWorkerThread, EW_BSP_OS_THREAD_PRIORITY_NORMAL, 1024, 0 );

#endif
}

This is the worker thread i made, the function   SIN_GetCharacter() only makes a call to the function HAL_UART_Receive_DMA (&huart6, data, 10), which writes in a 10 bytes array called data.

 

static void UartWorkerThread( const void* arg)
{

  SIN_GetCharacter();

  while ( DeviceInitialized )
  {
      if(newMessage==1)
        {

             EwInvokeCopy( UpdateUartCharProc, &data, sizeof(data));

        }

    /* sleep for a certain period... */
     EwBspOsDelay( 20 );

  }

  /* terminate the worker thread */
  WorkerThread = 0;
  EwBspOsThreadDestroy( EwBspOsThreadGetHandle());
}

The process method UpdateUartCharProc to update the GUI is the following:

static void UpdateUartCharProc( const void* aData )
{
  char* uartChar = (char *)aData;

  /* only in case that the device driver is still initialized and the worker
     thread is still running, the data should be provided to the device class
     - otherwise, a new autoobject will be created and a new worker thread
     started... */
  if ( DeviceInitialized )
  {

    for(int i = 0 ; i <sizeof(uartChar); i++)
    {

                      if ((( uartChar[i] >= 'A' ) && ( uartChar[i] <= 'Z' )) || (( uartChar[i] >= 'a' ) && ( uartChar[i] <= 'z' ))
                          || (( uartChar[i] >= '0' ) && ( uartChar[i] <= '9' )) || ( uartChar[i] == ' ' ))

                      {


                                  ApplicationDeviceClass device = EwGetAutoObject( &ApplicationDevice, ApplicationDeviceClass );
                                  ApplicationDeviceClass__TCPMessageUpdate( device, uartChar[i] );

                      }
    }
     
      newMessage=0; 
      SIN_GetCharacter();  
  }

}

I arrived at this point from various conclusions:

1 -  The data is available from the UART so the data buffer is correct.

2 - Calling EwInvokecopy from the uart callback seems to be too slow to process the data to the GUI.

3 - the newMessage flag is used to stop the buffer modification until all the char are sent to the gui, it is updated after all the char from the new message are processed

4 - tried to pass directly the array of char to the UpdateCharProc instead of single char with EwInvokecopy, don't konw if the code is correct.

5 - if i sent a "0123456789" i get printed only the fourth char.

1 Answer

0 votes
by
 
Best answer
Hello Riccardo,

it seems there are several things mixed up.

First of all, there are two principal integration scenarios possible when receiving data from an external data source:

Option 1: Receive data via interrupt service routine (ISR). Each time a set of data (either a single byte or a sequence of bytes) is received, an interrupt is generated and the registered interrupt service routine is called. There you can post the data for the GUI thread by using EwInvokeCopy(). There is no need for creating a separate worker thread, because the data is given to the GUI thread directly from the interrupt service routine.

Option 2: A separate worker thread is polling the hardware for received data. Each time a set of data is received, the worker thread is calling EwInvokeCopy() to provide the data to the GUI thread. In this case, there is no need to process the data in an interrupt service routine.

Second, within the GUI thread there should be NO access to the hardware. In your implementation you make additionally an access to SIN_GetCharacter() from the GUI thread. Please note, that the received data are put into the invocation queue from ISR or worker thread and that the data are taken out of the invocation queue from the GUI thread.

Third, please try to avoid additional synchronization variables and global variables.

I hope these hints help to get a clear software structure.

Best regards,
Manfred.
by

Thanks Manfred.

So the ideal process is from what i understand:

1 - Get the call from the interrupt callback when data is available.

2 - in the callback pass the data to the GUI. with no thread involved.

I took the example from https://ask.embedded-wizard.de/7887/how-create-uart-communication-stm32f769i-discovery-board  modifing the ADC worker thread.

I'd like to use the interrupt version of the solution.... the code should be this?

 

It is working, but i get only the fourth char of the 10 char buffer.... can't explain why.

 

Call from the ISR


void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
 
      HAL_UART_Transmit(&huart6, data, 10, 10);                   //debug on terminal to see if the message get in to the buffer and transmit back

      EwInvokeCopy( UpdateUartCharProc, &data, sizeof(data));     // call to the process for the data... pass the uint8_t array in one time 

      SIN_GetCharacter();                                         // listen for next message

 }

method to process the array, char by char, and sento to the GUI.

void UpdateUartCharProc( const void* aData )
{
  char* uartChar = (char *)aData;                                    // doubt on how to cast the void pointer passed to a char array


    for(int i = 0 ; i <sizeof(uartChar); i++)
    {

                      if ((( uartChar[i] >= 'A' ) && ( uartChar[i] <= 'Z' )) || (( uartChar[i] >= 'a' ) && ( uartChar[i] <= 'z' ))
                          || (( uartChar[i] >= '0' ) && ( uartChar[i] <= '9' )) || ( uartChar[i] == ' ' ))

                      {


                                  ApplicationDeviceClass device = EwGetAutoObject( &ApplicationDevice, ApplicationDeviceClass );
                                  ApplicationDeviceClass__TCPMessageUpdate( device, (XChar) uartChar[i] );

                      }
    }
  

}

Removed the condition for the device initialized just for test.

 

The interrupt call is working as expected, the problem is the method to preocess the data to the GUI. Don't know if it is correct.

 

Thanks

by

Please have a look to the ISR - there you call EwInvokeCopy() with the following parameters &data and sizeof(data). The variable data seems to be a global array. No idea how many bytes you copy into the invocation queue - maybe 10 bytes.

Within your UpdateUartCharProc() callback you have a loop which iterates from 0 to sizeof( uartChar ) - which is only the size of the pointer. Thus you cannot read the complete sequence - only the first four bytes. Then, you call for each character the TCP_MessageUpdate() method. I assume that each call overwrites the previously character. Now, because the loop iterates only from 0 to 3 (= four iterations), you get the fourth character as a result in your GUI application.

Do you have a debugger? You can step through your implementation and check what data is received.

by

Yes, i get the point.

Yes the variable data is an array of ten bytes

So the ISR from the UART would be like this.

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
 
     HAL_UART_Transmit(&huart6, data, sizeof(data) , 10);             //debug on terminal to see if the message get in to the buffer and transmit back

     EwInvokeCopy( UpdateUartCharProc,data, sizeof(data));            // call to the process for the data... pass the uint8_t array in one time 

     HAL_UART_Receive_DMA (&huart6, data,sizeof(data));               // listen for next message

}

 

"I assume that each call overwrites the previously character. Now, because the loop iterates only from 0 to 3 (= four iterations), you get the fourth character as a result in your GUI application."

Yes, i tried to modify the value from 3 to 10... i get always the last charachter of the loop.

void UpdateUartCharProc( const void* aData )
{
  char* uartChar = (char *)aData;     


    for(int i = 0 ; i <10; i++)
    {

                      if ((( uartChar[i] >= 'A' ) && ( uartChar[i] <= 'Z' )) || (( uartChar[i] >= 'a' ) && ( uartChar[i] <= 'z' ))
                          || (( uartChar[i] >= '0' ) && ( uartChar[i] <= '9' )) || ( uartChar[i] == ' ' ))

                      {
                                  ApplicationDeviceClass device = EwGetAutoObject( &ApplicationDevice, ApplicationDeviceClass );   // call to the process for the data... pass the uint8_t array in one time 

                                  ApplicationDeviceClass__TCPMessageUpdate( device, (XChar) uartChar[i] );

                                                                
                      }
                    
  }
}

What debugger you suggest? 

I tried using cube monitor, but when importing the .elf file i do not se the varibles in the list to monitor.

I modified the TCPMessageUpdate in EW to print every time a char get sent.

 

The string on the gui adds the new char to ne previous one.

TCPText.String =  TCPText.String + string(Application::Device.TCP_Message);

 

by

You still get only the last character, because you store the character in the property TCP_Message and inform the GUI by using notifyobservers. When you call 10 times the method TCPMessageUpdate the variable is overwritten again and again - only the last character is stored and the 10 notifications are combined to one. Please note, that the notifications are handled as signals which are processed within the main loop.

In order to find the best architecture, you should consider what you finally want to achieve.

Option 1: If you want to transfer single characters, then you should adapt your ISR callback so that you only receive one character and you put only one character into the invocation queue and finally your GUI receives one character.

Option 2: If you want to transfer complete messages (e.g. a string), then you should adapt your ISR to receive either a complete message or collect the data until a complete message is received. The complete message is then stored within the invocation queue. The TCPMessageUpdate should be adapted to receive a string instead of a single char.

I hope this helps to find a good architecture. Let me recommend also the chapter Be careful when exchanging strings.

Best regards,
Manfred.

by

 "When you call 10 times the method TCPMessageUpdate the variable is overwritten again and again - only the last character is stored and the 10 notifications are combined to one. Please note, that the notifications are handled as signals which are processed within the main loop."

That what i was missing.

So... i made it to work.

The call from the ISR was correct.

The problem was in the processing of the char passing them to the GUI.

this made it work.

void UpdateUartCharProc( const void* aData )
{
  char buf[50];

  sprintf (buf,(char*)aData);     

                      
                                  ApplicationDeviceClass device = EwGetAutoObject( &ApplicationDevice, ApplicationDeviceClass );   // call to the process for the data... pass the uint8_t array in one time 

                                  ApplicationDeviceClass__TCPMessageUpdate( device,EwNewStringAnsi( buf ) );
                    
}

 

Thanks again Manfred.

Embedded Wizard Website | Privacy Policy | Imprint

...