Low frequency variable oscillator

tomleijen

New Member
I am trying to create a signal generator to produce a square wave at frequencies between 0Hz and 200Hz, I want to be able to vary the frequency by sending serial commands to the picaxe.

Currently I am using a pixaxe 08M to read the serial commands and produce an analogue output (PWM smoothed by an RC) which I am feeding into an AD654 to convert the analogue voltage to a frequency (I have set up the AD654 to produce frequencies in the range of 0Hz to 200Hz)

I however would really like to eliminate the AD654 and produce the output frequency directly using only the one picaxe

Here is the code I am currently using:
Code:
b1 = MAX_speed
pwmout 2,64,b1
serin 4,T1200_4,("DATA"),b2,b3 'Wait for serial command 
if b3="%" 
then goto test1

test1:
symbol T1_speed = 100   'Test1 min speed 
symbol T1_rate = 5 'Test1 rate of change
do
        dec b1
        pwmout 2,64,b1
        pause T1_rate
 loop while b1>T1_speed
 do
        inc b1
        pwmout 2,64,b1
        pause T1_rate
 loop while b1<MAX_speed
 goto main
I am thinking I might get a 28X2 or similar and use the internal timer, overflow flags and interrupts to produce my signal, but I am not too sure how to go about that and I still think that might be overkill for something which seems so simple.

Any ideas or hints on how to go about this would be gladly appreciated :)
 

womai

Senior Member
Have a look at this thread:

http://www.picaxeforum.co.uk/showthread.php?t=8541

You will need an -X1 or -X2 series Picaxe that has timer interrupts. The interrupt routine takes care of the frequency generation. All the main routine has to do is set the appropriate timer preload value based on whatever it receives through your serial connection.

What is the required frequency accuracy? The is a slight overhead in the interrupt routine, so you need to tweak the preload value slightly if you should for maximum accuracy.

Wolfgang
 

tomleijen

New Member
I have been looking at generating a low frequency in the background using the method suggested by Womai and ended up with the following code:

Code:
' uses internal timer to generate a low frequency on the 28x2
setfreq m8
b0=0
settimer 65380 ' set time preload value for approx 100Hz
gosub timer_setup

eternal_loop:
    pause 4000 ' sleep for 1 sec, or do something else
goto eternal_loop

interrupt:
	toggle B.7 'toggle output
	gosub timer_setup
return

timer_setup:
    timer = 65535 ' generate interrupt at next overflow
    toflag = 0 ' clear timer overflow flag
    setintflags %10000000,%10000000 ' interrupt on timer overflow
return
this will produce a 100Hz square wave.

There are two things I would like help with:
1: How do I produce a higher frequency (i.e. 200Hz), 100Hz is about the highest stable frequency I could obtain by increasing the timer preload value:
Code:
settimer 65380 ' set time preload value for approx 100Hz
I think this is because at higher frequencies the code will be interrupting constantly, therefore producing no output signal.
I could solve this by doubling the clock frequency from m8 to em16, but I don't really want to do this - there must be a way to do it at 8MHz

2: How do I vary this signal?
I tried putting the settimer code in the main routine to change the frequency by changing the preload value but this didn't seem to work either:
Code:
' uses internal timer to generate a low frequency on the 28x2
setfreq m8
b0=0
settimer 65380 ' set time preload value for approx 100Hz
gosub timer_setup

eternal_loop:
    pause 4000 ' sleep for 1 sec, or do something else
    settimer 65000 ' set time preload value for approx 30Hz
    pause 4000 ' sleep for 1 sec, or do something else
    settimer 65380 ' set time preload value for approx 100Hz
goto eternal_loop

interrupt:
       toggle B.7 'toggle output
	gosub timer_setup
return

timer_setup:
    timer = 65535 ' generate interrupt at next overflow
    toflag = 0 ' clear timer overflow flag
    setintflags %10000000,%10000000 ' interrupt on timer overflow
return
Is this the correct code to use?
 

MartinM57

