Picaxe 28X2 pwm channels

stan74

Senior Member
Hi.I bought a 28X2 thinking it would be a replacement for a 16F876 in a robot I built 15 years ago.
The 2 PWM pins on a 16F876 are independent and could run at different mark space.
Are the 4 28X2 PWM pins independent? 2 for motors at 100Hz and 1 for 38KHz.
Sorry if the answer is of course they're independent.
 

hippy

Technical Support
Staff member
On the 28X2, PWMOUT C.1 and C.2 operate at the same frequency, PWMOUT B.0 and B.5 can both operate at different frequencies to C.1 and C.2 and each other.

In your example it is probably most flexible to use C.1 and C.2 for the motors, B.0 ( or B.5 ) for the 38kHz, leaving the other free for use at any frequency.

Note that the lowest PWMOUT frequency of a 28X2 is 244Hz at 4MHz.
 

stan74

Senior Member
Thanks for explaining that,things are looking up.I'll use B.0 and B.5 for the motors so I can change the speed of each.
 

stan74

Senior Member
I thought that if the duty cycle change then the frequency changed but this code from the manual says it doesn't.
I'm waiting for my download lead to arrive so I can't test it.

init:
pwmout C.2,150,100 ; start pwm
main:
pwmduty C.2,150 ; set pwm duty
pause 1000 ; pause 1 s
pwmduty C.2,50 ; set pwm duty
pause 1000 ; pause 1 s
goto main ; loop back to start
 

hippy

Technical Support
Staff member
Frequency of the PWM output stays as it was unless explicitly changed. PWMDUTY commands will only affect the 'length of pulse' not the rate they are produced.
 

stan74

Senior Member
Thanks hippy.The manual says to maintain 50:50 duty cycle and change frequency then have to adjust duty cycle.Confused me.
Any idea why this doesn't work? Nothing on scope.Downloads ok.

init:
pwmout C.1, 52, 105 ;38 KHz 50%
pwmout pwmdiv16, B.0,124,249 ; 1KHz 50%
pwmout pwmdiv16, B.5,124,249 ; 1KHz 50%

main:
pwmduty C.1,105
pwmduty B.0,249
pwmduty B.5,249
 

hippy

Technical Support
Staff member
Any idea why this doesn't work?
Probably because the program starts, initialises, then reaches an implied END at the end of the program rather than looping. It is all over before it has started.
 

stan74

Senior Member
You're right hippy. Doh,my 1st picaxe program,I thought once pwm initialised it carried on as long as powered,like setting a pin.
Strange though, C.1 does give 38KHz, B.0 1KHz but B.5 is 16KHz.
 

hippy

Technical Support
Staff member
Strange though, C.1 does give 38KHz, B.0 1KHz but B.5 is 16KHz.
That is probably related to the resetting PWMDIV noted in Manual 2; "Note that on X2 parts (only), use of any ‘pwmout’ command will reset all the other active pwm pins to pwmdiv1".

With a later PWMOUT resetting earlier PWMOUT I would have expected B.0 to jump to 16kHz and B.5 to become 1kHz.

There are details of how that can be resolved here -

http://www.picaxeforum.co.uk/showthread.php?21867-Using-PWMDIV-options-with-the-PICAXE-28X2-and-40X2

The best way to do things is initialise the PWMOUT channels with desired frequencies at 0% duty, fix any PWMDIV settings ( as per above ), then set the duties as desired.
 

hippy

Technical Support
Staff member
You would probably be better off using C.1 and C.2 for motors at 1kHz and using B.0 ( or B.5 ) for the 38kHz. Then it should simply be -

Code:
pwmout           B.0,  52, 105 ; 38kHz 50%
pwmout pwmdiv16, C.1, 124, 249 ;  1kHz 50%
pwmout pwmdiv16, C.2, 124, 249 ;  1kHz 50%
And I think that should just work as desired, though I have not checked that.

Because C.1 shares its timer with C.2, when C.2 is set, even though C.1 may go back to PWMDIV1, C.2 being PWMDIV16 will then force C.1 to PWMDIV16. B.0 will be reset to PWMDIV1 but as that is what it should be it's not a problem.
 

stan74

Senior Member
I read that div command resets other pwm channel just now and so used setfreq 4m and raised pwm to 4KHz so didn't use div and it all works but don't know if my small motors will work at 4KHz. I know they work at 100Hz. I'll connect one.
 

stan74

Senior Member
Thanks hippy.
Using C.1 and C.2 works at 1KHz but not B.0 and B.5 because of the div command??? Bit confusing but it works.
Hopefully I can send 38KHz to 3 or 5 IR leds and then connect each led to a port via resistor and make the port low to turn it on.
Or won't that work?
 

