Set Duty Cycle with PWMDuty

bhanlon56

New Member
I've built another ebike based on this design:
However I've used a pair of brushless motors recycled from electric drills. I am also reusing the control electronics. The speed of the motors is controlled by a 0 to 5V signal from the drill trigger switch. It was the switch mechanism that died in each drill.
I wanted to control the motors using high/low/stop micro-swiches mounted on the bike handlebars.
My design uses a picaxe to output a pwm signal whose duty cycle is controlled by the micro-switches. The pwm signal goes to a RC digital analog converter based on the information based at this page:
Seems easy enough. For example, if I used a 10K and 100nF combination with a 40kHz pwm signal at 50% duty cycle, I get about 2.5V out.
The pwmduty command uses a 0-1023 variable in its syntax. I assumed that if I used:
pwmduty c.2, 510
I would get roughly a 50% duty cycle and therefore 2.5V output from the picaxe DAC
However, there seemed to be no relationship between the variable used, in my example 510, in the pwmduty command and the output voltage. I ended up finding the values to use in the pwmduty command by trial and error.
What is the relationship between the variable used in the pwmduty command and the output duty cycle? If I use a quarter of 1023 (ie about 250), should I get a 25% duty cycle?
Thanks.
Code:
#no_data
                 
pwmout C.2,24, 0          ;make sure c.2 DAC voltage is zero
                 ;must have pwmout before main program
w0=30

main:

if pinC.1=1 then         ;c.1 connected as a kill switch.
   pwmduty c.2, 0         ;sets duty cycle to 0%
   w0=30
   goto main
endif
    
if pinC.3=1 then         ;c.3 increase speed switch
    goto increase_speed
endif

if pinC.4=1 then         ;C.4 decrease speed switch
    goto decrease_speed    
endif
goto main

increase_speed:
pwmduty c.2, w0
w0=w0+10 max 70        ;max 3.5V by trial and error
pause 500             ; else it goes straight to 3.5V without pause
goto main

decrease_speed:
if w0<30 then main     ;sets min DAC at 1.5V         
w0=w0-10 min 30                                             
pwmduty c.2, w0
pause 500
goto main
 

PhilHornby

Senior Member
What is the relationship between the variable used in the pwmduty command and the output duty cycle? If I use a quarter of 1023 (ie about 250), should I get a 25% duty cycle?
There is a post in here somewhere, that explains this in detail - but I can't find it :rolleyes:

