a little pwm advice please

Greetings,

Having just started on the great PICAXE learning curve and for this purpose have obtained a PICAXE28X1 starter kit (16F886), I seek an explanation for a quandary and how best to approach overcoming this.

Purely for the purpose of learning, I have been trying to achieve an adjustable square wave form to create a PWM servo drive. The simple program written seems to work perfectly and observing the outputs in simulation, the values behave as expected and desired.

In reality however, the output (which is an impressively symmetrical and free of noise), never exceeds approximately a 50% duty due to the ‘off’ duty period being extended by the time required to read the ‘main’ part of the program. The effect is unchanged by altering the total duty period even in the micro second range.

The code presented is a much simplified version using a similar construction which displays exactly the same tendencies. The actual times (as comments on right hand side) are read from a sillyscope reading the output. Regardless of the low pause input demanded, the off period remains about 12mS.

I have explored the onboard pwm elements but can see no way of using these to simply achieve the same requirements.

It seems to me that I have either missed some vital step in the set up or perhaps, the 28X1 chip is not suitable for such uses. Whatever, I have exhausted my limited resources of understanding and would appreciate suggestions from those who know more!

Code:
main:
	low 6
	readadc 0,b1    ;read pot input 0-255
	if b1 <10 then low 7	
	endif
	
	if b1 >10 and b1 <40 then goto out1:
	
	if b1 >40 and b1 <80then goto out2: 
	
	if b1 >80 and b1 <120 then goto out3:

	if b1 >120 and b1 <160 then goto out4:

	if b1 >160 and b1 <200 then goto out5:

	if b1 >200 and b1 <240 then goto out6:

	if b1 >250 then goto out7:

	goto main


out1:
	high 7         ;1H,L9, high2,low 12,total 14
	pause 1        
	low 7
	pause 9
	goto main

out2:
	high 7         ;2H,L8, high3,low 12, total 15
	pause 2        
	low 7
	pause 8
	goto main
	
out3:
	high 7         ;3H,7L, high4,low 12, total 16
	pause 3
	low 7
	pause 7
	goto main
	
out4:
	high 7         ;5H,5L, high5,low 12,total 17
	pause 5        
	low 7
	pause 5
	goto main

out5:
	high 7         ;7H,3L, high7,low 12,total 18
	pause 7        
	low 7
	pause 3
	goto main
out6:	
	
	high 7         ;9H,1L, high9,low 10, total 19
	pause 9        
	low 7
	pause 1
	goto main

out7:
	high 7         ;10H, full wave
	pause 10
	
	goto main
 
Last edited:

nick12ab

Senior Member
Firstly, I don't know where the main label is in your program as you haven't included.

Secondly, see the PWMOUT command. If the PWM needs to be as slow as you're making it, use a PICAXE-08M2 as a slave processor to do the PWM as a low clock speed will be needed for the PWMOUT command to work at that speed even with PWMDIV.
 
ah..but the servo command is surely meant to control a conventional servo as used in model aircraft as opposed to providing a means of a pwm output....
 

nick12ab

Senior Member
ah..but the servo command is surely meant to control a conventional servo as used in model aircraft as opposed to providing a means of a pwm output....
It will provide a PWM output but the maximum duty cycle is incredibly low - much more so than you already have.
 
Code:
main:
	readadc 0,b20                         ;read pot1 
	readadc 1,b21                         ;read pot2
	let w9 = b20+255
	let w11 = w9 - b21                    ;obtain 0-255 control
	let w8 = w11/2                        ; input range              
	
							  ;CLOCKWISE	
	let b1 = w8*2                         ;scale input values 
	let b2 = w8+124                       ;invert values below 125
	let b3 = b2 - b1                      ;on time proportion
	let b4 = 124 - b3                     ;off time proportion
	let b5 = b3/10				  ;on time period
	let b6 = b4/10                        ;off time period 
	
							  ;COUNTER CLOCKWISE 		
	let b8 = w8 -132                       ;on time proportion          
	let b9 = 123 -b8       ;               ;off time proportion 
	let b10 = b8/10                        ;on time period  
	let b11 = b9/10                        ;off time period
	
							   ;DEADBAND
	if w8 <125 then gosub CW:              ;rotate one way     
	if w8 >132 then gosub CCW:             ;rotate other way	
	goto main	
	
CW:	
	high 7
	pause b5
	low 7 
	pause b6
	return 
	

CCW:
      high 4
	pause b10                           
	low 4
	pause b11                           
      return
Here is the full code.

The adc inputs are two 10k pots across the 5v rail, the values from which require a little simple calculation to determine the output duty which is adjustable between 100% clockwise through 0 to 100% counter clockwise.

