Operator problem

# Thread: Operator problem

1. ## Operator problem

Hi ;

I'm having an interesting problem with the following programme block ..

Just to note that , The program works as written and compiled with it's current form below . I'm getting RB0 interrupts on every falling edge of PORTB.0 and when the programme goes to interrupt block it just clears the TMR1 register and RB0 int. flag and waits for the next RB0 interrupt by reading the INTF(RB0 int flag) continuously . . And when the INTF goes high (RB0 interrupt occurs) thus it leaves the while wend cycles and gets the variable in the TMR1 so that I can measure a period .. As I said , I can measure and display the period with this method ... But when It comes to calculating the revolutions per minute (RPM) with the ( * ) operator in the bold written code it just goes mad and misdisplays the RPM value on the screen ..

What I wanna know is : Why the ( * ) multiplication operator doesn't work in the code below and how come the ( */ ) operator gives me the right result ??

I mean let's say , time and rpm are word variables and time=19827 (us) , you know that 1 minute equals to 60 * 10^6 (us) = which is written in the form of 6000 * 10000 (just to state them both in word format)

So : When we take ( */ ) operator the result is supposed to be

6000*/ 10000 = 234375 (since the */ gives me the middle 16 bit of the result , as if we divide the result by 256)
rpm=div32 19827
result ===> rpm=11.82 thus "11"

Both the project on breadboard and PROTEUS give me the correct result ( 3026 )

How is that possible ? and why ( * ) operand doesn't work while it is supposed to work ?

Thanks in advance !

Code:
```'------
DISABLE

intblock:

TMR1=0
INTF=0     'RB0/INT FLG

WHile INTF=0
@ NOP
wend

time=TMR1
rpm=6000*/10000
rpm=div32 time

INTF=0'FLG

resume

enable
'------```
Last edited by gunayburak; - 1st April 2014 at 18:08.

2. ## Re: Operator problem

Hi,
Which device are you using?
I'm not sure you can access the full TMR1 register pair in one go - even with RD16 enabled. On some devices the 16bit timers are buffered and you need to read the two bytes in the correct order or you'll get the wrong result. On the other hand, you're saying that you can measure and display the period with this very method which I find a bit strange.

You could try something like
Code:
```DISABLE

intblock:
INTF=0     'RB0/INT FLG
T1CON.0 = 1    'Start timer 1

WHILE INTF=0
@ NOP
wend

T1CON.0 = 0 'Stop timer 1

time.lowbyte = TMR1L
time.highbyte = TMR1H

rpm=6000*/10000
rpm=div32 time

TMR1L = 0   ' Clear TMR1 so it's ready for next round.
TMR1H = 0```
/Henrik.

3. ## Re: Operator problem

DIV32 requires that PBP's system variables be loaded with a 32-bit value from a prior 16x16 bit multiplication.
The multiplication has to use at least 1 WORD variable.

But when you specify a multiplication of 2 constants ... the multiplication is done a compile-time as part of the optimization (constant folding).
The system variables never get loaded, because the multiplication was not done at run-time.

The mid-word multiplier is never done at compile-time, even if it has two constants.
It's always done at run-time, and the system variables do get loaded.

And ... TMR1 is declared as a WORD variable in the .pbpinc files.
It's OK to read the entire Timer1 value as long as two conditions are met ... the Timer must be stopped, and RD16 must NOT be set.

4. ## Re: Operator problem

Originally Posted by HenrikOlsson
Hi,
Which device are you using?
I'm not sure you can access the full TMR1 register pair in one go - even with RD16 enabled. On some devices the 16bit timers are buffered and you need to read the two bytes in the correct order or you'll get the wrong result. On the other hand, you're saying that you can measure and display the period with this very method which I find a bit strange.

You could try something like
Code:
```DISABLE

intblock:
INTF=0     'RB0/INT FLG
T1CON.0 = 1    'Start timer 1

WHILE INTF=0
@ NOP
wend

T1CON.0 = 0 'Stop timer 1

time.lowbyte = TMR1L
time.highbyte = TMR1H

rpm=6000*/10000
rpm=div32 time

TMR1L = 0   ' Clear TMR1 so it's ready for next round.
TMR1H = 0```
/Henrik.
That's a nice code too , Thanks Henrik . I am using PIC16F628A and apparently I can reach the entire TMR1 at once ...

