Simple generator for a range of predefined frequencies using the M2's PWM module

kranenborg

Senior Member
Dear forum members,

Often one needs a pre-defined range of frequencies to be generated, the values steered by a potentiometer or similar sensor.

The following simple code for a 08M2 provides for a range of frequencies (with fixed 50% duty cycle), using the M2's PWM module. In the attached program, a range of 10kHz - 60kHz is generated on the PWM-output of the 08M2 (C.2), based on the 8-bit ADC input at C.4.
Significantly lower frequencies can be generated by adding the PWMDIVxx keyword as the first parameter to the PWMOUT command in the program (or changing the M2's clock frequency). For example, using PWMDIV64 the range becomes 156Hz - 938Hz.
Higher frequencies are possible simply by increasing the clock frequency using SETFREQ m16 (20kHz - 120kHz) or even m32.

Since some expressions need to be evaluated given the M2's 16-bit arithmetic and ditto register size constraints, and the period parameter of the PWMOUT command has a byte size, the code is optimized to limit loss of information during mathematical operations, primarily multiplications and divisions. For example, the peculiar definitions of the min and max frequency range in the code facilitate maximal value range usage during expression evaluation in the registers (for example, generally one wants to do multiplications first up to values close to the 16-bit max value (65535) in a register before applying a division operator). The code is properly documented.

I hope this provides for a quick-and-dirty way to generate a large range of frequencies on M2-processors without any external hardware.
Regards, Jurjen
https://www.kranenborg.org/electronics

Edit: Attached file replaced due to small correction: period must be a byte variable
 

Attachments

Last edited:
Exploring All PWM Frequencies on the PICAXE 08M2 (50% Duty Cycle)

I wanted a quick way to see the full range of PWM frequencies that the PICAXE 08M2 can generate, so I put together a spreadsheet that calculates every possible PWM output frequency for PERIOD values from 255 down to 1. This helped me understand what the PWM Wizard is doing internally — especially since I noticed that the Wizard sometimes returns the same PERIOD value for different requested frequencies. That makes sense once you remember that the PERIOD register is only 8 bits.

Formulas Used:
Code:
Duty Cycle (approx. 50%) = (PERIOD + 1) * 2 – 1
Clock (MHz) = 4, 8, 16, 32
PWMDIV[x] = 1, 4, 16, 64
PWM Frequency = ROUNDUP((Clock * 250000 / PWMDIV[x]) / (PERIOD + 1))

CALIBFREQ is left at the default value of 0.

What the Spreadsheet Shows:

For each PERIOD value, the sheet calculates the resulting PWM frequency for all available resonator clock speeds and all PWMDIV settings.

Key Insights:
  • Frequency resolution is best at low frequencies.
    • Large PERIOD values produce small frequency steps.
  • Resolution becomes coarse at high frequencies.
    • Small PERIOD values mean each decrement produces a large jump in frequency.
  • Duty cycle accuracy decreases at high frequencies.
    • With small PERIOD values, the integer rounding makes a perfect 50% duty cycle impossible.
PWMDIV settings (1, 4, 16, 64) can be changed and the sheet recalculates everything automatically.

About the Spreadsheet:

1_PICAXE_08M2_PWM_Frequency_and_DUTY_Cycle_50%.JPG
  • APP: OpenOffice, EXCEL will be able to open the doc.
  • Filename: PICAXE_Period_CALC.ods
  • Format: ODS
  • No macros — just straightforward formulas.
  • Calculates all PERIOD values from 255 → 1 for all clock speeds and dividers.
Includes conditional formatting to make patterns easier to spot:

When a calculated frequency is a perfect integer, the cell background turns green.
When a calculated frequency is identical to the previous PERIOD’s result (showing a resolution plateau), the cell background turns orange.

If anyone wants to inspect or improve the sheet, I’m happy to share it.
 

Attachments

More interesting facts:

Additional sheet with key observations of PWM frequency error, where I compare the requested frequency to the actual frequency of the PWM output.

Adjusting the variables across different frequencies and step sizes to observe how the error improves or worsens.

As before, when a calculated frequency is a perfect integer, the cell background turns green.

2_PICAXE_08M2_PWM_Calculator.JPG
 

Attachments

CALIBFREQ Resonator Frequency Shift Analysis - SHEET 1 - rhb.20251226

I've reworked the original PWM generator code from POST#1. Instead of using a potentiometer to adjust the frequency (PERIOD), I now use it to adjust the CALIBFREQ offset parameter, which ranges from -31 to +31.

A pushbutton is used to select a discrete base frequency.

Code:
 1      8000 Hz
 2     10000 Hz
 3     12500 Hz
 4     16000 Hz
 5     20000 Hz
 6     25000 Hz
 7     31250 Hz
 8     40000 Hz
 9     50000 Hz
10     62500 Hz
11     80000 Hz
12    100000 Hz

Code:
#rem

    ---[ CALIBFREQ Resonator Frequency Shift Analysis ]---

      File: FreqGenerator_ADC_CALIBFREQ_step.bas
    Source: @kranenborg (20190406), modified by @radiosparks (20251221)
       Web: https://picaxeforum.co.uk/threads/simple-generator-for-a-range-of-predefined-frequencies-using-the-m2s-pwm-module.31526/post-326747

    Generates fixed PWM frequencies (50% duty cycle) selectable by pushbutton.
    Resonator frequency can be shifted with CALIBFREQ via potentiometer input.

    - 12 available frequencies: 8 kHz to 100 kHz
    - CALIBFREQ shift range: ?31?+31
    - Red LED = range limits
    - Yellow LED = centered (CALIBFREQ = 0)
    - Note: CALIBFREQ shift breaks serial timing

    Memory used: 274 bytes

#endrem

' ---[ Compiler Directives ]---

#picaxe 08M2
#no_data

SETFREQ m8      ; Using only one resonator frequency for this experiment.

' ---[ MCU PORT Connections ]---

    symbol LED2_Yellow  = C.0   ; Indicator: POT centered (CALIBFREQ = 0)
    symbol LED3_Red     = C.1   ; Indicator: POT at upper (CALIBFREQ = 31) or lower (CALIBFREQ = ?31) limit
    symbol outputPWM    = C.2   ; Pulse generator output
    symbol inputBTN1    = C.3   ; Button to select next fixed frequency (50% duty cycle)
    symbol inputPOT     = C.4   ; POT input to vary the CALIBFREQ frequency shift

' ---[ Variable Registers ]---

    '---[BIT]--- not used

    symbol spareb0      = b0
    symbol spareb1        = b1
    symbol spareb2      = b2
    symbol spareb3      = b3

    '---[BYTE]---

    symbol PotADCvalue  = b4    ; values returned from ADC 0-255
    symbol period8      = b5    ; byte 0-255
    symbol FREQshift    = b6    ; mapped result from ADC -31 to +31
    symbol stepFREQ     = b7
    symbol btnCounter   = b8
    symbol prevVal      = b9

    '---[WORD]---

    symbol dutyCycle    = w5    ; b11:b10 values from 0 to 1023

' ---[ Program Start ]---

init:

    btnCounter = 0
      stepFREQ = 0 ; start PWM at 8000 Hz
       prevVal = 0
     
    low LED2_Yellow
    low LED3_Red

setPWMfreq:

    #rem
    ---[ Discrete Frequency Selector - 50% Duty ]---
   
    Note: These frequencies were selected to make it easier to verify resonator accuracy
          using a frequency counter. The counter displays them as distinct values,
          helping confirm if the resonator is on the correct frequency.
    #endrem
   
    high LED3_Red    ; LED on
   
    select case stepFREQ
    case 0  : period8 = 249 : dutyCycle = 499  ;   8000 Hz
    case 1  : period8 = 199 : dutyCycle = 399  ;  10000 Hz
    case 2  : period8 = 159 : dutyCycle = 319  ;  12500 Hz
    case 3  : period8 = 124 : dutyCycle = 249  ;  16000 Hz
    case 4  : period8 = 99  : dutyCycle = 199  ;  20000 Hz
    case 5  : period8 = 79  : dutyCycle = 159  ;  25000 Hz
    case 6  : period8 = 63  : dutyCycle = 127  ;  31250 Hz
    case 7  : period8 = 49  : dutyCycle = 99   ;  40000 Hz
    case 8  : period8 = 39  : dutyCycle = 79   ;  50000 Hz
    case 9  : period8 = 31  : dutyCycle = 63   ;  62500 Hz
    case 10 : period8 = 24  : dutyCycle = 49   ;  80000 Hz
    case 11 : period8 = 19  : dutyCycle = 39   ; 100000 Hz
    end select
   
    PWMOUT outputPWM, period8, dutyCycle

    inc stepFREQ                ; step through selected PERIODS
    stepFREQ = stepFREQ // 12   ; roll-over stepFREQ back to 8000 Hz - range 0 to 11

    PAUSE 250
    low LED3_Red    ; LED off

main:

    ; input high when active low goto to "setPWM" label
    button inputBTN1,1,200,100,btnCounter,1,setPWMfreq
       
    READADC inputPOT, PotADCvalue
   
    #rem
        Instead of comparing PotADCvalue directly, it's scaled down first.
        Mapping PotADCvalue into -31 to +31 creates coarse steps.
        This acts like a small hysteresis (deadband), preventing jitter
        and giving smoother CALIBFREQ changes.
    #endrem
   
    FREQshift = PotADCvalue * 63 / 256 - 31
   
    ' Shift resonator frequency only when POT value changes to avoid jitter.
    if prevVal <> FREQshift then
       
        CALIBFREQ FREQshift

        ' Note: The signed 8-bit integer representation of 225 is -31 - JFYI

        if FREQshift = 225 or FREQshift = 31 then
            high LED3_Red       ; Turn ON LED3 at the upper and lower limits of POT
        else if FREQshift = 0 then
            high LED2_Yellow    ; Turn ON LED2 when POT centered (CALIBFREQ = 0)
        else
            low LED3_Red
            low LED2_Yellow
        end if

        pause 1000
        prevVal = FREQshift
       
    end if
   
goto main

'---[eof]---

CALIBFREQ_TEST.jpg
Time to drag out the vintage test gear! Fair warning: none of it has been calibrated in years, so the numbers are relative, not absolute — but good enough for seeing trends. ;)

