Autobaud strategy for multiple connected micros - Page 1
Forum section: electronics / general

author: cv007
author_rank: Frequent Contributor
timestamp: March 27, 2017, 05:43:58 pm
content: I created a circuit board that sits on the back of a 2 digit 14segment led display where I can assemble multiple displays in any arrangement I want. Each display can function on its own as each has a pic16f1578 driving the display and just power, ground and rx for connections (all displays connected together). Each display will have its own address and the 'protocol' is very simple- 3 byte 'packets'- address, command, data.Since the pic internal oscillator is not super-accurate,and I don't want a fixed baud rate, I will use the autobaud feature (also using the full baudrate register at lowest clock divisor for best accuracy). I plan on using a timeout value of 2 seconds where anytime the micro sees no rx for >2 seconds, it goes into autobaud where a 'U' is then required to sync back up.Whatever I have driving the display will also have to keep track of time, but that should be easy enough. Is this a good strategy? I want to prevent a display from getting out of (packet) sync for whatever reason, or over time (or voltage) the internal clock will drift- so I should be able to wait >2 seconds, where I know the displays will all be in a known state, send a sync byte and be back in business (if I wasn't already). Any better ideas?(I guess I'll also have to come up with a strategy for when the timeout is close to 2 seconds and a data packet is going to be sent- maybe then just wait enough to make sure 2 seconds have passed- a 'wait window' from 1.8-2.2 seconds)

author: Throy
author_rank: Regular Contributor
timestamp: March 27, 2017, 06:17:34 pm
content: Why not just send your sync byte at the beginning of every message? So it will look like this: sync, address, command, data.

author: cv007
author_rank: Frequent Contributor
timestamp: March 27, 2017, 07:06:55 pm
content: QuoteThat is what I originally had thought, but what happens if one display gets out of packet sequence for some reason- it will be autobaud syncing on something other than 'U' - then may never recover. I also had one of the commands (all displays latch data) be an 'end of packet' where the next packet would require the sync byte, but that could also possibly end up autobaud syncing on the wrong byte.My thinking now is with a timeout requiring a sync byte, the transmitter can always get the displays to a know state (displays are rx only, by the way) by simply waiting a couple seconds.

author: Ian.M
author_rank: Super Contributor
timestamp: March 27, 2017, 07:15:11 pm
content: I'd keep it a lot simpler than that. e.g. send a long break (at least 11 bits at your lowest supported baud rate) to enter auto-baud mode.Unless you have a certain method for distinguishing address bytes from command and data bytes, your simple protocol can get out of sync and end up a byte ahead or behind. Your PIC's USART supports address detection in 9 bit mode - use it.For efficiency, I would support variable length packets, where the command specifies the number of data bytes, and send a checksum for each packet. If you've got a spare open drain output driving a shared line going back to the master you could then ACK each valid packet by pulsing it low. If you don't get an ACK, the display at that address is either faulty or the packet was corrupted.

author: Throy
author_rank: Regular Contributor
timestamp: March 27, 2017, 07:25:35 pm
content: Quote from: Ian.M on March 27, 2017, 07:15:11 pmThat's how the LIN Bus does it.http://forums.ni.com/attachments/ni/30/3619/1/LIN.pdf

author: cv007
author_rank: Frequent Contributor
timestamp: March 27, 2017, 08:48:17 pm
content: QuoteI don't want to use any mode that is also not easy/common to do on the pc side (if transmitting from a pc), so I don't want to use 9bit mode or break. No communication back to the transmitter, so no type of ack possible either.I think the reality of getting out of a 3 byte sequence would be rare as I have been pumping data at the displays for days without errors (framing or overrun). Probably the only thing left is any clock drift that can/will occur from the initial autobaud sync. A simple 2 second wait (or set to whatever length suitable) will get back to a known state. If the display updates are frequently greater than the timeout, it will take care of itself. If the display updates are always less than the timeout, then will have to force a timeout from time to time.Appreciate the ideas, and will still listen to any more.

author: Ian.M
author_rank: Super Contributor
timestamp: March 27, 2017, 09:30:17 pm
content: A PC can send a break easily enough.If you want to avoid 9 bit mode, (and also code 0 which can be problematic from some MS programming languages), why not use ASCII for your packets?Send an address then a command letter followed by hex data, two characters per byte. Terminate the packet with any whitespace for easy resyncing. The only disadvantage is it needs double the baud rate to transfer the same number of bytes per second.It also lets you test the system from a terminal before you write the PC application.I'm a big fan of human-readable protocols if there's a PC in the 'loop' and you've got enough data link capacity to avoid the need for optimum packing or compression.

author: cv007
author_rank: Frequent Contributor
timestamp: March 27, 2017, 10:46:05 pm
content: QuoteI am too. I started to switch the address and data to ascii (2 bytes ea, command already ascii), but decided against it (don't remember why, but if I start to do it again I'll remember why). I think part of the reason was the conversion on both ends to/from ascii hex and the additional validation required. The usefulness of banging at the serial terminal would be short lived, and its easy enough to script output from the bash prompt or any of my favorite scripting languages (I use Linux, mostly). With Windows, I can get Rebol to do serial port easily.In Rebol (my favorite language- check it out, been around a long time), although it has serial port support, I can't get it to work right in Linux for some reason, so went with plan B which works from Rebol, bash, lua, etc.- use stty to set the serial port, then just write to the device-Rebol-call/wait "stty -F /dev/ttyUSB0 9600 raw"f: open/direct %/dev/ttyUSB0insert f rejoin [ #{00} "A" "1" ] ;"1"->digit0insert f rejoin [ #{FF} "L" 0 ] ;latch allclose flua-io.popen("stty -F /dev/ttyUSB0 9600 raw")f = io.open("/dev/ttyUSB0","w")f:write(string.char(0).."A1")f:write(string.char(255).."L"..string.char(0))f:flush()f:close()bash-#stty -F /dev/ttyUSB0 9600 raw#printf "\x00A1\xFFL\x00" > /dev/ttyUSB0these are probably simpler than firing up a terminal, and of course can be programmed to exactly what you want without resorting to compiling a C program (or whatever language one wants to compile)

author: mikeselectricstuff
author_rank: Super Contributor
timestamp: March 27, 2017, 10:58:57 pm
content: PIC internal oscs are within 2%, which is easily enough for UART, as long as the other end is reasonably accurate.Personally I'd avoid autobaud unless you really need it as it's one more thing to go wrong. If you do want something to time from, start with a zero byte.

author: cv007
author_rank: Frequent Contributor
timestamp: March 28, 2017, 02:08:43 am
content: QuoteIf they are both 2% off in the opposite direction, then maybe? I guess one could do the math to see when that last bit starts sneaking outside its bit period (or I guess when the pic 'majority detect circuit' gets confused).I have never used autobaud, but since this chip had it I thought it a good idea to use it (and really don't want to preset the baud rate in this case). I haven't tested it a whole lot, but so far it seems to be happy with whatever baud rate I try. I think the only thing that can go wrong is syncing on a byte that is not a 'U', but with an agreed upon timeout (by both sender and display), they both can be sure they are syncing when needed and on a correct byte.After some deeper reading of the datasheet, I think the autobaud can get into a situation where the baudrate is erroneously set to where it no longer detects the start bit (due to bit period being too long) which would render the rx inop at the current baud rate (autobaud on wrong byte(s) where the 5 rising edges causes baud register to end up high value but not overflowed = too low baudrate set = rx possibly no longer detecting valid start bits = rx inop). I think I have that correct (I may have it backwards).

author: BrianHG
author_rank: Super Contributor
timestamp: March 28, 2017, 03:07:41 am
content: Unless it's my own assembly written UART in the PIC, I would not trust the autobaud feature unless you are using a crystal clock & the baud rate is detected and set once.Q: Instead of assigning an ID to each display, why not send serial into the first display, take serial out of that PIC into the next display. This way, if you feed a line of characters followed by a CR, of end of line string, the displays will shift and latch the final display characters automatically without any addressing junk. Just straight text and a CR/LF. This way, if you can add any number of characters you like, say 10, 20, 100, and the display will work like a line of text in a terminal without any IDs.

author: cv007
author_rank: Frequent Contributor
timestamp: March 28, 2017, 05:19:41 am
content: QuoteI'll take a hardware uart and C any day of the week. Nothing wrong with assembly, but those days are thankfully over. I ditched Microchip years ago when 16F84(?) was popular because I found out Atmel had a free C compiler (gcc). When Atmel decided to use Visual Studio, I pretty much quit using Atmel (no loss for them). Now I'm back using Microchip as MPLABX works on both my Windows and Linux pc's.QuoteWell, it appears to be designed for LIN use. I don't know who uses that but I suspect its not average hobby guy. I assume stuff more important than what I do is getting done with this autobaud feature, so I will also assume its reliable. Its not that complicated, wait for start bit, clock the baudrate register, count 5 rising edges, stop clocking the baudrate register on the fifth. Done (basically). I assume it works the same every time just like the timers and every other thing one counts on to work. If I remember correctly, I had an 8052-Basic chip I wired on a breadboard with some ram long ago, and autobaud was used (space key I think). So, its been around forever but maybe was more important back then.QuoteI have a 20pin pic, which needs 17 pins to run the led display (14 segments, 1 dp, 2 common), which leaves one pin (mclr) which is possible to use as rx via pps (I like these newer pic chips). With the setup I have, each display is 'autonomous' and does not rely on any other display- one goes out to lunch, the others keep working (nice, although probably not terribly important). Also, with up to 254 displays (that would be cool, but expensive and unlikely), I can address each individually, or all at once (address 255), instead of having to clock out all digits every time I want to change one digit (I can also change each digits brightness, blink rate/setting, send raw segment data, change character table, etc.- I'm not sure how one would do those things when using a 'chained' system, you would be back to using some protocol which would be a lot more difficult than the 3 bytes per digit system).

author: BrianHG
author_rank: Super Contributor
timestamp: March 28, 2017, 06:09:00 am
content: Quote from: cv007 on March 28, 2017, 05:19:41 amMy system is not difficult. Except for the CR/LF character, each display in the chain shaves off the first new character from the source serial input and uses it for itself, passing all additional characters through. If the CR/LF is received, whether it is the first character or not, it will be passed through the chain to all displays which will intern will display their stored first character received and reset all the display devices to new begin at 0 once again and store and hold the first character received once again, only passing through all additional characters.Now, for shading & blinking features, you can make each display hold onto the first 2 characters instead of just 1. The 2 bytes would contain byte 1 with contrast and blink speed, the second would be the letter, or you can do 3 or 4 per digit if you like. It makes no difference because I'm sure the PIC has more than 6 bytes of ram to work with, then is just blindly passes everything else.This will allow you to stretch the length of display modules without end, even the buffering of you original com signal is being taken care of between displays, and the code to implement the 2-3 basic rules I mentioned above is child's play...

author: cv007
author_rank: Frequent Contributor
timestamp: March 28, 2017, 07:37:31 am
content: I don't think you caught the part where I don't have the extra pins to do that (or even want to), have the circuit board made already (see picture), but consider this-16 digits (8 displays)-chain method- (if I understand correctly)(generously assuming only 1 extra byte to take care of brightness, and all other features)change digit0, send 1x2 bytes, send CR- total 3 byteschange digit15, send 16x2 bytes, send CR - total 33 byteschange all digits, send 16x2 bytes, send CR - total 33 byteschange multiple digits- 5-33byteschange all brightness- 33 bytes3 byte method-change digit0, send addr cmd data, send latch command- total 6 byteschange digit15, send addr cmd data, send latch command- total 6 byteschange all digits, send addr cmd data x16, send latch command- total 49 byteschange multiple digits- 9-49byteschange all brightness- 6 bytesSo I'm not sure why the 'chain' would be any better except for the built-in addressing. In addition, when it comes time to latch the data (so they all update at once), the chain method will (possibly) get the rolling shutter effect to some degree- worse at slower baud rates, where I will be latching all digits at the same time. Change brightness- all happens at the same time. Blank all digits, all at the same time. I can also set the 'raw' segments without using the ascii lookup table, still in those 3 bytes.QuoteIf I need more than 254 digits, I guess I'll have to add another address byte, for a possible total of 65534 digits- even at those quantities it would probably be $3+ per pair = $98,301 (+ 6000 amps of power). I'll just consider 254 to be modules without end

author: Ian.M
author_rank: Super Contributor
timestamp: March 28, 2017, 09:18:58 am
content: Actually, you don't need to daisy-chain them to get the benefits of being able to writ to them as-if they were a single module. Simply assign each module a two character window position in a larger display buffer. All modules receive all data and commands, and write data to the buffer at the current cursor position, but only display what falls in their window. That also lets you scroll stuff sideways by command without any ripple effect as each module has the full buffer available.

author: cv007
author_rank: Frequent Contributor
timestamp: March 28, 2017, 03:44:23 pm
content: QuoteThe only benefit is I don't have to send the address byte, but instead everyone has to keep track of the cursor position and the 'cursor' can only advance one position at a time which would require a lot more writes than necessary (or give up, and just write the whole thing every time). I guess I could also add 'Auto-address-increment-on/off' commands where subsequent packets would just be 2 bytes and the address would increment each time.I can update 16 digits at 20hz (at relatively slow 9600 baud) with what I have now- which is plenty fast, so the reality is it doesn't make much difference either way until a lot more digits (where the limits of baudrate/update speed would start to appear).I'll stick with the simple address/command/data for now, and test my timeout method for autobaud enable.https://drive.google.com/file/d/0B1AI2QA--Kp-YXFLcUZEcjhfVHc/view?usp=sharing(ghosting is camera only, sparkle effect is real, though)

author: cv007
author_rank: Frequent Contributor
timestamp: April 09, 2017, 03:40:48 am
content: update, just to complete the thread (not sure if anyone interested, but here goes anyway)-I think I ended up using some combo of ideas/suggestions provided here. I have been streaming/scrolling data at the display for a while now, and the original idea worked ok but I was just not thrilled at keeping track of time on the pc side (for the 2sec timeout, where a 'sync'/autobaud is required). A 'keep out' time window has to be used as you really don't know when a byte actually goes out the pc uart. So, I concluded that autobaud is not worth the trouble in my case, so fixed baud it is. (I also attempted to 'monitor' the rx pin via pps to the timer1 gate, keeping track of maximum low times of 9bits when 0 transmitted, but could not get consistent values for some reason- the autobaud was always dead on, though).After looking at it a while, I also trimmed down the '3 byte' protocol to simply 2 types of bytes- command (where bit7 is clear) and data (bit7 set), where I only need to keep track of the previous byte in any case and cannot get 'out of sequence'. Addresses also auto-increment (except for the 'broadcast' address 127), so I can address any digit if wanted or simply let it auto-increment (most cases). After a Latch command, the address resets to the 'origin' address.something like this-in rx irq-just put rx byte in buffer[]main loop-process_rx loop()while new byte in buffer[] found-if bit7 clear, clear cmd_ok flagelse process_data()process_data()if cmd_ok, must be text or raw data- set digit dataelse must be data following a cmd byte-check all commands (look at previous cmd byte), act accordinglyset cmd_ok if any valid command0b0xxxxxxx 0b1xxxxxxxcmd&127 data|128----------------------//out of reset, text mode, address=126 (unused), brightness=32//set digit0-7 to '12345678', brightness 64'T' 127 text mode, address set to 127 (broadcast- no auto-increment)'B' 64 brightness set to 64 (all digits as address is 127)'T' 0 text mode, address set to 0 (leftmost digit)'1' digit0 = '1', address++'2' digit1 = '2', address++'3' ...'4''5''6''7''8''L' 'L' latch data (all act on latch command),address reset to origin (0 as set above with T cmd)//can now send text starting at digit0, then latch, repeat//or just send T 0 at start again//light up all segments on all digits at brightness 127'R' 127 raw mode, address set to 127'B' 127 brightness set to 127, all digits'D' 'P' set dp segment, all digits127 set segments 7-13 (high byte)127 set segments 0-6 (low byte)'L' 'L' latch dataThis ends up being a lot less verbose on the bus, and only needs a maximum of #digits + 2 to refresh every digit in text mode. So even at a low 9600 baud, I could update 126 digits at 7Hz+ (although many digits would be neat, I will probably never get more than 16 digits strung together at once). All digits can be addressed at once, addressed individually, or auto-incremented (every other data byte in raw mode, as 2bytes needed for 14 segments). If any invalid command attempted, nothing will be done until a valid command.Seems to work, and the 'driver' is quite simple on the pc side. I am using micropython on linux at the moment to download internet data, send to display- have not used python before, but seems like a nice language (yes, I am behind the times).