[SOLVED] Potential UART/Serial Conflict?

Hi guys

I’m interested to know what the best practise is when using multiple Serial instances?
I’ve spent over 3 days trying to get a UART working with an external metering chip (STPM32).
I can set registers but seem to be getting meaningless data back.
I’m using Log.info() to return data periodically throughout execution. Is there any chance that using both USBSerial for Log and Serial4 for the UART could result in some sort of conflict / buffer corruption? Do I need to explicitly use Serial4.end() /USBSerial.end() after the use of each instance?

Any thoughts would be much appreciated.

What happens if you use only one at a time? Does it work properly?

It’s been a little hard to know as the serial terminal has been the only way to view the received data.
I’m using a logic analyser at the moment to view the traces. One interesting thing is that when Serial4.begin() is called, the TX line goes low, and shortly after there is a blip on the RX line, (just after 0.2s). After data transmission begins however, the TX line stays high. I’m not quite sure why it goes low straight after initialisation though, isn’t this an error state?
Unfortunately I can’t easily verify whether the slave is sending this blip when TX goes low, without cutting a PCB trace.

Can you try using Serial.println()?

Hi @kennethlimcp

Please ignore the last screenshot, the blips were actually caused by the slave IC as it was reset…
I think I have found the solution to the problem though. I didn’t realise that the Serial library doesn’t support full duplex communication. The IC communicates via full duplex, 5 bytes each way per transaction. This is the code I was using, however I believe now that each Serial statement is actually blocking and designed for half duplex.
It doesn’t look like anyone has posted about this on the forum, so I’ll have a go and report back as it seems like the hardware supports it.

static uint8_t UARTWrp_SendAndReceiveByte(uint8_t data)
{
   Serial4.write(data);
   data = Serial4.read();

   return(data);
}
1 Like

That's odd. The Electron UARTs should support full duplex.
If it doesn't I'd consider that a bug that might need a GitHub issue.
@BDub, what do you think?

Meanwhile, could you try to explicitly disable halfduplex via Serial4.halfduplex(false)?

1 Like

Unless I’m missing something @ScruffR
I read that the arduino serial library doesn’t support full duplex, and I made an assumption that the Particle serial library was set up in such a way as well - but possibly I’m wrong?
I’ve verified the data coming back from the device seems to be correct using a logic analyser, and I know I can send data using Serial4.write(), so it seems like a lack of full duplex support could be the issue?
I’m just going through usart_hal.c now to see how it works.

I’ll give your idea a try now.

I gave this a try, it didn’t make any difference. Interestingly however setting halfduplex to true, did change my readings, however the register values appeared to be just as erroneous as before - suggesting that perhaps I was using full-duplex all along…
I’m really at a loss now for where to go next.

This is the code that ST provided to setup the peripheral:

huart.Instance = USART_STPM;
huart.Init.BaudRate = in_baudrate;
huart.Init.WordLength = UART_WORDLENGTH_8B;
huart.Init.StopBits = UART_STOPBITS_1;
huart.Init.Parity = UART_PARITY_NONE;
huart.Init.Mode = UART_MODE_TX_RX;
huart.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart.Init.OverSampling = UART_OVERSAMPLING_16;
HAL_UART_Init(&huart);

and to send and receive in full duplex:

static uint8_t UARTWrp_SendAndReceiveByte(uint8_t data)
{
   HAL_UART_Transmit( &data, 1, 0);
   HAL_UART_Receive( &data, 1, USART_TIMEOUT);

   return(data);
}

Partly what threw me off for a start, I don’t quite understand how in full duplex mode, the firmware is meant to simultaneously write to, and read from ‘data’?

This is a sample of the TX/RX data as measured between the master and slave. I believe the slave is working correctly as it is meant to send back the configuration data written to it during initialisation, and I am seeing these packets coming back. However they don’t seem to successfully make it into the ST micro.

The simple explanation (which does not exactly reflect the actual HW side of the HW interface, but transposes it to a pure SW view) is that you have two independent buffers for RX and TX data and two independent ISRs working off the data.
While the RX ISR will respond to incoming edges of the start bit to start a timer to trigger another ISR that keeps probing the lines at the due times depending on the baudrate and putting the incoming bits into the RX buffer, an TX cycle will be started via your write action to the TX buffer. This will also spin up a baudrate timer that also triggers an ISR that will do the pushing out of TX bits.
And since both sets of ISRs and timers are independent, you can send and receive form/into the buffer "in parallel", in your code you only place bytes into the buffer or read from the buffer.

I've written a ParticleSoftSerial library that crudely mimics this behaviour.

BTW, does that client work correctly on one of the other UART ports?

1 Like

