incPID Routine - Help Needed


Closed Thread
Results 1 to 40 of 64

Hybrid View

  1. #1
    Join Date
    Oct 2005
    Location
    Sweden
    Posts
    3,613


    Did you find this post helpful? Yes | No

    Default Re: incPID Routine - Help Needed

    Great! Now lets put that on hold for a bit and see if we can get a timer interrupt going - then we'll glue it together.
    I am a bit worried about the somewhat limited amount of RAM on '684 but we'll just have to see. I won't try to go into too much detail about DT-Ints, there's been several threads and several examples on the forum and obviosuly on Darrels site. If you don't already have the needed files you get them here. Place them either in the project directory or in the root folder of your PBP installation.

    TMR1 is a 16bit timer and it causes an interrupt (if enabled) at rollover from $FFFF to 0. If we would let the timer free-run we'd get an interrupt every 65536th instruction cycle. At 8Mhz, that would be every 32.768ms or a frequency of ~30.5 Hz. If we want any other frequency we need to make sure that the timer doesn't start at 0 but at some other value. This is done by preloading the timer inside the interrupt service routine.

    At 8Mhz one instruction cycle is 500ns. If we want an interrupt frequency of 100Hz we need the timer to roll over every 10ms, or once every 20000 instruction (20000*500ns=10ms). Because the timer interrupt when it rolls over we use 65536-20000=45536.


    OK, now the code:
    Code:
    ' Included the interrupt system
    INCLUDE "DT_INTS-14.bas"
    INCLUDE "ReEnterPBP.bas"
    
    ' When compiling for the 16F684 DT-Ints tells us to add these variables so we do that
    wsave VAR BYTE $20 SYSTEM
    wsave1 VAR BYTE $A0 SYSTEM
    
    TMR1_Reload CON 45536           ' Reload value for ~100Hz interrupt frequency at 8MHz, prescaler 1:1
    
    TMR1_Temp VAR WORD              ' Temporary variable use for reloading the timer
    IntCount VAR BYTE               ' Keep track of number of interrupts
    oldCount VAR BYTE
    newCount VAR BYTE
    Frequency VAR WORD
    UpdateDisplay VAR BIT
    
    LED1 VAR PortC.4
    LED2 VAR PortC.5
    
    ASM
    INT_LIST  macro    ; IntSource,        Label,  Type, ResetFlag?
            INT_Handler   TMR1_INT,  _ISR,   PBP,  yes
        endm
        INT_CREATE            ; Creates the interrupt processor
    ENDASM
    
    T1CON.0 = 1                       ' Start TMR1
    @   INT_ENABLE  TMR1_INT  ; Enable Timer 1 Interrupts
    
    Main:
        Toggle LED1
        Pause 1000
        Goto Main
    
     
    ' This is the interrupt routine for the TMR1 interrupt.
    ISR:
        T1CON.0 = 0                         ' Stop timer
        TMR1_Temp.HighByte = TMR1H          ' Get current "time"
        TMR1_Temp.LOWBYTE = TMR1L
        TMR1_Temp = TMR1_Temp + TMR1_Reload ' Add reload value to get correct interval
        TMR1H = TMR1_Temp.HIGHBYTE          ' Move result back to timer 
        TMR1L = TMR1_Temp.LOWBYTE
        T1CON.0 = 1                         ' Start timer
    
        If IntCount = 19 THEN
            Toggle LED2
            IntCount = 0
        ENDIF
    @ INT_RETURN
    The main routine in the above code will blink LED1 at 0.5Hz. The timer interrupt should cause LED2 to blink at ~5Hz. The code compiles here but I haven't tested it. Obviosuly you need to add your CONFIG, TRIS, ANSEL, CMCON, whatever at the top too.

    Give it a go, see if you can calculate and set the desired frequency.

    /Henrik.

  2. #2
    Join Date
    Jan 2011
    Location
    Sydney, Australia
    Posts
    172


    Did you find this post helpful? Yes | No

    Default Re: incPID Routine - Help Needed

    Thanks Henrik.

    It looks good but I can't see how LED2 will ever toggle. I think there should be a line
    IntCount = IntCount + 1
    somewhere within the interrupt routine.

    Anyway, I will have a play with it and let you know how I go.

    Cheers
    Barry
    VK2XBP

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


    Did you find this post helpful? Yes | No

    Default Re: incPID Routine - Help Needed

    Definitely so. Somehow that fell out of there.....
    Why do you need me for this ? ;-)

  4. #4
    Join Date
    Nov 2003
    Location
    Greece
    Posts
    4,139


    Did you find this post helpful? Yes | No

    Default Re: incPID Routine - Help Needed

    Nice job Henrik.

    LED1 will toggle LED1 every 1 second not 0.5 (based on correct 8MHz clock).

    Ioannis

  5. #5
    Join Date
    Jan 2011
    Location
    Sydney, Australia
    Posts
    172


    Did you find this post helpful? Yes | No

    Default Re: incPID Routine - Help Needed

    Hi Henrik,

    All good so far - Step 2 complete.
    I have the leds blinking and have modified the code to display Frequency (averaged over a ten cycle loop) on the LCD as follows:

    Code:
    ' Define LCD registers and bits
    Define	LCD_DREG	PORTC
    Define	LCD_DBIT	0
    Define	LCD_RSREG	PORTA
    Define	LCD_RSBIT	5
    Define	LCD_EREG	PORTC
    Define	LCD_EBIT	4
    Define	LCD_BITS	4
    Define	LCD_LINES	2
    
    
    CMCON0 = %00000111		' Disable comparators
    ANSEL = %00000000		' Set PORTA as digital I/O
    TRISC = 0               ' Set PORTC as output
    TRISA.1 = 0             ' Make TOCKI input
    OPTION_REG.0 = 0        ' Prescaler 1:1
    OPTION_REG.1 = 0        ' 
    OPTION_REG.2 = 0        '
    OPTION_REG.3 = 1        ' Prescaler assigned to WDT
    OPTION_REG.4 = 0        ' Increment on low-to-high transition
    OPTION_REG.5 = 1        ' Clock on T0CKI-pin
    OPTION_REG.6 = 0        '
    OPTION_REG.7 = 1        '
    
    OSCCON = %01110101      ' Set clock frequency to 8MHz
    define OSC  8
    
    
    ' Included the interrupt system
    INCLUDE "DT_INTS-14.bas"
    INCLUDE "ReEnterPBP.bas"
    
    ' When compiling for the 16F684 DT-Ints tells us to add these variables so we do that
    wsave VAR BYTE $20 SYSTEM
    wsave1 VAR BYTE $A0 SYSTEM
    
    TMR1_Reload CON 45536           ' Reload value for ~100Hz interrupt frequency at 8MHz, prescaler 1:1
    
    TMR1_Temp VAR WORD              ' Temporary variable use for reloading the timer
    IntCount VAR BYTE               ' Keep track of number of interrupts
    oldCount VAR BYTE
    newCount VAR BYTE
    Frequency VAR WORD
    UpdateDisplay VAR BIT
    
    LED1 VAR PortA.0
    LED2 VAR PortA.1
    
    ASM
    INT_LIST  macro    ; IntSource,        Label,  Type, ResetFlag?
            INT_Handler   TMR1_INT,  _ISR,   PBP,  yes
        endm
        INT_CREATE            ; Creates the interrupt processor
    ENDASM
    
    T1CON.0 = 1                       ' Start TMR1
    @   INT_ENABLE  TMR1_INT  ; Enable Timer 1 Interrupts
    
    Main:
    '    Toggle LED1
    '    Pause 1000
    '    Goto Main
    
        if intCount = 0 then
            LCDOUT $FE, $02, "NewCount: ", #newCount, "  "
            LCDOUT $FE, $C0
            LCDOUT "Frequency: ", #Frequency, "   "
            Frequency = 0
        endif
    Goto Main
     
    ' This is the interrupt routine for the TMR1 interrupt.
    ISR:
        T1CON.0 = 0                         ' Stop timer
        TMR1_Temp.HighByte = TMR1H          ' Get current "time"
        TMR1_Temp.LOWBYTE = TMR1L
        TMR1_Temp = TMR1_Temp + TMR1_Reload ' Add reload value to get correct interval
        TMR1H = TMR1_Temp.HIGHBYTE          ' Move result back to timer 
        TMR1L = TMR1_Temp.LOWBYTE
        newCount =  TMR0 - oldCount
        oldCount = TMR0
        T1CON.0 = 1                         ' Start timer
        IntCount = INtCount + 1             ' Increment interrupt counter
        Frequency = Frequency + newCount
        If IntCount = 9 THEN
            Frequency = Frequency/10
            IntCount = 0
        ENDIF
    @ INT_RETURN
    OK, I appreciate that the Frequency readout isn't really the true frequency but I just wanted to prove the averaging routine.

    Onwards and upwards... What's next?

    Cheers
    Barry
    VK2XBP

  6. #6
    Join Date
    Oct 2005
    Location
    Sweden
    Posts
    3,613


    Did you find this post helpful? Yes | No

    Default Re: incPID Routine - Help Needed

    Ioannis,
    Not sure I understand.... it will toggle LED1 once every second which means it will blink at 0.5Hz - isn't that what I wrote?

    Barry,
    Excellent! I wouldn't check IntCount in the main routine like that. Instead I'd check IntCount in the ISR and set a flag (UpdateDisplay or whatever). The main routine then checks this flag, executes the code and resets the flag. The reason for this is that when the code grows the Main routine may "miss" the when IntCount=0 (depending on what else it has to do and how long it takes). If you instead use the flag/semaphore method the Main routine WILL see that it's time to update display - when it gets around to it.

    OK, now that you have a stable time base and the pulses counted in hardware it's time to add in the PID again. The ISR would look something like:
    Code:
    ISR:
    
        T1CON.0 = 0                         ' Stop timer
        TMR1_Temp.HighByte = TMR1H          ' Get current "time"
        TMR1_Temp.LOWBYTE = TMR1L
        TMR1_Temp = TMR1_Temp + TMR1_Reload ' Add reload value to get correct interval
        TMR1H = TMR1_Temp.HIGHBYTE          ' Move result back to timer 
        TMR1L = TMR1_Temp.LOWBYTE
        T1CON.0 = 1				' Start timer
    
        ' Get the current motor velocity
        newCount =  TMR0 - oldCount
        oldCount = TMR0
    
        ' Calculate error and run PID
        pid_Error = SetPoint - newCount
        GOSUB PID
    
        IntCount = IntCount + 1             ' Increment interrupt counter
        If IntCount = 9 THEN
            UpdateDisplay = 1
            IntCount = 0
        ENDIF
    
    @ INT_RETURN
    Your setpoint in this case is pulses per interrupt period, not actual frequency in Hz.

    And the Main routine perhaps something like:
    Code:
    UpdateDisplay VAR BIT
    Main:
       If UpdateDisplay THEN
         'LCDOUT.....
         'LCDOUT.....
       UpdateDisplay = 0
    Goto Main
    This is untested but I'm sure you'll get the idea.

    /Henrik.

  7. #7
    Join Date
    Nov 2003
    Location
    Greece
    Posts
    4,139


    Did you find this post helpful? Yes | No

    Default Re: incPID Routine - Help Needed

    Ooops, yes Henrik. Sorry about that. Thought it was 0.5 second and not Hz as you stated.

    Ioannis

  8. #8
    Join Date
    Jan 2011
    Location
    Sydney, Australia
    Posts
    172


    Did you find this post helpful? Yes | No

    Default Re: incPID Routine - Help Needed

    OK, now I am a little confused....
    You said that Setpoint now becomes pulses per interrupt period but how is this referenced to the ADCin result of my speed set trimpot?
    Shouldn't the setpoint always be derived from the setting of the speed set trimpot and the error be calculated from the pulses per interrupt period?

    Cheers
    Barry
    VK2XBP

Members who have read this thread : 1

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