tabemann / zeptoforth

A not-so-small Forth for Cortex-M

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Strange UART transmit truncation problem on Raspberry Pi Pico

robzed opened this issue · comments

In the system I'm working with, I was getting a strange problem where was working until I got to larger packet sizes (>$A1 bytes) where the last characters would be lost. I'll explain my understanding, but the summary is that I suspect that there may be a buffering bug in Zeptoforth or the Raspberry Pi Pico - however, I don't rule out a problem with my code.

System Summary: Console on UART0, connection to RF chip on UART1. Additional connection to RF chip over SPI - not operated while TX or RX are in operation.

Anything transmitted to the RF chip more than about $A1 bytes (0.58.0) or $A4 (v1.0.2) would be truncated. Receive was not affected.

Here are some fragments of code.
`
: do_rx ( -- )

    1 UART>? IF

        1 UART>
 
        dup $21 <= IF
 
          [CHAR] ! EMIT #64 +
 
        THEN

        EMIT

    THEN
;

: do_tx_send_old ( -- )

  tx_index @ 0<> if

    tx_index @ 0 do

       1 >UART? IF
 
         tx_buffer I + c@ 1 >UART

       THEN

       do_rx

    loop

  then

;

Lots of things are not show here - UART or SPI set-up or buffer setup ... but many thousands of smaller packets (with full RF interactions) have been used for months with this set-up, so I'm pretty sure it's robust. All Forth code is on a single task/thread - there are no cross threading from an application perspective.

What I noticed was that transmission from the UART1 TX was completed when receive mode was happening from the RF chip (obviously it won't actually transmit any bytes), but not when the chip was in transmit mode (UART1 RX would have been 'silent').

I disconnected the UART1TX from the RF chip just in case there was contention - but operation was the same. Stops after $A1 characters from the Pico.

After much testing of various SPI modes and various other dead ends (remove do_rx call and other things), I came to the conclusion that the RX data and emitting back to the console must be delaying the transmit loop enough to transmit characters that I was trying to send (way less than 255, probably somewhere between $AF and $C0 characters.).

NOTICE that hte Zeptoforth buffer of 128 bytes ($80) plus the Pico transmit fifo of 32 bytes ($20) plus the serial transmit register (1 byte) is exactly where the size of when it stops working... i.e.. $A1 ...

This, plus the work around below, I suspect there is a problem with buffer full, >UART? and >UART inside Zeptoforth or the Raspberry Pi Pico peripheral. I looked at the Forth code that implement these commands - I didn't see any obvious problems.

My work around code is this:
: do_tx_send ( -- )

  tx_index @ 0<> if

    tx_index @ 0 do

      tx_buffer I + c@ 1 >UART

    loop

  then
;

NOTICE: It does not do any do_rx but we are not expecting any receive bytes, and other code read the rx buffer just before and after we put the RF chip into transmit mode.

Additionally it relies on the blocking of >UART when the buffer is full. I checked the Zeptoforth code for >UART and it looks good to me in this result.

I tested up to 258 characters transmitted - and it seems find and to work as I'd expect.

Thanks for finding this - I'll get to debugging it when I get home from work today. It is interesting that >uart works with blocking but it does not work with >uart? particularly because if >uart? is not working >uart should block anyways.

Wait a second...

I just looked at your code again:

: do_tx_send_old ( -- )
  tx_index @ 0<> if
    tx_index @ 0 do
      1 >UART? IF
        tx_buffer I + c@ 1 >UART
      THEN
      do_rx
    loop
  then
;

If 1 >UART? returns false here, it will simply skip the bytes in question, not outputting them. Are you sure this is the behavior you want? This may very well be why you're seeing what you're seeing.

I think what you want is something more like:

: do_tx_send_new ( -- )
  tx_index @ 0<> if
    0 begin dup tx_index @ < while
      1 >UART? IF
        tx_buffer over + c@ 1 >UART 1+
      THEN
      do_rx
    repeat drop
  then
;

(Note that I have not tried the above code.)

Yes, you are right. This is almost certainly the problem.

Feel free to close this ticket.

Many thanks.
Regards,
Rob