# Sine wave generation

Discussion in 'Embedded' started by ratemonotonic, Oct 14, 2007.

1. ### ratemonotonicGuest

Hi all ,

I want to generate a sine wave using PWM signal fed to a switches on a
H-Bridge circuit. I have researched and found that the best way is to
sine modulate the PWM signal using a look up table holding sine values
( which can be done by using a timer counter unit to generate a high
frequency triangle wave , and counter to compare at a value taken form
the LUT).

My question is what is the best way generate the data for the LUT and
how do I store the sine values in hex on a 8 bit macro ,to be loaded
in the compare register of the comparator ?

BR

ratemonotonic, Oct 14, 2007

2. ### Deep ResetGuest

Hard way - use sine tables and write them out, transcribe them into constant
definitions.
Easy way - write a program to generate constant definitions.

HTH

Deep.

Deep Reset, Oct 14, 2007

3. ### cbarn24050Guest

You only need the values in the first quadrant so it doesnt take very
long with a calculator to work them out manually.

cbarn24050, Oct 14, 2007
4. ### Jim GranvilleGuest

http://ccalc.shanebweb.com/

paste in any of these three lines

mode deg; x=-90/128;;x=x+90/64; ans=255*sin(x)+0.5

display bin; mode deg; x=-90/128;;x=x+90/64; ans=255*sin(x)+0.5

display hex; mode deg; x=-90/128;;x=x+90/64; ans=255*sin(x)+0.5

Press enter 64 times per quadrant. paste the results into your Source
code, in a CONST Array declaration,
( along with the Eqn line, as a comment for revision purposes.)

Typically you store one quadrant and reverse scan direction, and
reverse sign to build the 4 quadrants.

Comments: CCalc uses ;; as an Eqn block delimiter, and repeated enters
repeat the lack Eqn block on the stack.
Above sets 64 steps per quadrant, and 255 as Full scale.
The +0.5 takes care of rounding

To improve your Sine precision, you should match the highest slopes
to a natural slope in the timebase - that drops the errors on that
portion of the sine, and drops the distortion.

-jg

Jim Granville, Oct 14, 2007
5. ### Paul KeinanenGuest

Or use any spreadsheet program to do this.

You could also at the same time easily analyse how much the rounded 8
bit value differs from the ideal value and possibly select slightly
different points to reduce the average error.

Paul

Paul Keinanen, Oct 15, 2007
6. ### karthikbalaguruGuest

Manual coding of consts.
Generation of consts using your own code.

Karthik Balaguru

karthikbalaguru, Oct 15, 2007
7. ### Tom2000Guest

I use Excel to generate and normalize the sine values, then Word to
format them for inclusion into the program using a series of find and
replace operations.

I'm including a little routine below with a pre-calculated sine table
that might be of use to you.

Tom

;--------------------------------------------------------
;
; VF_Controller.asm
;
; Voltage-Frequency Converter
;
; PIC 12F683 firmware
; to drive my VF Controller Mk II circuit
;
; V1.00: tested OK with scope on breadboard.
; did not test it with the switching
; hardware yet.
;
; tjl Sep 20,2007
;
;--------------------------------------------------------
;
; This program implements a half-bridge voltage-frequency
; speed controller for small single phase shaded pole
; induction motors.
;
; Speed range is from approx 60 Hz to 20 Hz, with the
; voltage reduced by the following formula:
;
; motor V = design V * motor freq / design freq
;
; The wiper of a pot connected between +5v and ground
; is connected to physical pin 3. The pot setting
; determines the motor speed.
;
; Active low -> open outputs on physical pins 7 and 6
; drive the high side and low side half-H switches,
; respectively.
;
; A half-cycle sinewave is synthesized via PWM, and
; appears as an active high PWM signal on physical
; pin 5.
;
; Connect pins 7 and 6 to the cathodes of optoisolator
; LEDs, and the PWM pin paralleled to the anodes through
; a 330 ohm series resistor.
;
; Use the optoisolator transistors to drive a high side
; P-channel MOSFET and a low side N-channel MOSFET.
;
; Connect the motor between the junction of the two
; MOSFETs and neutral.
;
;--------------------------------------------------------
;
; Physical pin 3 (AN3) - Speed pot
; Physical pin 5 (CCP1) - PWM output
; Physical pin 6 (GP1) - Neg Switch (active low)
; Physical pin 7 (GP0) - Pos Switch (active low)
;
;--------------------------------------------------------
;
; 12F683 RAM:
; 96 bytes in bank0, 0x20-0x7f
; 32 bytes in bank1, 0xa0-0xbf
;
; Program memory:
; 2k 14-bit words, 0x000-0x7ff
;
; Data EEPROM:
; 256 bytes, accessed through DE decls or
; the four EExxx registers
;

;--------------------------------------------------------
;
; Config section
;
;--------------------------------------------------------