(You say "Picaxe 08M" in your circuit diagram, rather than "08M2". I'm assuming the PWM stuff is implemented the same way (the 08M is before my time :) ))

If you look on the "PICAXE" tab, of 'Picaxe Editor 6.1.0.0', under "Wizards", you'll find the "PWMOUT Wizard".

If I insert 40KHz as the frequency and ask for a duty cycle of 100%, it generates this: pwmout C.2, 24, 99 (for a 4MHz clock). A duty cycle of 0%, gives, as you might expect: pwmout C.2, 24, 0

So for that PWM frequency, at that clock speed the Duty Cycle variable has to be between 0 and 99.

Using Trial and Error, I ascertained that the control variable can be 0-999, @ 4KHz (assuming 4MHz clock).

As I say, somewhere there is a rational explanation, as to why.
 

AllyCat

Senior Member
Hi,
What is the relationship between the variable used in the pwmduty command and the output duty cycle?

pwmout C.2,24, 0 ;make sure c.2 DAC voltage is zero
The "full" duty cycle is set by the "24" in the PWMOUT command of your example (and the actual value by the next digit, i.e. 0). The full value is contained within a byte (i.e. maximum 255) so the full duty cycle is calculated by adding 1 and multiplying by 4. Thus, for your example the full duty cycle is 24 + 1 = 25 * 4 = 100 . You could increase the Duty Cycle range to 1000 by changing the value to 249.

The basic program will loop in about 10ms, so yes you need to include a PAUSE, or the value will apparently jump in a few tens of ms. Your program appears to have a lot of unnecessary detail, I believe it is basically :
Code:
#Picaxe 08M2
#no_data
pwmout C.2,24, 0    ; Set MAX DUTY to (24+1) * 4 = 100, duty to 0
main:
if pinC.3 = 1 then       ; c.3 increase speed switch
   w0 = w0 min 30 
   w0 = w0 + 10 max 70   ; Increase 10% to max 3.5V 
endif
if pinC.4 = 1 then       ; C.4 decrease speed switch
   w0 = w0 - 10 min 30   ; Decrease 10% to min 1.5V  
endif 
if pinC.1 = 1 then       ; c.1 connected as a kill switch.
   w0 = 0
endif                                          
pwmduty c.2, w0
pause 500
goto main
But I would pull out the "Magic Numbers" so that all the values and functions can be seen and adjusted when/if the program gets more complex. For example:

Code:
#Picaxe 08M2
#no_data
symbol Kill = pinc.1
symbol Increase = pinc.3
symbol Decrease = pinc.4
symbol Maxspeed = 70			; Percent of 5 volts
symbol Minspeed = 30
symbol Stepsize = 2			; Percent 
symbol Steptime = 100      ; Milliseconds

pwmout C.2,24, 0        ; Set MAX DUTY to (24+1)*4 = 100, duty to 0
do
   if Increase = 1 then   	; Increase to max 
      w0 = w0 + Stepsize MAX Maxspeed  MIN Minspeed
   endif
   if Decrease = 1 then 	; Decrease  to min
      w0 = w0 - Stepsize MIN Minspeed     
   endif  
   if Kill = 1 then 
      w0 = 0
   endif                                         
   pwmduty c.2, w0			; Set the Duty Cycles (speed)
   pause Steptime              ; Limit the ramping speed
loop
EDIT: It might be necessary to pay attention to what happens if more than one button is pressed at the same time. So I have moved the "Kill" section to immediately before the PWMDUTY , to ensure that it over-rides any of the other buttons. Alternatively, you might prefer to put the "important" code at the top of the listing, in which case the ELSE structure could be used, for example:
Code:
do
  if Kill = 1 then 
      w0 = 0
   elseif  Increase = 1 then   	; Increase to max 
      w0 = w0 + Stepsize MAX Maxspeed  MIN Minspeed
   elseif    ....
   endif 
         etc.....
Cheers, Alan.
 
Last edited:

bhanlon56

New Member
Thanks to both. I think I get it. Substituting 24 into the formula for pwmperiod quoted in the datasheet gives 2.5x10E-4 or a pwm frequency of 40KHz. But if my maths is correct, you can only change the variable to 249 if I lower the pwm frequency. It doesn't really matter though. I just needed the relationship between the value of the pwmduty variable and the resulting duty cycle. Thanks again.
 

AllyCat

Senior Member
Hi,

You can also increase the PWM frequency (or PWM resolution/steps for the same frequency) by increasing the SETFREQ m4. The M2s can go up to m32, so the frequency or resolution could be increased by a factor of 8, e.g. a maximum duty range of 1000 at a frequency of 32 kHz. However, if you're using an old 08M then the frequency is limited to 8 MHz (according to PE6) or 16 MHz (according to PE5).

IMHO PICaxe users often set their PWM frequency far too high; Sometimes it's worthwhile to work above 20 kHz, to make "high power" magnetically-based equipment inaudible (i.e. "ultrasonic"), but this certainly isn't needed for a simple low-pass filter. In this case I would choose the values (including the Low-Pass Filter Time Constant) purely to give the desired resolution. But in the example above, the duty cycle was being changed in steps of 10 anyway (which I changed to 2 in my version).

Cheers, Alan.
 

PhilHornby

Senior Member
This topic has puzzled me for years, so I thought I'd have a bit of a delve...

Having recently discovered that Hardware PWM is available on the Picaxe 14M2, I tried a bit of 'Compare-and-contrast' with the 'software' PWMout command. From what I can make out, on the 14M2, at least, HPWM (in pwmsingle mode) and PWMOUT behave identically.

This led me to Page 207/208 of the "PIC16(L)F1825/9 Datasheet", which describes operation of the Hardware PWM and gives formulas for calculating PWM Period and Resolution. Tricky little beast... :)

Reading the data sheet, it's clear everything hinges about an 8 bit register, which made me wonder why the 'Wizard' is so fixated on '249' and other strange numbers. It turns out, if you use 255 for the Period, you get the full 10bit PWM Resolution. It's a slightly oddball frequency - which the Wizard miscalculates, if you reverse the operation :rolleyes:

