incPID Routine - Help Needed


Closed Thread
Results 1 to 40 of 64

Hybrid View

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


    Did you find this post helpful? Yes | No

    Default Re: incPID Routine - Help Needed

    Hi Barry,
    Any jitter on the input (ADValue variable in this case) or in the time between sampling the input will cause jitter on the output. If there's jitter on the input variable the motor probably IS changing speed slightly as I think the time that COUNT statement is actually counting pulses is pretty stable. However the time between samples may vary a bit in this case since you rely on the time of execution of the rest of the code.

    Any jitter in the input signal gets "amplified" by the gain of the filter, especially the P and D gain. If you have P gain of 5 and a D gain of 10 then a difference of '1' on the input will cause the output jump '15' from one sample to the other. Now you're running very low gains so it shouldn't be too sensitive.

    If the time between samples varies then a difference of x counts means different things. Lets say the count is 100, next sample it's 110 and the next it's 120. Now, lets say that the time between samples 1 and 2 is longer than between samples 2 and 3. This means that the motor is actually accelerating "harder" between samples 2 and 3 (the increase in number of pulses was the same but during a shorter period of time) but the filter doesn't know this.

    The execution time of the PID routine may not be the same every time thru it and your DEBUG statements, when executed, will take quite a bit of time.

    You're running the PID loop at less than 10Hz, I think that's a bit slow - I'd probably run atleast 10 times faster than that. (For my own motorcontrol project I'm running it at 1220-2800Hz).

    What I'd do is to use DT-Ints to set up a timer interrupt at around 100Hz to begin with. I'd feed the pulses to another timer/counter and set that up to count the pulses in hardware instead of using the COUNT command. Each time the interrupt fires it grabs the count from the timers registers and subtracts the "last" count from it to get the "new" count. Then it feeds that to the PID routine.

    Outputting data to the display or PC should be handled by the main routine. Unfortunatly the '684 doesn't have a UART so you're forced to use the bitbanged routine (SEROUT, DEBUG etc). If an interrupt fires during one of these statements the timing will get messed up and corrupt the data. Because of this I'd try to set up buffer and a system of semaphores (free_to_send, data_to_send, sending_data) and have it send one or two bytes between each interrupt to make sure it's got enough time to spit the bits out before the next interrupt.

    /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

    Hi Henrik,

    Thank you once again for your very detailed response. Much appreciated.

    I understand that my PID loop is running slow and appreciate that it would be best "to count the pulses in hardware instead of the COUNT command" BUT I need some direction on how to make this happen. I have had zero experience using timer interrupts (though I have worked with Darrel's DT-Interrupt for IOC). Can you walk me through the process or direct me to a previous thread which explains the process? I am really struggling to get my head around timer registers, where and how the pulses are stored and what needs to be done to retrieve them for implementation with the PID routine.

    As far as the data outputting is concerned, I am quite happy to live with my "display on button press" routine just to see the filter gain terms after adjustment. I haven't played with a UART before so that will become a whole new learning curve for me. For the time being, I will be happy just to get the PID routine nice and stable. I can always program another PIC just to handle the RPM output routine...

    Cheers
    Barry
    VK2XBP

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


    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.

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

  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,

    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

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


    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.

  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

    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

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