Changing the sines frequency?


Closed Thread
Results 1 to 33 of 33

Hybrid View

  1. #1


    Did you find this post helpful? Yes | No

    Default Re: Changing the sines frequency?

    Of course I'm using DT-INTS_18. Above is the sample code provided by Darrel.

    I've misreaded this line : "The frequency will remain constant and cannot be changed during Run-Time." F*ck.

    You tell me to stop the timer, make a copy, and restart it. Only the "Timer1" variable can be changed in this DT's routine. Some routines are already made on this program : "StartTimer" and "StopTimer".

    I'm sorry, but I'd like you modify my program, because I'm completly stuck at this point so many days...

    The only thing I want is control the TMR1 interrupt frequency precisely, even in runtime.

    I've tried
    Code:
    TimerShadow VAR WORD
    TimerShadow=5000
    
    .
    .
    .
    
    StartTimer:
    Timer1=TimerReload+TimerShadow         
    TMR1ON=1                    
    RETURN
    But of course it didn't works.

    I've also modified the code assuming your tips :

    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 1000
    
    
    ' BAS includes
    INCLUDE "DT_INTS-18.bas"
    INCLUDE "ReEnterPBP-18.bas"
    INCLUDE "Sine_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
    frequency var word
    amplitude var word
    carrier VAR word
    flag var bit
    
    
    ' Variables definition
    ustep=90          ' 360 degrees phase angle
    vstep=60          ' 240 degrees phase angle
    wstep=30          ' 120 degrees phase angle
    frequency=1200    ' Frequency adjust
    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,ReloadTMR1,ASM,no
    INT_Handler TMR1_INT,_pwmint,PBP,yes
    endm
    INT_CREATE       
    ENDASM
    
        
    ' Timers configuration
    @Freq=4050   
    @Prescaler=1                  
    T1CON=$00
    
    
    ' Interrupts enable
    @INT_ENABLE TMR1_INT
    GOSUB StartTimer 
    
    
    ' Main program loop
    mainlp:
    
    ' Debug display
    if flag=0 then
    LCDOUT $FE,$2,"Freq. adjust :"
    LCDOUT $FE,$C0, DEC frequency
    if PORTC.4=1 then frequency=frequency-1
    if PORTC.5=1 then frequency=frequency+1
    IF PORTC.4 AND PORTC.5=1 then flag=%1
    else
    LCDOUT $FE,$2,"Amp. adjust :"
    LCDOUT $FE,$C0,DEC4 amplitude
    if PORTC.4=1 then amplitude=amplitude-1
    if PORTC.5=1 then amplitude=amplitude+1
    endif
    
    goto mainlp
    
    
    ' PWM calculation and update interrupt (Timer 1) 
    pwmint:
    
    ' PWM U phase calculation
    uduty=sine[ustep]
    uduty=uduty<<4**amplitude
    
    ' PWM V phase calculation
    vduty=sine[vstep]
    vduty=vduty<<4**amplitude
    
    ' PWM W phase calculation
    wduty=sine[wstep]
    wduty=wduty<<4**amplitude
    
    ' 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=90
    if vstep=0 then vstep=90
    if wstep=0 then wstep=90
    
    @INT_RETURN
    
    
    ' Timer1 interrupt handler
    ASM                               
    ReloadInst=8                  
    MaxCount=65536+(ReloadInst/Prescaler)
    TimerReload=MaxCount-(OSC*1000000/4/Prescaler/Freq)
    ENDASM
    
    
    ' Timer1 variables
    @Timer1=TMR1L                   
    Timer1 VAR WORD EXT
    TimerReload CON EXT              
    TMR1ON VAR T1CON.0        
    
    
    ' Timer1 reload
    ASM
    ReloadTMR1
    MOVE?CT 0,T1CON,TMR1ON     
    MOVLW LOW(TimerReload)    
    ADDWF TMR1L,F             
    BTFSC STATUS,C             
    INCF TMR1H,F           
    MOVLW HIGH(TimerReload)   
    ADDWF TMR1H,F             
    MOVE?CT 1,T1CON,TMR1ON     
    INT_RETURN
    ENDASM
    
    
    ' Timer1 start/stop control
    StartTimer:
    Timer1=TimerReload         
    TMR1ON=1                    
    RETURN
    
    StopTimer:
    TMR1ON=0                 
    RETURN
    Also, this afternoon, I will modify the ISR and replace it with TOGGLE PORTB.0, to measure interrupt frequency. I'll give you results.
    Last edited by pxidr84; - 6th March 2011 at 10:28.

  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?

    Now you are mixing your original code with Darrels template and what I tried to tell you earlier into a complete mess....

    The only thing I want is control the TMR1 interrupt frequency precisely, even in runtime.
    Yes, and to do that you change the reload value which you load TMR1 with at each interrupt. In the timer1 intterupt stop the timer, add your reload value to the value of the timer and restart it. Then do whatever else needs to be done in the ISR. Did you try the code I posted in my first reply?

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


    Did you find this post helpful? Yes | No

    Default Re: Changing the sines frequency?

    Adding to previous message: The higher in interrupt frequency you go the less "resolution" in interrupt frequency you'll get. As an example, preloading the the timer with 65535 would make it go a single tick bwetween interrupts, in other words an interrupt frequency of 2.5Mhz when using a 20MHz crystal. (Won't work in reality but serves the purpose of this discussion).

    Now, preloading the timer with 65534 would cut that interrupt frequency in half to 1.25Mhz.

    At the other end of the spectrum, going from a preload value of 0 to a preload value of 1 only changes the interrupt frequency by something like 0.001Hz.

    The higher the "base frequency" is (the PIC clock in this case) the more resolution you'll get at the upper end.

    So, in order to get a linear output frequency you need to calculate the correct preload value, either at runtime ot use a lookuptable and interpolate between points or something.

  4. #4


    Did you find this post helpful? Yes | No

    Default Re: Changing the sines frequency?

    Quote Originally Posted by HenrikOlsson View Post
    Now you are mixing your original code with Darrels template and what I tried to tell you earlier into a complete mess....


    Yes, and to do that you change the reload value which you load TMR1 with at each interrupt. In the timer1 intterupt stop the timer, add your reload value to the value of the timer and restart it. Then do whatever else needs to be done in the ISR. Did you try the code I posted in my first reply?

    Of course I've readed your first reply, and it's how I integrated your routine into my 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 1000
    
    
    ' BAS includes
    INCLUDE "DT_INTS-18.bas"
    INCLUDE "ReEnterPBP-18.bas"
    INCLUDE "Sine_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=90          ' 360 degrees phase angle
    vstep=60          ' 240 degrees phase angle
    wstep=30          ' 120 degrees phase angle
    timer=64120       ' Timer adjust
    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       
    ENDASM
    
        
    ' Timers configuration
    T1CON=%1
    
    
    ' Interrupts enable
    @INT_ENABLE TMR1_INT
    
    
    ' Main program loop
    mainlp:
    
    ' Debug display
    if flag=0 then
    LCDOUT $FE,$2,"Freq. adjust :"
    LCDOUT $FE,$C0,DEC 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,DEC4 amplitude
    if PORTC.4=1 then amplitude=amplitude-1
    if PORTC.5=1 then amplitude=amplitude+1
    endif
    
    goto mainlp
    
    TimerShadow VAR WORD
    TimerReload VAR WORD
    
    TimerReload=64000 'Test
    
    ' PWM calculation and update interrupt (Timer 1) 
    pwmint:
    
    ' Timer 1 update
    TMR1L=timer.lowbyte
    TMR1H=timer.highbyte
    
    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
    
    ' PWM U phase calculation
    uduty=sine[ustep]
    uduty=uduty<<4**amplitude
    
    ' PWM V phase calculation
    vduty=sine[vstep]
    vduty=vduty<<4**amplitude
    
    ' PWM W phase calculation
    wduty=sine[wstep]
    wduty=wduty<<4**amplitude
    
    ' 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=90
    if vstep=0 then vstep=90
    if wstep=0 then wstep=90
    
    @INT_RETURN
    It doesnt work at all, but I think it's how I integrate your routine in the code, badly I think.

    And with this :
    Code:
    pwmint:
    
    toggle PORTD.7
    
    @INT_RETURN
    I get a 100Hz output frequency on PORTD.7.
    Last edited by pxidr84; - 6th March 2011 at 18:14.

  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?

    I get a 100Hz output frequency on PORTD.7.
    Yes, but you still need to keep the reloading code in the interrupt routine otherwise the timer will "freewheel" again.

    In your code, as the very first thing in the ISR, you are currently overwriting the TMR1 register with the value of a variable called timer.
    Code:
    ' PWM calculation and update interrupt (Timer 1) 
    pwmint:
     
    ' Timer 1 update
    TMR1L=timer.lowbyte     '<---This is overwriting the TMR1 registers!
    TMR1H=timer.highbyte
    So, once the code gets to "copying" the TMR1 registers and adding the reload value to it you have overwritten it with something else.

    I've just tested the following code here:
    Code:
    DEFINE LOADER_USED 1
    DEFINE OSC 20
     
    INCLUDE "DT_INTS-18.bas"
    INCLUDE "ReEnterPBP-18.bas"
    INCLUDE "IncPID.pbp"
     
    TMRCopy VAR WORD
    TimerReloadValue VAR WORD
    TimerReloadValue = 63000
     
    ASM
    INT_LIST  macro    ; IntSource,        Label,  Type, ResetFlag?
            INT_Handler   TMR1_INT,  _TimezUp,   PBP,  yes
        endm
        INT_CREATE              ; Creates the interrupt processor
    ENDASM
     
    @ INT_ENABLE  TMR1_INT      ; enable Timer 1 interrupts
     
    TRISB.0 = 1                 ' +button
    TRISA.4 = 1                 ' -button
    TRISB.3 = 0                 ' Output to measure interrupt frequency 
     
    CMCON = 7
    ADCON1 = %00001111          ' No analog inputs.
    T1CON.0 = 1                 ' Start TMR1
     
    Main:
    If PortB.0 = 0 then
      TimerReloadValue = TimerReloadValue + 1
      LCDOUT $FE,1,#TimerReloadValue
    ENDIF
    If PortA.4 = 0 then
      TimerReloadValue = TimerReloadValue - 1
      LCDOUT $FE,1,#TimerReloadValue
    ENDIF
    Pause 20
    Goto Main
     
    ' ---------- Interrupt handler ------------
    TimezUp:
      T1CON.0 = 0                             ' Stop TMR1
      TMRCopy.HighByte = TMR1H                ' Copy value of TMR1 registers
      TMRCopy.LowByte = TMR1L 
      TMRCopy = TMRCopy + TimerReloadValue    ' Add reload value (compensates for overhead)
      TMR1H = TMRCOPY.HighByte                ' And back to TMR1
      TMR1L = TMRCopy.LowByte
      T1CON.0 = 1                             ' Restart Timer
    Toggle PortB.3
    @ INT_RETURN
    And it works fine. Pushing the buttons connected to PortB.0 and PortA.4 increases/decreases the reload value for the timer and the interrupt frequency.

    But, again, a change of 1 at the "top" will not result in the same change in interrupt frequency as a change of 1 at the "botton".

  6. #6


    Did you find this post helpful? Yes | No

    Default Re: Changing the sines frequency?

    Quote Originally Posted by HenrikOlsson View Post
    Yes, but you still need to keep the reloading code in the interrupt routine otherwise the timer will "freewheel" again.

    In your code, as the very first thing in the ISR, you are currently overwriting the TMR1 register with the value of a variable called timer.
    Code:
    ' PWM calculation and update interrupt (Timer 1) 
    pwmint:
     
    ' Timer 1 update
    TMR1L=timer.lowbyte     '<---This is overwriting the TMR1 registers!
    TMR1H=timer.highbyte
    So, once the code gets to "copying" the TMR1 registers and adding the reload value to it you have overwritten it with something else.

    I've just tested the following code here:
    Code:
    DEFINE LOADER_USED 1
    DEFINE OSC 20
     
    INCLUDE "DT_INTS-18.bas"
    INCLUDE "ReEnterPBP-18.bas"
    INCLUDE "IncPID.pbp"
     
    TMRCopy VAR WORD
    TimerReloadValue VAR WORD
    TimerReloadValue = 63000
     
    ASM
    INT_LIST  macro    ; IntSource,        Label,  Type, ResetFlag?
            INT_Handler   TMR1_INT,  _TimezUp,   PBP,  yes
        endm
        INT_CREATE              ; Creates the interrupt processor
    ENDASM
     
    @ INT_ENABLE  TMR1_INT      ; enable Timer 1 interrupts
     
    TRISB.0 = 1                 ' +button
    TRISA.4 = 1                 ' -button
    TRISB.3 = 0                 ' Output to measure interrupt frequency 
     
    CMCON = 7
    ADCON1 = %00001111          ' No analog inputs.
    T1CON.0 = 1                 ' Start TMR1
     
    Main:
    If PortB.0 = 0 then
      TimerReloadValue = TimerReloadValue + 1
      LCDOUT $FE,1,#TimerReloadValue
    ENDIF
    If PortA.4 = 0 then
      TimerReloadValue = TimerReloadValue - 1
      LCDOUT $FE,1,#TimerReloadValue
    ENDIF
    Pause 20
    Goto Main
     
    ' ---------- Interrupt handler ------------
    TimezUp:
      T1CON.0 = 0                             ' Stop TMR1
      TMRCopy.HighByte = TMR1H                ' Copy value of TMR1 registers
      TMRCopy.LowByte = TMR1L 
      TMRCopy = TMRCopy + TimerReloadValue    ' Add reload value (compensates for overhead)
      TMR1H = TMRCOPY.HighByte                ' And back to TMR1
      TMR1L = TMRCopy.LowByte
      T1CON.0 = 1                             ' Restart Timer
    Toggle PortB.3
    @ INT_RETURN
    And it works fine. Pushing the buttons connected to PortB.0 and PortA.4 increases/decreases the reload value for the timer and the interrupt frequency.

    But, again, a change of 1 at the "top" will not result in the same change in interrupt frequency as a change of 1 at the "botton".
    Tried this in my code with the sines, it works.

    Code:
    But, again, a change of 1 at the "top" will not result in the same change in interrupt frequency as a change of 1 at the "botton".
    So what the point since the first program? It is impossible to change proportionally this interrupt frequency in run time? Or any math relation/equation to calculate the correct timer value according to the desired frequency?

    Like you said, I can make a lookup table, but this program is intended to make a 3-phase VFD AC motor drive, adjustable from 0 to 120Hz with 0.1Hz increments... so if I use a lookup table, it will take 1200 variables! Doesn't sure that my PIC had enough memory capacity...
    And how to calculate the "correct preload value"?
    Last edited by pxidr84; - 6th March 2011 at 20:27.

  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?

    The difference is that it takes the overhead of saving all the PBP system variables into account. There's nothing you can do about the non linearity really - it's just the way it is. One way to reduce the effect of it is to have less "steps" in the SIN table, that willm bring the interrupt frequency down which makes the relationship between interrupt frequency and reload value more linear.

    You CAN calculate the reload value at runtime, Darrels timer template even shows you HOW to do it (at compile/assembly time) but you need to play some tricks to do it runtime. The following is NOT tested:
    Code:
    Dummy VAR WORD
    Dummy2 VAR WORD
    Dummy3 VAR WORD
    ReloadInstructions VAR BYTE
    TimerReload VAR WORD
    Frequency VAR WORD
    
    ReloadInstruction = 0  'Number of instructioncycles it takes to actually reload the timer. I don't know how many it is in this case. Tune later.
    
    'For 40Mhz operation and 1:1 prescaler the timer ticks at 10Mhz
    Dummy2 = 10000     'These can not be constants because 
    Dummy3 = 10000     'constants doesn't work with DIV32
    
    Frequency = 200    'Lowest possible frequency is ~153Hz. Don't go below that.
    
    Dummy = Dummy2 * Dummy3        'Intermediate result is now 10.000.000
    TimerReload = DIV32 Frequency       'Divide 10.000.000 in Frequency (200 in this case) 
    TimerReload = 65535 - TimerReload + 1 + ReloadInstructions  ' =15536 for 200Hz
    
    'Reloading the timer with 15536 makes it time out in 50000 ticks, one tick is 0.1us so
    'it times out it 5000us or 5ms, 1/0.005=200Hz which is the frequency we wanted.
    This should calculate the reload value, at runtime (don't do this IN the interrupt service routine). But I don't think you'll get 0.1Hz resolution at the top end.

  8. #8


    Did you find this post helpful? Yes | No

    Default Re: Changing the sines frequency?

    Thanks, I will make some experiments with this code.

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