Shop OBEX P1 Docs P2 Docs Learn Events
Serial with 5 devices and 1 terminal (used up all the cogs) — Parallax Forums

Serial with 5 devices and 1 terminal (used up all the cogs)

JkaneJkane Posts: 113
edited 2014-09-22 12:28 in Propeller 1
Hello,

I have been looking at the serial obex, there seem to be many, I have a question on the Fullduplexserial, I have a project that has 5 propeller boards, each doing different task, I combine the output of all these boards into a common master propeller board. (the 6th board)

each propeller, (not the master) runs fullduplex serial, output only and uses syntax (for example) Ser.str(string(" sent form interface device",13))

on on the master (the receiver) there is a simple loop

repeat
Ser.strin(@message) ' message that was received
pst.str(@message) ' the print it out on the master console

for each device.

and then I start a cog for each interface, well all was going well until I tried interface 4, I ran out of cogs, it seems there are two for each serial interface,

so the first cognew starts on cog id 2
second in cog id 4
third on cog id 6

and the fourth returns -1 (did not start)

so I was wondering, since I only am receiving, can i disable the cog for transmitting in fullduplexserial?

the layout is like below

Master <= (serial interface) Device 1
(serial interface) device 2
(serial interface) device 3
(serial interface) device 4
(serial interface) device 5

and the master start a serial interface for each interface. it maxes out at interface 3

or can I run two serial independent interfaces on one cog?.

regards

Jeff

