[SOLVED] Potential UART/Serial Conflict?

After looking a little more closely :slight_smile: This is exactly right. Serial1.blockOnOverrun(true); is true by default so the second write is pumped along as the buffer empties in real time.

You're probably right! I meant to say I believe Serial4 works to send and receive data fine, just that if you are relying on some kind of synchronous action you will have to force that to be the case because the ST is fast and you don't have HW flow control. Your delay() might be a decent solution in lieu of HW flow control.

You might try to instrument the code with a fast SPI output and send one byte as a marker before and after each serial transfer, so that you can see how delayed or wrong your serial stream may be.

@greyscale @BDub @ScruffR Thank you very much for your help and suggestions.

I think you are correct. After looking carefully at the registers, I can see that in fact the most significant byte has been knocked off the front of the register, and instead of the correct least significant byte, I'm seeing the CRC byte that is the last of the 5 bytes sent. I've included a logic screenshot with some crudely drawn lines in paint indicating each 'packet of 5 bytes'

So, looking at the RX (bottom line) on the right, the correct register data should end up as 0x03270327 and 0x17 is the CRC byte. What I'm actually getting in the register is 0x270327bd (data is sent LSB first) so you can see RX is actually ahead of itself and assumes the CRC byte from the first packet is the start of the next packet.

Furthermore, to confirm, the slave only uses the first byte as it’s start reference and then sends it’s 5 bytes as fast as possible (i.e. it’s not 1 byte each way sort of thing) This is obvious with the following screenshot where a delayMicroseconds(2000) is added at the end of the RX/TX function.

@BDub, does this mean me notion was corret and the actual HW side of the TXing is non-blocking but only the data transfer into the buffer is blocking?
Just to clarify the understanding for everybody who might drive by :sunglasses:

1 Like

So I’ve messed around with timing, flush, serial.available calls, but no luck on actually fixing it yet :sweat: .
Any ideas of further tests / things I could try?

I can't quite follow there. How could RX be ahead of itself (or the respective TX?
Since you seem to be sending the same command over and over, you expect the same response over and over, and hence you won't be able to tell which RX stream was triggered by what TX command.
Having said this, it could be that you actually see the "shifted" bytes of TX(n-1) and take it as being ahead of TX(n) where it in fact is just a late RX(n-1).

If you add some extra delays between the consecutive TXs, you may better see the time relation between TX(n) and RX(n).
Or for full speed testing send different commands to enable you to distinguish the corresponding responses.

Also how long is one command and how many bytes will that trigger on the RX side?
I see only one set of outgoing commands, but one 0xFF0F00000BD and a bunch of 0x2703270317. Where does that odd one out come from?

If one TX byte always triggers 5 RX bytes, it's not possible to hammer out the TX bytes full speed, since that will inevitably cause an RX buffer overrun resulting in dropped and hence corrupted RX data. You need to add some kind of flow control to prevent that.

The simplest way to do that would look like this

  uint8_t rx[5];
  Serial4.write(cmd);
  while(Serial4.available() < sizeof(rx)); // a timeout may be good to put here, omitted for demonstration 
  for (int i=0;i<sizeof(rx);)
    rx[i++]Serial4.read();

My attempted explanation was probably a bit vague sorry.
When I say the TX byte triggers a reply, I’m pretty sure it’s only the first frame (byte) that triggers the reply, and then all RX frames are sent (5)

As with the previous logic screenshot I sent, when the TX is slowed down you can see the 5 RX frames are only sent when the first TX frame is sent.

I might be a bit slow in understanding (or it’s the lack of insight into the actual functionality of your client device), but how would the client already know what to answer before having heard the whole story and what would a CRC be for if the response was already triggered without even knowing wheter the command was understood right?

Could you add some longer delay between individual TX calls and mark number the individual bytes and sent/received values per byte?

That last screenshot looks nice, but lacks context for me.

1 Like

Not at all, the client device is a metering device, basically an external DSP/ADC.
So the slave reply is always a full 5 bytes behind the master. So when the master sends it’s first frame, i.e. the address for the 32-bit register to be read, it will only receive the 4 bytes of that register in the next session. Does that make sense?