hippy

Technical Support
Staff member
Using C.1 and C.2 works at 1KHz but not B.0 and B.5 because of the div command???
All PWM channels support all available frequencies and frequency divisions. It is just that, due to the way things are implemented in hardware and firmware, some settings may undo other settings which may need to be corrected before output is what was desired.

It is like being in a library with four people; two of whom are only allowed to read books of the same format, hardback or softback, with the same colour cover, and anyone picking up a hardback has to swap any hardbacks being read by others with softbacks.

It can sometimes get a bit tricky working out how to get all the books one wants to have read into which hands. All reading softbacks with the same colour cover; that's easy. Throw in hardbacks and different colour covers and it becomes more challenging.

Hopefully I can send 38KHz to 3 or 5 IR leds and then connect each led to a port via resistor and make the port low to turn it on.
Or won't that work?
Yes that should work, is the way it is commonly done to have bit-banged IR output.
 

stan74

Senior Member
You've been a great help hippy. I banged out the code for a 3 ir bot last night. Syntax problems every time I used the syntax checker but quicker than assembler. here's the code. Symbol frontled = A.0 if frontled = 1 then doesn't work.
Is there a way of doing this to make code easier to read?

Code:
; object avoiding robot

init:
input A.0,A.1,A.1
output B.1,B.2,B.3
high B.1,B.2,B.3 ;leds off
pwmout B.0, 52, 105 ; 38000Hz at 50% @ 8MHz
pwmout pwmdiv16, C.1, 124, 0 ; 1000Hz at 0% @ 8MHz motors 
pwmout pwmdiv16, C.2, 124, 0 ; 1000Hz at 0% @ 8MHz  off
;lmotordir = B.6
;rmotordir = B.7
;lled = pinB.1
;fled = pinB.2
;rled = pinB.3
;lsens = pinA.0
;fsens = pinA.1
;rsens = pinA.2
    
main:
low B.2 ; front led on
if pinA.1 = 0 then ; forward is blocked?
    pwmduty C.1,0 ; motors off
    pwmduty C.2,0
    high B.2 ;front led off
    low B.3 ; right led on
else goto goforward
endif

if pinA.2 = 1 then ; right is clear
    low B.3 ; right led off
    high B.6 ; left motor forward
    low B.7 ; right motor reverse
    pwmduty C.1,249 ; spin clock wise
    pwmduty C.2,249 ;   50% speed
    low B.2 ; front led on
    
    do until pinA.1 = 1 ; spin clockwise until 
    loop ;                    no object
    
    high B.2 ; front led off
    pwmduty C.1,0 ; motors
    pwmduty C.2,0 ;  off
    goto goforward
endif        

; right is blocked so reverse for 1/2 second then spin half second
low B.1 ; left led on
if pinA.0 = 0 then ; left blocked
    high B.1 ; left led off
        low B.6 ; left motor reverse
        low B.7 ; right motor reverse
        pwmduty C.1,249 ; left motor 50%
        pwmduty C.2,249 ; right motor 50%
        pause 500 ; reverse for 1/2 second
        pwmduty C.1,0 ; motors
        pwmduty C.2,0 ;  off
                                
        high B.6; left motor forward
        low B.7 ; right motor reverse
        pwmduty C.1,249 ; spin clock wise
        pwmduty C.2,249 ; 50% speed
        pause 500 ;       for 1/2 second
        
end if

goforward:
high B.6 ; motors
high B.7 ; forward
pwmduty C.1,499 ; motors
pwmduty C.2,499 ;  full

goto main
 
Last edited by a moderator:

hippy

Technical Support
Staff member
Symbol frontled = A.0
if frontled = 1

You need to change to -

Symbol frontled = outpinA.0

To make that a 'pin variable' rather than a 'pin number'. You can also use "= pinA.0" though the preferenc eis usually to indicate it is being usd as an output pin.
 

stan74

Senior Member
I tried symbol lled = outpinB.1 high lled and got a syntax error.
also
if b3 < 100 then goto testright
elseif b3 > 156 then goto testleft
endif

says end without if but this is ok

if b3 < 100 then
goto testright
elseif b3 > 156 then goto testleft
endif.

I'll get the hang of the syntax.Python on a Raspberry Pi is hard.
 

hippy

Technical Support
Staff member
There are two ways to turn output pin B.1 on -

Let outpinB.1 = 1
High B.1

Let requires a pin variable name (outpinB.1), High requires a pin identifier (B.1). If the wrong thing is select then this will generate a syntax error. For example, both of these would give syntax errors -

