📜 ⬆️ ⬇️

Do-it-yourself PID controller

I. Statement of the problem


It is necessary to keep the temperature at a certain level and change the task. There is a microcontroller to which a temperature meter is attached, and a triac for power control. We will not warm our heads on TAU, no difference schemes , we just take and make a PID controller in the forehead.

Ii. Theoretical introductory


How is the PID controller obtained? We take the difference between the current temperature and the desired one, multiply by the adjustable coefficient, we get the power that must be given at the moment. This is a proportional component, it works when a mismatch occurs — that is, it instantly responds to both the setpoint change and the object behavior. Began to warm up? Power begins to subside. Overheated? Turned off, or even gave a cooling signal. Everything is good, only in real life the effect of the impact manifests itself with a delay, and the object is affected not only by us, but also by the environment: the heated reactor is not only hot inside, but also cools, giving heat to the room, and therefore just turn off power, it immediately begins to cool. Therefore, the net proportional regulator oscillates around the maintenance point, and the more it fluctuates, the higher the environmental / reactor contents are.

To compensate for the "external" effects on the reactor, an integral component is added to the circuit. All the mismatch that was in the system goes to the integrator (respectively, as soon as we overheat - the amount decreases, while underheated - the amount increases). And the accumulated integral, with its coefficient, gives its increase-decrease to power. As a result of this approach, in a stationary process, after some time the integral selects such a contribution to the sum with a power that compensates for environmental losses, and the oscillations disappear - the integral becomes stable, therefore the amount of output power becomes constant. And since this keeps the right temperature, there is no mismatch, the proportional component does not work at all.

To compensate for the effects of delays between exposure and system response, a differential component is added to the system. Just a proportional regulator gives power all the time until the temperature reaches the desired point, the proportional-differential begins to reduce the supplied power before it reaches the desired point - as the mismatch decreases, there is a negative derivative that reduces the effect. This minimizes overheating at large transitions.
')
So, we have dealt with the physical meaning, we turn to the basic questions of implementation.

Iii. Who should use the regulator?


- To technicians.

What follows from this? It follows from this that technicians understand the physical component, and have experience in setting up hardware PID controllers. This means that the software implementation must proceed from the convenience of customization by technicians - repeating the physical model. And this is extremely important! Very often, for the sake of simplifying the code, the coefficients are changed, for example, to inverse - to get rid of division. As a result, tuning becomes a hell and a nightmare, and one needs experience in setting up this particular regulator, instead of understanding the process. From here we get that our coefficients — the integration constant and the differentiation constant — should have the dimension of time, that is, be specified in seconds, and not at all in “1 / s”, as they like to do.

Iv. Area of ​​operation.


We are trying to make a universal regulator, which means that it should work both on small fast objects and on powerful large furnaces. Therefore, it should be assumed that the regulated temperature is limited in general by a meter. The most commonly used are HA (K) and HC (L). Their area of ​​applicability is somewhere up to 1200 ° C. Cooling requires more sophisticated equipment (cryostats), additional cooling control (fans and opening doors of heating cabinets) is also rarely required - which means we exclude from consideration for now. We obtain that the controlled temperature from ~ 15 ° C to ~ 1200 ° C, only the power supply is controlled.

The accuracy of the control is determined by a 1x measurement accuracy: calibration tables are given after 0.1 degrees; linearity inside the tables is, in principle, decent, so the accuracy is limited primarily by the amplifier and the current meter. In my case, I wanted to achieve an accuracy of maintaining 0.1 degrees, so the meter is set to 1/32 degrees: this gives ~ 3 quanta by 0.1 degrees, thus having a normal regulation “noise” + -1 quantum, we remain within the same 0.1 degrees Using 1/32 allows you to work with a fixed point - 5 bits = fractional part, the rest is whole. In 16 bits it turns out to present from 0 to 2047 °. Instead of working with negative numbers, we will work in Kelvin instead of Celsius, so it appears from 0 to 2047 ° K, which is equivalent to from -273 to 1775 ° C; in increments of 0.03125 °.

