PWM on a single pin for 1 second @20 khz without starting a cog?
in Propeller 1
As the title says, how would I be able to pulse a pin at 20khz with a 50% duty cycle for 1 second when needed without starting a new cog? I already have all cogs used in my current program. Any help is appreciated!

Comments
5 BLINK 20 KHZ 1 second MUTE
Sometime you are just evil, Peter, but you are always correct.
I really need a vacation down under to get the gist out of RPN and FORTH.
Maybe I start with mounting my globe upside down. Just to get a feeling for it.
Thanks,
Mike
My globe has Australia on top and you guys down under (it's all the same to Sol) so that means RPN is actually AFN ( Australian Forward Notation) so that is probably why it's all back to front for you guys
each cog has two timers, they run in parallel to normal execution. You can set modes and frequencies and pins and tons of other things I do not really understand by myself, but they are there. The nice thing is the timer are very fast, even when used out of spin, not assembly.
@Evanh posted the link for a detailed description. You basically need to set pin, frequency and mode, start it, wait a second and stop it.
@Peter, looks like I need to add AFN to my known Acronyms 'Australian Forward Notation'. Very nice. It looks like I am on the right trail there with the Globus south side up, since you did that too. Will try, wait some weeks to adjust, and test Tachyon again.
A friend of mine told me he has "CDO". I ask what "CDO" is. He said: It is like "OCD" but in alphabetical order...
Enjoy!
Mike
BTW, here is a scope shot of Tachyon executing the command:
22 BLINK 5 KHZ 8 ms MUTE
The solenoid needs around 20khz from what I read a long time ago. I can't find the spec sheet on it anymore
I don't know what Tachyon is and I am sure it would be a PAIN converting my entire program (over 1000 lines of code) to another language I know nothing about
This is what I have figured out so far..... which is not much at all....
ctra := %00100_000 << 26 + Controller_Pin
frqa := $A000_0000 ' ????????? lost.....
The other thing to remember is that Tachyon is interactive to you can just try an idea with instant results (and feedback) much as I did the one liner.
You could probably cherry pick 20-ish very specific settings for frequency and duty to give you 20khz with the timings you need, and have one of your cogs push those settings through the counter registers for you over your 1 second period. Would it need to be really fine granularity for it to work?
I think you can do exactly that, using two counters in NCO mode, and you can push that 1 second out to longer (or shorter).
What you do is pgm one NCO to 20.000kHz and the other to 20.0005kHz, and then externally XOR with a SingleGate Logic device.
(1G57/58/97/98 etc)
Over a time of 1 second the phase of those walks half a period, which swings XOR from 0% to 100%
If you want a movement of 0 to 50% or 50% to 0, then you need a quarter period in 1 second, or 20.00025kHz
Once you hit the target, you need to reconfig the counters, otherwise the phase movement continues and you will get a triangle wave modulation of Duty Cycle : Up-Down-up-Down
Maybe it's a smarter solenoid, and the PWM is converted to a DAC value ?
In which unlikely you would be able to use duty cycle mode effectively.
What Tim did make clear at the outset was the Cogs were all busy ... so having the hardware maintain the set level is important.
Assuming its a DC coil, 20KHz probably is a good starting point for duty mode. Worst case will be a noisy valve down near 0%. Just don't use that part of the range.
I am not that keen on counters, but it sounds to me that you could get what you want by altering my stepper driver code. This code alters the duration of the pulse, for a specific number of pulses, however, I suppose it would be easy enough to alter it to being time based (1 second), instead of being pulse based. Of course most of this code is useless to you, but the important thing is the math that alters the frequency. Here is an example:
PUB MoveStagePosition(Direction, TotalSteps) | Counter, RampingSteps, RunningSteps {{ Method Description: This method will ramp up a stepper motor until the maximum speed is obtained and it will maintain that maximum speed for a determined number of steps, at which point it will begin a ramp down of the stepper motor until it comes to a stop. Parameters: Direction = This parameter sets the OUTA register utilized for bDirectionPin. This setting will control the direction of rotation for stepper drives and it should be a value of 0 (LOW) or 1 (HIGH). TotalSteps = This is the total amount of steps or microsteps that you want the motor to rotate, and this should be equivalent to the number of high pulses sent to the stepper driver unit. Local Variables: Counter = This variable is used to hold the value of the System Counter for timing accuracy of the stepper driver. RampingSteps = After computations, this variable will contain the number of pulses required to obtain the highest speed possible with the provided parameters. RunningSteps = Providing TotalSteps is larger than the value of the global variable lTotalRampingSteps, this variable will contain the number of maximum speed steps. However, if TotalSteps is smaller than the value of the global variable lTotalRampingSteps, the value of this variable will become either 1 or 0. Example Usage: This method can be called directly as follows: X.MoveStagePosition(0, 5000) or X.MoveStagePosition(1, 5000) or it can be called through the IncreaseStagePosition and DecreaseStagePosition methods. }} 'If maximum speed can be obtained, determine the actual number of 'ramping and running steps. IF TotalSteps > lTotalRampingSteps 'Copy the global amount of ramping steps RampingSteps := lRampingSteps 'Determine the number of full speed steps. RunningSteps := TotalSteps - lTotalRampingSteps 'Else ramp upto the half-way point and then ramp down. ELSE RampingSteps := TotalSteps / 2 RunningSteps := TotalSteps // 2 'Set the OUTA register to match the desired direction of rotation. OUTA[bDirectionPin] := Direction 'Set up the CTRMODE of Counter A for NCO/PWM single-ended. CTRA[30..26] := %00100 'Set the output pin for Counter A. CTRA[5..0] := bStepPin 'Set the value to be added to PHSA with every clock cycle. FRQA := 1 'Set APIN as an output. DIRA[bStepPin] := 1 'Get the current System Counter value. Counter := CNT 'Ramp up the stepper motor to maximum speed. REPEAT RampingSteps 'Send out a high pulse on the step pin for the desired duration. PHSA := -lHighPulseWidth 'Wait for a specified period of time before sending another 'high pulse to the step pin. WAITCNT(Counter += lStartStopSpeed -= lRampIncDec) 'Maintain maximum speed REPEAT RunningSteps 'Send out a high pulse on the step pin for the desired duration. PHSA := -lHighPulseWidth 'Wait for a specified period of time before sending another 'high pulse to the step pin. WAITCNT(Counter += lStartStopSpeed) 'Ramp down the stepper motor and come to a stop. REPEAT RampingSteps 'Send out a high pulse on the step pin for the desired duration. PHSA := -lHighPulseWidth 'Wait for a specified period of time before sending another 'high pulse to the step pin. WAITCNT(Counter += lStartStopSpeed += lRampIncDec)pub pwm_burst(pin, freq, duty, ms) | cycletix, dutytix, t '' Output pwm burst on pin '' -- WARNING: Blocks while running cycletix := clkfreq / freq ' ticks in full pwm cycle dutytix := -cycletix * duty / 100 ' ticks in active portion of cycle ctra := (%00100 << 26) | pin ' set counter for pwm/nco mode frqa := 1 dira[pin] := 1 ms *= clkfreq / 1000 ' convert millis to ticks t := cnt repeat phsa := dutytix waitcnt(t += cycletix) if ((ms -= cycletix) =< 0) quit dira[pin] := 0CON _clkmode = xtal1 + pll16x _xinfreq = 5_000_000 PIN = 0 PUB Main | width ctra := %00100<<26 + PIN 'init counter frqa := 1 dira[PIN] := 1 repeat width := -21_000 'do pwm for 1 sec repeat 20_000 waitcnt(2200 + cnt) 'repeat with 17 kHz phsa := width/10 width += 1 'decrease pulswidthFrequency is about 17 kHz but you can adjust time, frequency and width by changing the numbers.
The first 3 lines are only once needed to initalize, the last 5 lines do one shot of PWM for a second.
Andy
This runs at 20khz. I tested it with an LED on pin 9. It contains a cognew so I could test it. You can simply initialize it in a child object that has an available counter and call it from a parent.
edit: I didn't configure the cog to run @ 80Mhz.
http://obex.parallax.com/object/839
It is like the one in the previous post but with counter initialization added for the inverted waveform on a second pin, and a square wave synchronized at the beat frequency on a third pin. It is bare bones so that the essentials don't get lost in apps overhead.
The cog counters are great for picking up little tasks. They're limited, of course, but when appropriate, they are a great but I think underused feature of the Prop 1.