As written, the frequency is controlled by the mathematics of the mid point input value and hence using ‘pause’ runs at about 8Hz.

If the code is written using ‘pauseus’ the frequency increases exactly as would be expected and the program is equally adequate but the same problem exists; the ‘low’ period is extended by a proportionally similar pause while the program reads the ‘main’ section of the code.

main:
high 7
pause 9
low 7
pause 1
goto main

Running just this snippet of code with either ‘pause’ or ‘pauseus’ outputs a perfectly usable 90% duty so it is clear that there is no problem in generating the desired wave form if no other functions are being used simultaneously.

At this stage, my interest is mainly to understand if it is possible to overcome this problematic delay and how to do it. I imagine there might be a possibility of separating the scaling function from the output function. Or as earlier suggested, perhaps I should be using a different chip or maybe I need to use more than one.

Thanks
 

hippy

Ex-Staff (retired)
The problem you have is two-fold; that when determining the programmatic delay you are not taking into account the overhead of calculating the delays and using PAUSE you only have coarse control of timing.

In your tight loop example which works; if you add processing after 'main:' with overhead of 2ms that is added to the PAUSE 1 which immediately precedes it when running, so you end up with a duty 9:3 not 9:1. And if that is the case you cannot subtract 2 from 1 and have a negative PAUSE so the best you can do is remove it and end up with 9:2.

The solution is to use SETFREQ and scale up the PAUSE values. If going from 4MHz to 32MHz you can scale-up the PAUSE 9 and 1 to 72 and 8, which is still a 9:1 ratio and at same frequency. The overhead reduces from 2 to 0.125 so you can likely ignore that or pad it out and reduce the 8 to 7.

You can also use PAUSEUS which has a higher resolution base unit so can perform more accurate subtractions. For example ( for illustration only ) 9000 and 1000 with that 0.125ms overhead would become 9000 and 875.
 
Last edited:

boriz

Senior Member
"...I have been trying to achieve an adjustable square wave form to create a PWM servo drive..."

"...ah..but the servo command is surely meant to control a conventional servo ..."

Are you trying to drive a servo or a motor?
 

Goeytex

Senior Member
The PWMOUT and PWMDUTY commands allow a 0 - 100 percent duty and save you from the futility
of attempting to do a 0 - 100 percent duty PWM using high, low,and pause commands.

If your only purpose is to understand if it is possible to do it as you are attempting, then I would
suggest that you are wasting your time except for gaining the knowledge of how not to do it with
a Picaxe.
 
Last edited:

hippy

Ex-Staff (retired)
The PWMOUT and PWMDUTY commands allow a 0 - 100 percent duty and save you from the futility of attempting to do a 0 - 100 percent duty PWM using high, low,and pause commands.
It's not entirely futile and if one needs low frequency PWM then bit-banged can be a good way to achieve it.

At normal PICAXE operating speeds even using PWMDIV options on PWMOUT gives a frequency that is relatively high and adding hardware dividers doesn't work as they will reduce the frequency but impose a 50:50 duty unless the divider is something more and 'smart'.

The issue here seems to be that the processing overhead hasn't been taken into account and that's relatively straight forward to fix in principle.

There are other issues which appear insurmountable but can be worked round; an x:y duty would seem to be limited to x:overhead and overhead:y but one can dynamically adjust between x-overhead:y and x:y-overhead to improve the cases when approaching 0% and 100%.
 

Goeytex

Senior Member
No, not entirely futile, I have done it using settimer and an interrupt, with some tricky
"maths" on a 20X2. But I can't seem to locate thumb drive I save it on. I'll post it when I do.

But I would say pretty futile if you want the Picaxe to do anything else besides the manual PWM,
as adding other tasks to the program will affect the PWM loop.


My point being why bit bang it when pwmout is available ? John has no real application
or need to do it this way and says he is only doing this to learn.

It may be a good way to learn certain limitations of the Picaxe due to it's relatively high
processor overhead. Maybe better to learn this stuff sooner than later.

A simple program like

main:
high 1
low 1
goto main

and a scope may be enlightening to a Picaxe newbie when the signal is not what might have been
initially expected
 
Last edited:
Gentlemens (which gender specific assumption I hope is not the cause of offence or distress),

I thank you for your suggestions which lead me to deduce after further experimentation of frequency changes and alternative pause(us) values that the creation of such a PWM output with this chip is not a sensible proposition though I admit that I have yet to understand why such a tiny bit of calculation causes such a large delay.

On returning to the internal PWM I managed to get this to work far better than on my first attempts and maybe I will be able to use this in the future.

