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: AustraliaPosts: 32 |
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 KingdomPosts: 6783 |
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 KingdomPosts: 857 |
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: AustraliaPosts: 32 |
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: AustraliaPosts: 32 |
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 KingdomPosts: 3800 |
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: AustraliaPosts: 32 |
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: AustraliaPosts: 2135 |
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 KingdomPosts: 3800 |
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: AustraliaPosts: 32 |
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: AustraliaPosts: 32 |
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 KingdomPosts: 857 |
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: AustraliaPosts: 32 |
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: AustraliaPosts: 6098 |
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: AustraliaPosts: 2135 |
Edited 2024-06-20 10:52 by phil99 |
||||
ElectroPI Newbie Joined: 27/04/2012 Location: AustraliaPosts: 32 |
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 KingdomPosts: 857 |
@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 KingdomPosts: 6783 |
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: NetherlandsPosts: 4223 |
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: AustraliaPosts: 32 |
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 |