Thanks for the explanation. Unfortunately it’s not easy to check on other UART ports as it’s a custom 4 layer PCB.
The ST provided firmware also runs CRC on the input buffer which is quite confusing to understand - adding further to my uncertainty where the problem lies.

I’ve tried adding this:

static uint8_t UARTWrp_SendAndReceiveByte(uint8_t data)
{
  Serial4.write(data);
  data = Serial4.read();

  Log.info("Receiving UART Data from STPM32: %X",data);

  return(data);
}

Which does return data, however it also changes the data that I was receiving before adding the Log statement - is this expected?

Could it be a timing issue, since the default buffer size is only 32 64 byte and if you can’t siphon off the incoming data fast enough, your new bytes will just be dropped as there is no flow control.

For the TX side, you can check the Serial4.availableForWrite() info to see how much space you’ve got left in the buffer.

1 Like

About this, that might be due to the asyncronicity of the SW & HW read and write tasks.
Your HW write may not have completed by the time you are already reading the data from the buffer.
If the buffer was empty and the response hasn't yet been completely read, you will get a (int)-1 as a read value.
You should always check if you actually have got data available in the buffer to read or check the read int whether its <0 before using the result.

To avoid race conditions for your logging, you may also want to use different variables for writing and reading.

Thanks, really appreciating your help.
I’m just trying a different baud rate now in case that makes any difference. Will try the availabilityForWrite() command soon.

1 Like

@ScruffR

Using availabilityForWrite(), I begin with 64bytes, then it continues to drop until it reaches 2 bytes, after which transmission appears to continue. Is there a way I can clear this buffer at the end of each full duplex session?

This should go back up, once the µC had time to send the data out. Each sent byte should increase the available space again.

But there was a version where this function didn’t quite work correctly.
What version are you running?
Can you try 0.6.1-rc.2?

Yea, I thought it should too… I’m on 0.6.0. Trying your suggestion now.

No fix there unfortunately - I don’t understand why the buffer isn’t emptying. I’ve tried re initialising Serial4 at before each write/read call but that seems to make the comms far more patchy.

Time to call the big guys
@BDub, any insight why Serial4 (esp. Serial4.availableForWrite()) doesn’t seem to do what’s expected?

1 Like

So this is the code where the async function is called from:

static void Metro_HAL_UsartTxStart(METRO_NB_Device_t in_Metro_Device_Id)
{
  uint8_t data;
  //huart.Instance = USART_STPM;
  /* Enable the Peripheral clear pending error if any*/

  //__HAL_UART_ENABLE(&huart); //UART already enabled in Particle baudrate set
  Metro_HAL_WaitMicroSecond(100);

  /*p_Metro_Device_Config stores device configuration data*/
  if (p_Metro_Device_Config[in_Metro_Device_Id].STPM_com.txOngoing == 0)
  {
    data  =  UARTWrp_SendAndReceiveByte(*p_Metro_Device_Config[in_Metro_Device_Id].STPM_com.pTxRe    adBuf);


    p_Metro_Device_Config[in_Metro_Device_Id].STPM_com.pTxReadBuf += 1;
    p_Metro_Device_Config[in_Metro_Device_Id].STPM_com.txOngoing = 1;
    do
    {
       p_Metro_Device_Config[in_Metro_Device_Id].STPM_com.rxData = data;
       Metro_HAL_RxHandler();

       if (p_Metro_Device_Config[in_Metro_Device_Id].STPM_com.pTxWriteBuf != 0)
      {
          Metro_HAL_TxHandler();

       if (p_Metro_Device_Config[in_Metro_Device_Id].STPM_com.txValid==1)
      {
          data  = UARTWrp_SendAndReceiveByte(p_Metro_Device_Config[in_Metro_Device_Id].STPM_com.txData);
      }
  }
}while(p_Metro_Device_Config[in_Metro_Device_Id].STPM_com.txOngoing);

 }
/* Disable the Peripheral */
//__HAL_UART_DISABLE(&huart); 
}

Here is the send/receive function as called above

static uint8_t UARTWrp_SendAndReceiveByte(uint8_t data)
{
  //HAL_UART_Transmit( &data, 1, 0);
  Serial4.write(data);
  //HAL_UART_Receive( &data, 1, USART_TIMEOUT);
  data = Serial4.read();
  return(data);
}

I’ve left the commented out UART related statements from the original firmware to show what I’ve replaced them with. I’ve initialised Serial4 with Serial4.begin(9600) in a previous init function (in the same file, with:

#include "Serial4/Serial4.h"

included at the top of the file.

As you’ll see the original firmware initialises and closes the serial comms each time Metro_HAL_UsartTxStart is called, however this doesn’t seem to work in my case. (With the above firmware I can at least write configuration data to the device and the logic analyser seems to show both TX and RX as correct) so, again I suspect data corruption / overrun or something else is the issue on the receive side)