I measured the output frequency by adjusting CALIBFREQ to its minimum(-31), maximum(+31), and midpoint(0) values. In the BASIC code a red LED lights up for the upper and lower limits of POT. The yellow LED lights up when the POT is centered (CALIBFREQ = 0). Using this data, I created a spreadsheet to analyze how much CALIBFREQ can shift the resonator frequency.​

Some interesting facts appeared:
  • Finer resolution at lower frequencies:
    • The frequency change per step is smaller when the period is longer, resulting in finer control.
  • Reciprocal relationship:
    • As expected, frequency and period are inversely related — higher frequencies correspond to shorter periods. The frequency steps are non-linear.
  • Duty cycle accuracy:
    • At shorter periods (i.e., higher frequencies), the duty cycle becomes less accurate due to timing resolution limits.

3_PICAXE_08M2_CALIBFREQ_TEST_3.JPG
===

CALIBFREQ TEST 3 - SHEET 2

Then I measured all 63 CALIBFREQ step frequencies at a base frequency of 8000 Hz and plotted them. Same story: the lower end has tiny changes per step, and the higher end has bigger jumps. The whole sweep isn’t symmetrical — the negative side and positive side don’t mirror each other.

Results Confirm:
  • At lower base frequencies, each step produces a smaller change.
  • At higher base frequencies, each step produces a larger change.
  • The frequency shift is not symmetrical across the -31 to +31 range.
Some instability (jitter) noticed in the PWM clock frequency, especially as you get close to the resonator frequency. Very small in the range of a few nano-seconds. Well within spec from the data sheet +-0.25%. Rise and fall time of the square wave is in the typical range of 70nS to 80nS.

In the graph, any small deviations from perfect straightness are just jitter (random noise?) in my measurement or timer hardware. I used the absolute value for the SHIFT (red) because it shows the asymmetry much more clearly.

4_PICAXE_08M2_CALIBFREQ_TEST_3_8kHz.JPG

As always, I share my results.

---[ It's Greek to me 8) -> "διδάσκω" = "didaskó" ]---
To teach, instruct, or impart knowledge. It implies causing someone to learn.
 

Attachments

Last edited:
Back
Top