earlephilhower / arduino-pico

Raspberry Pi Pico Arduino core, for all RP2040 and RP2350 boards
GNU Lesser General Public License v2.1
2.03k stars 421 forks source link

SerialPIO differs to Hardware Serial #2541

Closed flatt3rn closed 3 days ago

flatt3rn commented 1 week ago

Hello,

I have quite some trouble getting SerialPIO to work. I am using a Raspi Pico W with a NEO6M GPS Module. When I am using the Hardware Serial interface, I get the right data. When using the Software Serial / Serial PIO I get some part of the data and some corrupted data.

My sample code looks like this:

#include "SoftwareSerial.h"

SerialPIO mySerial( 4, 5 );

void setup() {
  mySerial.begin(9600);
  Serial.begin(9600);   
}

void loop() {
  while (mySerial.available()) {
    Serial.write(mySerial.read());
  }
}

With that I get the following result:

$GPGSV,3,2,11,15,09,17�������������������������������������������ԕib�b��b��b��b���bb��b��b���bb��b��b���bR��j*�A11����������ѹ���Y�9��
5)�$GPRMC,174555.00,V,,,,,,,131024,,,N*7F
$G���������������������������������������������NN)�bbbbbbR��jR":AM��ű��������������������������5H�����XfXbXbbX`lXbfX`pnXXb`XbbXdnfXXbbX``X125,,12,76,243,*76
$GPGSV,3,2,11,15,09,17�������������������������������������������ԕib�b��b��b��b���bb��b��b���bb��b��b���bR��j(�A11����������չ���Y�9��5)�$GPRMC,174556.00,V,,,,,,,131024,,,N*7C
$G���������������������������������������������NN)�bbbbbbR��jR":AM��ű��������������������������5H�����XfXbXbbX`lXbfX`pnXXb`XbbXdnfXXbbX``X125,,12,76,243,*76
$GPGSV,3,2,11,15,09,17�������������������������������������������ԕib�b��b��b��b���bb��b��b���bb��b��b���bR��j*�A11����������ٹ���Y�9��5)�$GPRMC,174557.00,V,,,,,,,131024,,,N*7D
$G���������������������������������������������NN)�bbbbbbR��jR":AM��ű��������������������������5H�����XfXbXbbX`lXbfX`pnXXb`XbbXdnfXXbbX``X125,,12,76,243,*76
$GPGSV,3,2,11,15,09,17�������������������������������������������ԕib�b��b��b��b���bb��b��b���bb��b��b���bR��j*�A11����������ݹ���Y�9��5)�$GPRMC,174558.00,V,,,,,,,131024,,,N*72
$G���������������������������������������������NN)�bbbbbbR�5)�AM��ű������������������������Uf`H�����XfXbXbbX`lXbfX`pnXXb`XbbXdnfXXbb,00,125,,12,76,243,*76
$GPGSV,3,2,11,15,0����������������������������������������������ԕib�b��b��b��b���bb��b��b���bb��b��b���bR��j(�A11��������������Y�9���5)�$GPRMC,174559.00,V,,,,,,,131024,,,N*73
$G���������������������������������������������NN)�bbbbbbR�"5)�AM��ű������������������������Uf`H�����XfXbXbbX`lXbfX`pnXXb`XbbXdnfXXbb,00,125,,12,76,243,*76

When using the Hardware Serial on the same device I get:

17:31:38.501 -> $GPGLL,,,,,153139.00,V,N*46
17:31:39.117 -> $GPRMC,153140.00,V,,,,,,,131024,,,N*7A
17:31:39.165 -> $GPVTG,,,,,,,,,N*30
17:31:39.165 -> $GPGGA,153140.00,,,,,0,00,99.99,,,,,,*64
17:31:39.205 -> $GPGSA,A,1,,,,,,,,,,,,,99.99,99.99,99.99*30
17:31:39.238 -> $GPGSV,4,1,15,02,01,035,,05,03,195,,10,14,328,,12,14,212,21*7F
17:31:39.336 -> $GPGSV,4,2,15,13,50,139,,14,34,055,,15,68,206,22,17,30,096,*71
17:31:39.400 -> $GPGSV,4,3,15,19,21,126,,21,02,018,,22,55,067,,23,32,291,*77
17:31:39.438 -> $GPGSV,4,4,15,24,50,277,23,30,03,087,,33,28,208,27*47
17:31:39.505 -> $GPGLL,,,,,153140.00,V,N*48
17:31:40.104 -> $GPRMC,153141.00,V,,,,,,,131024,,,N*7B

It looks like some problems with the SerialPIO Class.

earlephilhower commented 1 week ago

Because this is special HW only you have, there's not much we can do here. Most likely it's a timing issue. You could try adjusting the baud up or down a few percent to see if it can match whatever you're connecting it to.

You could also try capturing the devices' serial waveform at high enough resolution to see the actual bit-times to verify.

earlephilhower commented 1 week ago

Quick loopback test (wire from GPIO 0 <-> GPIO 3) looks fine:

#include <SerialPIO.h>

SerialPIO s(2,3);

void setup1() {
  Serial1.setRX(1);
  Serial1.setTX(0);
  Serial1.begin(9600);
  while (1) {
    delay(10);
    Serial1.println("yo, yo, yo baby pop");
  }
}

void setup() {
  s.begin(9600);
}

void loop() {
  while (s.available()) {
    Serial.write(s.read());
  }
}

and

...
11:49:11.758 -> yo, yo, yo baby pop
11:49:11.789 -> yo, yo, yo baby pop
11:49:11.821 -> yo, yo, yo baby pop
11:49:11.821 -> yo, yo, yo baby pop
11:49:11.854 -> yo, yo, yo baby pop
11:49:11.886 -> yo, yo, yo baby pop
...