It would be very interesting anyway to see what Goeytex has previously managed if the thumb drive appears and also to consider any thoughts any of you may have about which PICs may be more suitable for the purposes of creating PWM wave forms. It is certainly something I could make much use of as an alternative to some of the various analogue systems I have used in the past.

As Goeytex notes, I have pursued this primarily to learn and I certainly agree that it is far better to learn such things sooner than later.

Thanks.
 

hippy

Ex-Staff (retired)
I thank you for your suggestions which lead me to deduce after further experimentation of frequency changes and alternative pause(us) values that the creation of such a PWM output with this chip is not a sensible proposition though I admit that I have yet to understand why such a tiny bit of calculation causes such a large delay.
The PICAXE is an interpreter system which uses a tokenised form of the source code so the program code does not itself run at the native execution speed of the underlying microcontroller. It's likely that the calculations are taking longer than you expect.

In most PICAXE programs speed of execution isn't a problem and where it is that can usually be mitigated by running at higher speeds and by careful choice of code. We wouldn't say the PICAXE was inappropriate for bit-banged PWM but does require the right choice of technique. For most PWM use we would recommend PWMOUT and the bottom frequency limit can be lowered by running the PICAXE below its default speed.
 

nick12ab

Senior Member
In most PICAXE programs speed of execution isn't a problem and where it is that can usually be mitigated by running at higher speeds and by careful choice of code. We wouldn't say the PICAXE was inappropriate for bit-banged PWM but does require the right choice of technique. For most PWM use we would recommend PWMOUT and the bottom frequency limit can be lowered by running the PICAXE below its default speed.
Also note that PWMDIV_4, PWMDIV_8 or PWMDIV_64 can be added to the end of a PWMOUT command to reduce the speed.
 

Goeytex

Senior Member
I have yet to understand why such a tiny bit of calculation causes such a large delay.
The Picaxe uses a high level language (Picaxe Basic) where the Basic Interpreter resides in memory and
commands are processed "on the fly". This means that the time for a command to be processed is much
higher than with a microprocessor that, for example has the code, compiled into assembly language.

As a general rule of thumb it takes a Picaxe 250 us to process a command at 8 mhz. Compared that to the same PIC
using compiled C code that may take only a few processor clocks to run that same command.

This is why, for example, a "pauseus 10" pauses for 260us instead of 10us.

But the Picaxe is more than capable of producing a good and stable PWM if you use the PWMOUT and PWMDUTY commands
where the PWMDUTY command is placed inside the loop with the ADC.

Code:
#picaxe 28x1
#no_data
setfreq M8

pwmout pwmdiv16, 1, 249, 0    ' 500hz PWM  -  start PWM with 0% duty

Main:
do
    Readadc10 1, W2
    w2 = w2 max 1000
    pwmduty 1,w2
loop
 
Last edited:
With thanks to the associated ensemble, my ascent up the PIC learning curve has been much assisted by the explanations and upon a revisit to using the internal PWM features of the 28X1, I managed to make things work as I had originally intended although at rather higher frequencies than I would typically have designed into analogue circuits.

This results is (what appears to be!) a perfectly effective 100% - 0 -100% PWM output in response to a pot instruction input moderated by an electrically identical position-reading pot input. This was tested with the outputs indirectly driving a small gear motor. I await the arrival of some suitable MOSFETS to test the same set up on much higher power outputs.

For possible interest to others, a very simple bit of code.
Code:
main:
	readadc 0,b20                       ;read pot1 
	readadc 1,b21                       ;read pot2
	let w9 = b20+255
	let w11 = w9 - b21                  ;obtain 0-255 control
	let w8 = w11/2                      ; input range              
	
	if w8 >125 and w8 <132 then DB      ;DEADBAND
	                                       
	if w8 <125 then goto CW:            ;rotate one way     
	if w8 >132 then goto CCW:           ;rotate other way	
	goto main	
							  	
DB:   low 1
	low 2
	goto main

CW:                                       ;CLOCKWISE
	low 2                               ;pull down 'off'pole
	let b1 = w8*2
	let b2 = w8+125                     ;scale input values  
	let b3 = b2 - b1
	let w12 = b3*8

      pwmout pwmdiv16, 1, 249, w12        ;output on 'on' pole
	goto main
      
CCW:                                      ;ANTICLOCKWISE 
	low 1                               ;pull down 'offo' pole
	let b4 = w8 - 130                   ;scale input vlaues
	let w13 = b4*8
	
	pwmout pwmdiv16, 2, 249, w13        ;output on 'on'pole
	goto main
 
Top