Moderator
As you are just producing an output on a regular basis, rather than multi-tasking per se, how about this for an (largely unthought through) off-the-wall idea...

...on an X2 part (not 20X2) set up a PWM output and feed it into input 0, and use the SETTIMER COUNT preload command to generate timer overflow interrupts after a certain count of PWM pulses i.e. at the appropriate frequency. Then in the interrupt routine just toggle the output pin.

You can change the frequency by changing the PWM frequency, or the preload value, or both.

Just an idea...there wouldn't be much code at all...
 
Last edited:

womai

Senior Member
Well, doubling the clock (to 16 MHz) certainly looks like the most straightforward method to me. You can even go to 32 or 40 MHz which will give you even more headroom to do other things. Unless your design needs to be very low power, there aren't too many reasons why you would want to limit yourself in this area.

With a Picaxe 28X1 running at 16 MHz I am able to reliably generate 1000 Hz, but I am using pulsout instead of toggle, with very short pulse width, so the duty cycle is nowhere close to 50%. That would be 500 Hz in your case with toggle. But if your main program isn't very processor-hungry the maybe that's a solution - set up the interrupt to be called 200 times per second, then in the interrupt routine replace the toggle with a pulsout that gives you 0.25msec pulse width - voila, you got your 200 Hz square wave output.

You can get my code here (from my Picaxe scope project):

http://www.pdamusician.com/lcscope/design_firmware.html

(use the 28X1 download link (old firmware), not the one for the 28X2).

The 28X2 had indeed speed issues there (though I was able to reach around 800 Hz, or 400 for you), so for my latest scope revision I turned the strategy around - generate the square wave in a tight loop as the main program and jump out of that whenever I receive serial data (background hserin into scratchpad). You can find that code if you download the 28X2 firmware from the same page above (it's right at the beginning of the source code).

Yet another option - use an underclocked 08M as a slave to generate the 200 Hz with PWM (underclocking is necessary to get such low frequencies with PWM, otherwise 4 kHz is the lower limit). The 28X2 then communicates over a (slow) serial connection to tell the 08M what to set the PWN frequency to. The 28X2 is then free to do whatever it wants.

Wolfgang

Wolfgang
 

MartinM57

Moderator
...on an X2 part set up a PWM output and feed it into input 0, and use the SETTIMER COUNT preload command to generate timer overflow interrupts after a certain count of PWM pulses i.e. at the appropriate frequency. Then in the interrupt routine just toggle the output pin.

You can change the frequency by changing the PWM frequency, or the preload value, or both.

Just an idea...there wouldn't be much code at all...
..and it works rather well!

Code is pretty should be self-explanatory .. 1 Hz to 213 Hz depending on the value you set to counter

Don't set counter above about 65530 or else it all goes awry as the interrupts occur too fast and the output is unstable.

The simple theory is simple - to get 1Hz output you need to toggle the output every 2 times per second. With a 20KHz PWM signal that is every 10000 cycles, so set counter to 65535-10000 = 53535. It gets a bit complicated as the output frequency is at the other extreme - a change in counter of 1 makes more than 1Hz difference to the output. The solution is to increase the PWM frequency...an exercise for the student :)

Also by setting counter to different values each time the interrupt occurs (55000, 60000, 55000, 60000, 55000, ... etc) you can get very very low frequency PWM :D

Have fun!

Code:
#picaxe 28x2
#no_table

'1Hz - 200Hz output square wave - 28X2
'output on C.4 = leg 15
'uses...
'...20KHz PWMOUT on C.1 = leg 12....connected to...
'...counter input C.0 = leg 11

'Adjust SYMBOL definition to alter output frequency
'55535 = 1Hz
'60620 = 2Hz
'65510 = 187Hz
'65511 = 199Hz
'65530 = 213Hz
SYMBOL counter = 55535

'initial setup
setfreq m8
dirsc = %00010010
setintflags %10000000,%10000000

'20Khz 50% on C.1 - leg 12
pwmout C.1, 99, 200

