Using a smart pin by more than one cog
pik33
Posts: 2,366
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
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.
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.
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.
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.
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.
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:
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.
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.
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.