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

    Hi Barry,
    Lets take it one step at the time and see what we get.

    The '684 has three timers, TMR0, TMR1 and TMR2. Since you're using the ECCP module to generate PWM (thru the HPWM command) it occupies TMR2 so we can't use that for anything else. That leaves us TMR0 and TMR1.

    Looking at the datasheet sections for TMR0 and TMR1 we can see that they both can be configured as either a timer or as counter so we just need to decide which to use as our counter. TMR0 is only 8 bits so if we're going to use that as our counter we need to make sure to not overflow it. If you have 100 counts per revolution and 2500rpm we're getting 4167 counts per second. If we were to run the PID loop at 10Hz we'd get 416 counts between updates which clearly overflows the 8bit counter, we'd need to either lower the input frequency or increase the update rate - you get the idea.

    Looking at the datasheet section for TMR0 we can see that it's controlled thru the 6 lower bits in OPTION_REG. All we really need to do to set up as a counter is to set the T0CS bit (OPTION_REG.5 = 1). Now TMR0 will count the pulses comming in on the T0CKI pin (pin 11).
    Code:
    TRISA.2 = 1		' Make TOCKI input
    OPTION_REG.0 = 0	' Prescaler 1:1
    OPTION_REG.1 = 0
    OPTION_REG.2 = 0
    OPTION_REG.3 = 1	' Precaler assigned to WDT
    OPTION_REG.4 = 0	' Increment in low-to-high transition
    OPTION_REG.5 = 1	' Clock on T0CKI-pin
    OPTION_REG.6 = 0
    OPTION_REG.7 = 1
    At this point all you need to do is to read the count out of the TMR0 register:
    Code:
    newCount VAR BYTE
    newCount = 0
    Main:
      newCount = TMR0 - newCount
      DEBUG "TMR0 count: ", #TMR0, 13
      DEBUG "Count since last time: ", #newCount, 13, 13
      Pause 1000
    Goto Main
    Try this and/or a variation of it to see if you can get TMR0 to count the pulses in your application.

    One final note, the idea with the incPID routine was for it be INCLUDEd in a program, like yours. There's nothing wrong with copy/pasting it like you've done but it may distract the view from your actual application.

    /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.

    I will need to wade through this tomorrow...oops, it is already tomorrow!

    I will get back to you after I have had some time to digest it and put it into action

    Cheers
    Barry
    VK2XBP

  3. #3
    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,

    I loaded the following code into my'684 and wired it up on my breadboard with a 2 x 16 LCD.
    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
    newCount    var byte
    newCount = 0
     
    Main:
        newCount = TMR0 - newCount
        lcdout $FE, 1
        LCDOUT "TMR0 Count: ", #TMR0
        LCDOUT $FE, $C0
        LCDOUT "Count since: ", #newCount
        Pause 1000
    Goto Main
    I adjusted the speed of the motor such that the pulse train frequency from the photo interruptor was under 200Hz.
    Each loop of the main program gives me a valid reading for #TMR0 and #newCount but there is no correlation between the numbers.

    To be honest, I don't really see how there could be. TMR0 doesn't appear to be reset anywhere so it should just roll over from 255 to 0 each time the maximum count is reached.

    When/how does TMR0 start and stop counting?

    You did say in your last post that we would take this "one step at a time."
    First step completed - we are now counting pulses. What next?

    If I understand the process correctly, we need to set up one timer (say TMR0) as an accurate interval timer (say 10ms) and the other timer (TMR1) as a counter for the pulses. As the interval timer ends its cycle, the value of the counter is returned as ADValue for the PID routine. Am I on the right path here?

    I look forward to your "next step" suggestions.

    Cheers
    Barry
    VK2XBP

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


    Did you find this post helpful? Yes | No

    Default Re: incPID Routine - Help Needed

    Hi Barry,
    TMR0 starts counting as soon as you set to BE a counter (OPTION_REG.5=1) and it will keep counting the incoming pulses. It will overflow att 255->0. The idea was that, because both newCount and TMR0 is a BYTE, that it will/should automatically give you the correct result but I think I may have made a little mistake there.

    Lets try this instead:
    Code:
    newCount VAR BYTE
    oldCount VAR BYTE
    newCount = 0
    oldCount = 0
    
    Main:
      newCount =  TMR0 - oldCount
      oldCount = TMR0
      LCDOUT "TMR0 Count: ", #TMR0
      LCDOUT $FE, $C0
      LCDOUT "Count since: ", #newCount
      Pause 1000
    Goto Main
    When we have TMR0 counting pulses reliably we'll move to the next step which will be set TMR1 up to generate an interrupt periodically (using DT Ints). This interrupt will then "capture" the current count, calculate the "speed" using the above code and run the PID-loop. But lets make sure we have TMR0 and the code above working properly first. Basically it should now display the frequency of the incoming signal but the timebase will be a bit off due to LCDOUT statements taking a bit of time on top of the PAUSE 1000.

    /Henrik.

  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

    All good Henrik.
    It is nice and stable and giving me a good frequency output.
    I verified the result by inputing a 10Hz and then 100Hz square wave from my signal generator and the result was within 1Hz.

    Let's go to Step 2

    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

    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.

  7. #7
    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

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