Shop OBEX P1 Docs P2 Docs Learn Events
How do you use "PUB Start : OK"? — Parallax Forums

How do you use "PUB Start : OK"?

lardomlardom Posts: 1,659
edited 2018-09-14 13:22 in Propeller 1
I'm debugging an RF project on a single Propeller. So far I have successfully controlled one servo with a potentiometer but when I tried to control two servos using code I have successfully used in the past it fails. ...I think it's because I have run out of cogs. I created a test to check if the syntax, and logic, was correct using a single Propeller and it worked.
I am going to re-test with two Propellers and I don't expect a major problem.
If I knew how to use "PUB Start : okay" I could save myself some grief. How do I do it?

Comments

  • The tile is not to intuitive.

    You know, of course, you are going to be asked to supply the offending code. : )
  • @Publison, My goal is to post it to the OBEX but at this point it's not readable yet.
    This is the method
    PUB Start : result
     
        stop       
        cog := cognew(Main, @Stack) + 1        'calibrate bits
        result := cog
    
    This is the object tree:
    Object%20Tree.PNG
    My naming conventions don't make sense to anyone else but me so I'll save that for the final step but if you need more code to understand what I'm asking I'll do it.
  • You would generally do something like this:
      if (myobject.start() == false)
        ' Houston, we have a probem
    
    This assumes, of course, that one has been added to the cognew return value and then returned to the caller.

    It seems like you should have a multi-servo driver cog for your project; there is no need to have a new cog for each servo.
  • RaymanRayman Posts: 14,646
    edited 2018-09-13 18:55
    I think the Parallax Library servo object can do 32 servos with 1 cog...

    Look for: Servo32v7_RampDemo.spin that uses Servo32v7.spin in the library...
  • Or RTFM,

    As Jon pointed out this is a small trick used in most P1 examples.

    The cognew() call returns -1 on failure or 0 to 7 as ID of the cog started.

    To stop a cog later you might need its ID and need to now if it is started.

    if you directly save the return value you will need to compare it with -1 for failure and >-1 for SUCCESS

    if you save the return value with a added 1 then you can compare like Jon showed.


    Because 0==false <>0 == true

    Mike
  • JonnyMac wrote: »
    It seems like you should have a multi-servo driver cog for your project; there is no need to have a new cog for each servo.
    I do have a working multi-servo driver. The problem I was that I could not send values to it from a transmitter object. I had been trying for two days to figure out why I was able to pass values to a single-servo driver but not to the two-servo driver. It made no sense, so today I decided to post my problem. Then I took my bicycle to the bike trail to clear my head.
    When I got back I was prepared to separate the tx half from the rx half. I tested it one more time and this time it worked.
    I'm actually disappointed that I didn't have a genuine issue: Maybe I should have given it one more day but when I get stuck I think it's better to ask for help even if I look stupid.
    On the positive side, I'm almost there in terms of what I wanted to accomplish.
  • It might be helpful to share your protocol.
  • What's with the thread title Larry? Are you cryptic for a reason?

    Maybe a title that goes "how to use "PUB Start : okay" would be more suitable, and also some code.
  • lardomlardom Posts: 1,659
    edited 2018-09-14 06:34
    @JonnyMac, I made a screenshot of the terminal: "253" tells the Prop that I want to control the two servos. It's followed by two values, 0 to 127. This is refreshed 50 times a sec. I will add a second mode, "254", in a demo.
    What's with the thread title Larry? Are you cryptic for a reason?

    Maybe a title that goes "how to use "PUB Start : okay" would be more suitable, and also some code.

    Wow, I screwed that up. :blush: Thanks for using a tactful word!
    158 x 127 - 2K
  • Peter JakackiPeter Jakacki Posts: 10,193
    edited 2018-09-14 06:50
    Hey Larry, if you go back and edit the very first post in this thread you can change the thread title. Easy as that.

    You really need to post your code or a lot more detail but if you are worried about anyone copying it, I wouldn't be. You can always pm the code to someone whom you trust though.

    P.S. Even your questions are cryptic :)
  • lardomlardom Posts: 1,659
    edited 2018-09-14 14:06
    @Peter Jakacki, I changed the title. People would see the misleading title in 'search' which is not a good thing.
    I'm not so concerned about keeping my code secret. My goal is to share it 'but' I 'am' concerned about asking someone to wade through a whole project which is still largely unreadable. It's a big 'scratch pad'.
    OTOH, when I have a working demo I really would appreciate if a professional member of this forum would proofread it so I could present it to the rest of the community. That's asking a lot but I want it to be 'headache free' if someone downloads it from the OBEX.
    edit: Yeah, my questions are "cryptic". Learning to ask the right questions is a skill in itself. Very often I have to work at defining a problem.
  • Peter JakackiPeter Jakacki Posts: 10,193
    edited 2018-09-14 14:07
    If you want it to be "headache free" then that's a good thing but you can start by not being concerned about requesting the forum to wade through it, that's what we do. Besides even if we respond to the request we are under no obligation anyway but I'm sure there are a few that won't be able to stop themselves from helping :)

    I have my local Dropbox folders that I work from which are synched across my computers through the cloud and to which I share the link for anyone who wants to wade through at their leisure. What they do with it is up to them.
  • cavelambcavelamb Posts: 720
    edited 2018-09-14 19:33
    The manual says COGNEW returns -1 if no cogs are available or the COG ID if successful.

    stop
    cog := cognew(Main, @Stack) + 1 'calibrate bits
    result := cog
    This looks like you are adding 1 to the COG ID.
    That would make it a bit hard to communicate with the cog you started.
    cog := cognew(Main, @Stack)
    if cog == -1 ' we have a problem
    else
    result := cog
  • msrobotsmsrobots Posts: 3,709
    edited 2018-09-14 20:35
    As I said it is a convention used, thinking of it, you might be right but usually you never use the cogID except on stopping a cog.

    Variables in the VAR section get initialized to 0 on program load.
    so this
    VAR
    long cog
    
    PUB stop
      if (cog) 
        cogstop(cog-1)
        cog := 0
    
    PUB start
      stop
      cog := cognew(Main, @Stack) + 1 'calibrate bits
      result := cog
    
    PUB doSomething
      if start
         …
         …
         stop
    

    Now doSomething calls start and start
    will have cog as 0 when loaded so the first stop inside of start will not try to stop any cog since cog is 0 and 0 is false
    then start will try to start a cog and if successful write the cogID+1 into cog and result. On failure it sets both values to 0.
    if start was successful doSomething does something and calls stop. if not then doSomething does nothing.

    now compare to using the cogID as is.

    First you need to make sure that at program start cog is -1. So you need to put it in the DAT section to initialize it at program start.
    DAT
    cog long -1
    
    PUB stop
      if (cog>-1) 
        cogstop(cog)
        cog := -1
    
    PUB start
      stop
      cog := cognew(Main, @Stack)  'calibrate bits
      result := cog
    
    PUB doSomething
      if start>-1
         …
         …
         stop
    

    Now doSomething calls start and start
    will have cog as -1 when loaded so the first stop inside of start will not try to stop any cog since cog is -1 and cog>-1 is false
    then start will try to start a cog and if successful write the cogID into cog and result. On failure it sets both values to -1.
    if start was successful doSomething does something and calls stop. if not then doSomething does nothing.

    as you might see both variants do basically the same, but the second one is slightly larger (4 subtraction vs 1 subtraction and on addition)

    Enjoy!

    Mike
  • JonnyMacJonnyMac Posts: 9,104
    edited 2018-09-15 01:51
    This looks like you are adding 1 to the COG ID.
    Adding one to the return value from cognew is standard practice. This allows a simple true/false test on the value (see my example above).
    That would make it a bit hard to communicate with the cog you started.
    No, it doesn't. When you start a cog that requires communication you must setup a mailbox -- this is done with cognew by passing a hub address through the par register.
  • I stand corrected.
    Maybe the OP does too?
  • Finally, it's working! I rewrote about 50% of my dual-servo driver.
    The problem was just bad code. It does take me a while to spot bugs but finding the solution is something I enjoy a lot.
    I'm going to take a short break and then jump right back in. I want to start work on the second mode of operation.
  • Why not post the code so that others can review and comment?
  • JonnyMac wrote: »
    Why not post the code so that others can review and comment?

    There is a lot in it that I learned from you such as the receiver object listens to pin 1, which is controlled by the tx object.
    When I run it with the 433Mhz modules the rx values are exactly the same as cog-to-cog. That is huge.
    It's set up to run on the serial terminal.
  • Have you considered learning to use the counters for generating precision pulses? It's actually very easy -- I use this strategy all the time in a varied of ways. Since you have need for a 2-servo object, I've attached my Spin-only, dual servo object that uses this technique.

    The high-wait-low strategy will always be off just a bit due to the Spin instruction fetch/decode overhead. Let me illustrate.

    First, let me show you how to time-test a section of code.
      elapsedtix := -cnt
    
      ' put code to test here
      
      elapsedtix += cnt - 544
    
      term.dec(elapsedtix)
    
    Note that there is a - 544 in the mix to remove Spin overhead from the test result.

    Now, let's update that to generate a 1ms pulse in the manner you're using.
      dira[16]~~
      
      elapsedtix := -cnt
    
      outa[16]~~
      waitcnt(cnt + clkfreq/1000)
      outa[16]~
      
      elapsedtix += cnt - 544
    
      term.dec(elapsedtix)
    
    The result is 81488, not 80000 which would be exactly 1ms. No, it's not a big error, but when dealing with servos, this could make a difference. This is why I uses the counter modules for servo pulses. I've attached a simple 2-channel servo driver to show you how that works. The driver even includes simple ramping.
  • @JonnyMac, Thank you. It bears repeating, you are one of the best. Making the project 'headache free' means I have to allow for digital servos and that means I need to generate precision pulses.
    You may remember that I set out to do the same thing with IR. The IR signal is a bit tougher to work with. Combined with my failure to get the dual-servo driver to work I put the IR version aside. I'll get back to it when I'm done with the RF version.
    A precision servo pulse for an IR driver is even more critical because the IR detector is finicky.
  • Peter JakackiPeter Jakacki Posts: 10,193
    edited 2018-09-16 04:04
    Hey Larry, I think you could probably simplify your algorithms. I did a Q&D conversion of your SendCode routine into Tachyon and this is what it looks like.
    39000          := zero       ( adjust simply for scope timing instead of 48000 )
     zero 2*        := one
     one 2*         := start
     CLKFREQ 50 /   := pause
    
    pub SendCode ( code -- )
        1 HIGH start WAITX 1 LOW zero WAITX
        8 FOR 
          1 HIGH zero WAITX DUP 1 AND IF zero WAITX THEN
          1 LOW zero WAITX 2/
        NEXT
        DROP pause WAITX   
        ;
    

    BTW - You top file has multiple repeat loops as if you were testing but of course only the first one gets to loop.
    Here's the Tachyon code for your incrementing code loop
    BEGIN 256 FOR I SendCode NEXT AGAIN
    
    Original Spin code:
    repeat 
        number := index++
        Tx.SendCode(number)
        if index > 255
          index~
    

    Then I type "$71 SendCode" and this is what I got:
    800 x 480 - 40K
  • A precision servo pulse for an IR driver is even more critical because the IR detector is finicky.
    Did you ever see the move, Jerry Maguire? Let me paraphrase a statement from Jerry to his client, Rod. "Help us help you."
    IR in and out is pretty straightforward. Your statement vis-a-vis a servo pulse for an IR driver makes no sense to me? Do you mean a precision pulse for gating? Easy to do, and more than one way to skin that cat. Please understand, I'm trying to be helpful. I think the most difficult part of this process is explaining exactly what you're after, and not holding back details that would allow others to assist.
  • @"Peter Jakacki", I'm going to learn Tachyon. I toggled the 433Mhz transmitter with waitcnt(clkfreq / 3000 + cnt) and got a square wave for about 20 seconds. When I toggled it using waitcnt(clkfreq / 2800 + cnt) the square wave was steady. That's too fast for Spin but I want to take advantage of that speed for projects that I have in mind for myself...and I have quite a few.
  • lardom wrote: »
    @"Peter Jakacki", I'm going to learn Tachyon. I toggled the 433Mhz transmitter with waitcnt(clkfreq / 3000 + cnt) and got a square wave for about 20 seconds. When I toggled it using waitcnt(clkfreq / 2800 + cnt) the square wave was steady. That's too fast for Spin but I want to take advantage of that speed for projects that I have in mind for myself...and I have quite a few.

    Great, you will have lots of interactive fun. But try this in Tachyon:
    1 APIN 3 KHZ
    
    You will see a steady 3kHz square wave on P1 using a counter.
  • I'm stuck! Up until yesterday I did all my transmitter/receiver tests on one Propeller. I wrote a new driver that takes advantage of the 'enable' inputs on the L298 which allowed me to eliminate one cog. In a previous driver I used two cogs, one for each wheel.
    After I got things working I wired up two DC motors to a second Propeller and it stopped working. All I could think of was that the 433Mhz modules were the problem. I did a number of tests and finally I said "I'm gonna connect the pin of the first Prop directly (through a resistor) to the pin of the second Prop and see if that solves the problem.
    I still could not get it to work. I posted my test code. TX side:
    CON     
    
      _CLKMODE = XTAL1 + PLL16X        
      _XINFREQ = 5_000_000
        
      'start_bit = 191008  'equals 192000 minus overhead 
      start_bit = 192000  '192992 = overhead
      one       = 96000     
      
    OBJ
    
      'pst : "Parallax Serial Terminal"
     
    PUB Pulsewidth_test
      dira[1]~~
      'pst.Start(115_200)
      'waitcnt(clkfreq*2+cnt)       
      repeat        
        outa[1]~~
        waitcnt(one + cnt)          
        outa[1]~                             
        'pst.Str(String(pst#NL, "start_bit = ")) 
        'pst.Dec(start_bit)
        'waitcnt(clkfreq/10+cnt)  
        waitcnt(clkfreq/5+cnt)
    
    RX side:
    CON
    
      _CLKMODE = XTAL1 + PLL16X        
      _XINFREQ = 5_000_000     
    
    VAR
                        
      long  TPulse, Time_one, Time_two
    
    OBJ
    
      pst : "Parallax Serial Terminal"
    
    PUB Main | pulse_size
    
        pst.Start(115_200)
        waitcnt(clkfreq*2+cnt)
    
       { repeat
          repeat until ina[1] == 0
          Time_one := cnt 
          repeat until ina[1] == 1
          Time_two := cnt
          pulse_size := Time_two - Time_one
          pst.Str(String(pst#NL, "pulse_size = "))
          pst.Dec(pulse_size)  }
          
        repeat
          waitpeq(%010,%010, 0)
          'waitpne(%010,%010, 0) 
          Time_one := cnt
          waitpne(%010,%010, 0)
          'waitpeq(%010,%010, 0) 
          Time_two := cnt
          pulse_size := Time_two - Time_one
          pst.Str(String(pst#NL, "pulse_size = "))
          pst.Dec(pulse_size)  
        
        {ctra := (%01000 << 26) | 1            'pos detector 
        frqa := 1
    
        repeat                
          phsa := 0                           ' clear accumulator       
          waitpeq(%010,%010, 0)               ' wait for high      
          waitpne(%010,%010, 0)               ' wait for pulse      
          TPulse := phsa           
          pst.Str(String(pst#NL, "TPulse = "))
          pst.Dec(TPulse)   }
    
Sign In or Register to comment.