View previous topic :: View next topic |
Author |
Message |
njepsen
Joined: 13 Aug 2007 Posts: 469
|
Posted: Fri Jun 26, 2020 6:55 am Post subject: Software UART issues |
|
|
I am using a sw UART with the Atmega 1284p, and i also have an onboard Raspberry Pi, which talks to the Atmega on a slow SW UART. All the hardware UARTS are talking to the more important things so are not available.
The issue i, having is trying to receive a 200byte string of text whilst there are interrupts going on. The first 50 or so char are received OK, then I get text mixed with non printable char.
I cant turn interrupts off because I use a timer to generate the system clock and another to keep the watchdog from barking/biting me.
Any advice?
The code for the receiver looks like this: Code: |
'*******************************************************************************
$regfile = "m1284pdef.dat"
$crystal = 9830400
$framesize = 800
$hwstack = 550 '
$swstack = 550
$frameprotect = 1
config submode = old 'old = need to declare subs or functions
$include "config_mmcsd_hc.bas"
$PROG &HFF , &HC7 , &HD0 , &HFD
'==============================================================================
'*****************************************************
Function pi_response(byval picommandstr As String) As String
Local Tempstr7 As String * 250
Local Td As Long
#if Pi_fitted = 1
'(
This function runs a command and returns the response to the caller
******* VALID PI COMMANDS *********
upload,1 pi free to send audio files to the server
upload,0 inhibit uploads
status? get status from pi
shutdown tell pi to shutdown in an orderly manner. takes 30s
pitakeboth recording + image
ssh,0
ssh,1
ppp,0 point to point coms on and off
ppp,1
startrecord,xx start a recording xx seconds long
stoprecord
bitrate,nn nn = 8,12,16,24,
register,phonenumber in format "%2bnnnnnnnnnnn"
Pi terminators;
after all echos $$ <CR>
after all data ## <CR>
')
Tempstr7 = ""
Td = Local_secl + 10 '10 secs
If picommandstr <> "" Then
Print #7 , picommandstr 'send cmd to pi ser port
Do
Rxbyte = Inkey(#8) '#8 is pi sw uart ser port
If Rxbyte <>0 Then
Tempstr7 = Tempstr7 + Chr(rxbyte)
End If
Loop Until Local_secl >= Td 'or rxbyte = 35 ' 35 = # expected terminator Td is time delay timeout.
Delchars Tempstr7 , 32 'remove spaces
If Tempstr7 <> "" Then
Print #1 , ">> Pi said " ; Tempstr7
Else
Print #1 , ">> no response from pi"
Tempstr7 = "no response from pi"
End If
pi_response = Tempstr7 'return
end if
#endif
End Function
'****************************************************** |
(BASCOM-AVR version : 2.0.8.2 ) _________________ Neil |
|
Back to top |
|
|
JC
Joined: 15 Dec 2007 Posts: 585 Location: Cleveland, OH
|
Posted: Fri Jun 26, 2020 4:48 pm Post subject: |
|
|
Interesting question.
I probably should stay out of this discussion as I don't know enough about how the SW UART works.
That said:
Probably not related to your problem, but how is Local_sec1 defined?
Does it roll over at 59 seconds, or count to some huge number?
If it rolls over at 59 to 0, then if it is at 56 seconds, and you add 10 sec for a timeout limit, it will never time out.
If it is a Word it will roll over about once every 17 hours, causing occasional errors, if it isn't reset elsewhere.
Because Bascom uses SW timing for the SW UARTS, (which leaves all of the HW T/Cs available to the programmer), any interrupts will obviously lengthen any SW delays. That likely makes the SW UART read a bit value too late, it is reading the following bit and missing one, giving a framing error.
HW UARTS. (IIRC), usually resynch on each Start Bit. One might expect the SW UART to do the same, (it seems reasonable). That makes it odd that you can receive 50 characters via the SW UART before you get errors.
If the problem was that you have too many interrupts firing and taking too long, delaying the reading of a bit within a byte, (and the SW UART does re-synch on the next Byte's Start Bit), then one would expect that the errors during ANY incoming character, and not that you would receive 50 characters correctly before the error showed itself.
So, perhaps the --> assumption <-- that the SW UART resynchs on each Start Bit is invalid?
A buffer overflow occurring after about 50 incoming characters seems unlikely, as that would likely give loss of bytes, not corrupted bytes, but again even that is uncertain as the SW UART implementation isn't known. It would certainly be worthwhile, however, to look at your buffer sizes, and ability to handle the incoming data streams in a timely manner, to make sure that that isn't the source of the problem. Testing here could include both decreasing and well as increasing buffer sizes to see the impact upon your ability to receive 50 characters before a problem occurs. (Does changing the buffer size change how many characters you can correctly receive, 25, 50, 100...?)
Program structure?
Obviously the goal is to not have any interrupts running when you are receiving data via a SW UART. So several approaches come to mind, (all untested!).
As you review your overall program structure, is it possible to shorten any of your ISRs? Can you change anything to jest setting a flag in the ISR and handling the processing within the Main Loop?
What is your System Clock ISR firing at? At your present SW UART baud rate, how many times will the System Clock ISR fire during the reception of a single byte? If your System Clock is firing multiple times during the timeframe for one byte to be received that would cause a cumulative error. Can you slow down your System Clock ISR Rate?
What is the Baud Rate error ( % ) for your current baud rate and micro's clock frequency? Your goal should be to have it be < 2 % for a HW USART, (Giving a possible error of (+) 2 % on one end, and (-) 2 % on the other device, for a total of 4% baud rate error, max, for reliable communications, IIRC). That works well for HW UARTs, but clearly doesn't apply as a safe operating range for a SW UART, especially if you are trying to run ISRs simultaneously.
If you are right on the edge of reliable communications, baud rate wise, then perhaps the ISRs are slowing the baud rate down to the point where it is impacting the data reception.
Normally, slowing a HW UART baud rate down won't impact the data reception IF the baud rate error remains unchanged, (lots of caveats here). However, for a SW UART slowing the baud rate down will increase the bit time for each bit. This would widen the window during which one had to read the bit's value.
This might, or might not, improve the situation. Recall that the longer bit time will also allow time for more ISRs to fire. So it might, or might not, improve the situation, depending upon the specifics of the program. If the overall ISR processing time is rather low, then slowing the SW UART baud rate might improve the system performance. It the micro is spending a lot of its processing time within ISRs, then lengthening the SW UART bit time likely won't improve the situation.
That said, slowing down the SW UART's baud rate is certainly an easy test to perform, although it might have an impact on other aspects of your system's data processing. (Assuming you have control over the baud rate at both ends of the comm link.)
Time for a processor change?
At the end of the day perhaps you have just reached a limitation on the data processing throughput you can achieve with the Mega.
Recall that the new AVRs, such as the AVR128DA48, can run in spec at 20 MHz, (3 or 5 volts), and have some new (Xmega type) features. I asked Bascom Support if they wanted me to send them a few for their tinkering and haven't heard back, but they are not (yet?) supported). The Xmegas are supported, and run in spec at 32 MHz. That gives one a tremendous increase in processing power, (both millions more clock cycles / Sec, and faster execution of every instruction, a double win). The larger ones also have up to 8 HW UARTS, (IIRC). If your ISRs execute much faster then there is less impact upon reading a SW UART bit within its given window. The AVR128DA48 has a two level priority interrupt controller, the Xmegas have a 3 level priority interrupt controller, which can also be helpful at times for structuring how multiple ISRs will be handled when they occur "simultaneously", (although perhaps not directly helpful regarding the usage of SW UARTS).
Last thought:
Many people dislike having multiple processors on a board for a given project. But if one thinks of adding a second micro as just another sensor or chip performing a given task, then it sometimes makes sense to do so. In this case the second processor's only purpose would be to handle several UART data links, and pass the incoming data on to the Master micro via one UART channel. The data from each of the channels would be places in defined packets, e.g. <A,X,Y,Z...>, Where following the Start of Packet character the next data element tells the Master Micro which device's data is contained within this packet. The second micro is functioning as a smart data collator / multiplexer.
Simply switching to an Xmega with more processing power and lots of HW UARTs might be an easier approach to a system upgrade.
Oh well, thought I'd share a few thoughts. Hopefully you will get your system up and running reliably!
JC |
|
Back to top |
|
|
njepsen
Joined: 13 Aug 2007 Posts: 469
|
Posted: Sat Jun 27, 2020 6:31 am Post subject: |
|
|
Hello JC, and many thanks for your comments and suggestions.
To answer some of your questions:
Local_sec1 is a long, so it wont roll over any time soon.
Yes I realize my ISRs will slow down my RTC, but the project is a telemetry system and communicates with a server every 15 minutes, & i sync the RTC at that time so time errors are not an issue.
In actual fact, i have not tested whether it is the interrupts that are causing the issue - I'm guessing really, based on the fact that i use another SW uart which also has the same issue but behaves much better if I turn off the ISRs. This 2nd SW uart is a debug port, which provides a real time commentary on whats happening to a local serial connector, and I can plug a laptop in here and see whats going on. Also - if I limit the number of characters coming from the Pi, the problem goes away.
As I said before -its difficult to stop the ISRs because that causes a watchdog after a few seconds.
I dont know how the SW UART works and how many samples per bit, where it syncs etc but it must be quite smart to handle the full range of baud rates.
You asked - does changing the buffer size help. Which buffer?
I have 2 timer ISRs, and very little happens in each. Code: |
The other serial coms are not talking when this problem arises,(because ser coms is under program control, not asynchronous) so thats probably not the issue.
'******************************************************************************
timer1overflow:
timer1 = 55936
decr wd_holdoffw
toggle heartbeatLED
if wd_holdoffw > 0 then
dn20 = 0
reset watchdog 'processor WD, set to 8sec. This is the only time in the whole code this happens
else
'the following code is very rarely if never . Causes system reset after 8 secs.
print #1 , "WDHoldoff FAIL"
if dn20 = 0 then Call Write_err_message( "WDHoldoff FAIL") 'write once only
dn20 = 1
end if
return
'***************************************************************************************
Timer2overflow:
'***************************************************************************************
'T2 rolls over 37.5x per seconds
'incr secflagb
'used to do 1 sec profile Lmax check in main loop
Return
'*****************************************************************************
Timer3overflow:
'********************************************************************************
'T3 rolls over every 1 second
Timer3 = 55936 '65536 - 9600x1
Incr 1_secflagb 'used to read profile Lmax
Incr Unix_time 'used in many places for recordings and images
Incr Local_secl 'RTC
Return
end |
Processor Change:
You may be right. I have about 70 of these things out in the field, but a new PCB would not be the end of the world for the next version. _________________ Neil |
|
Back to top |
|
|
JC
Joined: 15 Dec 2007 Posts: 585 Location: Cleveland, OH
|
Posted: Sat Jun 27, 2020 10:50 pm Post subject: |
|
|
Which buffers...
Um, my mistake.
As I was thinking about the SW UART issue I forgot that you are reading the SW UART in the foreground, not within an interrupt driven, read a byte and stuff it in a buffer, background process.
JC |
|
Back to top |
|
|
njepsen
Joined: 13 Aug 2007 Posts: 469
|
Posted: Sun Jun 28, 2020 1:39 am Post subject: |
|
|
Good idea - i'll try that. Easy to do. You also got me looking at the xmega, which have some very powerful and useful advantages.
I'll get back to you re the interrupt driven SW uart. _________________ Neil |
|
Back to top |
|
|
JC
Joined: 15 Dec 2007 Posts: 585 Location: Cleveland, OH
|
Posted: Sun Jun 28, 2020 2:25 am Post subject: |
|
|
I think I wasn't clear above.
In the HW UART the module finds the Start bit, samples the data bits, puts the result in a HW register, and then sets a flag and then optionally generates an interrupt.
This is all performed without any foreground processing code on your part.
In Bascom, when the HW UART is configured for an interrupt driven, ring buffered, configuration, then once again the HW module does the low level work and the ISR grabs the incoming data byte, stuffs it in a ring buffer, (perhaps sets some internal error flags for parity error, framing error, etc.), and exits.
This is very fast, it only takes a few instructions to read a Byte, stuff it in a buffer, and adjust the buffer pointers.
The only actual processing your code actually does in this case is see if there is any data in the ring buffer, and if so read it out.
That process can be interrupted as needed, nothing there is super time critical, as the incoming data capture was already done by the hardware.
With a SW UART, whether written by you, or as provided by a Bascom instruction, it is running in the foreground.
The timing to read the incoming data bits is therefore important, and the presence of interrupts can disrupt the reading with respect to the Start Bit, thereby giving incorrect data.
Just putting a software UART command within an interrupt won't change anything.
When it fires, and your foreground code goes to read the SW UART pin, it is still a foreground process, and is subject to the same timing errors if any other ISRs are running.
JC |
|
Back to top |
|
|
Paulvk
Joined: 28 Jul 2006 Posts: 1257 Location: SYDNEY
|
Posted: Sun Jun 28, 2020 3:54 am Post subject: |
|
|
Hello JC
I thought that once you enter an interrupt other interrupts are stopped until you exit?
hence staying for short as possible in interrupts
Regards paul |
|
Back to top |
|
|
EDC
Joined: 26 Mar 2014 Posts: 971
|
|
Back to top |
|
|
JC
Joined: 15 Dec 2007 Posts: 585 Location: Cleveland, OH
|
Posted: Sun Jun 28, 2020 3:47 pm Post subject: |
|
|
I must be having a bad week...
Paul, you are correct, that in the Megas and Tinies once you enter an interrupt the others are placed on hold.
(My excuse is that the last couple of projects I've done have used an Xmega, and with the 3-tiered interrupts that isn't the case.)
Sorry if I am giving you bad advice.
But hopefully it is helpful to carefully re-think the system!
JC |
|
Back to top |
|
|
laborratte
Joined: 27 Jul 2005 Posts: 299 Location: Berlin
|
Posted: Tue Jun 30, 2020 4:52 pm Post subject: |
|
|
Just a thought:
In your loop
Code: | Do
Rxbyte = Inkey(#8) '#8 is pi sw uart ser port
If Rxbyte <>0 Then
Tempstr7 = Tempstr7 + Chr(rxbyte)
End If
Loop Until Local_secl >= Td 'or rxbyte = 35 ' 35 = # expected terminator Td is time delay timeout.
|
your are concatenate strings (Tempstr7) whihch IMHO eats up more and more time as the string grows. Might be possible at >50 chars the time consumption is too high for your data stream and you are missing bits.
try to fill up a string overlay array with a pointer which schould be much faster.
[edit] if pi terminates every echo or data with <cr> you can easily use input instead of an inkey loop |
|
Back to top |
|
|
njepsen
Joined: 13 Aug 2007 Posts: 469
|
Posted: Tue Jun 30, 2020 9:44 pm Post subject: |
|
|
Thanks Laborratte - I normally use a string array, but thought that building a string might be faster, alythough at 9600 i didnt think it would be a problem. . Easy to try. Yes there is a CR LF. _________________ Neil |
|
Back to top |
|
|
AdrianJ
Joined: 16 Jan 2006 Posts: 2483 Location: Queensland
|
Posted: Wed Jul 01, 2020 1:30 am Post subject: |
|
|
Building strings from an array is good, but be aware that the string MUST end with a null ( chr(0) ) Else when you try and use the string somewhere else, you will get what looks like corruption, and very long strings.
My practice was always to insert the incoming character, then insert a null at the next location. That way you always have a terminating null, even if the transmission fails unexpectedly. The overhead of inserting 2 characters at each input is negligible.
One little-known trick with Bascom is to use the Mid function as an assignment:
Code: |
if chrcount < maxlen 'check that the current chrcount is not longer than the string length assignment
Mid(tempstr,chrcount) = Rxbyte
end if
|
That inserts the Rxbyte directly into the string tempstr at the location pointed to by chrcount. And it even automagically converts the byte value to a character in the process.
So to insert the null, immediately following the Mid... statement, do
Code: |
incr chrcount
Mid(tempstr,chrcount) = 0
|
_________________ Adrian Jansen
Computer language is a framework for creativity |
|
Back to top |
|
|
EDC
Joined: 26 Mar 2014 Posts: 971
|
Posted: Wed Jul 01, 2020 1:49 am Post subject: |
|
|
Mark never said that you cannot watch MCS.LIB .. It is stated that this is prohibited to public anywhere else.
I like to read and learn from this lib (mcs.lib)
If string is expected then zero is inserted in the lib automatically check me if Im wrong |
|
Back to top |
|
|
AdrianJ
Joined: 16 Jan 2006 Posts: 2483 Location: Queensland
|
Posted: Wed Jul 01, 2020 4:49 am Post subject: |
|
|
@EDC
Yes, if you concat a character on the end of a string, just by doing tempstr = tempstr + chr the null is inserted automatically. But last time I looked, if you use the Mid function to insert a character, it is not. Else you could not insert a character somewhere along an existing string without having it truncate after the insertion. _________________ Adrian Jansen
Computer language is a framework for creativity |
|
Back to top |
|
|
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum You cannot attach files in this forum You cannot download files in this forum
|
|