Is there any way for the program to determine what the current SETFREQ setting is?


Senior Member
I'm working on some library code.
I'd like to make it as self configuring as possible so I'd like the code to be able to cater for different SETFREQ settings in the main program.

I'm aware of the predefined constants _08M2, _14M2, etc that enable the code to determine the PICAXE chip.
Does anyone know of a way for the code to determine the current SETFREQ setting?

Reading the PIC configuration registers doesn't seem a complete solution. While you can determine whether an external crystal is being used you can't determine what the crystal frequency is. This would be solved if I could find that the SETFREQ was EM16, EM32, etc.


Technical Support
Staff member
There is no easy way to tell what frequency an external crystal is, and nothing I know of which tracks what SETFREQ has indicated it should be.

If it's a one off setting one could define a SYMBOL to set the frequency ...
#Picaxe 28X2

Symbol MHZ = EM16

SetFreq MHZ

b0 = MHZ
Select Case b0
  Case EM16 : SerTxd( "Using EM16" )
  Else      : SerTxd( "Unknown" )
End Select
Unfortunately one cannot have "Select Case MHZ" because that has to be a variable and not a SYMBOL constant.

If the SETFREQ were changed within a program one could use a shadow variable and a macro ...
#Picaxe 28X2

Symbol mhz = b10

#Macro Freq(N)
  SetFreq N
  mhz = N

Gosub ShowSetFreq
Gosub ShowSetFreq

  Select Case mhz
    Case M8   : SerTxd( "Using M8" )
    Case EM16 : SerTxd( "Using EM16" )
    Else      : SerTxd( "Unknown" )
  End Select
  SerTxd(CR, LF)
To determine what an actual external crystal frequency was requires a fixed time reference.

One way would be counting to see how long it took for something like an external RC to charge up. A looping counter will give a higher number for how long that took for a faster frequency.

One could use an external RTC and count how long a second lasted, possibly use an internal timer driven from a watch crystal.

And of course an external PICAXE or NE555 timer which produced a fixed length pulse. One could possibly use a PULSIN to measure that.

Mains gives a fixed-ish cycle so one could pull that from the low-voltage side of a transformer. Connecting directly to mains would definitely not be recommended, would strongly be advised against.


Senior Member
This would be solved if I could find that the SETFREQ was EM16, EM32, etc.
I'm not an X2 user, but I don't think that's useful anyway. AFAIK the EM parameter is ignored; the "EM" is simply used to select the external oscillator and the program has no control over the frequency, which is always 4 times the nominal crystal value (by using the PLL).

There might be a (non-additional hardware) solution by using the PIC's "Watchdog" timer, which always operates at the same frequency regardless of the SETFREQ value. The actual 2.1 seconds watchdog timeout probably takes far too long to be useful (and generates a Reset) but it's used for the SLEEP and NAP functions, which are divided down from an on-chip "32 kHz" oscillator. I don't have a specific method to propose, but I believe that the "clock selection register" (OSCCON : IRCF<3:0>) has TWO 32 kHz inputs, one is the Watchdog reference and the other is the "master" (4 MHz?) oscillator, divided down (or up) by the Hardware Divider / PLL system. Maybe you can compare two oscillators (via the counter-timers, most of which have selectable inputs), or one oscillator against the Program Instruction Clock? Two possibly useful "modules" are the "Touch" (CPS module) and the "Timer 1 Gate Control" hardware, both accessible via PEEK/POKESFR commands.

Cheers, Alan.


Senior Member
Hippy & AlleyCat,

Thanks for the suggestions.

Just to clarify I am not after a way to determine what the clock frequency is.

I wanted to see if anyone knew of a way for the running code to determine the current SETFREQ setting so that my library could transparently cater for whatever SETFREQ happened to be set to the main program. I have never seen anything like this referred to in the forum so I'm not surprised to find out that the answer is "no".

In the case were an external crystal was being used it would have been the programmer's responsibility to set the SETFREQ value correctly.
I'm assuming it is the programmer's responsibility to set the EM value when using an external crystal so that the firmware knows what the clock frequency is for commands like SERVO?


Senior Member

It depends why your "library" needs to know the SETFREQ and it may be important to separate actions by the Program Editor at "Compile Time" and the PICaxe Interpreter at "Run Time". AFAIK Servos are only "recommended" to be used at (at most) two SETFREQs: M4 or M16 for the M2 family and M8 or {E}M32 for the X2 family. The X2 family seems particularly complex with the validity of M32 / EM32 , etc. parameters varying between the 20X2 and 28/40X2, so I'll restrict my comments mainly to the M2 family. There are also some differences between PE5 and PE6 (and AxePad ?) and the Simulator, so I'll also restrict my observations to the operation of a "Real PICaxe" at Run Time.

The SETFREQ "parameter" is simply a "number", normally a constant, but I was surprised to see that the PE will accept an instruction such as SETFREQ b0 , even where b0 has not been defined or might contain an "incorrect" value. You can discover the "normal" and acceptable values even in the simulator by executing a command such as SERTXD (#m4," ",#m8," ",#m16," ",#m32," ",#em64) . In many "setup" commands the parameter is simply sent directly to an SFR with little or no modification and I believe for SETFREQ the SFR is the OSCCON register with an address of $39 . Therefore, I think that the "SETFREQ" parameter that was (last) used in a Real PICaxe at Run Time could be discovered by a routine such as (untested):
symbol OSCCON = $39
; Might need to mask out some bits here with e.g. a "b1 = b1 AND %11111000"
LOOKDOWN b1 , (M4 , M8 , M16 , M32 , 0) , b2
sertxd("SETFREQ is ")
on b2 gosub s4 , s8 , s16 , s32 , s0
s4:  sertxd("M4") : return
s8:  sertxd("M8") : return
s16:  sertxd("M16") : return
s32:  sertxd("M32") : return
s0:  sertxd("Unknown") : return
It might be necessary to also read and use the CONFIG1 SFR, etc..

Cheers, Alan.