'counter on C.0 - leg 11
SETTIMER COUNT counter
timer = 0xffff ' generate interrupt at next overflow

do
   'nothing!
loop


interrupt:
	toggle c.4
	SETTIMER COUNT counter
	timer = 0xffff ' generate interrupt at next overflow
	setintflags %10000000,%10000000
return
 
Last edited:

tomleijen

New Member
Thanks a lot for your help, I just tried out MartinM57's code because I need a 50% duty cycle.

First just to clarify: I am using a 28X2, and think I fried my external oscillator so I want to stick with the internal timer, this will save me components too, as I am using this method to save components (eventually I will move this to a 20x2 but I had started it before the 20x2 came out).

I thought you'd be interested to see how I am getting on,
This is my current code:
Code:
#picaxe 28x2
#no_table

w1 = 64286 'will produce 200hz at pwmout C.1, 1, 2

'initial setup
setfreq m8
adcsetup=1'setup adc
dirsc = %00010010
setintflags %10000000,%10000000

pwmout C.1, 255, 510

'counter on C.0 - leg 11
SETTIMER COUNT w1
timer = 0xffff ' generate interrupt at next overflow

main:
	readadc 0,w2 'this is really crude at the moment
			'because I will eventually automate this
	let w3=w2*2 'this will produce the 50% duty
	pwmout C.1, w2, w3
goto main

interrupt:
	toggle c.4
	SETTIMER COUNT w1
	timer = 0xffff ' generate interrupt at next overflow
	setintflags %10000000,%10000000
return
The PWM frequency is currently controlled by a variable resistor, but will eventually be automated based on signals coming in thu an XBee module.

I chose to vary PWM instead of counter preload because the PWM produced a more linear change in frequency - I could probably calculate it but as far as I can see, it would be a lot of effort for no difference

Here are some oscilloscope recordings too:
Varying preload:

I set the RPM measurement to 100 pulses per rev so the scale for RPM isn't correct and the curve isn't perfect either, but it shows the nonlinear relationship.

Varying PWM:


N.B. I don't have two tools, just two software packages ;)
 

tomleijen

New Member
After doing some more measurements, I realised that the PWM one isn't linear either, but at least it's a bit easier to control, as my previous signal was also based on a varying pwm signal.

I will probably still be getting my calculator out to try and make it linear ;)

Here is another screenshot demonstrating the nonlinearity of the varying pwm:


the code in the main loop to vary the PWM:
Code:
main:
	w2=40
	do
	inc w2
	let w3=w2*2
	pause 100
	pwmout C.1, w2, w3
	loop while w2<255
	
	do
	dec w2
	let w3=w2*2
	pause 100
	pwmout C.1, w2, w3
	loop while w2>1
goto main
 
Last edited:

MartinM57

Moderator
Excellent idea to use varying PWM to get a (more) linear response - well spotted! Need to get the calculator out though

Unfortuantely this won't port to a 20X2 as the SETTIMER part of manual 2 says "External Counter (not available on 20X2)"

..and before anyone else asks, what software tools are you using. They look good..
 

MartinM57

Moderator
Also...
Code:
	readadc 0,w2
	let w3=w2*2 'this will produce 50% duty
	pwmout C.1, w2, w3
..is a neat trick I haven't come across before. I've filed it away for future remembering...
 

tomleijen

New Member
The scope I am using is a TiePie HandyScope HS3-100
The ones with the RPM are from MultiChannel which is generic scope software (works with most pc based scopes) and the other one is with proprietary software from TiePie
 

MartinM57

Moderator
Excellent idea to use varying PWM to get a (more) linear response - well spotted! Need to get the calculator out though
Actually I'm not convinced (and your second trace helps to not convince me)

Moving from the "counting" domain to the "frequency" domain and vice versa is inherently (I think - not clever enough at maths) non-linear.

So I don't think it matters if you adjust the PWM frequency or the preload.

There may be reasons to prefer changing one or the other, but I haven't thought that through...

BTW, the PWM doesn't have to be 50% - SETTIMER only counts the +ve going transitions
 
Top