PWMOUT pin,255,x gives a PWM 'Period' of precisely 255uS (3921Hz) according to my 'scope. (As does hpwm pwmsingle,pwmHHHH,%1000,255,x)

A couple of interesting quirks: Specifying 'Duty Cycles' as 0, results in no output at all. Specifying 'Duty Cycles' as 1023 does not quite give the opposite logic '1' - there's still a low going pulse of some 250nS present. (Specifying 1024 makes the output collapse to zero again).

The waveform at the extremes (Duty Cycles close to zero and 1023) is a little untidy (which makes it hard to know where to measure it) :-

24507

Whereas in the middle of the range, it's much 'neater'

24508
 
Last edited:

AllyCat

Senior Member
Hi,

As I understand it, ALL the PIC(axe) "PWM" instructions (apart from one very ancient "Legacy") use the internal (silicon) Hardware, and the "H" in the PICaxe commands refers to the "H Bridge" mode. Thus the HPWM commands control 4 pins in unison (i.e. the same frequency and a "related" Duty Cycle), although some of these pins can be (optionally) "switched off" and/or used for other purposes.

The numerical programming values are quite "logical" (and I believe fully described in the base PIC data sheets) but slightly "complicated" because Microchip (wanted to) fit the primary "Period" (cf frequency) into a single byte. Another complication is that a basic 256 "timeslot" PWM waveform (i.e. 256 steps) can generate 257 levels (i.e. low-pass filtered ADC values).

Obviously PWM with a full 256 (or 1024) timeslots is "desirable", but a value of 256 does not fit into a single byte. :( However, PWM with a zero number (which a byte can hold) of timeslots is useless, so Microchip could have used 0 to represent 256, but they chose instead to subtract 1 from the desired number for timeslots. Thus "0" represents one timeslot (or 4 because each byte unit represents 4 timeslots) and 255 represents 256 * 4 = 1024.

However, for the Duty Cycle, a value of zero is required so the problem arises in how to code 256 or 1024, which is also an issue with the ADC and DAC converters. The general "solution" seems to be NOT to bother, because it can be "fixed" by sending a "High" to the PWM or DAC output pin (or by setting the maximum PWM period to 1020 = 255 * 4 timeslots). ADCs are more difficult; one solution (supported by hippy) is to code the "full scale" value as 1023, but the Microchip data sheet does appear to show an ADC level of 1024 (but not to provide a binary code for it to be reported! ).

Finally yes, I do agree that the PWM Wizard has some "issues"; I did notice that an exact 50% duty cycle configuration can be reported as 51%.

Cheers, Alan.
 

PhilHornby

Senior Member
As I understand it, ALL the PIC(axe) "PWM" instructions (apart from one very ancient "Legacy") use the internal (silicon) Hardware,
But if it that is the case, why does the pwmout 'instruction' allow you target pins that the internal PWM hardware can't? (i.e. B.2/RA2) on the 14M2 - I can't find anything in the datasheet that says any of the PWM outputs can be sent to RA2 (those it's entirely possible I've not read it correctly!).

He also said:
and the "H" in the PICaxe commands refers to the "H Bridge" mode. Thus the HPWM commands control 4 pins in unison (i.e. the same frequency and a "related" Duty Cycle), although some of these pins can be (optionally) "switched off" and/or used for other purposes.
The hpwm pwmsingle,pwmHHHH,%1000,255,x statement I used, just output a PWM signal to B.5/RC2/hpwm D. This is the mode that the manual says doesn't exist on the 14M2.

Flushed with that success, I had a go at enabling it on the 08M2 (since the underlying hardware can do it). No joy - the Picaxe Programming Editor wouldn't let me access the SFR's needed to configure it. (It had already summarily dismissed an hpwm command )
 

AllyCat

Senior Member
Hi,

The "PWM" hardware is basically linked to the "Capture-Compare" Latch hardware (ECCP, section 24), with CCP1 described as "Full Bridge" (pins P1A - P1D), CCP2 as "Half Bridge" (P2A and P2B) and CCP3 and 4 as "Standard PWM". Then several of the output pins can be swapped by using the "Alternate Pin Function" register (APFCON). On the 14M2, pin B.2/RA2 is the CCP3 output, so it appears to be usable as a PWM output.

If by "the manual says..", you mean one of the PICaxe manuals, then yes there are numerous inconsistencies, partly because there are so many different PICaxe chips, so generalisations are dangerous. Sometimes the Program Editor is a better reference, but I did notice just today that PE6 says the 08M runs at up to 8 MHz, but PE5 says up to 16 MHz.

What "it" did you try to enable on the 08M2? The 08M2 has only a CCP1, described as an "Enhanced PWM Half Bridge" (which appears to be on C.0 and C.2), so can't support an H-bridge. Four pins are listed as having some PWM capability, but one of those is C.5 (C.1 is not) as an Alternate Pin Function. I have wondered if that gives the possibility of making the Programming Input pin into an Output (which it is on the base PIC). That might be useful on occasions, but I've not (yet) found any way to do it (even with SFR instructions), because the interpreter appears to either block or cancel the instructions. But the APF register does appear to give quite a high level of control (for example switching the HSERIN pin away from C.1/SCL onto C.5). Switching the CCP1/P1A (PWM) output away from C.2/SDA onto C.5 might be quite useful. ;)