Let B.1 = 1
High outpinB.1

The last isn't technically a syntax error per se, but the code is so unlikely to do what the programmer was expecting it to do when writing the code that we choose to flag it as an error.

When replacing 'outpinB.1' or 'B.1' using a name defined by a SYMBOL definition, that definition has to reflect what the command the name is used in -

"Symbol LED = B.1" will cause an error with "Let LED = 1", and,

"Symbol LED = outpinB.1" will cause an error with "High LED".

It is usually best to use just one consistent form to set pins, but where this isn't desirable it may be necessary to define two separate symbols for use in each ...

Code:
Symbol LED        = B.1
Symbol OUTPIN_LED = outpinB.1

High LED
Let OUTPIN_LED = 1
As for the IF-THEN-GOTO; that is a single line IF statement so cannot have an ELSE or ELSE-IF following it. It is treated the same as "IF condition THEN label" which is the allowed statement in full.

When breaking the line after the IF-THEN, that turns it into a multi-line IF which can then have ELSE or ELSE-IF clauses.

There is tendency for people to write "IF-THEN-statement" and the compilers do their best to figure out if it's a single line or multi-line IF but the IF-THEN-GOTO is always treated as a single-line IF.
 

stan74

Senior Member
That's brill hippy.Thank you very much for your time explaining.The code is much easier to follow now.
I prefer high LED to let LED = 1 because that looks like a variable not a port.Well to me it does.
The last time I saw the let statement in Basic was on a Sinclair Spectrum and it says in the Picaxe manual on page 137 - " The &#8216;let&#8217; keyword is optional."
 

stan74

Senior Member
Hi hippy,sorry to be a pain.I HAVE been googleing before posting.
So...
symbol name= B.1
input name
output name
high name
low name
is ok

but....if name = 1 then....has to be

symbol name = pinB.1
input B.1.....not input name...syntax error
etc.

The code is now 1000 bytes and compiles ok so I'll get used to Picaxe basic rules.As long as it works,quirks don't bother me.
I didn't find outpin in the manual index.
 

hippy

Technical Support
Staff member
There are actually three 'pin things' in PICAXE Basic -

B.1 - A pin identifier
outpinB.1 - A variable which sets the output pin
pinB.1 - A variable which holds the input pin value

The first (B.1) is used in HIGH, LOW and similar commands.

The second (outpinB.1) is used in LET assignments.

The last (pinB.1) is required in IF statements or when otherwise reading an input pin.

There's a special case for "Let pinB.1 =" where, because it's on the left of the equals, the assigned side, it is the equivalent of "Let outpinB.1 =". The two are unique and distinct when on the right -

"Let b0 = pinB.1" / "IF pinB.1 = ...." reads the input pin

"Let b0 = outpinB.1" / "IF outpinB.1 = ...." reads what the pin was set to as output.

Extending the example in Post #17, you may need three separate SYMBOL defintions for the one pin to allow their use in a program ...

Code:
Symbol CLK        = B.1
Symbol OUTPIN_CLK = outpinB.1
Symbol PIN_CLK    = pinB.1

High CLK
Let OUTPIN_CLK = 1

Input CLK
If PIN_CLK = 1 Then ...
However, because of that "pinX.Y" equivalence to "outpinX.Y" when on the assigned side of a LET command, if you never need to read how output pins were set, that can be simplified to ...

Code:
Symbol CLK        = B.1
Symbol PIN_CLK    = pinB.1

High CLK
Let PIN_CLK = 1

Input CLK
If PIN_CLK = 1 Then ...
 

stan74

Senior Member
Thanks. I got it now. I tried:-
symbol Lmot_dir = B.0
symbol go_forward = b1
go_forward = 1
;
Lmot_dir = go_forward.... it doesn't work but it's readable :)
 

stan74

Senior Member
ps I changed all the motor speed and directions into subroutines and was surprised the code is half the size.
IR sensors arrived today but I'm waiting for ultra sonic 4pin modules to arrive because they seem better at measuring distance
but I don't know about fabric covered furniture.Like IR is poor with dark objects.So black curtains would be hard.
 

hippy

Technical Support
Staff member
Lmot_dir = go_forward.... it doesn't work but it's readable :)
You probably want something like ...

Code:
Symbol lmot_dir    = outpinB.0

Symbol GO_FORWARD  = 1
Symbol GO_BACKWARD = 0

Do
  lmot_dir = GO_FORWARD  : Pause 1000
  lmot_dir = GO_BACKWARD : Pause 1000
Loop
 
Top