PASM programming issues
Atal1101
Posts: 11
Hey all!
Just recently started working with PASM for a recent project on the Propeller involving taking an input pulse wave at 50Hz on P0 and outputting a pulse of the same duty cycle at ~16kHz on P1. This output pulse will go into a H-Bridge circuit and essentially forms a 0-10VDC DAC with 5000 increments of resolution to drive a CNC mill spindle.
I've included my complete code for the project below. If anyone can tell a noob what simple mistakes have been made or at least point me in the right direction, it would be greatly appreciated.
'Dyna PWM Output
'Outputs a 25kHz wave based on the input frequency PWM
'Computes based on 50Hz input frequency
'Runs in 2 cores
CON
_CLKMODE=PLL16x+XTAL1
_XINFREQ=5_000_000
inpin=0
outpin=1
TestLight=16
VAR
long speed
PUB StartUp
dira[inpin]~
dira[outpin]~~
dira[TestLight]~~
coginit(2,@pulsein, @speed) 'Runs counter in POS mode to measure input duty cycle
coginit(3,@pulseout, @speed)'Runs counter in PWM modified NCO mode to output high frequency PWM wave
repeat
waitcnt(5*clkfreq+cnt)
DAT
org
pulsein movs ctra, #inpin 'Set input pin for counter
movi ctra, %01000 'Set counter mode
mov frqa, $1 'Set counter to increment each clock input is high
mov phsa, $0 'Confirm that counter output is set to zero
:inloop waitpeq %1, $1 'Wait for the pulse to come
waitpeq %0, $1 'and go
mov inwidth, phsa 'Take the number of clocks in the pulse
wrlong inwidth, par 'and write the speed (equivelant duty cycle, translated 0-500 RPM) to the main variable "speed"
mov phsa, $0000_0000 'Zero phsa for next loop
jmp :inloop 'Return to beginning of loop
pulseout movs ctra, #outpin 'Set output pin of counter
movi ctra, %00100 'Set counter mode
mov frqa, #1 'Set counter to increment once each clock
mov outtime, cnt 'Set up current time from system clock
add outtime, outperiod 'and set up the first period
:outloop rdlong outduty, par 'Get up to date pulse width
mov phsawrite, maxphsa 'Take the maximum amount that the phsa register can reach
sub phsawrite, outduty 'and subtract the duty amount from it
waitcnt cnt, outtime 'then wait until the next period is set to begin
mov phsa, phsawrite 'Alter the phsa register so that the correct amount of clocks occur before the pulse drops
jmp :outloop 'Return to beginning of loop
inwidth res 1
outduty res 1
outtime res 1
outperiod long 5000
maxphsa long $FFFF_FFFF
phsawrite res 1
Alex
Just recently started working with PASM for a recent project on the Propeller involving taking an input pulse wave at 50Hz on P0 and outputting a pulse of the same duty cycle at ~16kHz on P1. This output pulse will go into a H-Bridge circuit and essentially forms a 0-10VDC DAC with 5000 increments of resolution to drive a CNC mill spindle.
I've included my complete code for the project below. If anyone can tell a noob what simple mistakes have been made or at least point me in the right direction, it would be greatly appreciated.
'Dyna PWM Output
'Outputs a 25kHz wave based on the input frequency PWM
'Computes based on 50Hz input frequency
'Runs in 2 cores
CON
_CLKMODE=PLL16x+XTAL1
_XINFREQ=5_000_000
inpin=0
outpin=1
TestLight=16
VAR
long speed
PUB StartUp
dira[inpin]~
dira[outpin]~~
dira[TestLight]~~
coginit(2,@pulsein, @speed) 'Runs counter in POS mode to measure input duty cycle
coginit(3,@pulseout, @speed)'Runs counter in PWM modified NCO mode to output high frequency PWM wave
repeat
waitcnt(5*clkfreq+cnt)
DAT
org
pulsein movs ctra, #inpin 'Set input pin for counter
movi ctra, %01000 'Set counter mode
mov frqa, $1 'Set counter to increment each clock input is high
mov phsa, $0 'Confirm that counter output is set to zero
:inloop waitpeq %1, $1 'Wait for the pulse to come
waitpeq %0, $1 'and go
mov inwidth, phsa 'Take the number of clocks in the pulse
wrlong inwidth, par 'and write the speed (equivelant duty cycle, translated 0-500 RPM) to the main variable "speed"
mov phsa, $0000_0000 'Zero phsa for next loop
jmp :inloop 'Return to beginning of loop
pulseout movs ctra, #outpin 'Set output pin of counter
movi ctra, %00100 'Set counter mode
mov frqa, #1 'Set counter to increment once each clock
mov outtime, cnt 'Set up current time from system clock
add outtime, outperiod 'and set up the first period
:outloop rdlong outduty, par 'Get up to date pulse width
mov phsawrite, maxphsa 'Take the maximum amount that the phsa register can reach
sub phsawrite, outduty 'and subtract the duty amount from it
waitcnt cnt, outtime 'then wait until the next period is set to begin
mov phsa, phsawrite 'Alter the phsa register so that the correct amount of clocks occur before the pulse drops
jmp :outloop 'Return to beginning of loop
inwidth res 1
outduty res 1
outtime res 1
outperiod long 5000
maxphsa long $FFFF_FFFF
phsawrite res 1
Alex
Comments
-Phil
Could you please post your code using these guides:
That will save the indentation that is crucial in SPIN programing.
LOL
Back to the issue at hand. You seem to be using $ for immediate operands. That's the prefix for hexadecimal numbers. The correct symbol is #, e.g. mov x,#1 stores the value 1 in x.
-Phil
Phil,
Thanks for the catch there. I've updated all of the spots I think you're talking about and have enclosed the complete code here below once again.
However, the main PASM programs still aren't loading properly and reading/writing the pulse wave as they are supposed to be. Any thoughts on what could be preventing the PASM programs from loading?
Binary numbers (usually) need immediate mode as well, e.g. movi ctra, #%01000.
Then in pulseout you say movi ctra, #%00100, do you mean movi ctra, #%00100_000. Which in turn brings us to missing output setups. dira is local to each cog so has to be done in PASM.
Use cognew instead of coginit to start a cog, if you not have a very good reason to choose the cognumber.
And finally: I think you need not 2 cogs to do what you want. If the output frequency is a multiple of the 50 Hz input frequency, then just make 2 nested loops. The inner loop updates the PWM pulses and the outer loop measures the pulswith of the 50 Hz signal, every time the inner loop has looped 320 times (for example @16kHz).
Andy
It is normal to indent below each CON, VAR, PUB and PRI sections. It makes code easier to follow.
The DIR settings in spin are not used as you don't output with this cog. Setting the DIR to input is the default.
IIRC you need to also set the DIR to output in the cog that outputs from the counter. Maybe someone else can confirm this.
Also in the startup cog, you perform a repeat with waitcnt but nothing else is being done. It would be simpler to shut this cog down with
cogstop(cogid)
The WAITPEQ instructions need to refer to a register setup to hold the data required and the mask. Only the mask may be an immediate value (prefix #) but this uses 9bit mask with the remaining upper bits zeroed.
You cannot mix RES and LONG. All RES must come at the end your pasm code. So each set of RES and LONGs must be at the end of each pasm block to which it is used. What happens is that the compiler reserves these RES blocks and increments the program counter. The RES blocks do not get included in the output object saving space. see the Propeller Manual for a better explanation.
I am no counter expert, so cannot help your here.
Each cog that wants to use a pin as an output needs to set its own DIRA for that pin to be an output.
The logic works like this: (only showing four cogs, but you get the point)
If you output directly to a pin, that cog must set the DIRA pin as an output.
If you are outputting from VGA or Counter, IIRC you must also set the DIRA pin as an output. (should have checked but busy compiling P1V)
Each output pin is wire ORed together with its respective pin from the other cogs. There is no contention.
He, he, I just woke up too. It's late afternoon here
Code is still not starting properly. Neither cog is loading, as evidenced by a quick test code I added to send out the speed it has recorded every 5 seconds via PST. It never moves from its initialized value, which tells me that it isn't reading the input pulse. In addition, the machine spindle never starts moving, even at the slow dummy speed I initialize the variable with.
Input uses a 220 ohm resistor on a 5V input line. Output goes directly to a H-Bridge IC.
Here is the newly updated code:
mov inwidth, phsa/320
loads inwidth with the contents of the register whose address is @phsa/320.
And this instruction
mov phsa, $0000_0000 'Zero phsa for next loop
loads phsa with the contents of location 0.
-Phil
I can see how I must have gotten the first mov instruction wrong. I meant to take the value of phsa, divide it by 320, and then eventually output via par]/b] to the shared speed variable. Can I just do the division of the value of @speed in the wrlong instruction, or is there some other way I should do this division.
As well, for zeroing the phsa register, should I just use the 'literal' zero (#0)?
Yes to the #0.
Also, I notice that your waitpeqs refer to locations 0 and 1, not the values 0 and 1. You can define a register to hold the value of the input mask, then refer to it, viz:
-Phil
Is this just a single PASM command, or can you give me a basic rundown on the subroutine and it's makeup?
-Phil
Try this: I tested this with a function generator and o'scope, and it looks like it's functioning.
Chris
Chris,
I set it up on the motor control I'm running, and while it runs great and smooth at both full speed and stopped, everything in between has a rough pulsed drive. Did you happen to check this with PWM when you tested it? Otherwise, I have to assume that it is some issue with the main motor drive I have it connected to and PWM.
Alex
The output frequency is ~16KHz, which you mentioned in the original post, but the comments in your code mention 25KHz output.
Chris