1. This forum section is a read-only archive which contains old newsgroup posts. If you wish to post a query, please do so in one of our main forum sections (here). This way you will get a faster, better response from the members on Motherboard Point.

Sine wave generation

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

  1. 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 ?

    ratemonotonic, Oct 14, 2007
    1. Advertisements

  2. ratemonotonic

    Deep Reset Guest

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


    Deep Reset, Oct 14, 2007
    1. Advertisements

  3. ratemonotonic

    cbarn24050 Guest

    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. Download this Freeware Calc :


    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.

    Jim Granville, Oct 14, 2007
  5. 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 Keinanen, Oct 15, 2007
  6. Manual coding of consts.
    Generation of consts using your own code.

    Karthik Balaguru
    karthikbalaguru, Oct 15, 2007
  7. ratemonotonic

    Tom2000 Guest

    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.


    ; 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>


    ; Radix, Defines, and Constants

    radix dec

    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

    ; Macro definitions

    bank0 macro
    bcf STATUS,RP0

    bank1 macro
    bsf STATUS,RP0

    ; 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
    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


    ; Program code begins here

    org 0 ; processor reset vector

    goto main

    org 0x04 ; interrupt vector

    goto Int_Svc_Rtn

    ; Interrupt service routine


    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

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


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

    bsf INTCON,GIE ; enable system interrupts

    ; Main program routine begins here


    ; Disable system interrupts

    bcf INTCON,GIE

    ; set the internal oscillator to 8 MHz

    movlw 0x71
    movwf OSCCON

    ; init I/O & ADC

    movlw 0x07
    movwf CMCON0 ; 0,1,2 are digital I/O
    movlw b'01011000' ; AN3 analog, others digital
    I/O, Fosc/16
    movwf ANSEL
    movlw b'00010011' ; Preset PosSw and NegSw as
    movwf TRISIO
    movlw b'00001101' ; AN3, left-justified,ADON
    movwf ADCON0
    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


    call GetSpeed
    call SetPosPhase
    call SendCycle
    call SetNegPhase
    call SendCycle

    goto mainloop

    ; Subroutines

    PWMcalc ; 8 x 8 unsigned mult of sineval x
    ; result in pwmvalhi and pwmvallo
    ; modifies sineval,

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

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

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

    bsf ADCON0,1 ; start an ADC measurement
    btfss PIR1,ADIF ; wait for completion
    goto $-1
    bcf PIR1,ADIF

    movf ADRESH,W ; get the reading
    movwf temp0

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

    movlw 1
    movwf running
    goto gslp1

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

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

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

    movlw VoltTbl ; calc addr of volt table
    addwf temp0,W
    call EERead
    movwf voltval

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

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

    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


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

    clrf sineptr

    sclp1 movf sineptr,W ; get sine value
    call EERead
    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


    EERead ; EEPROM read. Enter with addr in W,
    ; exit with data in W
    movwf EEADR
    bsf EECON1,RD
    movf EEDAT,W

    SetPosPhase ; Open PWM path to Q1

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

    SetNegPhase ; Open PWM path to Q2

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

    StartPWM ; Set up and start PWM

    bsf TRISIO,PWMpin ; Disable the PWM output pin

    movlw PWM_val ; Set the PWM period into TMR2
    movwf PR2

    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
    bcf TRISIO,PWMpin ; as an output


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

    Tom2000, Oct 15, 2007
  8. 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. ratemonotonic

    Al Guest

    Check this out:


    and this:


    Al, Oct 15, 2007
    1. Advertisements

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments (here). After that, you can post your question and our members will help you out.