Smart Pin Mode 4 (pulse out) question
tomcrawford
Posts: 1,126
in Propeller 2
I'm fiddling with feedback servo and trying to use smart pin mode 4 to generate the control pulses.
I have a 20 msec heartbeat which I can see on sync, but noting happens on ThePin. I think I am setting the smartpin mode correctly, and am setting x[31..16] to zero so it just does a pulse for the duration in y. I would be grateful if some kind soul could help. AdThanksVance
CON oscmode = $010c3f04 'standard stuff freq = 160_000_000 baud = 115200 'must configure RUN command to match this msec20 = freq/50 'tics in 20 millisec microsec = freq/1_000_000 thepin = 48 sync = 49 pulseMode = %00100_0 'smart pin mode OBJ ser: "PrintfSerial" 'access to Propellor output (output only) var long param byte cog byte theservo pub main clkset(oscmode, freq) ser.start(baud) 'start up serial terminal ser.str(string("hello, world")) ser.nl param := microsec*1500 '1500 usec cog := cognew(@entry, @param) 'send ADDRESS of param ser.str(string("cog started: ")) ser.dec(cog) ser.nl repeat 'forever. DAT entry ORG 0 mov hubAdrs, ptra 'boss cog passed an address: save it rdlong PWidth, hubAdrs 'fetch the pulse width dirh #sync 'scope sync wrpin #pulseMode, #thepin 'set smart pin mode wxpin #0, #thepin 'x[31:16] = 0 getct target addct1 target, ##msec20 'set up 20 msec timer loop waitct1 'remainder of 20 msec addct1 target, ##msec20 'set up for next pass outh #sync 'scope sync waitx ##160 outl #sync wypin pWidth, #thepin 'start a pulse jmp #loop hubadrs res 1 'address in hub memory target res 1 'target time pWidth res 1
I have a 20 msec heartbeat which I can see on sync, but noting happens on ThePin. I think I am setting the smartpin mode correctly, and am setting x[31..16] to zero so it just does a pulse for the duration in y. I would be grateful if some kind soul could help. AdThanksVance
Comments
"Smart pins should be configured while their DIR signal is low...Once configured, DIR can be raised high..."
I also made PWidth a long in case I was somehow not getting it correctly from the hub. I'm sure it is something simple I can't see.
Looks well written overall. I think you just need to fix one of those little gotcha's with smartpins - The output DIR control has to be overridden because the DIRH instruction is now controlling the smartpin, not the output. Namely pulseMode needs set to %01_00100_0. That's the %TT bits doing their bit.
The attached logic analyzer capture shows what I am seeing. Each of the negative pulses measures 10 nsec, which is the sampling rate.
Appreciate any help. tc
If you want a continuous level rather than a pulse count, then the NCO or PWM modes on next pages of document would be better. EDIT: I'd recommend %01001 = PWM sawtooth
X[15:0] = sys_clock_freq / 1_000_000
X[31:16] = 20_000
Eg:
And set Y = 1500 for your initial level.
Edit: Added example
The %00100 = pulse/cycle output mode, appears to have no prescaler, instead it has SysLCK/N for the ramp, and compares that, counting pulses each time.
That may be too quick for 1.5ms time frames.
PWM mode expects to run forever, so repeats the PWM pulse with 16 bits of prescaler and 16 bits of PWM period/Duty.
You could reload that on-the fly, for each pulse
%01001 = PWM sawtooth
X[31:16] = PWM frame period, you could set this to max at 20ms, then work back from here. 20ms in 160MHz is ~ 22 bits. No separate timer needed.
X[15:0] = prescaler, here 160M/N, a couple of choices are SysCLK/48 or SysCLK/49
1/(160M/48/2^16) = 19.6608 ms
1/(160M/49/2^16) = 20.0704 ms
Y[15:0] establishes the PWM output value which gets captured at each frame start and used for its duration. It should range from zero to the frame period.
At each base period, the captured output value is compared to the counter. If it is equal or greater, a high is output. If it is less, a low is output. Therefore, a zero will always output a low and the frame period value will always output a high
for 1.5ms of Duty Cycle, the load values are
2^16*(1.5m/19.6608m) = 5000
2^16*(1.5m/20.0704m) = 4897.959
Software here only needs to be ready every 20ms to load the following cycles duty cycle, from a small table, or in a small loop.
Simplest, but modest precision....
If one part in 5000 is not good enough precision, (it's over 12 bits) , you need to either add more SW. or choose another mode.
eg you could map 16 bits to 2ms, using a prescaler of 5, and alternate PWM and SW timer.
This jumps precision to 32000 steps for 1.0 ms span. giving load values of
1.0m/(5/160M) = 32000 for 1.000 ms
1.5m/(5/160M) = 48000 for 1.500 ms
2.0m/(5/160M) = 64000 for 2.000 ms
or, pushing a little further, the actual time change span is 1.0ms, so prescaler can become a faster 160M/3, and frame period is 53333, for a IN rate of 53333*3/160M = 999.99375 us
Here, interleave of SW timers and PWM pin control starts to sounds tricky, as you need HI/VAR/LO..LO..LO
Probably simplest to set a loop that always runs Smart-Pin, to avoid possible glitches..
L=0, load 53333 (gives 100% hi, for 999.99375 us ) then next load VAR Duty modulation of N=0..53333 for 0..999.99375 us variable portion.
then load 0 result, 18 times, then repeat for ~20ms total. Each 20ms repeat can choose a next-duty value to load.
A variant of that could alternate the prescaler as /3, /3, /(3*18) for [999.99375 us, 999.99375 us, 17.9998875 us] time intervals. (code can do something else in that 18ms window)
As evanh mentioned so those loads of 53333/Var/0/53333/Var/0.. need to be phased ahead of when they will apply, when doing the prescaler change.
I think that means when you load 53333, the HW is running 0, so that's when you flip prescaler to slower, then on load Var flip back faster.
It will be obvious when the phasing is wrong
Is there a smart pin mode that can give SysCLK granularity over 20ms times ?
%00110 = NCO frequency has 32b adder, but only 16b phase field.
You were so close, all you needed was to set the base period.
Here's my test code
X[15:0] establishes a base period in clock cycles which forms the empirical high-time and low-time units.
does not really apply, and is overruled by
.. base period counter will be compared to on each clock cycle, as it counts from X[15:0] down to 1, before starting over at X[15:0] if decremented Y > 0.
On each clock, if the base period counter > X[31:16] and Y > 0, the output will be high (else low).
X[31:16] = 0 bypasses the faster PWM logic, { X[31:16] /X[15:0] } and instead uses only (Y>0) monostable action.
pwidth above will be 1500*180M/1M = 270000, so the 32b Y field sets the 'one shot' width, to 1 sysclk granularity.
Perhaps you might like to add the basics to a post in the P2Tricks and Traps thread with a link to this thread for more details?
I will when I get it cleaned up. It demonstrates a number of techniques.
1. Setting the clock to 160 MHz (in spin)
2. Starting a PASM cog, including passing a parameter via PTRA
3. Setting up two smart pins, using mode %00100 (pulse output)
4. Running a 50Hz loop using waitct1/addct1
5. Using a smart pin to generate a scope sync in one instruction