Changing the sines frequency?


Closed Thread
Results 1 to 33 of 33

Hybrid View

  1. #1

    Default Changing the sines frequency?

    Hi everyone,

    I'm again here with my three-phase sines.

    All works great, I can change the frequency (by varying the interrupt "timer" variable) and the amplitude of my sines (by varying the "amplitude" variable).

    But something is strange with the timer : the output sines frequency is not proportinal with the timer value. There is what I get from real experimentations :


    Of course, for the calculation of U/F variables, is far more complicated.

    So, how I can calculate the output frequency of my sines internally by the PIC?
    Or how I can change the sines frequency proportionally with an another method than varying "timer" variable?

    Thanks.

    Code :
    Code:
    ' PIC initialization
    DEFINE OSC 40      
    DEFINE LCD_DREG PORTC
    DEFINE LCD_EREG PORTD
    DEFINE LCD_RSREG PORTD
    DEFINE LCD_EBIT 0
    DEFINE LCD_RSBIT 1
    DEFINE LCD_COMMANDUS 4000
    DEFINE LCD_DATAUS 100
    DEFINE USE_LOWPRIORITY 1
    
    
    ' BAS includes
    INCLUDE "DT_INTS-18.bas"
    INCLUDE "ReEnterPBP-18.bas"
    INCLUDE "ReEnterPBP-18LP.bas"
    INCLUDE "Sine_table.bas"
    INCLUDE "Freq_table.bas"
    
    
    ' Port registers configuration
    TRISB=%11000000   ' PWM 0,1,2,3,4,5 outputs
    TRISC=%00110000   ' +/- buttons
    
    
    ' PCPWM registers configuration
    DTCON=%110        ' Deadtime (600ns)
    PTCON0=%0         ' 1:1 postscale, Fosc/4 1:1 prescale, free running mode
    PTCON1=%10000000  ' PWM time base is ON, counts up, 19.45kHz/4
    PWMCON0=%1000000  ' PWM 0,1,2,3,4,5 set in pair mode
    PWMCON1=%1        ' PWM timer sync configuration
    
    
    ' PWM calculation variables
    ustep var byte
    vstep var byte
    wstep var byte
    uduty var word
    vduty var word
    wduty var word
    timer var word
    amplitude var word
    carrier VAR word
    flag var bit
    
    
    ' Variables definition
    ustep=72          ' 360 degrees phase angle
    vstep=48          ' 240 degrees phase angle
    wstep=24          ' 120 degrees phase angle
    timer=64120       ' Timer adjust (64120=120Hz) 
    amplitude=65535   ' Sinewave amplitude adjust (65535=max amplitude)
    carrier=1023      ' Carrier frequency adjust (1023=13kHz)
    flag=%0           ' Menu flag
    
    
    ' PWM carrier frequency register configuration
    PTPERL=carrier.lowbyte  
    PTPERH=carrier.highbyte
    
      
    ' Interrupt processors 
    ASM
    INT_LIST macro
             INT_Handler TMR1_INT,_pwmint,PBP,yes
             endm
             INT_CREATE 
        
    INT_LIST_L macro
               INT_Handler TMR0_INT,_mainint,PBP,no
               endm
               INT_CREATE_L       
    ENDASM
    
        
    ' Timers configuration
    T1CON=%10000001
    T0CON=%10000111
    
    
    ' Interrupts enable
    @ INT_ENABLE TMR1_INT
    @ INT_ENABLE TMR0_INT
    
    
    ' PWM calculation and update interrupt (Timer 1) 
    pwmint:
    
    ' Timer 1 update
    TMR1L=timer.lowbyte
    TMR1H=timer.highbyte
    
    ' PWM U phase calculation
    uduty=sine[ustep]
    uduty=uduty<<4**amplitude+3
    
    ' PWM V phase calculation
    vduty=sine[vstep]
    vduty=vduty<<4**amplitude+3
    
    ' PWM W phase calculation
    wduty=sine[wstep]
    wduty=wduty<<4**amplitude+3
    
    ' PWM U, V and W update
    PDC0L=uduty.lowbyte
    PDC0H=uduty.highbyte
    PDC1L=vduty.lowbyte
    PDC1H=vduty.highbyte
    PDC2L=wduty.lowbyte
    PDC2H=wduty.highbyte
    
    ' Phase angle calculation
    ustep=ustep-1
    vstep=vstep-1
    wstep=wstep-1
    
    ' Phase angle reinitialization
    if ustep=0 then ustep=72
    if vstep=0 then vstep=72
    if wstep=0 then wstep=72
    
    @ INT_RETURN
    
    
    ' Main program loop interrupt (Timer 0)
    mainint:
    
    ' Debug display
    if flag=0 then
    LCDOUT $FE,$2,"Timer adjust :"
    LCDOUT $FE,$C0,DEC5 timer
    if PORTC.4=1 then timer=timer-1
    if PORTC.5=1 then timer=timer+1
    IF PORTC.4 AND PORTC.5=1 then flag=%1
    else
    LCDOUT $FE,$2,"Amp. adjust :"
    LCDOUT $FE,$C0,DEC5 amplitude
    if PORTC.4=1 then amplitude=amplitude-1
    if PORTC.5=1 then amplitude=amplitude+1
    endif
    
    @ INT_RETURN
    Last edited by pxidr84; - 2nd March 2011 at 14:40.

  2. #2
    Join Date
    Oct 2005
    Location
    Sweden
    Posts
    3,612


    Did you find this post helpful? Yes | No

    Default Re: Changing the sines frequency?

    Hi,
    Running the PIC at 40Mhz and having a prescaler on TMR1 of 1:1 (no prescaler) makes it "tick" at 10MHz. Your sine-table, if I understand correctly contains 72 "steps" so you need 72 interrupts per second per Hz output frequency.

    At 120 Hz output frequency you need 120*72= 8640Hz or an interrupt interval of 115.7us. At 10Mhz one "tick" is 0.1us so you need 1116 ticks between interrupts and therefor you should (theoretically) reload the timer with: 65536-1116 = 64420.

    At 10Hz you need 720 interrupts per second or an interrupt interval of 1388.9us. That's 13889 ticks so you need to reload the timer with 65536-13889 = 51647.


    Now, when the timer overflows and trips the interrupt a lot of things has to happend. The DT-Ints interrupt "engine" has to save all the PBP system varibles and this takes time. By the time the code actually gets to reloading TMR1 with your calculated reload value several hundred cycles have passed. If you then reload it with the "original", calculated value you are effectively "turning back the time" or reclaiming time that has already passed.

    What you usually do is stop the timer, make a copy of its content and then ADD the reload value to the value of the copy. Then you put the value back in the timer and restart it. Obviously this too takes a couple of cycles so to be really accurate you need to tweak the reload values to account for it. Basically
    Code:
    TimerShadow VAR WORD
     
    T1CON.0 = 0  'Stop TMR1
    TimerShadow.HighByte = TMR1H
    TimerShadow.LowByte = TMR1L
    TimerShadow = TimerShadow + TimerReload
    TMR1H = TimerShadow.HighByte
    TMR1L = TimerShadow.LowByte
    T1CON.0 = 1  'Restart TMR1
    I see that you have 16bit read/write of TMR1 enabled, I don't know how that affects the above but I'd probably turn that off to begin with.

    In the INT_List for the low priority interrupt you have the Reset_Flag set to NO for TMR0 - why is that? You don't seem to reset it "manually" in the ISR.

    Finally, in your TMR0 interupt, don't do the LCDOUT etc in there. Set a flag and get out of there, then in your main routine you check the flag, if set do whatever and reset the flag.

  3. #3


    Did you find this post helpful? Yes | No

    Default Re: Changing the sines frequency?

    Thanks Henrik (again), I will make some experiments with your tips.

  4. #4


    Did you find this post helpful? Yes | No

    Default Re: Changing the sines frequency?

    Quote Originally Posted by HenrikOlsson View Post
    Hi,
    Running the PIC at 40Mhz and having a prescaler on TMR1 of 1:1 (no prescaler) makes it "tick" at 10MHz. Your sine-table, if I understand correctly contains 72 "steps" so you need 72 interrupts per second per Hz output frequency.

    At 120 Hz output frequency you need 120*72= 8640Hz or an interrupt interval of 115.7us. At 10Mhz one "tick" is 0.1us so you need 1116 ticks between interrupts and therefor you should (theoretically) reload the timer with: 65536-1116 = 64420.

    At 10Hz you need 720 interrupts per second or an interrupt interval of 1388.9us. That's 13889 ticks so you need to reload the timer with 65536-13889 = 51647.


    Now, when the timer overflows and trips the interrupt a lot of things has to happend. The DT-Ints interrupt "engine" has to save all the PBP system varibles and this takes time. By the time the code actually gets to reloading TMR1 with your calculated reload value several hundred cycles have passed. If you then reload it with the "original", calculated value you are effectively "turning back the time" or reclaiming time that has already passed.

    What you usually do is stop the timer, make a copy of its content and then ADD the reload value to the value of the copy. Then you put the value back in the timer and restart it. Obviously this too takes a couple of cycles so to be really accurate you need to tweak the reload values to account for it. Basically
    Code:
    TimerShadow VAR WORD
     
    T1CON.0 = 0  'Stop TMR1
    TimerShadow.HighByte = TMR1H
    TimerShadow.LowByte = TMR1L
    TimerShadow = TimerShadow + TimerReload
    TMR1H = TimerShadow.HighByte
    TMR1L = TimerShadow.LowByte
    T1CON.0 = 1  'Restart TMR1
    I see that you have 16bit read/write of TMR1 enabled, I don't know how that affects the above but I'd probably turn that off to begin with.

    In the INT_List for the low priority interrupt you have the Reset_Flag set to NO for TMR0 - why is that? You don't seem to reset it "manually" in the ISR.

    Finally, in your TMR0 interupt, don't do the LCDOUT etc in there. Set a flag and get out of there, then in your main routine you check the flag, if set do whatever and reset the flag.
    Excuse me for my misunderstanding...
    I've made some experiments with your routine, I can change the frequency, but even not proportionnaly, how do you integrate your routine into my program?
    Last edited by pxidr84; - 4th March 2011 at 16:54.

  5. #5
    Join Date
    Oct 2005
    Location
    Sweden
    Posts
    3,612


    Did you find this post helpful? Yes | No

    Default Re: Changing the sines frequency?

    Hi,
    It's possible that you are simply trying to interrupt to fast and that it's overrunning it self - ie, another interrupt occurs before while servicing the first one. Try removing the all code from your ISR and replace with a simple TOGGLE PORTB.0 or whatever, measure the frequency of the output, that should give a clue as to what is going on.

  6. #6


    Did you find this post helpful? Yes | No

    Default Re: Changing the sines frequency?

    Now I use this DT routine -> http://darreltaylor.com/DT_INTS-14/TimerTemplate.html

    Hurray, I can set my desired interrupt frequency, so I can get my desired output frequency. It's the same principle of your code above.

    But I've a little problem :

    I like to define this ASM variable (@Freq = 10 ) to a PBP variable (for modify it in PBP and display it on the LCD for example).

    I've tried this :
    intfreq VAR WORD
    intfreq=5000
    @Freq = _intfreq

    But it doesn't work at all.

    Here's the DT timer template code :

    Code:
    ; Initialize your hardware first
    
    DEFINE OSC 20
    
    INCLUDE "DT_INTS-14.bas"     ; Base Interrupt System
    INCLUDE "ReEnterPBP.bas"     ; Include if using PBP interrupts
    
    ASM
    INT_LIST  macro    ; IntSource,        Label,  Type, ResetFlag?
            INT_Handler   TMR1_INT,   ReloadTMR1,   ASM,  no    ; MUST be first
            INT_Handler   TMR1_INT,   _T1handler,   PBP,  yes
        endm
        INT_CREATE               ; Creates the interrupt processor
    ENDASM
    
    ;--- Change these to match the desired interrupt frequency -------------------
    ;--- See http://DarrelTaylor.com/DT_INTS-14/TimerTemplate.html for more Info.
    @Freq       = 10                  ; Frequency of Interrupts in Hz
    @Prescaler  = 8                   ; Timers Prescaler setting
    T1CON = $30                       ; $30 = Prescaler 1:8, TMR1 OFF
    ; $00=1:1, $10=1:2, $20=1:4, $30=1:8 --  Must match @Prescaler value
    
    @ INT_ENABLE  TMR1_INT            ; enable Timer 1 interrupts
    GOSUB StartTimer                  ; Start the Timer
    
    ;____Your Main Program goes here______________________________________________
    Main:
      ;   ---- Your Main Program goes here ----
    GOTO Main
    
    ;____This routine is Called on each TMR1 Interrupt____________________________
    T1handler:
      ;   ---- Your interrupt routine goes here ----
      
    @ INT_RETURN
    
    ;---[TMR1 reload - interrupt handler]-----------------------------------------
    ASM                               ; Calculate Timer Reload Constant
    ReloadInst  = 8                   ; # of Intructions used to reload timer
      if ((Prescaler == 1)||(Prescaler == 2)||(Prescaler == 4)||(Prescaler == 8))
    MaxCount    = 65536 + (ReloadInst / Prescaler)
    TimerReload = MaxCount - (OSC*1000000/4/Prescaler/Freq)
        if ((TimerReload < 0) || (TimerReload > (65535-ReloadInst)))
            error Invalid Timer Values - check "OSC", "Freq" and "Prescaler"
        endif
      else
          error Invalid Prescaler
      endif
    ENDASM
    
    @Timer1 = TMR1L                   ; map timer registers to a word variable
    Timer1       VAR WORD EXT
    TimerReload  CON EXT              ; Get the External Constant
    TMR1ON       VAR T1CON.0          ; Alias the Timers ON/OFF bit
    
    ;---Reload Timer1------
    ASM
    ReloadTMR1
        MOVE?CT  0, T1CON, TMR1ON     ;  1     stop timer
        MOVLW    LOW(TimerReload)     ;  1     Add TimerReload to the 
        ADDWF    TMR1L,F              ;  1     value in Timer1
        BTFSC    STATUS,C             ;  1/2
        INCF     TMR1H,F              ;  1
        MOVLW    HIGH(TimerReload)    ;  1
        ADDWF    TMR1H,F              ;  1
        MOVE?CT  1, T1CON, TMR1ON     ;  1     start timer
      INT_RETURN
    ENDASM
    
    ;---Start/Stop controls -----
    StartTimer:
        Timer1  = TimerReload         ; Load Timer
        TMR1ON = 1                    ; start timer
    RETURN
    
    StopTimer:
        TMR1ON = 0                    ; stop timer
    RETURN

  7. #7
    Join Date
    Oct 2005
    Location
    Sweden
    Posts
    3,612


    Did you find this post helpful? Yes | No

    Default Re: Changing the sines frequency?

    Hi
    Aren't you using a PIC18F4431? If so you should use DT-INTS-18 but I suspect that is what you are doing even if you posted the DT-INTS-14 code?

    Right, as far as I know the Freq variable isn't a variable it's an assembler constant. The TimerReload value is calculated at assembly time (not runtime) so once the code is assembled Freq no longer exists and can't be changed.

    If you're using the timer template then it's the TimerReload value you should change in order to change the frequency - which is the same thing as we've been thru earlier in the thread.

    /Henrik.

Members who have read this thread : 0

You do not have permission to view the list of names.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts