Home
JAQForum Ver 24.01
Log In or Join  
Active Topics
Local Time 12:52 25 Nov 2024 Privacy Policy
Jump to

Notice. New forum software under development. It's going to miss a few functions and look a bit ugly for a while, but I'm working on it full time now as the old forum was too unstable. Couple days, all good. If you notice any issues, please contact me.

Forum Index : Microcontroller and PC projects : PICO high speed serial problem

     Page 1 of 2    
Author Message
ElectroPI
Newbie

Joined: 27/04/2012
Location: Australia
Posts: 32
Posted: 09:08am 19 Jun 2024
Copy link to clipboard 
Print this post

Hi all,
I'm having some issues when running the PICO serial ports at high speed using MMBASIC 5.08.00. The system I've setup comprises 4 RP2040-zeros numbered PICO#0 to PICO#3. I'm using all 4 ADCs on each PICO to digitise 16 analogue signals with PICOs #1-3 sending their data to PICO#0 using multidrop on COM1 (with delays so there's no data collision). PICO#0 connects to a computer via its USB port so the 16 ADC readings can be saved. I'll skip most of the details but to get the speed I require I'm using 921600 baud multidrop between PICOs #1-3 and PICO#0 and I'm also sending data from PICO#0 to the computer via its USB port at 921600 baud. I do have time to run at 460800 baud but not much slower but the problem still appears - I lose the occasional character in the data transfer. I've narrowed it down to comms between a PICO and PICO#0.

So to check what's happening I setup a simple system using 2 raspi PICOs as shown:



PICO#1's COM1 Tx line is connected to PICO#0's Rx line. The options on both are:
OPTION CASE UPPER
OPTION AUTORUN ON

The first test I performed was to send a string of 32 characters (plus cr/lf) 10 times per second to the computer via PICO#1's USB port (921600 baud). It runs very well and doesn't drop any characters. I moved the USB cable over to PICO#0 and ran the same test - all good.

Then I loaded the following program into PICO#1 ie it sends 32 characters at 10Hz to PICO#0:

SETPIN GP1,GP0,COM1
OPEN "COM1:921600" AS #5
dat$= "12345678901234567890123456789012"
DO
 PRINT #5,dat$;
 PAUSE 100
LOOP


In PICO#0 I run the following program ie it collects the 32 bytes from PICO#1 and sends it to the computer:

SETPIN GP1,GP0,COM1
OPEN "COM1:921600" AS #5
DO
 inp$=INPUT$(70,#5)
 DO
 LOOP UNTIL LOC(#5)>=32
 inp$=INPUT$(70,#5)
 PRINT inp$  
LOOP


Note that I use the first inp$=INPUT$(70,#5) line to clear the input buffer before waiting for the next 32 bytes. I use 70 in both INPUT statements since the INPUT command reads what's in the input serial buffer even if it's less than 70. 70 is large enough to allow for over two 32 byte strings. What I get is the occasional hiccup. I let TeraTerm run continuously and at random times I see the following:



If I replace the PRINT statement in PICO#0 with

IF LEN(inp$)<>32 THEN PRINT inp$


I get lines printed roughly a minute or so apart as follows (ignore the first line since the program needs to sync first):



I've checked the serial line with a CRO and I can't see any change to the length of the data packet being sent by PICO#1 so I can only assume PICO#0 isn't keeping up with receiving data at that speed.

Any ideas?
 
Mixtel90

Guru

Joined: 05/10/2019
Location: United Kingdom
Posts: 6783
Posted: 09:53am 19 Jun 2024
Copy link to clipboard 
Print this post

Surely Print#5,dat$; has no CRLF as it's suppressed by the ";"? That would give a buffer overrun, wouldn't it?
Edited 2024-06-19 19:54 by Mixtel90
Mick

Zilog Inside! nascom.info for Nascom & Gemini
Preliminary MMBasic docs & my PCB designs
 
PhenixRising
Guru

Joined: 07/11/2023
Location: United Kingdom
Posts: 857
Posted: 09:55am 19 Jun 2024
Copy link to clipboard 
Print this post

For test purposes, I have bombarded four multi-drop Picos @921600 for days on end with no issues whatsoever but I use binary and only require 8-byte packets.
 
ElectroPI
Newbie

Joined: 27/04/2012
Location: Australia
Posts: 32
Posted: 10:18am 19 Jun 2024
Copy link to clipboard 
Print this post

I'm not sending cr/lf because in my original application using 4 PICOs I send ADC data as binary ie 2 bytes from each ADC. I use BIN2STR$(UINT16,1000*da0(0)) where da0(0) is the reading from ADC0, etc. This gives me values between 0 and 3300 which is comfortable as 2 bytes, little-edian format. I can't use cr or lf because I'm transferring binary values. 16 ADCs at 2 bytes each = 32 bytes.

So in my test programs as listed above I'm sending 32 bytes as 32 characters so I can see what's happening. In PICO#0 I'm reading everything from the buffer so there shouldn't be an over-run if it only received 32 bytes. Note that I'm not using the LINE INPUT command which would require cr/lf.
 
ElectroPI
Newbie

Joined: 27/04/2012
Location: Australia
Posts: 32
Posted: 10:50am 19 Jun 2024
Copy link to clipboard 
Print this post

Hi PhenixRising,
I just changed my test programs to send/receive 8 bytes and yep, same problem. Took a few minutes but it dropped the number '6' from the string:



How are you checking to see when your 8 bytes have been received? I assume you're reading the serial buffer using the INPUT$ command?
 
JohnS
Guru

Joined: 18/11/2011
Location: United Kingdom
Posts: 3800
Posted: 11:56am 19 Jun 2024
Copy link to clipboard 
Print this post

I agree, the CRLF should not be needed.

Currently I've no idea why any char goes missing - sorry!

Well... some hardware (*) reason, I suspect, but what?

Oh and those INPUTs with 70 are weird; if you want 32, use 32, but I don't think it's the cause.

John (* because I'm a software person)
 
ElectroPI
Newbie

Joined: 27/04/2012
Location: Australia
Posts: 32
Posted: 12:13pm 19 Jun 2024
Copy link to clipboard 
Print this post

The INPUT$(nbr,#fnbr) command "will return as many characters as are waiting in the receive buffer up to 'nbr' ". So I picked 70 just to make sure I can get at least 2 strings of 32 bytes even though there should only be 32 bytes received each time it loops. You're right, I don't think it's the cause and I have tried other values.
 
phil99

Guru

Joined: 11/02/2018
Location: Australia
Posts: 2135
Posted: 12:31pm 19 Jun 2024
Copy link to clipboard 
Print this post

Random glitches like this have been reported by a number of people. They are usually attributed to the USB system hogging processor time.
 
JohnS
Guru

Joined: 18/11/2011
Location: United Kingdom
Posts: 3800
Posted: 12:42pm 19 Jun 2024
Copy link to clipboard 
Print this post

An oddity of the code is that if LOC(#5) is >32 you've kind of misaligned things, whereas if you INPUT 32 the extra would stay buffered till the next loop.

There may well be a timing issue in that the PRINT may take longer than it takes for 32 chars to arrive - maybe?  Or longer than it takes 70?

I'd change the 70 to 32.

I'd also want to think hard (or measure) how long the PRINT can take.

Perhaps check that LOC(#5) is never >70!!

John
Edited 2024-06-19 22:43 by JohnS
 
ElectroPI
Newbie

Joined: 27/04/2012
Location: Australia
Posts: 32
Posted: 12:47pm 19 Jun 2024
Copy link to clipboard 
Print this post

But it's not occurring on the usb side. I've connected each PICO to the computer via usb and sent the same 32 byte string with cr/lf on the end at 921600 baud and they haven't missed at all. It seems to be the INPUT$ command using the PICO's serial port.
 
ElectroPI
Newbie

Joined: 27/04/2012
Location: Australia
Posts: 32
Posted: 01:06pm 19 Jun 2024
Copy link to clipboard 
Print this post

Hi John,
There shouldn't be any extra because I've only sent 32 bytes.

The reason I found the problem is because in my 4 PICO-zero setup I'm sending data from each of 3 PICOs to PICO#0 as low byte, high byte, low byte, high byte, etc for 32 bytes. Since the data is coming from the ADCs with floating inputs I should get values around 500 to 600. But occasionally I'd get values up in the thousands. When I checked I found that a dropped byte would make the packet go from low byte, high byte, low byte, high byte to high byte, low byte, high byte, low byte until the next packet.

The other test I did was to replace the PRINT command with an IF statement as I described in my first post ie it only prints when there isn't 32 bytes in the receive buffer. So except for being out of sync when I first type RUN I should never see anything printed after that. But of course I do after about a minute or 2 and it keeps happening as long as the program is running.
 
PhenixRising
Guru

Joined: 07/11/2023
Location: United Kingdom
Posts: 857
Posted: 01:38pm 19 Jun 2024
Copy link to clipboard 
Print this post

  ElectroPI said  
How are you checking to see when your 8 bytes have been received? I assume you're reading the serial buffer using the INPUT$ command?


Well 8 bytes is the maximum but the packets can be shorter.

If there is data in the buffer, I read one character.
if the character = &HAA, that's my header so this could be a packet

Read the next character
If the character = my_node_address then looks like it's for me. If not, continue to read the data anyway (to empty the buffer) but don't do anything with it.

Read the next character
In my case, this is a control byte;upper nibble = command, lower nibble = # of data-bytes to follow.

Read each data-byte

Final byte is checksum

Perform checksum and if all is good, execute the command on the Pico
TX a status byte indicating good/bad checksum and other status info

Go back and wait for more
Edited 2024-06-19 23:42 by PhenixRising
 
ElectroPI
Newbie

Joined: 27/04/2012
Location: Australia
Posts: 32
Posted: 10:35pm 19 Jun 2024
Copy link to clipboard 
Print this post

OK, I see you need the extra overhead because your packets vary in size.

My application has fixed packet sizes - each of 3 PICOs sends its 4 channel ADC data as 8 bytes (2 per ADC), little-edian format to PICO#0. Each PICO has a slight delay before sending to make sure there's no overlap of data. PICO#0 has its own 8 bytes so it appends the 24 bytes received from the others and then sends the 32 byte packet to the computer. I'm running the ADCs at 360 samples per second so that's why I need the high speed to transfer data.

So as I mentioned I found that a byte is randomly dropped when transferring data to PICO#0 so to eliminate problems with multi-drop, timing, etc I setup 2 PICOs with a hardwired connection from Tx on PICO#1 to RX on PICO#0 as shown in my first post. And that's what surprised me - a byte still gets dropped randomly during data transfer. I slowed down data transfer to ten packets per second but the problem still occurs even at 1 transfer per second except you have to wait longer to see it.

My conclusion is that there must be a bug in the INPUT$ command.
Matherp - could this be the case?
 
TassyJim

Guru

Joined: 07/08/2011
Location: Australia
Posts: 6098
Posted: 12:37am 20 Jun 2024
Copy link to clipboard 
Print this post

Have you tried adding a second stop bit to the sending side (assuming the port is one that allows configuring 2 stop bits).

Jim
VK7JH
MMedit   MMBasic Help
 
phil99

Guru

Joined: 11/02/2018
Location: Australia
Posts: 2135
Posted: 12:49am 20 Jun 2024
Copy link to clipboard 
Print this post

  Apendix A said  The OPEN Command
A serial port is opened using the command:
OPEN comspec$ AS #fnbr
‘fnbr’ is the file number to be used. It must be in the range of 1 to 10. The # is optional.
‘comspec$’ is the communication specification and is a string (it can be a string variable) specifying the serial
port to be opened and optional parameters. The default is 9600 baud, 8 data bits, no parity and one stop bit.
It has the form "COMn: baud, buf, int, int-trigger, EVEN, ODD, S2, 7BIT" where:
 ‘n’ is the serial port number for either COM1: or COM2:.
 ‘baud’ is the baud rate. This can be any number from 1200 to 921600. Default is 9600.
 ‘buf’ is the receive buffer size in bytes (default size is 256). The transmit buffer is fixed at 256 bytes.
 ‘int’ is interrupt subroutine to be called when the serial port has received some data.
 ‘int-trigger’ is the number of characters received which will trigger an interrupt.
All parameters except the serial port name (COMn:) are optional. If any one parameter is left out then all the
following parameters must also be left out and the defaults will be used.
Five options can be added to the end of 'comspec$'. These are:
'S2' specifies that two stop bits will be sent following each character transmitted.
 EVEN specifies that an even parity bit will be applied, this will result in a 9-bit transfer unless 7BIT is set.
 ODD specifies that an odd parity bit will be applied, this will result in a 9-bit transfer unless 7BIT is set
 7BIT specifies that there a 7bits of data. This is normally used with EVEN or ODD
 INV specifies that the output signals will be inverted and input assumed to be inverted

Edited 2024-06-20 10:52 by phil99
 
ElectroPI
Newbie

Joined: 27/04/2012
Location: Australia
Posts: 32
Posted: 04:07am 20 Jun 2024
Copy link to clipboard 
Print this post

Thanks Jim and Phil. I was wondering if there was a simple way to add a slight delay after sending each character in case it was a timing issue in the INPUT$ command and using 2 stop bits is a brilliant idea. So far it seems to be working but I'll let it run longer to see how it goes.

BTW I tried something else - I upped the cpu speed of PICO#0 to 378000000 and this seems to solve the problem when using 1 stop bit. I can even send the 32 byte string from PICO#1 to PICO#0 at 400 strings per second and it seems to be very stable with no lost bytes. Once again I need to let this run for quite a while to be absolutely sure.

Unfortunately both solutions are work-arounds to what seems to be a timing problem in the INPUT$ command when using high baud rates.

all the best and thanks everyone for all the suggestions
Peter
 
PhenixRising
Guru

Joined: 07/11/2023
Location: United Kingdom
Posts: 857
Posted: 05:13am 20 Jun 2024
Copy link to clipboard 
Print this post

@ElectroPI

The one thing I never do is blindly flush the buffer. I evaluate everything that comes out.
 
Mixtel90

Guru

Joined: 05/10/2019
Location: United Kingdom
Posts: 6783
Posted: 07:20am 20 Jun 2024
Copy link to clipboard 
Print this post

I think that's an excellent idea.

Firing a continuous string of data that you *do* want isn't going to work in the long term over any non-clocked serial interface, particularly if the distance involved is non-trivial. You have to allow for cable capacitance and random glitches introduced by the environment so a proper, frame and checksum system is very important if you want reliability - even if it has more overhead.

The MATH CRC function can be used to get a CRC checksum of a set of data.

If the distance is short then it's better to stick with I2C or SPI really, even though they are unbuffered. They are both clocked and are designed specifically for this purpose.

.
Edited 2024-06-20 17:25 by Mixtel90
Mick

Zilog Inside! nascom.info for Nascom & Gemini
Preliminary MMBasic docs & my PCB designs
 
Volhout
Guru

Joined: 05/03/2018
Location: Netherlands
Posts: 4223
Posted: 07:26am 20 Jun 2024
Copy link to clipboard 
Print this post

  ElectroPI said  Unfortunately both solutions are work-arounds to what seems to be a timing problem in the INPUT$ command when using high baud rates.


They are solutions to a problem! When you increase the speed enough, there will allways be a moment when the CPU can't keep up.

And you will find more problems. Because at this data rate (close to 1 Mbit/s), doing something usefull with the data will introduce new challenges. At 378MHz, MMBasic can execute close to 100000 (MM)Basic instructions per second.

If you look at system design (how much data MMBasic can process per second) depends of coarse on the complexity of the processing, but if you assume 10 MMBasic instructions per value (i.e. a byte) you can process maximum 10000 values per second. For 16 ADC channels that would be 625 per second (1.6ms), and a baudrate of 16x(16bit+start/stop)x600 = 172kbaud would be sufficient.

Maybe your design could also work, just as well, with 115200 baud, at 300 ADC refreshes per second) where the problem does not exist.

Volhout
Edited 2024-06-20 17:41 by Volhout
PicomiteVGA PETSCII ROBOTS
 
ElectroPI
Newbie

Joined: 27/04/2012
Location: Australia
Posts: 32
Posted: 10:40pm 20 Jun 2024
Copy link to clipboard 
Print this post

Thanks for all the suggestions. My original four RP2040-zeros are side by side on a PCB roughly 150mm long so it's not as if I'm sending data over long distances. Though with a high baud rate & random dropped bytes I wasn't sure if it was timing or maybe the multi-drop wasn't pulling the serial line low enough (I used Schottky diodes) or whatever. I do have some queries regarding handling the ADCs but I'll post a new topic for that.

So if you re-read my original post, all tests I've now performed are using 2 raspi PICOs interconnected with 3 wires about 40mm long: +5 to +5, 0V to 0V and COM1 Tx from PICO#1 to COM1 Rx on PICO#0. I move the USB lead from one to the other to re-program each PICO but I leave it plugged into PICO#0 for the data transfer tests.

I confirmed that sending a 32 byte string + cr/lf (PRINT dat$) at 921600 baud via the USB port on each PICO to a computer running TeraTerm and Putty didn't drop any bytes when sending at 1 string per second and also 100 strings per sec. I then loaded a program into PICO#1 to send the 32 byte string via COM1 Tx at 921600 baud but once per second and looked at the signal with a CRO (oscilloscope). The packet didn't change size so confirmed 32 bytes were always sent (it would have been obvious if a byte was dropped).

Then I programmed PICO#0 to wait for a min of 32 bytes to be received from PICO#1 using LOC(), it inputs everything from the receive buffer using INPUT$(70,#5) (the 70 is there so it'll read up to 2 strings just in case) and to send everything it receives to TeraTerm at 921600 baud. So on the screen it should display 1 line of 32 characters each second.

So imagine my surprise when occasionally it skipped a second and then 1 second later printed nearly 2 strings of characters. 1 string per sec is hardly stretching any buffer or timing limits but there is was - it was dropping the occasional byte. The work-around as we now found is to either send the data with 2 stop bits or to up the speed of PICO#0. So it seems to indicate a timing problem in MMBASIC either with the INPUT$ command or thinking about it overnight, maybe there's an interaction using LOC() while serial data is being received by the serial port, I just don't know.

Another test I'll do later today is to ditch the LOC() command and use
"COMn: baud, buf, int, int-trigger, EVEN, ODD, S2, 7BIT"
With int-trigger set to 32 it'll trigger an interrupt when 32 bytes are received and I'll read the data then. That should confirm if LOC() was the problem.

Peter
 
     Page 1 of 2    
Print this page
© JAQ Software 2024