Cheers, Alan.
 

PhilHornby

Senior Member
On the 14M2, pin B.2/RA2 is the CCP3 output, so it appears to be usable as a PWM output.
Ah ok - I thought the Capture/Compare stuff was all some kind of input. (I'm completely ignorant as to what Capture/Compare can do for me ;) )

and he said:
What "it" did you try to enable on the 08M2?
I came across a program somewhere, that used a PIC12F1840 to read a pot. and PWM an output signal. (So about 4 lines of Picaxe Basic, on an 08M2 :) ).

It looked so simple, I thought I'd have a go at 'translating' it. My interest in the base/hardware PWM functionality, stems from my perception that the 'software' pwmout is not completely stable. (My scope's measure function, jitters by a tiny amount. Obviously, this could well be my scope!)

The code I found - written in 'C was as follows :-

Rich (BB code):
// Set up PWM module of the 12F1840
    TRISAbits.TRISA2 = 0;      // Set RA2 (pin 5) as an output
    APFCONbits.CCP1SEL = 0;    // Send PWM to RA2 (pin 5)
    PR2      = 0b11111111;     // Set PWM period
    T2CON    = 0b00000110;     // PWM ON, Prescale clock by 16
    CCP1CON  = 0b00001100;     // PWM single outout mode, active high
    CCP1ASE  = 0b00000000;     // PWM Shutdown interaction disabled
    PWM1CON  = 0b10000000;     // PWM Autostart, no delay
    PSTR1CON = 0b00010001;     // PWM P1A enabled, P1B disabled
    CCPR1L   = 0b00000000;     // PWM duty cycle (start at 0%)
    
     // Scale ADC result and write to PWM control register
    CCPR1L = (ADCresult*.259);     //Write scaled PWM value to register
