RS485 ROUTER - A Propeller/Tachyon Application
Peter Jakacki
Posts: 10,193
The title might look familiar, I borrowed it off nglordi's post as I never think to post much in the way of projects. But here's one which required five RS485 MODBUS ports operating at 115,200 baud. Normally these only operate at much slower speeds and since MODBUS uses short gaps for delineating packets it's important that the packet stays together even at higher speeds. So here are 57 lines of code in Tachyon Forth that allow a MODBUS master to talk to 4 RS485 ports, each of which may have many units strung off them. Although it is possible to have the master talk directly to the slaves the problem is noise and ringing etc over long lines, so splitting this up in 4 lines allows for more units or longer lines etc. When the master talks to the slaves they all receive the same packet which has already been buffered by the router and then when a slave responds then that packet is buffered until there is enough data to start sending back to the master.
The "application" takes 336 bytes of code and while it is running I can still talk to it in Forth and debug it from the console port. Each RS485 port can work up to 2M baud so 115.2k is certainly not pushing any boundaries in Tachyon.
Here's the packet timing using another Tachyon unit for the master and one of my units as a slave at address 7.
The "application" takes 336 bytes of code and while it is running I can still talk to it in Forth and debug it from the console port. Each RS485 port can work up to 2M baud so 115.2k is certainly not pushing any boundaries in Tachyon.
*** MODBUS RS485 ROUTER *** 64 == rxsz rxsz 8 + == comsz rxsz 1- == rxszm --- port buffers: combuf(2) stat(2) rxrd(2) rxwr(2) rxbuf(rxsz) comsz bytes combuf1 comsz bytes combuf2 comsz bytes combuf3 comsz bytes combuf4 comsz bytes combuf5 --- some offset constants combuf1 8 + == rxbuf1 combuf1 4 + == rxrd1 combuf1 6 + == rxwr1 --- Calculate baud rate ticks pub b ( baud -- ticks ) CLKFREQ SWAP U/ ; --- &mode.te.tx.rx baud ticks buffer size buffer start TABLE pars --- MO TE TX RX BAUD RX SIZE BUFFERS &00.00.01.01 , 115200 b , rxsz || combuf1 || --- COM1 RS485 PORT 1 &00.02.03.03 , 115200 b , rxsz || combuf2 || --- COM2 RS485 PORT 2 &00.04.05.05 , 115200 b , rxsz || combuf3 || --- COM3 RS485 PORT 3 &00.06.07.07 , 115200 b , rxsz || combuf4 || --- COM4 RS485 PORT 4 &00.08.09.09 , 115200 b , rxsz || combuf5 || --- COM5 RS485 MASTER --- Load HSUART ROM from cog 3 with 12 byte pars entries for 5 cogs - then flush all buffers pub !COMS " HSUART " 3 pars 12 5 LOADCOGS 50 ms combuf1 comsz 5 * ERASE ; --- common transmit routine - store to channels combuf when ready pub TX ( dat ch -- ) 1- comsz * combuf1 + TX! ; --- common receive buffer check pri RX? ( chan -- offset diff ) 1- comsz * rxwr1 OVER + W@ rxrd1 3RD + W@ - rxszm AND ; --- common read buffered receive data routine pri RX ( chan -- dat ) RX? IF rxrd1 OVER + W@ rxszm AND rxbuf1 3RD + + C@ $100 OR SWAP rxrd1 + W++ ELSE DROP FALSE THEN ; pub ROUTER --- start delivering data from master to slave ports when enough data is already buffered 5 RX? NIP 3 > IF BEGIN 5 RX ?DUP WHILE 4 FOR DUP I 1+ TX NEXT DROP REPEAT THEN --- scan all 4 slave ports and send to master if enough data is buffered 4 FOR I 1+ RX? NIP 3 > IF BEGIN I 1+ RX ?DUP WHILE 5 TX REPEAT NEXT ; --- setup 5 high-speed RS485 UARTS and run router in background ( leave console active ) pub MAIN CR !COMS ' ROUTER keypoll W! ;
Here's the packet timing using another Tachyon unit for the master and one of my units as a slave at address 7.
Comments
Thanks Jon, but in return I would say you do such neat things with action Props and WS2812 style LEDs that I would say "why don't you start there"
Starting with what you do know, you know what to expect. The difference now though is that you can interact at the console level, either directly or having it run in its own cog.
There are just three words specifically for this type of serial LED, they are LEDPIN to set the target pin, LED for playing with a single LED or LEDS where you specify the array and bytes which of course varies with whether they are RGB or RGBW. In fact they could all be combined in just one word if needed These are just the primitives but you can extend the language to include the JM pizazz!
Some basics:
19 LEDPIN --- use pin 19
red LED --- just light up a single LED with one of 7 ANSI colors ( red /green/yellow/blue/magenta/cyan/white )
$404000 LED --- use a 24-bit color
20 LEDPIN --- I decide to connect a 40 LED array on pin 20
0 120 LEDS --- will simply write out memory from address 0 fro 120 bytes to those 40 WS2812 LEDs.
To make 8 lines of 40 LEDs continually refresh from a buffer (P0..P7 for simplicity): Then kick it off using cog 3: Then all you have to worry about is writing to the myleds buffer knowing that one line of code is looking after those strings of LEDs.
Sorry to have LED you astray
Well that's a good start, I have those books although I had "lost" (lent) my copies of Starting Forth but ended up finding an original a few years ago. I have fond memories of my first copy of that about the same time I had a copy of my K&R white book which was boring (but the cover was cute).
While it is possible to follow along with an old 79 standard Forth on a PC, it is infinitely better to connect up Tachyon and try some of those basics. There are a lot of things that are non-standard about Tachyon, a Forth for a non-standard micro, but those non-standard things are where the most fun and usefulness are. Remember though that Tachyon compiles everything you type in rather than interpreting the words, so that once you hit the enter key it is read to run, so you can include all those compile-time only words which you can't use with standard Forths, for looping, branching, printing " Hello World" etc
The latest versions of Tachyon though do away with DO and LOOP and +LOOP favoring the simpler FOR NEXT but allowing indexing and then augmenting those words with FROM for a starting index and BY for the index step. The looping words have their own stack so all those restrictions that apply in Starting Forth because they use the return stack don't apply in Tachyon so loops can be referenced outside of the loop when you call other functions. I will probably have to rewrite my Introduction to Tachyon page though. Have fun!