Shop OBEX P1 Docs P2 Docs Learn Events
Using a smart pin by more than one cog — Parallax Forums

Using a smart pin by more than one cog

pik33pik33 Posts: 2,366
edited 2021-08-27 13:07 in Propeller 2

I am programming a robot now. It has 8 ultrasonic sensors in it.

This fragment of code measured the distance:

pinstart (us_echo_0 addpins 7, P_INVERT_IN | P_COUNT_HIGHS,0,0)

' the code below later went to the pub ultrasonic () |s,r

repeat
  waitms(50)
  r:=rdpin(us_echo_0+s)+/1676                               ' 1676~=285000000/170000 (cycles/s / (340k mm/s*2))
  if (r<0) | (r>9999)
    r:=0
  sensors[s]:=r  
  pinclear(us_echo_0+s)                                     ' clear the echo counter 
  pinl(us_trig_0+s)                                         ' inverted in buffer, set 1 on trig input
  waitus(40)                            ' 40 us trig pulse
  pinh(us_trig_0+s)                                         ' 0 on trig
  pinstart(us_echo_0+s, P_INVERT_IN  | P_COUNT_HIGHS,0,0)   ' restart the echo length counter
  s+=3                                                      ' avoid to run adjacent sensors
  s+//=8 

It worked. Then I decided to put this to the separate cog, so I made this

pub ultrasonic() |s,r

Then in the main program at cog #0 I left

pinstart (us_echo_0 addpins 7, P_INVERT_IN | P_COUNT_HIGHS,0,0)
cogspin(5,ultrasonic(),usstack)

It started to count endlessly on pins us_echo_n as if pinclear didn't clear anything.

The solution was moving

pinstart (us_echo_0 addpins 7, P_INVERT_IN | P_COUNT_HIGHS,0,0)

to the ultrasonic() method

and all of this started to work again.

It seems I have to initialize and read a pin fron the same cog. Is it true?