5. ## Re: Operator problem

Originally Posted by Darrel Taylor
DIV32 requires that PBP's system variables be loaded with a 32-bit value from a prior 16x16 bit multiplication.
The multiplication has to use at least 1 WORD variable.

But when you specify a multiplication of 2 constants ... the multiplication is done a compile-time as part of the optimization (constant folding).
The system variables never get loaded, because the multiplication was not done at run-time.

The mid-word multiplier is never done at compile-time, even if it has two constants.
It's always done at run-time, and the system variables do get loaded.

And ... TMR1 is declared as a WORD variable in the .pbpinc files.
It's OK to read the entire Timer1 value as long as two conditions are met ... the Timer must be stopped, and RD16 must NOT be set.
You're amazing Darrel ! .. That's such a nice explanation and mind-flashing answer .. Just outta curiosity .. Where do you get such knowledges from ? I mean we can't even get these infos from the compilers reference manual ...

Thank you guys for your prompt answers !

6. ## Re: Operator problem

Thanks gunayburak,

Everything about PBP is completely transparent.
The libraries (*.lib), macro files (*.mac), the generated assembly language (*.asm) and resulting Listing files (*.LST) are all plain text files that can be viewed with NotePad or similar text editors.

With only 15-years or so of studying those files ... and attempting to solve hundreds of other peoples problems (cause I can't make my own), and you too will know everything there is to know about PicBasic Pro.

Except for about a million things.

7. ## Re: Operator problem

Yet there is one more problem digging my mind ..
Was I not supposed to get 11 according to the calculation I did at my first post ?
How come I get 3026 ( which is the correct mathematical result and the result I expect to get with only * operand ??

Thanks once again ..

8. ## Re: Operator problem

Assuming the operations are being done at run-time ...
Internally, both * and */ do the same 16x16 bit multiplication, and the system variables contain the same 32-bit "product".

For the mid-word multiplier, the middle two bytes are then copied to the result variable, in this case rpm.
That result is then discarded and rpm is assigned the value from the DIV32, which used the 32-bit value in the system variables, not the mid-word value.
Code:
```rpm=6000*/10000  ; <-- This result in rpm is discarded
rpm=div32 time```
The same thing applies to the High-Word multiplier **.

9. ## Re: Operator problem

Thanks to both of you darrel and henrik ...

By the night I will be done with my pid rpm controller unit ...
Thanks to henrik's code which helped me so much with my projects ..
I modified his code to suit my rpm controller

10. ## Re: Operator problem

Hi,
Excellent!
All credits goes to Darrel though, I pretty much missed the ball alltogether on this one. Which is OK because I learned a thing or two from it, th e
I'm glad to hear the PID routine gets used and that it works. Out of curiosity, modifications were needed?

/Henrik.

11. ## Re: Operator problem

Hi Henrik ,
Sorry for the late answer .. I was busy with becoming a freak of PID
Well , the first modification I did is removing the acceleration feedforward and velocity feedforward block of the PID routine just to see the raw PID feedback mechanism works without any help of those and also due to lack of the memory of my pic16f628a ..
And I just added a line on the main routine where it applies the calculated P+I+D = pid output to the CCPM registers .. But unlike yours , I used such a code below ;
'------------------------
..
..
gosub PID
duty=duty+pid_out
..
..
'-----------------------------

Yet there is one more question digging my mind like hell about one code that you wrote in the INTEGRAL routine ...

IF pid_Eit>0 then pid_Ei=0

...........

Shouldn't we have cleared the pid_Ei temporary integral error accumulator storage for also the condition pid_Eit<0 ??
Last edited by gunayburak; - 4th April 2014 at 08:49.

12. ## Re: Operator problem

Hi,
Yeah, someday I need to update the routine to allow optional inclusion of the feedforward terms. At the time, PBP3 wasn't available so we had no conditional compilation.

If it works it works but I'm not quite sure about the duty=duty+pid_out, that way you keep accumulating the output of the filter. But perhaps that's what you want.

Right, the nagging question then...
pid_EiT is a WORD variable so as far as PBP is concerned it can never be <0. It's either 0 or positive.
The idea behind that piece of code is to allow the accumulator to grow even if the calculations "this time", due to truncation etc, would otherwise yeild a result of 0. It's not perfect but at least it guarantees that the accumulator WILL grow no matter how small the error and/or integral gain is.

/Henrik.

13. ## Re: Operator problem

Hi Henrik ;

Why are you not sure about the code line ;

duty=duty+pid_Out

CCP1CON.4=DUTY.0
CCP1CON.5=DUTY.1
CCPR1L=DUTY>>2

Since we're trying to reach to a setpoint and that is the RPM in our case , The only way to keep the motor speed/RPM stable is giving an actual duty cycle command with an additional (positive or negative ) error dependant variable which is duty=duty+pid_Out ...
In such an equation in case we calculate a "zero" error , our duty doesn't need to change and keeps its duty as long as the error is zero , but when the error changes pid_out variable contributes to the next duty cycle with a positive or negative value depending the errors sign ... (I interpret and speak through the code you and Darrel created)

Let's take your code part which is applying the code directly to the registers as it is ...

HPWM 1 , pid_out,10000
pause 10 '''''''' this delay must be the lag , dead time I suppose , which I find very accurate for the slow responsing systems but I can't say the same for fast responsing systems ..

Well ... Now let's say we're at the desired RPM level for the motor , then what ? , pid_out is gonna be zero since there is no error ? Which means unpowering the motor for a short time or loosing the voltage on the motor till the balance condition breaks ...

I may be wrong with all those thoughts since I am a newbie engineering student ... Please enlighten and correct me if I'm wrong with those written above ..

Regards

Thanks for your counsel ..

14. ## Re: Operator problem

Hi,

Just because the error is zero doesn't mean the output from PID filter is.
Yes, the proportional term will be zero but the integral term won't neccesarily be. It'll be whatever it took to get the error to zero and therefor the output of the pid filter will be equal to the integral term - which you then continously add to your dutycycle untill a negative error develops, at which point the integral term decreases and eventually goes negative. Again the RPM reaches the setpoint but now the pid filter output is negative so it'll keep deceasing the duty cycle untill a positive error develops and the cycle repeats.

The velocity feedforward basically does what you say. It'll provide a "startingpoint" from which the PID then can add or subtract depending on the dynamic response of the system. That startingpoint then varies depending on the setpoint so you can tweak to work pretty well without any PID at all, then the PID handles the "disturbances" and load varioations only. But now you don't have the velocity feedforward in the code any more ;-)

The Pause 10 was just part of the example. Of course the sample time, how often the filter is executed needs to be adapted to the system you're trying to control. 100Hz will be WAY to fast for controlling the temperature in a house while it'll be WAY to slow for controlling the current thru a motorwinding - for example.

But again, I'm not saying you're doing anything wrong. In fact you may very well be doing it right I'm just trying to explain how I understand it. And, most importantly, as long as it works for you whatever is fine by me!

/Henrik.

15. ## Re: Operator problem

Thanks for your splendid explanations Henrik .. I've just seen once again that I misunderstood the integral term of the PID control system ... But I'm trying to match the theoritical knowledge in my head and the code you've written .. especially the lines pointing out the Integral term calcs .

We keep accumulating the error over and over and again during a period of cycles that we choose in the programme (Ti) And then we divide the sum of the errors to Ti ... Thus we get the arithmetic mean of the error sum , Am I Right ? On the other hand "the integral" in math gives us the field under the curve .... That's where I find the code a bit awkward .. Because after summing the errors we just do not use it in the process but using the arithmetic mean of the errors we summed ...

Please set my mind free of these mind killing questions digging and eating my head ...

16. ## Re: Operator problem

Hi,
Correct.
1) We accumulate (in the pid_Ei variable) the error over Ti number of samples
2) We multiply the accumulated error by the integral gain
3) We divide the result by Ti to get the average error over Ti number of samples.
4) We add the result to the PID_I variable and make sure it doesn't violate or min/max settings.
5) We clear the accumulator (the pid_Ei variable).

So, PID_I is the actual integral term, it holds the history of all past errors and is what's used to "build" the final output together with the P and D terms (and the feedforward terms). The accumulation/averaging kind of low pass filters the integral term. If you you don't want to use it then simply use a Ti of 1 - it'll then update the I-term every cycle with the error at exactly that sample.

There are different ways of implementing a PID filter and this is only one of them. Another change I'm thinking of implementing is the option to have the differential term work from the feedback instead of the setpoint.

/Henrik.

#### Posting Permissions

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