Harmonic series problem

Chris Kelly

Well-known member
Hi guys
Hope all is well, and glad to see Hippy back again these days!
I'm using 5No 08M2's to generate sine waves in a harmonic series. E.g/
Pic 1 - f0 = 100Hz
Pic 2 - f1 = 200Hz
Pic 3 - f3 = 300Hz
Pic 4 - f4 = 400Hz
Pic 5 - f5 = 500Hz

Have tried this in a 'do' loop:
Code:
Do
Readadc C.4, w1
PWMout C.2, 49, 0(0% duty)
Pauseus w1
PWMout C.2 49, 399(100% duty)
Pauseus w1
Loop
This works and gives a sine wave after filtering, and is therefore the fastest. Slower frequencies can be achieved with more PWMout commands and therefore more pauses. (E.g/ 0% duty, 50% duty, 100% duty, 50% duty gives the same result but at half the sine frequency)
And word w1 allows me to tune the speed coarsely.

I've scrabbled together a half decent series, but what I actually need is a SHIFTED series.
So each frequency is shifted by the same amount and not scaled. E.g 120hz, 220hz, 320hz, 420hz, 520hz is the original series shifted by 20hz.

This is totally beyond me 😂. I'd hoped to just code in a different scaling factor for each, such as
Code:
w1 = 1.25 w1
But with varying factors for each pic. But am at a loss as to how use decimals.
Ideally I would control all 5 pics with 1 buffered voltage, so that once the shifted series is set, I could globally scale all 5 pics together and keep them tuned.

Any ideas would be appreciated!!

Cheers
Chris
 
Last edited:

hippy

Technical Support
Staff member
The other trick is to use the ** operator which is a 32-bit multiplication with an implicit divide by 65536

w1 = w1 * 1.25

with 0.25 * 65536 = 16384

That gives -

w1 = w1 ** 16384 + w1

Division is fairly slow so if one can avoid those it's handy, though, in this case, a divide by 4 can be replaced by a shift right twice which should be a lot quicker. Unfortunately shift is not available on an 08M2.
 

inglewoodpete

Senior Member
Division is fairly slow so if one can avoid those it's handy, though, in this case, a divide by 4 can be replaced by a shift right twice which should be a lot quicker.
You really only have to use division if the values are dynamic and actually need to be calculated.

I have used PICAXEs for lighting the facades of public buildings etc. using PWM. While the effect is different, I wanted a logarithmic range for the PWM so that the visual result appears to be linear to the human eye.

To get the required effect quickly and efficiently, I used EEPROM to store a lookup table of (either) 256 byte values or 128 word values. I then used a byte register as a pointer into the table to read the required PWM value into a separate byte or word register.
 

AllyCat