Comments

  • evanhevanh Posts: 15,915

    It's probably because DIR never goes low. The launcher cog is holding them all high. Which means the smartpins never get reset. The pinclear() turns the smartpins off but the X, Y, Z and any hidden smartpin state are still loaded with old data when the smartpins are turned back on again. Only a low on DIR guarantees a reset state.

  • evanhevanh Posts: 15,915

    Of course, one solution is to account for old state and not try to reset them on every pulse. It is possible to bit-bash the outputs while the smartpins are on.

  • JonnyMacJonnyMac Posts: 9,102
    edited 2021-08-27 16:19

    The key to this, I believe -- and as Evan pointed out, is remembering that the smart pin "guts" (mode, x, y, z) are stored in the pin, but the control mechanism is the DIR bit for that pin which can be different in each cog. If any cog makes a smart pin IO an output, the smart pin mode is activated. Another cog cannot disable the smart pin by manipulating its DIR bit.

    In my version of FullDuplexSerial I setup the UART pins in the calling cog, and use them in another (buffer management cog). In that case, though, I never need to disable the smart pins.

    In my S.BUS receiver I can't do that because there is an element in the code that wants to wait for the serial input to be quiet (space between packets). The only way to deal with this is to disable the smart pin so that I can use the pin as a normal input. When the quiet space is detected, I drive the pin low again to re-engage the UART. That is to say that in my S.BUS object, the smart pin is setup and manipulated in the same cog -- you've found a similar solution.

    May I ask what sensor you're using? FWIW, this is the guts of my Ping object, which only deals with a single sensor.

    pub ticks(pin) : result | timeout
    
    '' Request distance from sensor on pin
    '' -- returns 1-way distance in microseconds
    '' -- will return 0 if bad or missing sensor
    
      pinclear(pin)                                                 ' reset pin
    
      pinhigh(pin)                                                  ' generate trigger pulse
      waitus(5)
      pinlow(pin)
      waitus(5)                                                     ' let trigger register
    
      timeout := getms()                                            ' start time-out timing
      pinstart(pin, P_HIGH_TICKS, 0, 0)                             ' measure return pulse (system ticks)
      repeat
        if ((getms()-timeout) > 25)                                 ' if > 25ms, abort
          return 0
      until pinread(pin)                                            ' wait for end of pulse
    
      return (rdpin(pin) / (clkfreq / 1_000_000)) >> 1              ' convert pulse to microseconds
    
  • pik33pik33 Posts: 2,366

    It is possible to bit-bash the outputs while the smartpins are on.

    Yes, it is. Instead of pinclear, keep the old count and compute the difference. This may cause problems when overflow occurs.

    I tried to understand why the code works different in these two version: the DIR holding by the launcher cog can explain this. The simplest way to avoid thes kind of traps is to avoid controlling the pin from more than one cog unless it is necessary.

  • JonnyMacJonnyMac Posts: 9,102
    edited 2021-08-27 17:36

    It is possible to bit-bash the outputs while the smartpins are on.

    I've not found this to be the case -- am I wrong?

    I went back and looked at my SBUS code. It turns out that I am configuring the pin in the calling cog.

      x := muldiv64(clkfreq, $1_0000, 100_000) & $FFFFFC00          ' set bit timing
      x |= (9-1)                                                    ' set bits (9: 8 data, 1 parity)
    
      pinstart(rxp, M_SBUS, x, 0)                                   ' configure smart pin for S.BUS rx
      pinfloat(rxp)                                                 ' release
    
      cog := coginit(COGEXEC_NEW, @entry, @rxp) + 1                 ' start s.bus rx/decode cog
    

    Note that I disable it in that cog with pinlfoat(). Now, this doesn't affect the pin settings (mode and x register). Here's a snip from the cog code:

    sbus_rx         fltl      rxd                                   ' reset uart, release smart pin
                    wrpin     #0, rxd                               ' restore tri-state mode
    
    wquiet          mov       t1, #250                              ' wait for low between frames
    .wq1            waitx     us1
                    testp     rxd                           wc      ' check state of rx pin
        if_c        jmp       #wquiet                               ' if activity, restart wait
                    djnz      t1, #.wq1
    
                    wrpin     M_SBUS, rxd                           ' restore smart pin uart
                    drvl      rxd                                   ' re-enable smart pin
    

    Maybe I don't need to clear the mode register as I'm only using that pin as input to detect the gap between packets. In your case, though, you want to create an output pulse so you definitely need to clear the mode register to create a clean pulse. You can then restore it to your smart pin setting to measure.

  • pik33pik33 Posts: 2,366
    edited 2021-08-27 20:08

    The code starts the trigger and then measures the echo time. The trigger and the echo are different pins, so it is possible to not reset the pin, using a difference between readings instead.

    The robot has SBUS remote controller, so I also use your SBUS code which runs in a separate cog and works without any problems. Now I know what this pinfloat(rxp) is for.

  • Okay, I understand -- I was wrapped up in the idea that the trigger pin and measure pin were the same. If you do want to simplify things by resetting the pins (which clears Z), you need only float them, then take them back to outputs. I do this in a couple other projects.

    I'm glad the SBUS code is useful for you. Looking forward to seeing pictures of your robot.

  • Cluso99Cluso99 Posts: 18,069

    Without reading the whole thread....

    You can use a smart pin to do input timing without actually preventing the pin being used to input/output by bit bashing. You do this by using the smart pin logic of a nearby pin (+ or - up to 3 pins). This does mean (I think) you cannot use the pin used as a smart pin as a normal I/o pin.

    Let me explain...
    In the boot code, P63 is the input RXD pin. When autobauding, Chip uses the smart pins of P0 & P1 setup to time the bits on P63 while still looking at P63. During this process, I believe P0 & P1 could not be used normally. Note they are -1 & -2 pins away from P63 due to wrapping.

    Hope this makes sense and perhaps it might be useful here.

Sign In or Register to comment.