V. Range of customizability.


To control a microreactor with a powerful power plant, it may turn out that 1% of power is enough for heating by 10 degrees, while for a large inert furnace, 100% of the capacity of heating is just enough to heat a degree. (In real life, it looks like this - there are several heaters with manual control - they are turned on by a separate switch and produce initial heating, in the future maintaining the operating point provides the thermostat, controlling another heater, which at full power produces a maximum of + 10 ° C to that are constantly heated). Based on this, it is logical to assume that the limiting coefficient of proportionality is 100% power per degree. It no longer makes sense, since we want controllability of 0.1 degrees. Minimal, for simplicity, I took inverse - 1% power per 100 degrees.

The ranges of temporal coefficients are calculated simply based on our operating conditions for the controller. Since we control through the power of the triac by calculating the delay of the moment of switching on after passing through 0, the limit frequency of the regulator is 50 Hz. If we are sure that we control the power which doesn’t plus or minus, we can work at 100 Hz, but this is not always the case, and therefore it is better to dose an equal amount of both positive and negative half-waves each time. To simplify life, I reduced the operation time to 25 Hz, thus any calculated impact will last for 4 half-waves, and during this time I will be able to calculate a new impact.

Thus, time constants are set in 1/25 sec, from 0 to ~ 2000 sec (2000 * 25 = 50,000, just 16bit interlaces).

Well, we also have a minimum and maximum power limit, from 0 to 100%.

Vi. Power management


From this point on, all theoretical calculations end, a bitter practice begins, tied to a specific implementation.

So, we have already decided that we control the delay in opening the triac after passing through 0. Thus, a delay of 0 means 100% power, infinite delay = 0% power.

Question: with what accuracy can we control the power? In general, with the accuracy of the timing of our timer. On the other hand, what power is needed? We calculate what% of the power you need to apply for 0.04sec. In principle, according to experience, power control even with an accuracy of 1% at a frequency of 0.1 seconds is enough to maintain a temperature of 1 degree. We manage 0.04sek (2.5 times faster). Therefore, it was decided to calculate the power table in 1/250 of the maximum (in 0.4% steps). This allows the table to have a not very large (500 bytes), and at the same time have an accuracy above 1%. If your case requires greater accuracy - it is not so difficult to count.

Now let's talk about the calculation of this table itself. In-1x it should be noted that there is a moment of triggering a signal passing through zero. In my case - 12V. That is, when the input voltage drops below 12V, I will receive a signal passing through 0.