list p=12F683
#include <p12F683.inc>

__CONFIG _FCMEN_ON & _IESO_OFF & _CP_OFF & _CPD_OFF &
_BOD_OFF & _MCLRE_OFF & _WDT_OFF & _PWRTE_ON & _INTOSCIO

;--------------------------------------------------------
;
;
;--------------------------------------------------------

errorlevel -302

; I/O

#define PosSw 0
#define NegSw 1
#define PWMpin 2
#define Pot 4

; constants

#define Tmrbase 60 ; tune this for 60 Hz at max
speed setting
#define PWM_val 254 ; 7.8 kHz w/ 8 MHz clock and no
prescaling

;--------------------------------------------------------
;
; Macro definitions
;
;--------------------------------------------------------

bank0 macro
bcf STATUS,RP0
endm

bank1 macro
bsf STATUS,RP0
endm

;--------------------------------------------------------
;
; Storage
;
;--------------------------------------------------------

; EEPROM table data

org 0x2100

SineTbl de 0,13,25,37,50,62,74,86
de 98,109,120,131,142,152,162,171
de 180,189,197,205,212,219,225,231
de 236,240,244,247,250,252,254,255
de 255,255,254,252,250,247,244,240
de 236,231,225,219,212,205,197,189
de 180,171,162,152,142,131,120,109
de 98,86,74,62,50,37,25,13

VoltTbl de 255,247,240,233,227,221,215,210
de 205,200,195,191,186,182,178,174
de 171,168,164,161,158,155,152,149
de 147,144,142,139,137,135,133,131
de 129,126,125,123,121,119,118,116
de 114,113,111,110,108,107,106,104
de 103,102,101,99,98,97,96,95
de 94,92,92,90,90,89,88,87

; Un-init RAM

cblock 0x20

w_bup ; save W and STATUS from ISR
stat_bup
semaphore ; semaphore set by ISR when TMR0 times out
rawspeed ; raw ADC speed pot measurement value
speedval ; speed value to load into TMR0
sineptr ; SineTbl pointer
sineval ; from SineTbl
voltval ; from VoltTbl
pwmvalhi ; high byte of sine x volt mult
pwmvallo ; low byte of sine x volt mult
running
temp0
temp1
temp2
temp3

endc

;--------------------------------------------------------
;
; Program code begins here
;
;--------------------------------------------------------

org 0 ; processor reset vector

goto main

org 0x04 ; interrupt vector

goto Int_Svc_Rtn

;--------------------------------------------------------
;
; Interrupt service routine
;
;--------------------------------------------------------

Int_Svc_Rtn

movwf w_bup ; save the W and STATUS regs
movf STATUS,W
movwf stat_bup

btfss INTCON,T0IF ; TMR0 overflow?
goto enableints ; no, just enable ints and
return

bcf INTCON,T0IF
movf speedval,W ; reload the TMR0 preset
movwf TMR0
movlw 1
movwf semaphore ; tell SendCycle that an int
has occurred

enableints

movf stat_bup,W ; restore the W and STATUS regs
movwf STATUS
movf w_bup,W

bsf INTCON,GIE ; enable system interrupts
retfie

;--------------------------------------------------------
;
; Main program routine begins here
;
;--------------------------------------------------------

main

; Disable system interrupts

bcf INTCON,GIE

; set the internal oscillator to 8 MHz

bank1
movlw 0x71
movwf OSCCON
bank0

movlw 0x07
movwf CMCON0 ; 0,1,2 are digital I/O
bank1
movlw b'01011000' ; AN3 analog, others digital
I/O, Fosc/16
movwf ANSEL
movlw b'00010011' ; Preset PosSw and NegSw as
inputs
movwf TRISIO
bank0
bcf GPIO,PWMpin ; set PWM output low

; set up PWM

call StartPWM

; begin

clrf running ; force parm init at first
GetSpeed call
call Dly1Sec ; 1 sec pause to let HV pwr
supply settle

mainloop

call GetSpeed
call SetPosPhase
call SendCycle
call SetNegPhase
call SendCycle

goto mainloop

;--------------------------------------------------------
;
; Subroutines
;
;--------------------------------------------------------

PWMcalc ; 8 x 8 unsigned mult of sineval x
voltval
; result in pwmvalhi and pwmvallo
; modifies sineval,
temp0,temp1,temp3

bank0
clrf pwmvalhi
clrf pwmvallo
movf voltval,W
movwf temp0 ; holds low byte of shifted
voltval
clrf temp1 ; holds high byte of shifted
voltval
movlw 8
movwf temp3 ; counter
mult1 rrf sineval,F
btfss STATUS,C
goto mult2
movf temp0,W
btfss STATUS,C
goto mult3
incf pwmvalhi,F
mult3 movf temp1,W
mult2 bcf STATUS,C
rlf temp0,F
rlf temp1,F
decfsz temp3,F
goto mult1
return