I see!
So if I understand this right, you’d send 5 bytes to indicate what you want, but the client withholds that data till it sees another “read-trigger” (e.g. first byte of a subsequent command) or possibly just a dummy byte.
Is that correct?

Basically yes, so sometimes the master will send dummy frames of 0xFF for bytes 1 or 2 if it doesn’t want to read or write anything. But yes basically in the very first session, the slave stays quiet and the master uses 4 bytes + CRC to say what it wants to read back in the next session, and any data it wants to write in this session. Datasheet is here if it helps.

If it worked one byte each way, the timing would probably be easier.

Looks like SPI framing over UART.
Are you sure the first frame doesn’t get a filler response frame from the chip?

If I understand correctly, not as I'm aware. Immediately after the first TX frame, the slave replies with the first LSB requested in the previous session.

Does anybody know if Serial.flush() waits until the last byte is actually sent by the hardware, or just until the last byte is transferred out of the tx buffer? Asking because I remember running into something with another chip.
Maybe the Serial.flush() followed by while(Serial.read()) actually completes before the last byte in the rx frame, which would cause the byteshift.

Maybe do this between frames (to clear the byteshift) while ironing this out:
Serial.flush()
delay 10ms
while(Serial.read()>=0)

No luck there @greyscale, this caused all data to be received as 0xFF for some reason…

Essentially it slowed TX down quite a bit, however RX just comes through in the usual bursts:

Hmm - that’s odd. I was expecting it to add delay between the 5-byte frames instead of every byte. (In retrospect, a little confusing - seems like the chipspec calls the 5-byte set a frame, but also calls a single byte on the uart a frame too)

0xFF is really -1 as an unsigned int, so that sounds like you don’t have any rx backed up, but you’re not really waiting for incoming bytes.

How about this for each 5-byte frame -

// clear lines between 5-byte frames
Serial.flush() // tx buffer is empty
delay(10) // extra delay between 5-byte frames
while(Serial.read()>=0) // nothing in rx buffer - discarding anything old

// send all 5 bytes
for (i=0; i<5; i++)
Serial.write(outbuf[i])
Serial.flush()
while(Serial.available()<5) // wait until we have all 5 bytes to read
// read 5 bytes
for (i=0; i<5; i++)
inbuf[i] = Serial.read()

ScruffR had posted some code with Serial.available() - I think that’s more reliable than the delay I would have used otherwise. .

I’ll just post this terminal screenshot for clarity. On the 5th line where you see ‘TX Start called’ that’s the large parent function that I posted up top (I tried to quote it here but it mucked up the formatting). Byte 0xbd received immediately after is actually the CRC byte from the session before. You’ll see in the parent function there is two calls to UARTWrp_SendAndReceiveByte, one at the top and the other in the ‘do loop’

So actually, instead of what we see, after TX start called, the first byte we should see is the 0x27, then the next 3 bytes and then finally the 0x17 which is the final byte and CRC frame that belongs to the series 0x03270327

(Sorry I keep editing this post)
So essentially what that means to me is that the RX/TX function I’m using, isn’t able to read the data received directly after the first TX is sent. It only receives it on the next call.

This actually drains the RX buffer and any subsequent read will return (int)-1 which would be seen as 0xFF till new data arrives.

So…

I changed the code to:

static uint8_t UARTWrp_SendAndReceiveByte(uint8_t data)
{
   uint8_t returnData = 0;

   Serial4.write(data);
   Serial4.flush();
   while(Serial4.available()==0)
   {
   }
  
   returnData = Serial4.read();

   return(returnData);
}

Which I hoped might fix it, unfortunately same behaviour as before, where we seem to have a data shift of one byte.

Could you try the whole HW protocol puzzle in an isolated test sketch to focus on that one issue only without any timing restrictions possibly imposed be the rest of your project?

And I’d rather wait for the whole 5 byte response to arrive before returning from the function.