This means that for 100% power, the start time = 12V transit time.
Solve the system of equations
 ;  IntMoment: = 12V
 ;  Max: = sqr (220 * sqrt (2))
 ;  {Sqr (Sin (Pi / 2) * K) = Max
 ;  {Sqr (Sin (X) * K) = IntMoment
 ;
 ;  2 * k / MaxCode = 1 - cos (T * Pi)
 ;  cos (T * Pi) = 1-2 * k / MaxCode
 ;  T * Pi = arccos (1-2 * k / MaxCode)
 ;  T = arccos (1-2 * k / MaxCode) / Pi


The processor works for me at a frequency of 32786, the PLL is set to 384/2, the half-wave has 100Hz, whence we get that the code for loading a constant into the timer for time T has the form:

65536-(T*(32768*384/2)/100.0 + 773)

We need to calculate the delay time, giving a uniform increase in the area of ​​the included part of the sinusoid. That is, we need to have time counts that give a uniform increase in power. The total power that we give out is integral over the entire sinusoid. [who knows how to embed formulas in Habré? no way I write in the maple-notation then].

 Max = int (sqr (sin (x)), x = 0..Pi)
 int (sqr (sin (x)), x = 0..T * Pi) = x / 2 - sin (2 * x) / 4 + C |  0..T * PI = (T * Pi) / 2 - sin (2 * T * Pi) / 4
 (T * Pi) / 2 - sin (2 * T * Pi) / 4 = Q * Pi / 2


Thus, we need to go through all Q with a given accuracy, and for each of them find T.

I decided this for myself in this stupid way:
Perl Generator
 #!/usr/bin/perl # (T*Pi)/2 - sin(2*T*Pi)/4 = Q*Pi/2 use constant PI => 4 * atan2(1, 1); $T = 1; for( $i = 250; $i >= 0; $i-- ) { $int = $i*PI/2/250; $ev = ($T*PI)/2-sin(2*$T*PI)/4; while( abs($ev-$int) > 0.0005 ) { $T -= 0.0001; $ev = ($T*PI)/2-sin(2*$T*PI)/4; } #print $i."\t".$T."\n"; $code = 65536-($T*(32768*384/2)/100.0 + 773); printf "DB 0%02Xh, 0%02Xh ; %04Xh = $i/250 of power\n", $code%256, int($code/256), $code, $i; } 


Everything, at the output, we received a label of 250 values, corresponding to the constants of timer loading until the moment of ignition after receiving a signal about passing through 0 (more precisely, through 12V, as I said above).

VII. Input Measurement


I skip this question, because it deserves a separate large article. How I solved the issue with thermal resistance can be found in the archive of my blog, who has died in Bose .

The main thing we need to know is that we measure the data with the frequency we need (in this case, 25 Hz), and with the required accuracy (the output is a number from 0 to 2048 degrees Kelvin in 1/32 degrees). The data are assumed to be already normalized for all further calculations.

If anyone is interested - write in the comments, sign for the next time how this is done for thermocouples.

Viii. Impact calculation


And now it has happened: we have all the data in order to finally produce something for which we were plotting everything: calculate what kind of power should be fed to the control element.

Recall the PID controller formula:

U = K * ( Err + (1/Ti)*Int + Td*dErr )



We again came to the question that was raised in Section III : technology will use this. Therefore, it is extremely important to prevent the classical error of all implementations - "the dimensions of the coefficients as it will." We make the device to control the physical process, and therefore, the model must comply.

Produce the output of all dimensions. Partially looking ahead, I have already described in IV , but now we will reveal more:


Now you can clearly see why the proportional factor is out of the bracket - this allows you to leave the differential and int coefficients just as time constants, as a result, the operator operates with simple and clear numbers — a percentage per degree for proportional and seconds for integral and differential coefficients.

And thanks to a convenient selection of the position of points and the dimensions of time, as we will see now, all calculations are made almost "in the forehead."

Except for one thing - we have the value of Ti , and 1 / Ti is required for the calculation. The operation of dividing a high bit is very expensive. The multiplication operation is several times cheaper, so we will use the excellent article Division by Invariant Integers using Multiplication . After all, K / Ti / Td changes extremely rarely in our country, and therefore we can allow ourselves to somehow pervert with them after they are changed, the main thing is that the main calculation cycle works quickly.

Thus, instead of Ti for calculations, we decompose into a set of Ti_m , Ti_sh1 , Ti_sh2 ; and on each cycle we perform the calculation:
 T1 = MULUH(Ti_m, Int) Q = SHR(T1+SHR(Int-T1, Ti_sh1), Ti_sh2) 


Now we calculate the balance of digit capacity. To do this, we will write out the full formula step by step:
  1. Eo = E ; We need an error has passed. Errors - 16bit
  2. E = YX ; We calculate the new mismatch. 16bit
  3. Int = Int + (E+Eo)/2 ; We integrate an error. In this case, we consider the half-sum of the difference (difference scheme). 32bit = 32bit + 16bit
  4. cI = Int * (1/Ti) ; We consider the integral contribution - 32bit * 32bit => 32bit
  5. cD = Td * (E-Eo) ; We consider differential contribution - 16 * 16 => 32bit
  6. PID = E + cI + cD ; Podsbochnoe; 16 + 32 + 32 => 32bit
  7. U = K*PID/256 ; Coeff; 32 * 16/8 bit => 40bit.


For all calculations, the position of the point up to the 7th step remains on the 5th right place. At the last moment there is an interesting feint ears. K is set in 1/256, respectively, after multiplying the point is shifted to the left to 5 + 8 = 13 places, so we should discard the lower 8 bits of the result. And the lowest byte of the result is the power we need in 2/5%. This is another reason why the power is aligned in steps of 1/250 - this allows the result to fit into one byte and get the desired result easily from the table.

Further, we remember that we are only interested in power from 0 to 250 - therefore, the 7th step of the calculations goes very simply, as soon as we get a negative number - we immediately add uMin. As soon as they found out that any high byte is not zero, we immediately add uMax. And only if the power is added in the range - we make a test for less than uMin or more than uMax.

If anyone is interested:
complete footjacking payments
 ; PID  CalcMainEnd: ; , Go-Go. CalcPid: ; 1. Eo = E | 16bit Pid1: MOV Err0H, ErrH MOV Err0L, ErrL ; 2. E = YX | 16bit Pid2: CLR C MOV A, SettingL SUBB A, ThermoL MOV ErrL, A MOV A, SettingH SUBB A, ThermoH MOV ErrH, A JNB OV, Pid2Ov JB ACC.7, Pid2Max Pid2Min: MOV ErrL, #LOW(-500*32) MOV ErrH, #HIGH(-500*32) SJMP Pid2End Pid2Max: MOV ErrL, #LOW(500*32) MOV ErrH, #HIGH(500*32) SJMP Pid2End Pid2Ov: JNB ACC.7, Pid2OvP Pid2OvN: ;     CLR C MOV A, ErrL SUBB A, #LOW(-500*32) MOV A, ErrH SUBB A, #HIGH(-500*32) JNC Pid2End ;  > -500 =>   SJMP Pid2Min Pid2OvP: CLR C MOV A, ErrL SUBB A, #LOW(500*32) MOV A, ErrH SUBB A, #HIGH(500*32) JNC Pid2Max ;  < 500 =>   Pid2End: ; 3. Int = Int + (E+Eo)/2 | 32bit+16bit Pid3: JNB PowerReady, Pid3End ;    --     MOV A, ErrL ADD A, Err0L MOV R0, A ;  MOV A, ErrH ADDC A, Err0H MOV C, ACC.7 ;     16 ,       RRC A ;     XCH A, R0 ; A=  , R0 -    RRC A ;  JNB IntS, Pid3IntPos ; Int ,    R0:A,        Int CLR C CPL A ADD A, #1 XCH A, R0 CPL A ADDC A, #0 XCH A, R0 Pid3IntPos: ;  Int  R0:A   ,     ADD A, IntLL MOV IntLL, A MOV A, IntLH ADDC A, R0 MOV IntLH, A MOV A, R0 JB ACC.7, Pid3Neg ;   ? ;   ,    JNC jPid3End ;       --     . INC IntHL ;    MOV A, IntHL JNZ Pid3End ;      4  --   INC IntHH ;       MOV A, IntHH JNZ Pid3End ;       --   MOV IntHH, #0FFh ;     --    MOV IntHL, #0FFh MOV IntLH, #0FFh MOV IntLL, #0FFh jPid3End: SJMP Pid3End Pid3Neg: ;   ,      ,  FFh MOV A, IntHL ADDC A, #0FFh MOV IntHL, A MOV A, IntHH ADDC A, #0FFh MOV IntHH, A JC Pid3End ;    ,      CPL IntS ;   ,      CPL C ;     MOV A, #0 SUBB A, IntLL MOV IntLL, A MOV A, #0 SUBB A, IntLH MOV IntLH, A MOV A, #0 SUBB A, IntHL MOV IntHL, A MOV A, #0 SUBB A, IntHH MOV IntHH, A ;      --      Pid3End: ; 5. cI = Int*(1/Ti) | 32*32=>32bit Pid5: ; R3:R2:R1:R0 = Int*(1/Ti) JB Ti_sh1, Pid5Calc ;  Ti_sh1=0,  1/Ti=1  Ti=0.      MOV A, Ti_mLL ORL A, Ti_mLH ORL A, Ti_mHL ORL A, Ti_mHH JZ Pid5Zero MOV R0, IntLL MOV R1, IntLH MOV R2, IntHL MOV R3, IntHH AJMP Pid5End Pid5Zero: MOV A, #0 MOV R0, A MOV R1, A MOV R2, A MOV R3, A MOV IntLL, A MOV IntLH, A MOV IntHL, A MOV IntHH, A AJMP Pid5End Pid5Calc: ; R7:R6:R5:R4[:R3] = MULUH(Int*Ti_m) // R3      MOV R2, #0 ;; R7:R6 = IntHH*Ti_mHH MOV A, IntHH MOV B, Ti_mHH MUL AB MOV R7, B MOV R6, A ; R6:R5 += IntHL*Ti_mHH MOV A, IntHL MOV B, Ti_mHH MUL AB MOV R5, A MOV A, R6 ADD A, B MOV R6, A MOV A, R2 ; A=0 ADDC A, R7 MOV R7, A ; R5:R4 += IntLH*Ti_mHH MOV A, IntLH MOV B, Ti_mHH MUL AB MOV R4, A MOV A, R5 ADD A, B MOV R5, A MOV A, R2 ; A=0 ADDC A, R6 MOV R6, A MOV A, R2 ; A=0 ADDC A, R7 MOV R7, A ; R4:R3 += IntLL*Ti_mHH MOV A, IntLL MOV B, Ti_mHH MUL AB MOV R3, A MOV A, R4 ADD A, B MOV R4, A MOV A, R2 ; A=0 ADDC A, R5 MOV R5, A MOV A, R2 ; A=0 ADDC A, R6 MOV R6, A MOV A, R2 ; A=0 ADDC A, R7 MOV R7, A ;; R6:R5 += IntHH*Ti_mHL MOV A, IntHH MOV B, Ti_mHL MUL AB ADD A, R5 MOV R5, A MOV A, R6 ADDC A, B MOV R6, A MOV A, R2 ; A=0 ADDC A, R7 MOV R7, A ; R5:R4 += IntHL*Ti_mHL MOV A, IntHL MOV B, Ti_mHL MUL AB ADD A, R4 MOV R4, A MOV A, R5 ADDC A, B MOV R5, A MOV A, R2 ; A=0 ADDC A, R6 MOV R6, A MOV A, R2 ; A=0 ADDC A, R7 MOV R7, A ; R4:R3 += IntLH*Ti_mHL MOV A, IntLH MOV B, Ti_mHL MUL AB MOV A, R3 MOV R3, A MOV A, R4 ADDC A, B MOV R4, A MOV A, R2 ; A=0 ADDC A, R5 MOV R5, A MOV A, R2 ; A=0 ADDC A, R6 MOV R6, A MOV A, R2 ; A=0 ADDC A, R7 MOV R7, A ;; R5:R4 += IntHH*Ti_mLH MOV A, IntHH MOV B, Ti_mLH MUL AB ADD A, R4 MOV R4, A MOV A, R5 ADDC A, B MOV R5, A MOV A, R2 ; A=0 ADDC A, R6 MOV R6, A MOV A, R2 ; A=0 ADDC A, R7 MOV R7, A ; R4:R3 += IntHL*Ti_mLH MOV A, IntHL MOV B, Ti_mLH MUL AB ADD A, R3 MOV R3, A MOV A, R4 ADDC A, B MOV R4, A MOV A, R2 ; A=0 ADDC A, R5 MOV R5, A MOV A, R2 ; A=0 ADDC A, R6 MOV R6, A MOV A, R2 ; A=0 ADDC A, R7 MOV R7, A ;; R4:R3 += IntHH*Ti_mLL MOV A, IntHH MOV B, Ti_mLL MUL AB ADD A, R3 MOV R3, A MOV A, R4 ADDC A, B MOV R4, A MOV A, R2 ; A=0 ADDC A, R5 MOV R5, A MOV A, R2 ; A=0 ADDC A, R6 MOV R6, A MOV A, R2 ; A=0 ADDC A, R7 MOV R7, A ;;;  R3 > 7F -- MOV A, R3 JNB ACC.7, Pid5Shift ;  R3<80 --    ANL A, #7Fh JZ Pid5Round ;  = 80 --    MOV A, #1 ADD A, R4 MOV R4, A MOV A, R2 ; A=0 ADDC A, R5 MOV R5, A MOV A, R2 ; A=0 ADDC A, R6 MOV R6, A MOV A, R2 ; A=0 ADDC A, R7 MOV R7, A SJMP Pid5Shift Pid5Round: MOV A, R4 ORL A, #01h MOV R4, A ;JMP Pid5Shift Pid5Shift: ; R3:R2:R1:R0 = (Int-R7:R6:R5:R4) >> 1 CLR C MOV A, IntLL SUBB A, R4 MOV R0, A MOV A, IntLH SUBB A, R5 MOV R1, A MOV A, IntHL SUBB A, R6 MOV R2, A MOV A, IntHH SUBB A, R7 RRC A ; >>1    MOV R3, A MOV A, R2 RRC A MOV R2, A MOV A, R1 RRC A MOV R1, A MOV A, R0 RRC A ;MOV R0, A ; R3:R2:R1:R0 += R7:R6:R5:R4 ;MOV A, R0 ADD A, R4 MOV R0, A MOV A, R1 ADDC A, R5 MOV R1, A MOV A, R2 ADDC A, R6 MOV R2, A MOV A, R3 ADDC A, R7 MOV R7, A ;     sh2. ; sh2    16 (    Ti 16;     16 ) MOV A, Ti_sh2 JNB ACC.4, Pid5ShiftUnder16 ;   >=16 -- 2   mov' MOV R0, 18h+2; R2, bank 3 MOV R1, 18h+3; R3, bank 3 MOV R2, #0 MOV R3, #0 Pid5ShiftUnder16: JNB ACC.3, Pid5ShiftUnder8 ;    >=8 -- 1   mov' MOV R0, 18h+1; R1, bank 3 MOV R1, 18h+2; R2, bank 3 MOV R2, 18h+3; R3, bank 3 MOV R3, #0 Pid5ShiftUnder8: ANL A, #07h JZ Pid5End ;       --  MOV R4, A SJMP Pid5ShiftRight Pid5NextShift: CLR C ;    C      ! Pid5ShiftRight: MOV A, R3 RRC A MOV R3, A MOV A, R2 RRC A MOV R2, A MOV A, R1 RRC A MOV R1, A MOV A, R0 RRC A MOV R0, A DJNZ R4, Pid5NextShift ; ,      ;  ,     R3:R2:R1:R0 ;   ,     IntS Pid5End: ; 4. PID += [ cD = Td * (E-Eo) ] | 16*16=>32bit Pid4: ; cD = R7:R6:R5:R4; ErrD = E-Eo CLR C MOV A, ErrL SUBB A, Err0L MOV DiffL, A MOV A, ErrH SUBB A, Err0H MOV DiffH, A MOV C, ACC.7 ;    MOV DiffS, C ;   E-Eo JNC Pid4Mul ; Diff -- ,   MOV A, DiffL CPL A ADD A, #1 MOV DiffL, A MOV A, DiffH CPL A ADDC A, #0 MOV DiffH, A Pid4Mul: ; R7:R6 = DiffH*TdH ; MOV A, DiffH =    A=DiffH MOV B, TdH MUL AB MOV R6, A MOV R7, B ; R5:R4 = DiffL*TdL MOV A, DiffL MOV B, TdL MUL AB MOV R4, A MOV R5, B ; R6:R5 += DiffH*TdL MOV A, DiffH MOV B, TdL MUL AB ADD A, R5 MOV R5, A MOV A, R6 ADD A, B MOV R6, A MOV A, R7 ADDC A, #0 MOV R7, A ; R6:R5 += DiffL*TdH MOV A, DiffL MOV B, TdH MUL AB ADD A, R5 MOV R5, A MOV A, R6 ADD A, B MOV R6, A MOV A, R7 ADDC A, #0 MOV R7, A ; 6. PID = E + cI + cD | 32bit Pid6: ; R3:R2:R1:R0  cI,   IntS; ; R7:R6:R5:R4 = cD;   DiffS ; E     JB IntS, ChkDiffN JNB DiffS, Pid6Add ; Int>0, Diff>0 => Add SJMP Pid6Sub ; Int>0, Diff<0 => Sub ChkDiffN: JNB DiffS, Pid6Sub ; Int<0, Diff>0 => Sub ; Int<0, Diff<0 => Add Pid6Add: ;   =>       MOV A, R0 ADD A, R4 MOV R0, A MOV A, R1 ADDC A, R5 MOV R1, A MOV A, R2 ADDC A, R6 MOV R2, A MOV A, R3 ADDC A, R7 MOV R3, A JNC Pid6Err ;    -      MOV R3, #0FFh MOV R2, #0FFh MOV R1, #0FFh MOV R0, #0FFh SJMP Pid6Err Pid6Sub: ;   --         CLR C MOV A, R4 SUBB A, R0 MOV R0, A MOV A, R5 SUBB A, R1 MOV R1, A MOV A, R6 SUBB A, R2 MOV R2, A MOV A, R7 SUBB A, R3 MOV R3, A JNC Pid6Err ;    --     DiffS CPL DiffS ;   ,  DiffS      MOV R6, #0 ; R6=0 MOV A, R0 CPL A ADDC A, R6 ; R6=0, C=1 =>  +1 MOV R0, A MOV A, R1 CPL A ADDC A, R6 ; + MOV R1, A MOV A, R2 CPL A ADDC A, R6 MOV R2, A MOV A, R3 CPL A ADDC A, R6 MOV R3, A Pid6Err: MOV R6, #0 ; R6=0 ;  R3:R2:R1:R0 --  cI+cD;    DiffS ;  / Err,     ;   Err  DiffS MOV R4, ErrL MOV A, ErrH JB ACC.7, Pid6ChkDiffS JNB DiffS, Pid6SumErrNoInv ; Err>0, Diff>0 => NoInv SJMP Pid6SumErrInv Pid6ChkDiffS: JNB DiffS, Pid6SumErrNoInv ; Err<0, Diff>0 => NoInv Pid6SumErrInv: ;  Err    DiffS --  SETB C ;     C MOV A, ErrL CPL A ADDC A, R6 ; A+=R6+C, R6=0 C=1 => A+=1 MOV R4, A ; R4=ErrL MOV A, ErrH CPL A ADDC A, R6 Pid6SumErrNoInv: MOV R5, A ; ErrH Pid6SumErr: ; ,  R5:R4  Err,     DiffS;   -  MOV A, R0 ADD A, R4 MOV R0, A MOV A, R5 CLR F0 JNB ACC.7, Pid6SubErrPos SETB F0 MOV R6, #0FFh ;   =>  FF Pid6SubErrPos: ADDC A, R1 MOV R1, A MOV A, R2 ADDC A, R6 ; + MOV R2, A MOV A, R3 ADDC A, R6 ; + MOV R3, A MOV R6, #0 ;         JNC Pid6ChkF0 JB F0, Pid7 ; Err<0,   =>   ,   SJMP Pid6SumOv ; Err>0,   =>  Pid6ChkF0: JNB F0, Pid7 ; Err>0,   =>   ;SJMP Pid6SumUf ; Err<0,   =>   Pid6SumUf: ;  Err<0    =>   CPL DiffS MOV A, R0 CPL A ADD A, #1 ; C=?,   1   MOV R0, A MOV A, R1 CPL A ADDC A, R6 MOV R1, A MOV A, R2 CPL A ADDC A, R6 MOV R2, A MOV A, R3 CPL A ADDC A, R6 MOV R3, A SJMP Pid7 ;     DiffS    Pid6SumOv: ;   =>    MOV R0, #0FFh MOV R1, #0FFh MOV R2, #0FFh MOV R3, #0FFh ; 7. U = K*PID/256 | 32bit*16bit/8bit => 40bit, ; |    10bit ; |   Pid7: ;  R3:R2:R1:R0   PID,  DiffS   ;   K*PID/256,    10 ; K  ,   PID < 0 =>  JB DiffS, Pid7Umin ;       16 , ;   R3 != 0 =>      MOV A, R3 JNZ Pid7Umax ; [R2:R1:R0 * KH:HL] = [R7:R6:R5:R4:R3] ; ,     R7=0 R6=0, ;  ,  R7  R6    ;    ; R7:R6 = R2*KH MOV A, R2 JZ Pid7S1 MOV A, KH JNZ Pid7Umax ;  R2!=0  KH!=0 => R7:R6>0 =>  Pid7S1: ; R6:R5 = R2*KL MOV A, R2 MOV B, KL MUL AB MOV R5, A MOV A, B JNZ Pid7Umax ;  R6 > 0 =>  ; R6:R5 = R1*KH MOV A, R1 MOV B, KH MUL AB ADD A, R5 JC Pid7Umax ;  R6 > 0 =>  MOV R5, A MOV A, B JNZ Pid7Umax ;  R6 > 0 =>  ; R5:R4 = R0*KH MOV A, R0 MOV B, KH MUL AB MOV R4, A MOV A, R5 ADD A, B JC Pid7Umax ;  R6 > 0 =>  MOV R5, A ; R5:R4 = R1*KL MOV A, R1 MOV B, KL MUL AB ADD A, R4 MOV R4, A MOV A, R5 ADDC A, B JC Pid7Umax ;  R6 > 0 =>  MOV R5, A ; R4:R3 = R0*KL MOV A, R0 MOV B, KL MUL AB RLC A ; C = R3>=0x80, Z=R3>0x80 MOV R3, #0FFh ; R3<>0x80 =>  JNZ Pid7S2 MOV R3, #0FEh ; R3==0x80 =>    Pid7S2: MOV A, R4 ADDC A, B ;  , ,  - ANL A, R3 ;        --     MOV R4, A MOV A, R5 ADDC A, R6 ; R6=0     ,           JC Pid7Umax ;  R6 > 0 =>  MOV R5, A ; R5:R4 =>   16   ;    R5:R4  Umax/Umin MOV A, UmaxL SUBB A, R4 ; C=0    MOV A, UmaxH SUBB A, R5 JC Pid7Umax ;  R5:R4>Umax => R5:R4 = Umax MOV A, UminL SUBB A, R4 ; C=0    MOV A, UminH SUBB A, R5 JNC Pid7Umin ;  R5:R4<Umin => R5:R4 = Umin ;   MOV UH, R5 MOV UL, R4 SETB UReady AJMP CalcExit Pid7Umax: ;    MOV UH, UmaxH MOV UL, UmaxL SETB UReady AJMP CalcExit Pid7Umin: ;    MOV UH, UminH MOV UL, UminL SETB UReady AJMP CalcExit 


Ix. Application impact.


So, we have a calculated impact, and our task is to apply it. To do this, the total cycle works with a frequency of 50 Hz. On the even cycle - measurement and calculation is made, on the odd - application of the impact. Thus, the general scheme is: power is set, measurement and calculation are performed through one sine wave, and one through the use of a new one.

X. Underwater stones.


Compared to the difference scheme, there are very few pitfalls in the direct scheme, here is a list of those I have seen:


Xi. Findings.


As a result, the direct implementation scheme does not have the problems that the difference scheme has , but it requires more computational costs. However, with proper implementation, the direct scheme is quite applicable even on cheap 8-bit microcontrollers, and gives more predictable results.

Source: https://habr.com/ru/post/145991/


All Articles