LoadPWM ; pwmvalhi to CCPR1L, pwmvallo<7:6>
to CCP1CON<5:4>
bank0
bcf CCP1CON,5
bcf CCP1CON,4
btfsc pwmvallo,7
bsf CCP1CON,5
btfsc pwmvallo,6
bsf CCP1CON,4
movf pwmvalhi,W
movwf CCPR1L
return

GetSpeed ; read speed pot setting, set delay,
; then set TMR0 and semaphore

btfss PIR1,ADIF ; wait for completion
goto \$-1

movwf temp0

movf running,F ; running?
btfss STATUS,Z
goto gslp2 ; if not, force parm
initialization

movlw 1
movwf running
goto gslp1

gslp2 subwf rawspeed,W ; exit if it speed pot setting
has not changed
btfsc STATUS,Z
return

gslp1 movf temp0,W ; save the new reading
movwf rawspeed

comf temp0,F ; subtract speed reading from
255...
bcf STATUS,C ; ...and divide it by 4
rrf temp0,F
bcf STATUS,C
rrf temp0,F ; temp0 contains 0 (max) to 63
(min)

movlw VoltTbl ; calc addr of volt table
movwf voltval

bcf STATUS,C ; double the speed index
rlf temp0,F
movlw Tmrbase ; add speed factor to timer
base value
movwf speedval

comf speedval,F ; subtract from 256 (255), save
as TMR0

bcf INTCON,GIE ; disable interrupts
movf TMR0,F ; write to TMR0
bcf INTCON,T0IF
bsf INTCON,T0IE
clrf semaphore ; clear interrupt semaphore
bsf INTCON,GIE ; enable interrupts

return

SendCycle ; set pwm from sine table and volts
tbl,
; wait for interrupt, set next step's
vals

clrf sineptr

sclp1 movf sineptr,W ; get sine value
movwf sineval
call PWMcalc ; calc PWM from sine &
pre-calc'd volts val
call LoadPWM ; set new PWM duty cycle

movf semaphore,F ; wait for TMR0 interrupt
btfsc STATUS,Z
goto \$-2
clrf semaphore

incf sineptr,F ; loop through the sine table
movlw 64 ; (SineTbl size)
subwf sineptr,W
btfss STATUS,Z
goto sclp1

return

; exit with data in W
bank1
bsf EECON1,RD
movf EEDAT,W
bank0
return

SetPosPhase ; Open PWM path to Q1

bank0
bsf GPIO,NegSw
bsf GPIO,PosSw
bank1
movlw b'0010010'
movwf TRISIO
bank0
bcf GPIO,PosSw
return

SetNegPhase ; Open PWM path to Q2

bank0
bsf GPIO,PosSw
bsf GPIO,NegSw
bank1
movlw b'0010001'
movwf TRISIO
bank0
bcf GPIO,NegSw
return

StartPWM ; Set up and start PWM

bank1
bsf TRISIO,PWMpin ; Disable the PWM output pin

movlw PWM_val ; Set the PWM period into TMR2
movwf PR2
bank0

movlw b'00001100' ; Set CCP module for PWM mode
movwf CP1CON

movlw 0 ; Inital duty cycle = 0%
movwf CCPR1L

clrf T2CON ; Set TMR2 scalers for 1:1
bsf T2CON,TMR2ON ; Start TMR2

bcf PIR1,TMR2IF ; Clear TMR2's interrupt flag

btfss PIR1,TMR2IF ; Wait for TMR2 overflow
goto \$-1
bcf PIR1,TMR2IF

bank1 ; Start PWM by setting the PWM
pin
bcf TRISIO,PWMpin ; as an output
bank0

return

Dly1Sec ; 1 sec delay at 8 MHz
movlw 7
bank1
movwf OPTION_REG ; set for 1:256 prescaling for
TMR0
bank0
movlw 31
movwf temp0
dlylp clrf TMR0 ; TMR0: 254 ticks of 256 instr
cycles
bcf INTCON,T0IF
btfss INTCON,T0IF
goto \$-1
decf temp0,F
btfss STATUS,Z
goto dlylp
movlw 1
bank1
movwf OPTION_REG ; restore 1:4 prescaling for
TMR0
bank0
return

end

Tom2000, Oct 15, 2007
8. ### Spehro PefhanyGuest

Just use your favorite programming language to write a program to spit
out the values, which you can write into or redirect into a file (to
be pasted or included into the source code). Try it-- you'll use this
technique again and again.

Best regards,
Spehro Pefhany

Spehro Pefhany, Oct 15, 2007
9. ### AlGuest

Check this out:

http://www.tinaja.com/glib/hack86.pdf

and this:

http://www.tinaja.com/magsn01.asp

Al

Al, Oct 15, 2007