Senior Member
Hi,
w1 = w1 ** 16384 + w1
The trick of "Adding the number you first thought of" is a useful way to quickly scale up a value by between 1.000 and 1.9999, but somewhat faster is to start with the larger number. ;) For example, if the larger number (w2) is to be 25% larger than w1, then w1 = w2 ** 52429 will be a little faster, where 52429 is (almost exactly) the same as 65536 * 4 / 5 (excluding PICaxe's inherent 16-bit calculation overflow).
Code:
Do
Readadc C.4, w1
PWMout C.2, 49, 0(0% duty)
Pauseus w1
PWMout C.2 49, 399(100% duty)
Pauseus w1
Loop
This works and gives a sine wave after filtering, and is therefore the fastest.
However, I'm puzzled what the code in #1 is usefully doing. Firstly, READADC produces only a byte value (0 up to 255), so the delay produced by the PAUSEUS w1 will be between about 700 us (the PICaxe basic interpreter/decode execution delay) and ~3.2 ms. Secondly, PWMout C.2, 49, 0 ; (0% duty) is the same as LOW c.2 (but takes longer to execute) and PWMout C.2 49, 399 ; (100% duty) is the same as HIGH c.2 . So the program appears to be basically the same as:
Code:
Do
  Readadc10 C.4 , w1    ; Values up to 1023 , so extends the control range
  TOGGLE C.2            ; Toggle guarantees 50% duty cycle, but HIGH and LOW could be used in the loop
  Pauseus w1
Loop
This only produces a square wave, so a Low Pass filter is essential (to pass most of the fundamental frequency but attenuate the 3rd, 5th , etc.. harmonic frequencies), to give a "pseudo" sine wave over a very limited range of frequencies. Also, the frequency is determined mainly by the relatively "unknown" execution times of the PICaxe Basic instruction set. Normally, it would be better to generate the square wave directly from the PWM hardware, calculated using the "PWM Wizard" in PE6 or PE5, for example pwmout pwmdiv64, C.2, 155, 312 for 100 Hz, which will run "continuously" without a program loop. That could give an adjustable frequency by measuring/calculating b1 and w1 , for example w1 = b1 + 1 * 2 : pwmout pwmdiv64 , C.2 , b1 , w1 (where 0 <= b1<= 255).

For "Low" frequencies (e.g. perhaps 50 / 60 Hz for a mains inverter) you might synthesise a "Sine Wave" from PWM values (used as a DAC) even with a PICaxe (but "real" assembler would be much faster). To minimise the (PWM) modulation ripple (and PWMOUT glitches), you'd probably use quite a small PWM period, perhaps 40 cycles (i.e. (9+1) * 4 ), which would give a duty cycle range of 0 to 40 (interpreted as +/- 20 levels from the centre line). Thus (untested) code might be:
Code:
setfreq m16           ; For (somewhat) higher frequencies
calibfreq b0          ; For minor frequency trimming (maybe also other "tricks" for a more coarse control)
DATA (20,30,37,40,37,30,20,10,3,0 3,10)   ;  Sin 30 degrees = 0.5 (i.e 10/20) Sin 60 degrees = 0.866 (i.e. 17/20)
FOR b1 = 0 to 11      ; 12 cycles of 30 degrees
  READ b1 , b2
  PWMOUT c.2, 9, b2   ; Note that PWMDUTY is far too slow for most applications like this
NEXT
Cheers, Alan.
 
Last edited:

Chris Kelly

Well-known member
Hi Alan,

Yes you're right - it seems like the adc value is almost redundant, and the frequencies end up just as a by-product of the code execution time.

Thanks for the code suggestion. I think 50hz to 60Hz might end up too slow for the project though.

Cheers

Chris
 

AllyCat

Senior Member
Hi,

The hardware PWM method can work up to very high (audio) frequencies and usually the problem it that it won't work below about 60 Hz, even with PWMDIV64 and SETFREQ M4. The "Toggle" loop should work up to about 1 kHz (with an increased SETFREQ) or you could sandwich the READADC and/or a PAUSEUS between a HIGH and a LOW inside the loop (the LOOP , READADC{10} and PAUSEUS 0 commands each have an execution time of just under 1 ms @ SETFREQ M4). LOW, HIGH and TOGGLE commands each take around 0.4 ms

PICaxe will nearly always struggle to generate a "multi-level" output signal at "useful" frequencies, but you might produce a "Quasi-Sinewave" as interleaved Negative and Positive pulses from two output pins, with a pair of mixing resistors. Or by Tri-Stating a single pin (INPUT or REVERSE commands) biassed at half-supply with resistors and pulled alternately up and down by HIGH and LOW commands. Untested:
Code:
HIGH c.1                ; Output pulses will be Negative-going
READADC c.3 ,w1
DO
   PULSOUT  c.2 , w1    ; Positive-going
   PAUSEUS w1           ; Optional
   PULSOUT c.1 , w1     ; Negative-going
   PAUSEUS w1           ; More Optional (LOOP command will introduce a delay)
LOOP
; or:
DO
   HIGH c.2
   PAUSEUS w1      ; Optional
   REVERSE c.2     ; Switch to Input/Tri-state
   PAUSEUS w1      ; Optional
   LOW c.2
   PAUSEUS w1      ; Optional
   REVERSE c.2
   PAUSEUS w1      ; Optional
LOOP
In each case the normal Low-Pass filter input resistor could be split into two or three similar resistors.

Cheers, Alan.
 
Top