need help about pwm

andrd

New Member
hi. i´m using picaxe since 2009 and this is my first time asking for help here in forum.
i´m doing a pwm generator project using a 20m2, two potenciometer a 4x16 oled a mosfet driver and a a mosfet,
the project consists in a adjustable period and duty generator.
the project already works and adjust the period and duty by using a switch witch select period/duty and
a potenciometer witch adjust the adc value to period and duty.
my problem is that i what to convert raw period value to frequency and the raw duty value to % so i can send to
the lcd and adjust Hz and % in real time instead of period and duty values.

the frequency must be between 1000Hz and 150000Hz
and the duty must be between 0 and 100%
a quick example of what i have...

Code:
readadc pot_period, w0
readadc pot_duty, w1
serout LCD, n2400_16 , (#w0, "period", #w1 "duty")
pwmout X, w0, w1


and this is  what i want...

readadc pot_period, w0
readadc pot_duty, w1

W2=W0 ?????? This is the equation that i need
W3=W1 ??????

serout LCD, n2400_16 , (#w2, "Hz", #w3 "%")
pwmout X, w0, w1
hope you can understand my question, and help me solving this issue.
Thanks in advance
André Bodas[/code]
 
Last edited:

hippy

Technical Support
Staff member
READADC returns a 0 to 255 result, so to get that to a percentage, 0 to 100, one would divide by 255 and multiply by 100. On a PICAXE one has to do the multiply before the divide, because any number up to 254 divided by 255 would give a 0 result. So -

w2 = w0 * 100 / 255

Would make w2 a percentage, 0-100.

Double checking for overflow; maximum w0 is 255, times 100 gives 25500 and that's below the 65535 maximum so no accidental or unwanted overflow or errors in doing that.
Code:
w0 =   0 : Gosub Convert ;   0%
w0 =  64 : Gosub Convert ;  25%
w0 = 128 : Gosub Convert ;  50%
w0 = 192 : Gosub Convert ;  75%
w0 = 255 : Gosub Convert ; 100%
End
 
Convert:
  w2 = w0 * 100 / 255
  SerTxd( #w0, " = ", #w2, "%", CR, LF )
  Return
You can however go to a higher resolution by using READADC10 which gives a 0 to 1023 value. That can be converted to a 0.0% to 100.0% value by multiplying by 1000 and dividing by 1023, which is pretty close to dividing by 1024 which is easy in a PICAXE but does make the code a little more complex -
Code:
  w4 = w0 ** 1000
  w3 = w0 *  1000 / 1024
  w3 = w4 * 64 + w3
  BinToAscii w3, b15,b14,b13,b12,b11
  SerTxd( #w0, " = ", b14,b13,b12,".",b11, "%", CR, LF )
However, we can optimise that because 1000/1024 is the same as 2000/2048, 4000/4096, 8000/8192, 16000/16384, 32000/32768, 64000/65536, and that last one can be implemented using just the ** operator -
Code:
  w3 = w0 ** 64000
  BinToAscii w3, b15,b14,b13,b12,b11
  SerTxd( #w0, " = ", b14,b13,b12,".",b11, "%", CR, LF )
Note though, that because we divided by 1024 and not 1023, there is a slight error, the raw input value of 1023 is shown as 99.9% not 100.0%. That may be acceptable, even useful, because it means it always fits in a five character "XX.X%" field. If we do want to see 100.0% we can tweak things by just adding 1 whenever the value is 50.0% or greater, or by multiplying by 64063 -
Code:
w0 =   0  : Gosub Convert ;   0.0%
w0 =  255 : Gosub Convert ;  25.0%
w0 =  511 : Gosub Convert ;  50.0%
w0 =  766 : Gosub Convert ;  75.0%
w0 = 1023 : Gosub Convert ; 100.0%
End
 
Convert:
  w3 = w0 ** 64063
  BinToAscii w3, b15,b14,b13,b12,b11
  SerTxd( #w0, " = ", b14,b13,b12,".",b11, "%", CR, LF )
  return
 
Last edited:

AllyCat

Senior Member
Hi,
i what to convert raw period value to frequency
Frequency is the inverse of period so you need to divide a (large) constant by the period to get the frequency, i.e. frequency = A_BIG_NUMBER / period. Unfortunately, PICaxe Basic is very limited in its division capabilities, but several of us have written higher resolution division subroutines, or even full floating-point architectures, particularly in the Code Snippets section of the forum.

For example, here is one of my more recent efforts, which links to one of my earlier and simpler attempts in 2012.

Cheers, Alan.
 

AllyCat

Senior Member
Hi again,
the frequency must be between 1000Hz and 150000Hz
Code:
serout LCD, n2400_16 , (#w2, "Hz", #w3 "%")
pwmout X, w0, w1
You can't store a value as high as 150000 in the word variable w2, but that can be solved easily by dividing by 10 and embedding the 0 in the display string, e.g. SEROUT ....(#w2,"0 Hz"....) or more elegantly by showing kHz with two decimal places, i.e. xxx.xx kHz.

Conversely, to generate 1000 Hz needs for example a PWMOUT , PWMDIV16.... 249 , ... (if a 16 MHz clock). Note that the frequency (or period) element in the PWMOUT command (w0) can be only a byte value; Using w0 will work, but might lead to mistakes. But then, the lowest integer values for w0 in that expression can only give frequency values of 83.33 , 125 and 250 kHz, also with very poor resolution for the duty cycle.

However, a solution is to use the two higher bits of a READADC10 (i.e. the high byte) from the pot, to select PWM divisors of 16 , 8 , 4 and 2. This gives a "semi-Logarithmic" scale for the Frequency or Period potentiometer, but I won't spoil the fun by explaining (yet) how to handle the low byte. ;)

Also, stepping through the pwm division factors is equivalent to multiplying or dividing the frequency or period values by powers of 2, which might avoid the need for higher resolution (i.e. > 16 bits) calculations

Cheers, Alan.
 

andrd

New Member
Ok, thanks for your help. Now i understand that what i want is truly complicated, and memory nearly impossible since i want to had extra functions to the project. (i want to build a "nearly all in one automobile component tester" voltmeter, pulse/frequency counter, pulse/frequency generator, variable pwm generator, etc. )
i will try to adopt another method to solve my pwm readout problem... i will try program "the most used" pwm frequency´s, and will try to use lookup comand to make something like this:
Code:
readadc x ,b0 
lookup b0,(16khz, 32khz, 39khz ...125khz, 200khz etc..),b1  
serout X, ("b1")       ´data to be sent to lcd

lookup b0,(249, 124, 100 ... 31, 19 etc..),b2 
pwmout x, b2,b3     ´data to be sent  in pwm comand
then for the duty cycle, i think that i it be easy to manage...
please give me your opinion about this idea.

And once again thanks for your time and effort helping me ;)

André
 

AllyCat

Senior Member
Hi,

Yes, using lookup tables is an alternative solution, but IMHO a full "mathematical" method will probably take less Program memory and maybe less programming effort. Various "tricks" are possible in PICaxe Basic because many of the keywords actually represent numbers (bytes), so LOOKUP b2 , (m4 , m8 , m16 , m32) , b3 is valid syntax. But unfortunately "PWMDIV16" (and "16khz", etc) are NOT numbers, as can be shown by writing something like SERTXD (,#PWMDIV16) into the simulator, which produces a Syntax Error.

For a satisfactory range of pulse frequencies and duty cycles, you probably will need to change the PWMDIVxx values, which requires conditional expressions (IF ... THEN or SELECT ... CASE), whether you use a "lookup" or a "mathematical" method.. It might be possible to change the main clock frequency with a lookup as above, but that would then need to be "undone" for the ASCII serial communications baud rate.

Here is an example of how it might be done (only tested in the simulator). To keep the program simpler, I have used three frequency ranges of 1 - 4 , 4 - 16 and 16 - 160 kHz , the latter showing how the frequency steps become larger if shorter counting periods are used. The "period" ADC input is arranged to give increasing frequency, which IMHO is more intuitive (but could be reversed). Not all the "magic numbers" are fully documented, and some may need adjustment to reduce rounding errors, but I believe the basic program does what you outlined in only just over 100 bytes. :)

Code:
; PWM Frequency and Duty Cycle Generator , AllyCat April 2019
#picaxe 20m2
#no_data
#terminal 4800
do
    readadc10 b.1 , w1                    ; Duty Cycle (max 1023)
    readadc10 b.2 , w2                    ; b4 = Fine period , b5 = Coarse period
    setfreq m16                            ; Minimum frequency 1kHz with PWMDIV16
    if b5 < 2 then
        b6 = b4 * 188 / 256            ; Scale to 4:1 period range (250 to 62)
    else
        b6 = w2 - 512  * 113 / 256    ; Scale to 10:1 period range (250 to 25)
    endif
    b6 = 250 - b6                        ; Min ADC gives Max Period
    w4 = b6 * 256 ** w1                ; PWM ON time count
    w5 = 25000 / b6                    ; Frequency in tens of Hz (with PWMDIV16 @ 16MHz)
    w1 = w1 + 1 ** 6400                ; Requested Duty Cycle in % (+1 rounds up to 100%)
    b6 = b6 - 1                            ; PWM command uses zero for minimum period
    if b5 = 0 then                        ; Low frequency range
        pwmout pwmdiv16 , c.5 , b6 , w4        ; 1 - 4 kHz
    else if b5 = 1 then                ; Medium range
        pwmout pwmdiv4 , c.5 , b6 , w4        ; 4 - 16 kHz
        w5 = w5 * 4                        ; Adjust reported frequency
    else                                    ; High range
        pwmout c.5 , b6 , w4                      ; 16 - 160 kHz
        w5 = w5 * 16                    ; Adjust reported frequency
    endif   
    sertxd (cr , lf)
    serout b.7 , n2400_16 , (#w5 , "0 Hz " , #w1 , "%")
loop
Cheers, Alan.
 
Top