Been going a few minutes now, nothing weird popped up...

earlephilhower commented 1 week ago

I just removed the delay between messages completely and I'm still not seeing anything weird in loopback.

flatt3rn commented 1 week ago

Quick loopback test (wire from GPIO 0 <-> GPIO 3) looks fine:

#include <SerialPIO.h>

SerialPIO s(2,3);

void setup1() {
  Serial1.setRX(1);
  Serial1.setTX(0);
  Serial1.begin(9600);
  while (1) {
    delay(10);
    Serial1.println("yo, yo, yo baby pop");
  }
}

void setup() {
  s.begin(9600);
}

void loop() {
  while (s.available()) {
    Serial.write(s.read());
  }
}

and

...
11:49:11.758 -> yo, yo, yo baby pop
11:49:11.789 -> yo, yo, yo baby pop
11:49:11.821 -> yo, yo, yo baby pop
11:49:11.821 -> yo, yo, yo baby pop
11:49:11.854 -> yo, yo, yo baby pop
11:49:11.886 -> yo, yo, yo baby pop
...

Been going a few minutes now, nothing weird popped up...

I tried the exact code from above and connected GPIO0 to GPIO3. At first it does not output anything over the Serial Monitor. I checked the GPIO Pins => they worked fine. Then I though it is a version issue. I updated Raspberry Pi Pico/RP2040 in the board manager from version 4.0.1 to 4.1.1. There i noticed that also Arduino Mbed OS RP2040 Boards by Arduino was installed. Does that make a difference (I am not to familiar with the Arduino IDE, I mostly use the C SDK)? I deinstalled it and now your sketch is working at least.

Unfortuanelty the NEO6M GPS module still is not working. In the next days I have access to an oscilloscope and will check the actual baud rate. Because a small shift in the baudrate would also be plausible for me. I will give an update on that.

flatt3rn commented 1 week ago

Out of curiosity I tested different baudrates with this code:

#include "SoftwareSerial.h"

#define START_BAUDRATE 9590
#define END_BAUDRATE 9700
#define BAUDRATE_STEP 10

SerialPIO mySerial( 3, 4 );

void setup1() {
  for (uint32_t i = START_BAUDRATE; i < END_BAUDRATE; i+=BAUDRATE_STEP) {
    Serial.print("\n\n\n\nBaudrate: ");
    Serial.println(i);

    mySerial.begin(i);
    delay(4000);
    mySerial.end();
  }
}

void setup() {  
  Serial.begin(115200);   
}

void loop() {
  while (mySerial.available()) {
    Serial.write(mySerial.read());
  }
}

My module is starting to work an 9610 baud. Unfortunately I don't have another reference module to check if just mine is faulty. But since others had similar issues it might help other people at least finding the problem.

But I am still interested, if the Arduino Mbed OS RP2040 Boards package is an issue.

earlephilhower commented 1 week ago

The Arduino Mbed OS RP2040 Boards package isn't an issue, it won't be used at all if you select this core from the IDE.

maxgerhardt commented 1 week ago

Does the SerialPIO class do 8-times oversampling and a majority-vote for the bit value or does it capture the bit once at the time it thinks the bit should be on line?

earlephilhower commented 1 week ago

It samples at 50% of the bit time, starting the clock at the start bit edge.

https://github.com/earlephilhower/arduino-pico/blob/fdd755715655bd9ddd6a3d7d6653be5a177c8e6b/cores/rp2040/pio_uart.pio#L71-L104

maxgerhardt commented 1 week ago

Maybe a way to debug this is to modify the PIO program to also toggle GPIO pin whenever its read the bit from the line, and compare that to the UART signals. Or there is a bug that the initial delay is not correctly done and it samples the signal right at where it's supposed to change -- with some variance in the actual baud, always missing it.

earlephilhower commented 1 week ago

We can just work through things manually, the logic is simple enough.

The SP sets X/OSR to 19 for 8N1 case here (the 1st insn is overwritten on creation to support different bits/parity/etc.).

So the loop will shift (right) in 20 bits, with the 1st = middle of the start bit, 2nd = edge (thrown out), 3rd = middle of bit 0, etc. (Right shift since UART is LSB-first, and 20 times because the jmp x-- is a post-decrement)

FIFO data is: xSx7.x6x5.x4x3.x2x1.x0xs.GGGG.GGGG.GGGG where S = stop, s = start, G = garbage(0, not shifted in), x = transition

Right-shift 33 - 19 = 14 bits 0000.0000.0000.00xS.x7x6.x5x4.x3x2.x1x0

And take every other bit starting at 0 S76543210

We strip off the stop bit before placing it in the SerialPIO FIFO.

So, as far as I can see, all is well logically with the existing code. We went through a few iterations to get it right, but it hasn't been touched since 2022 (other than the PIO2 support for RP2350 and inversion support).

earlephilhower commented 1 week ago

One thing I just thought of would be the actual 1/2 bit times. We use a 1/2 bit timer countdown running at sysclk.

clock_get_hz(clk_sys) / (_baud * 2) - 7 in this case = 133M / (9600 *2) - 7 = 6920.

Looking at the PIO source code this might be a single 133MHZ cycle too short (because there are now 5 insns, not 7, but it's post-decrement so a loop with 6920 runs for 6921 times) but that's trivial.

Actual bitrate = 133000000 / ( 2 * (6921 + 5) ) = 9601.5 baud sampling rate, a 0.016% error and not significant.

earlephilhower commented 3 days ago

No update in a week and nothing seems wrong, so closing. If we get a waveform w/detained enough timing we can possibly see more...