Rich (BB code):
This is what I converted it to (which fails on the pokesfr PR2 with "Illegal pokesfr address" :( (The 1st of many!)

Rich (BB code):
#picaxe 08M2

symbol APFCON           = $4D                               ;$11D - Alternate Pin Function
;CCP1SEL = bit 0
symbol PR2              = $0B                               ;$01B - Timer 2 Period Register
symbol T2CON            = $0C                               ;$01C - Timer 2 Control Register
symbol CCPR1L           = $81                               ;$291 - Capture/Compare/PWM Register 1 (LSB)
symbol CCPR1H           = $82                               ;$291 - Capture/Compare/PWM Register 1 (MSB)
symbol CCP1CON          = $83                               ;$293 - CCP1 Control Register
symbol PWM1CON          = $84                               ;$294 - Enhanced PWM Control Register
symbol CCP1AS           = $85                               ;$295 - ? contains CCP1ASE bit (bit 7)
symbol PSTR1CON         = $86                               ;$296 - PWM Steering Control Register


      peeksfr APFCON,b0
      bit0 = 0                                              ;ensure CCP1SEL cleared.
      pokesfr APFCON,b0
      
      pokesfr PR2,%11111111                                 ;Set PWM period
      pokesfr T2CON,%00000110                               ;PWM ON, Prescale clock by 16
      pokesfr CCP1CON,%00001100                             ;PWM single outout mode, active high
      pokesfr CCP1ASE                                       ;CAN'T FIND THIS ?????
      pokesfr PWM1CON,%10000000                             ;PWM Autostart, no delay
      pokesfr PSTR1CON,%00010001                            ;PWM P1A enabled, P1B disabled
      pokesfr CCPR1L,%00000000                              ;PWM duty cycle (start at 0%)
      
      pokesfr CCPR1L,50
      
      do
            pause 1
      loop
 

AllyCat

Senior Member
Hi,
Rich (BB code):
symbol PR2              = $0B                               ;$01B - Timer 2 Period Register
symbol T2CON            = $0C                               ;$01C - Timer 2 Control Register
You are declaring PR2 as $0B (which is "illegal" in PICaxe Basic) but the comment says (correctly) that its address is $1B ! Similarly T2CON should be $1C, but at least $0C is the first "legal" address, so the PE won't complain.

I haven't looked into how much the CCP is "usable" on/via PICaxe Basic, but in principle the silicon hardware involves a Timer, a Latch/Register(s) and an I/O Pulse. It can operate in one of two modes "Capture" or "Compare" (of which PWM is basically an enhancement/subset). In "Capture" mode it basically waits for an "event" on an input (pin or internally) and then copies the Timer value into a Register (which then can be read by the program when required). In "Compare" mode, the user loads a numerical value into a Register (or two) and when the Timer reaches that value, it generates a pulse. Typically, in PWM mode, the output pulse will be started at the counter "rollover" and terminated by the Compare pulse, to generate the PWM waveform.

Then, the HPWM mode will be more complicated, because the pulse(s) can be Enabled and Inverted on various pins, and "Dead Zones" introduced, to prevent "Shoot Through" currents if some of the FETs in the H-Bridge are "slow" to turn Off.

Cheers, Alan.
 

PhilHornby

Senior Member
You are declaring PR2 as $0B (which is "illegal" in PICaxe Basic) but the comment says (correctly) that its address is $1B ! Similarly T2CON should be $1C, but at least $0C is the first "legal" address, so the PE won't complain.
I typed in all the right numbers...just not necessarily in the right order :rolleyes:

This version does actually work! Whether or not there's any benefit over the pwmout approach, I'm not sure :)
The Setfreq M16 and hserout are just leftovers from another program ...

Rich (BB code):
#picaxe 08M2
#terminal 9600

symbol APFCON           = $5D                               ;$11D - Alternate Pin Function
;CCP1SEL = bit 0
symbol PR2              = $1B                               ;$01B - Timer 2 Period Register
symbol T2CON            = $1C                               ;$01C - Timer 2 Control Register
symbol CCPR1L           = $B1                               ;$291 - Capture/Compare/PWM Register 1 (LSB)
symbol CCPR1H           = $B2                               ;$291 - Capture/Compare/PWM Register 1 (MSB)
symbol CCP1CON          = $B3                               ;$293 - CCP1 Control Register
symbol PWM1CON          = $B4                               ;$294 - Enhanced PWM Control Register
symbol CCP1AS           = $B5                               ;$295 - ? contains CCP1ASE bit (bit 7)
symbol PSTR1CON         = $B6                               ;$296 - PWM Steering Control Register


      setfreq M16
      hsersetup B9600_16,%10                                ;sertxd compatible mode
      hserout 0,(cr,lf,"START PROGRAM")

      output C.2                                            ;RA2 as output (essential!)

      peeksfr APFCON,b0
      bit0 = 0                                              ;ensure CCP1SEL cleared.
      pokesfr APFCON,b0
      
      pokesfr PR2,%11111111                                 ;Set PWM period
      pokesfr T2CON,%00000110                               ;PWM ON, Prescale clock by 16
      pokesfr CCP1CON,%00001100                             ;PWM single outout mode, active high
      
      pokesfr CCP1AS,%00000000                              ;clear the CCP1ASE bit and rest of auto-shutdown logic
      
      pokesfr PWM1CON,%10000000                             ;PWM Autostart, no delay
      pokesfr PSTR1CON,%00010001                            ;PWM P1A enabled, P1B disabled
      pokesfr CCPR1L,%00000000                              ;PWM duty cycle (start at 0%)
      
      pokesfr CCPR1L,50
      
      hserout 0,(cr,lf,"WELL?")
      do
            pause 1
      loop
It gives this (on C.2) :-

24512
 

PhilHornby

Senior Member
As I understand it, ALL the PIC(axe) "PWM" instructions (apart from one very ancient "Legacy") use the internal (silicon) Hardware,
Having seen the interactions between the pwmout statement and manipulations performed using pokesfr, I now concur!
Maybe there just wasn't enough room left in the firmware to allow all the possible permutations?

One such permutation, which may be of value, is the ability to use pwmout on Pin C.4 of the 08M2. Doing so, allows the concurrent use of PWM and I²C (tested), which can't be done otherwise because of the 'shared' pins. You can also simultaneously output PWM to C.2 (or toggle between them) - which could allow for rudimentary left/right steering of a model?

Rich (BB code):
#picaxe 08M2
;
; SFRs
; 
;To convert to Picaxe equivalent, we do the following (on M2):-
;
;  1    1    D          Example RAW PIC value
;0001 0001 1101
;xx|| |xx| ||||         xx = 'ditched' bits
;  \\ \  | ||||
;   \\ \ | ||||
;    \\ || ||||
;     0101 1101
;       5    D          Translated Picaxe value
;
symbol APFCON           = $5D                               ;$11D - Alternate Pin Function
#define CCP1SEL bit0                                        
#define P1BSEL  bit1                                        
symbol PSTR1CON         = $B6                               ;$296 - PWM Steering Control Register
      ;
      ; PWM o/p steering. P1B defaults to RA0/C.0 
      ; We can redirect it to RA4/C.4 successfully though.
      ;
      peeksfr APFCON,b0
      P1BSEL = 1                                            ;P1B on RA4/C.4 instead of default RA0/C.0
                                                            ;See description of Register 12-1, Page 99
                                                            ;of PIC12(L)F1840 datasheet
      pokesfr APFCON,b0
      
      pokesfr PSTR1CON,%00000010                            ;Steer PWM to P1B (only). Steer ASAP (STR1SYNC=0)
                                                            ;See description of Register 24-4, Page 181
                                                            ;of PIC12(L)F1840 datasheet
                                                            
      pwmout C.2, 255, 511                                  ;Now starts on C.4, despite the REQUIRED reference to C.2!
                                                            ;pwmout clearly just expects PSTR1CON to be in default state
      output c.4                                            ;Allow the P1B PWM signal out on RA4/C.4

      stop
Trying to make use of the default P1A on C.5 (Serin) failed, mainly because the Picaxe firmware immediately overwrites any changes made to TRISA (the Port Direction Register) and output C.5 is verboten. I'm not entirely sure why, but trying to make use of PWM P1B but letting it default to C.0 doesn't work either.

On the 14M2, the hpwm statement allows the use of Pin C.1, which is otherwise inaccessible. Again, there is a possibility of sending the same PWM signal to multiple pins (and dynamically changing them).

Rich (BB code):
#picaxe 14m2
      
      hpwm pwmsingle,pwmHHHH,%0010,255,511                  ;start pwm on "hwpm B" (C.1)
                        ;     DCBA                      
The 18M2 looks like it could benefit from the same approach as the 08M2, since it similarly restricted, I'll leave that for someone else to try - last time I used an 18M2, I melted it into the centre of my breadboard, through applying power to the wrong pins o_O
 
Last edited:

PhilHornby

Senior Member
I'm not entirely sure why, but trying to make use of PWM P1B but letting it default to C.0 doesn't work either.
"Hoist by my own petard" ... as the saying goes :rolleyes:

It turns out my habitual use of hserout instead sertxd was to blame ...

The following (snippet) enables P1B, and lets it appear on its default pin: C.0 aka Serout

Rich (BB code):
      output c.0
      pokesfr PSTR1CON,%00000010                            ;Steer PWM to P1B (only). Steer ASAP (STR1SYNC=0)                                               
      pwmout C.2, 255, 511                                  ;Now starts on C.0, despite the REQUIRED reference to C.2!
 
Top