Comments

  • RobotWorkshopRobotWorkshop Posts: 2,307
    edited 2014-09-19 13:01
    Hello Jeff,

    Have you tried the multiport serial driver?

    http://obex.parallax.com/object/413

    It is supposed to handle 4 serial ports per COG.

    Robert
  • Dave HeinDave Hein Posts: 6,347
    edited 2014-09-19 13:09
    Each instance of FullDuplexSerial only requires 1 cog. It sounds like you are starting another cog that then starts FullDuplexSerial. I think what you need is a single cog running a polling loop that checks for incoming characters from each serial port. The polling loop would get one character at a time, and after a complete string has been received it would print it out. This should only require 6 cogs -- the main cog and the 5 serial cogs.

    Can you post your code?
  • JonnyMacJonnyMac Posts: 9,105
    edited 2014-09-19 13:29
    Have you considered using a half-duplex, multi-drop serial connection (open-true) that allows you master propeller to poll the others for status? If the transactions are small this can be a very workable solution, and will allow your project to extend beyond five external boards.
  • JkaneJkane Posts: 113
    edited 2014-09-19 13:41
    Dave Hein wrote: »
    Each instance of FullDuplexSerial only requires 1 cog. It sounds like you are starting another cog that then starts FullDuplexSerial. I think what you need is a single cog running a polling loop that checks for incoming characters from each serial port. The polling loop would get one character at a time, and after a complete string has been received it would print it out. This should only require 6 cogs -- the main cog and the 5 serial cogs.

    Can you post your code?

    Yes, I will post my code, but let me check what you said, I start a cog then start the serial within the cog, but starting them in the main, wont' that give me the same thing

    say, in the main I start 5 serial devices,

    then start 5 devices with cogs, each one works on one interface. ( the data from each interface is non-uniform, variable length and at various times)


    I liked the independent cogs, nice and tidy. but if i stay with this design i need two boards, (which i really don't want to do),

    awhile ago, i tried running this on a single cog, the results, at the time were somewhat unpredicatable because the data was mixed, any serial port that had data in it posted data, so I got all the messages from the various devices got mixed, If i waited unil a cr or nl, same problem, it would spin until test and block the reception of the other from the other serial ports, and then since there was so much data coming, the data would be lost, so i went with the seperate cogs

    let me look at the design again, see if I can combine two serials on one cog

    does the propeller have the concept of threads? like 1 cog with 5 threads would be nice.

    regards

    Jeff
  • JkaneJkane Posts: 113
    edited 2014-09-19 14:00
    Jon,

    I made a PCB that will allow one propeller (with a 40 pin connector) to connect to 7 devices, each connector to each serial has 3 pins, tx,rx and gnd, (odd pin 1-27) I used some suggestions on making a serial interface board using 470 ohm and SN74LVC1G17DBVCR, i don't have any transmission problems anymore, or power or grounds, which is nice.

    but I think you are suggesting 1 wire, half duplex, poll on the master for data from the interface devices, when any of the devices has data, it is allowed to send, and the master has to block the other senders, but what i have seen is that I lose data on the halted devices, (note - there is alot of data and I am running at 57 Baud, the data is sensor data, which is coming quite quickly).

    Monday I will try again.

    Thanks

    Jeff
  • Dave HeinDave Hein Posts: 6,347
    edited 2014-09-19 14:48
    Jeff,

    My suggestion would look something like the code below. It runs in a loop collecting received bytes from the 5 ports, and prints out strings after receiving a value of 13. Of course, this would work only if the data is received in short bursts from each port. This code would not be able to keep up with a continuous stream coming from each port. The receive buffer in FullDuplexSerial is only 16 bytes, so you may want to increase that to something larger than the maximum message you would expect to receive, and maybe make it large enough to handle two messages. This would give you time to process a message while the other cogs are buffering up their messages.
    obj
      term   : "FullDuplexSerial"
      ser[5] : "FullDuplexSerial"
    
    con
      rx0 = 0
      tx0 = 1
      rx1 = 2
      tx1 = 3
      rx2 = 4
      tx2 = 5
      rx3 = 6
      tx3 = 7
      rx4 = 8
      tx4 = 9
    
    var
      byte buffer[500]
    
    dat
      index0 long 0, 100, 200, 300, 400
      index  long 0, 100, 200, 300, 400
    
    pub main | i, val
      term.start(31, 30, 0, 115200)
      ser[0].start(rx0, tx0, 0, 57600)
      ser[1].start(rx1, tx1, 0, 57600)
      ser[2].start(rx2, tx2, 0, 57600)
      ser[3].start(rx3, tx3, 0, 57600)
      ser[4].start(rx4, tx4, 0, 57600)
    
      repeat
        repeat i from 0 to 4
          val := ser[i].rxcheck
          if val <> -1
            buffer[index[i]++] := val
            if val == 13
              buffer[index[i]] := 0
              term.dec(i)
              term.str(string(": "))
              term.str(@buffer[index0[i]])
              index[i] := index0[i]
    
  • Tracy AllenTracy Allen Posts: 6,664
    edited 2014-09-19 15:40
    does the propeller have the concept of threads? like 1 cog with 5 threads would be nice.

    The 4-port object creates the illusion of 8 separate threads in pasm. Transmit and receive are separate tasks, times 4 ports = 8 threads. The execution of code in 1 single cog jumps rapidly from thread to thread. Often it is idly hopping around waiting for something to happen, but when a character needs to be sent or received, the corresponding thread moves in short pieces through the steps necessary to compete the task. Still interspersed with other threads. All of the threads can have different things going on at the same time, all of them in stages of transmitting or receiving. The pieces of each thread are called coroutines in propeller-talk. There are variations of timing in how long it takes to get back to a given coroutine, but at clkfreq=80MHz it comes down worst case to a few microseconds. At 57600 baud each bit takes 17.4µs, so all the uncertainty has to fall well within that window.

    Each of the serial ports has its own rx and tx buffers, so characters for the different threads don't get mixed up. Those buffers can be different sizes and as large as you want within available memory. When running the 4-port object, you'd still need a snippet like the one Dave proposed, to read characters out of the serial port buffers up to the CR end of line, then to re transmit that line. You can have two instances of the 4-port object, but a small change needs to be made to the code.
  • JkaneJkane Posts: 113
    edited 2014-09-19 16:56
    Dave Hein wrote: »
    Jeff,

    My suggestion would look something like the code below. It runs in a loop collecting received bytes from the 5 ports, and prints out strings after receiving a value of 13. Of course, this would work only if the data is received in short bursts from each port. This code would not be able to keep up with a continuous stream coming from each port. The receive buffer in FullDuplexSerial is only 16 bytes, so you may want to increase that to something larger than the maximum message you would expect to receive, and maybe make it large enough to handle two messages. This would give you time to process a message while the other cogs are buffering up their messages.
    obj
      term   : "FullDuplexSerial"
      ser[5] : "FullDuplexSerial"
    
    con
      rx0 = 0
      tx0 = 1
      rx1 = 2
      tx1 = 3
      rx2 = 4
      tx2 = 5
      rx3 = 6
      tx3 = 7
      rx4 = 8
      tx4 = 9
    
    var
      byte buffer[500]
    
    dat
      index0 long 0, 100, 200, 300, 400
      index  long 0, 100, 200, 300, 400
    
    pub main | i, val
      term.start(31, 30, 0, 115200)
      ser[0].start(rx0, tx0, 0, 57600)
      ser[1].start(rx1, tx1, 0, 57600)
      ser[2].start(rx2, tx2, 0, 57600)
      ser[3].start(rx3, tx3, 0, 57600)
      ser[4].start(rx4, tx4, 0, 57600)
    
      repeat
        repeat i from 0 to 4
          val := ser[i].rxcheck
          if val <> -1
            buffer[index[i]++] := val
            if val == 13
              buffer[index[i]] := 0
              term.dec(i)
              term.str(string(": "))
              term.str(@buffer[index0[i]])
              index[i] := index0[i]
    

    Nice, I'll try it Monday morning, as far as processing, my plan is to move the output collected to a pc (via serial),or to something like the logger USB stick (and sneaker net it over), from there I will start processing the data, which will be quite large, probably 100 Mbytes per run, the idea is to sort thru all the data processed and determine if the device being tested is good or bad, I'm leaning toward a neural net, then all I have to do is train it on what good is and hopefully it will be able to discern between good and bad, but first I have to get thru the collecting process.

    currently the collecting process is collecting

    10 current readings (amps)
    1 sound
    2 ir
    1 psi
    10 voltage readings
    22 values of what type of test is being conducted.
    test time is about 20 minutes pre device

    hopefully this will be enough data to determine good vs. bad, if not, more sensors are needed

    thanks, I'll get back to you on Monday.

    Jeff
  • Cluso99Cluso99 Posts: 18,069
    edited 2014-09-20 14:50
    You might need to increase the buffer sizes too. I posted a modified FullDuplexSerial_rr00x (not sure what number x was) that permits the buffer sizes to be increased (256 is max, not 512) in powers of 2.
  • kwinnkwinn Posts: 8,697
    edited 2014-09-20 20:14
    I would advise going with Tracy's suggestion and use the four port serial object. Two instances of that object would provide 8 full duplex serial ports using only 2 cogs, and the object can provide large buffers for the data.
  • localrogerlocalroger Posts: 3,451
    edited 2014-09-21 15:55
    Thirding or fourthing or whatever the multiport serial object. It's a work of art. I just used it in a major project where I needed four full-duplex serial ports and because of that object I still have three free cogs.
  • Duane DegnDuane Degn Posts: 10,588
    edited 2014-09-21 19:55
    kwinn wrote: »
    I would advise going with Tracy's suggestion and use the four port serial object. Two instances of that object would provide 8 full duplex serial ports using only 2 cogs, and the object can provide large buffers for the data.

    I'm pretty sure you can't use multiple instances of the object. You need to change the object and save as a different name to keep the two copies from conflicting.
    Duane is right, any change will do, but it has to be substantive, not comments or just the program name. You can change the size of one of the serial port buffers, for example, the following constants lead to a substantive program change...
    RX_SIZE0 = 60 ' receive buffer allocations
    becomes
    RX_SIZE0 = 61 ' receive buffer allocations
    Rename that version and declare as an additional OBJ...
    fds : "FullDuplexSerial4port"
    fds2 : "FullDuplexSerial4port2"
    That does duplicate all the spin code, and of course it starts another pasm cog to handle the 4 additional ports.
  • JkaneJkane Posts: 113
    edited 2014-09-22 08:13
    Dave Hein wrote: »
    Jeff,

    My suggestion would look something like the code below. It runs in a loop collecting received bytes from the 5 ports, and prints out strings after receiving a value of 13. Of course, this would work only if the data is received in short bursts from each port. This code would not be able to keep up with a continuous stream coming from each port. The receive buffer in FullDuplexSerial is only 16 bytes, so you may want to increase that to something larger than the maximum message you would expect to receive, and maybe make it large enough to handle two messages. This would give you time to process a message while the other cogs are buffering up their messages.
    obj
      term   : "FullDuplexSerial"
      ser[5] : "FullDuplexSerial"
    
    con
      rx0 = 0
      tx0 = 1
      rx1 = 2
      tx1 = 3
      rx2 = 4
      tx2 = 5
      rx3 = 6
      tx3 = 7
      rx4 = 8
      tx4 = 9
    
    var
      byte buffer[500]
    
    dat
      index0 long 0, 100, 200, 300, 400
      index  long 0, 100, 200, 300, 400
    
    pub main | i, val
      term.start(31, 30, 0, 115200)
      ser[0].start(rx0, tx0, 0, 57600)
      ser[1].start(rx1, tx1, 0, 57600)
      ser[2].start(rx2, tx2, 0, 57600)
      ser[3].start(rx3, tx3, 0, 57600)
      ser[4].start(rx4, tx4, 0, 57600)
    
      repeat
        repeat i from 0 to 4
          val := ser[i].rxcheck
          if val <> -1
            buffer[index[i]++] := val
            if val == 13
              buffer[index[i]] := 0
              term.dec(i)
              term.str(string(": "))
              term.str(@buffer[index0[i]])
              index[i] := index0[i]
    

    Dave,

    The code worked great, no problems, (except other non-related issues)

    I have a slightly related/unrelated question, one of my prop's is running at 6.25 Mhz, my receiving board is running at 5.00 Mhz, sometimes it get errors (formating to str) from the data from the 6.25, I know the rule is don't do this, but is there any way I can balance the two 5.00 and 6.25 Mhz together.

    Thanks again

    Jeff
  • Dave HeinDave Hein Posts: 6,347
    edited 2014-09-22 09:06
    You should be able use a serial connection between Props with different clock frequencies. The async protocol allows for something like a 5% error in the bit times. You should be getting something on the order of 0.1% difference between a 5MHz Prop versus a 6.25 MHz Prop.

    I suspect your problem is caused by using the small 16-byte buffers in FullDuplexSerial. I've attached a modified version of FDS that sets the buffer size by defining TX_BUF_SIZE and RX_BUF_SIZE. I set them to 64 and 128, but you can change them to some other power of 2. I haven't tested this code, but it compiles OK, and I think I made all the changes correctly.
  • JkaneJkane Posts: 113
    edited 2014-09-22 10:24
    Dave Hein wrote: »
    You should be able use a serial connection between Props with different clock frequencies. The async protocol allows for something like a 5% error in the bit times. You should be getting something on the order of 0.1% difference between a 5MHz Prop versus a 6.25 MHz Prop.

    I suspect your problem is caused by using the small 16-byte buffers in FullDuplexSerial. I've attached a modified version of FDS that sets the buffer size by defining TX_BUF_SIZE and RX_BUF_SIZE. I set them to 64 and 128, but you can change them to some other power of 2. I haven't tested this code, but it compiles OK, and I think I made all the changes correctly.

    Thanks, I'll try it, one question (more), I have multiple boards, I take it that I should use the same serial buffer spin code (FullDuplexSerial) and buffer size on all the boards, except reverse the TX and RX buffers, since the interface boards are senders and the only board receiving is the master board that receives all the interface board serial data.

    I'll test this can get back to you.

    thanks again.

    regards

    Jeff
  • Dave HeinDave Hein Posts: 6,347
    edited 2014-09-22 10:47
    The receiver buffer size doesn't matter much on the senders since they aren't using them. The transmit buffer size should be large enough to hold a complete message. This will prevent the senders from waiting for the transmit buffer to empty. Of course, if waiting is not an issue for the senders you can make the transmit buffer small.

    On the master board I would make the receive buffers large enough to hold two messages. If you're message length is 20 bytes make the receive buffer size 64 bytes. The transmit buffer size for the 5 connections to the senders doesn't matter since you're not use it, so make them small. 16 would be OK, or you could even make them 4 bytes in size.

    The TERM serial driver should have a large transmit buffer so it doesn't wait to transmit bytes. I would make this as large as 128 or 256. This will reduce the latency on reading the 5 receive buffers. You would need to create a copy of fds2.spin if you want to have different parameters for the TERM serial driver than for the 5 receive serial drivers.
  • kwinnkwinn Posts: 8,697
    edited 2014-09-22 11:34
    Duane Degn wrote: »
    I'm pretty sure you can't use multiple instances of the object. You need to change the object and save as a different name to keep the two copies from conflicting.

    You're right of course, and I am using "instance" where I should have stated that it required another copy with a change to the code and a different name. I have been using FDS4Port1, FDS4Port2, and FDS4Port3 with those changes for so long I didn't even think of that.
  • JkaneJkane Posts: 113
    edited 2014-09-22 12:28
    Dave Hein wrote: »
    The receiver buffer size doesn't matter much on the senders since they aren't using them. The transmit buffer size should be large enough to hold a complete message. This will prevent the senders from waiting for the transmit buffer to empty. Of course, if waiting is not an issue for the senders you can make the transmit buffer small.

    On the master board I would make the receive buffers large enough to hold two messages. If you're message length is 20 bytes make the receive buffer size 64 bytes. The transmit buffer size for the 5 connections to the senders doesn't matter since you're not use it, so make them small. 16 would be OK, or you could even make them 4 bytes in size.

    The TERM serial driver should have a large transmit buffer so it doesn't wait to transmit bytes. I would make this as large as 128 or 256. This will reduce the latency on reading the 5 receive buffers. You would need to create a copy of fds2.spin if you want to have different parameters for the TERM serial driver than for the 5 receive serial drivers.

    Dave,

    all is working perfectly, no transmission errors, all connections are up and running, I think I could probably run more interfaces if needed

    Thanks for the help

    regards

    Jeff
Sign In or Register to comment.