1 \ PID controller written in Forth
2 \ Based on the code presented here:
3 \ http://brettbeauregard.com/blog/2011/04/improving-the-beginners-pid-introduction/
5 \ MSP_EXP430FR5739 MSP_EXP430FR5969 MSP_EXP430FR5994 MSP_EXP430FR6989
6 \ MSP_EXP430FR4133 MSP_EXP430FR2433 MSP_EXP430FR2355 CHIPSTICK_FR2433
10 [UNDEFINED] VARIABLE [IF]
11 \ https://forth-standard.org/standard/core/VARIABLE
12 \ VARIABLE <name> -- define a Forth VARIABLE
17 MOV #DOVAR,-4(W) \ CFA = DOVAR
22 [UNDEFINED] CONSTANT [IF]
23 \ https://forth-standard.org/standard/core/CONSTANT
24 \ CONSTANT <name> n -- define a Forth CONSTANT
28 MOV TOS,-2(W) \ PFA = n
35 [UNDEFINED] STATE [IF]
36 \ https://forth-standard.org/standard/core/STATE
37 \ STATE -- a-addr holds compiler state
38 STATEADR CONSTANT STATE
42 \ https://forth-standard.org/standard/core/ROT
43 \ ROT x1 x2 x3 -- x2 x3 x1
45 MOV @PSP,W \ 2 fetch x2
46 MOV TOS,0(PSP) \ 3 store x3
47 MOV 2(PSP),TOS \ 3 fetch x1
48 MOV W,2(PSP) \ 3 store x2
54 \ https://forth-standard.org/standard/core/SWAP
55 \ SWAP x1 x2 -- x2 x1 swap top two items
65 \ https://forth-standard.org/standard/core/DUP
66 \ DUP x -- x x duplicate top of stack
68 BW1 SUB #2,PSP \ 2 push old TOS..
69 MOV TOS,0(PSP) \ 3 ..onto stack
73 \ https://forth-standard.org/standard/core/qDUP
74 \ ?DUP x -- 0 | x x DUP if nonzero
76 CMP #0,TOS \ 2 test for TOS nonzero
83 \ https://forth-standard.org/standard/core/AND
84 \ C AND x1 x2 -- x3 logical AND
91 [UNDEFINED] SPACE [IF]
92 \ https://forth-standard.org/standard/core/SPACE
93 \ SPACE -- output a space
99 \ https://forth-standard.org/standard/core/Rfrom
100 \ R> -- x R: x -- pop from return stack ; CALL #RFROM performs DOVAR
107 \ https://forth-standard.org/standard/core/Fetch
108 \ @ c-addr -- char fetch char from memory
116 \ https://forth-standard.org/standard/core/Store
117 \ ! x a-addr -- store cell in memory
126 \ https://forth-standard.org/standard/core/CFetch
127 \ C@ c-addr -- char fetch char from memory
135 \ https://forth-standard.org/standard/core/OnePlus
136 \ 1+ n1/u1 -- n2/u2 add 1 to TOS
144 \ https://forth-standard.org/standard/core/Plus
145 \ + n1/u1 n2/u2 -- n3/u3 add n1+n2
153 \ https://forth-standard.org/standard/core/Minus
154 \ - n1/u1 n2/u2 -- n3/u3 n3 = n1-n2
156 SUB @PSP+,TOS \ 2 -- n2-n1 ( = -n3)
158 ADD #1,TOS \ 1 -- n3 = -(n2-n1) = n1-n2
164 \ https://forth-standard.org/standard/core/MAX
165 \ MAX n1 n2 -- n3 signed maximum
173 \ https://forth-standard.org/standard/core/MIN
174 \ MIN n1 n2 -- n3 signed minimum
184 [UNDEFINED] 2NIP [IF]
194 [UNDEFINED] 2DUP [IF]
195 \ https://forth-standard.org/standard/core/TwoDUP
196 \ 2DUP x1 x2 -- x1 x2 x1 x2 dup top 2 cells
198 SUB #4,PSP \ -- x1 x x x2
199 MOV TOS,2(PSP) \ -- x1 x2 x x2
200 MOV 4(PSP),0(PSP) \ -- x1 x2 x1 x2
205 [UNDEFINED] 2SWAP [IF]
206 \ https://forth-standard.org/standard/core/TwoSWAP
207 \ 2SWAP x1 x2 x3 x4 -- x3 x4 x1 x2
209 MOV @PSP,W \ -- x1 x2 x3 x4 W=x3
210 MOV 4(PSP),0(PSP) \ -- x1 x2 x1 x4
211 MOV W,4(PSP) \ -- x3 x2 x1 x4
212 MOV TOS,W \ -- x3 x2 x1 x4 W=x4
213 MOV 2(PSP),TOS \ -- x3 x2 x1 x2 W=x4
214 MOV W,2(PSP) \ -- x3 x4 x1 x2
219 [UNDEFINED] 2ROT [IF]
220 \ https://forth-standard.org/standard/double/TwoROT
221 \ Rotate the top three cell pairs on the stack bringing cell pair x1 x2 to the top of the stack.
225 MOV 4(PSP),8(PSP) \ 5
226 MOV 2(PSP),6(PSP) \ 5
235 [UNDEFINED] 2DROP [IF]
236 \ https://forth-standard.org/standard/core/TwoDROP
237 \ 2DROP x1 x2 -- drop 2 cells
245 [UNDEFINED] 2OVER [IF]
246 \ https://forth-standard.org/standard/core/TwoOVER
247 \ 2OVER x1 x2 x3 x4 -- x1 x2 x3 x4 x1 x2
249 SUB #4,PSP \ -- x1 x2 x3 x x x4
250 MOV TOS,2(PSP) \ -- x1 x2 x3 x4 x x4
251 MOV 8(PSP),0(PSP) \ -- x1 x2 x3 x4 x1 x4
252 MOV 6(PSP),TOS \ -- x1 x2 x3 x4 x1 x2
257 [UNDEFINED] DABS [IF]
258 \ https://forth-standard.org/standard/double/DABS
259 \ DABS d1 -- |d1| absolute value
261 AND #-1,TOS \ clear V, set N
262 U< IF \ if positive (N=0)
273 \ https://forth-standard.org/standard/core/TwoFetch
274 \ 2@ a-addr -- x1 x2 fetch 2 cells ; the lower address will appear on top of stack
284 \ https://forth-standard.org/standard/core/TwoStore
285 \ x1 x2 addr -- Store the cell pair x1 x2 at a-addr, with x2 at a-addr and x1 at the next consecutive cell.
294 \ https://forth-standard.org/standard/core/TwotoR
295 \ ( x1 x2 -- ) ( R: -- x1 x2 ) Transfer cell pair x1 x2 to the return stack.
303 \ https://forth-standard.org/standard/core/TwoRFetch
304 \ ( -- x1 x2 ) ( R: x1 x2 -- x1 x2 ) Copy cell pair x1 x2 from the return stack.
313 \ https://forth-standard.org/standard/core/TwoRfrom
314 \ ( -- x1 x2 ) ( R: x1 x2 -- ) Transfer cell pair x1 x2 from the return stack
323 [UNDEFINED] 2VARIABLE [IF]
324 \ https://forth-standard.org/standard/double/TwoVARIABLE
330 [UNDEFINED] 2CONSTANT [IF] \ defined if MEM_EXT
331 \ https://forth-standard.org/standard/double/TwoCONSTANT
332 : 2CONSTANT \ udlo/dlo/Qlo udhi/dhi/Qhi -- to create double or Q15.16 CONSTANT
333 CREATE , , \ compile Qhi then Qlo
334 DOES> 2@ \ execution part addr -- Qhi Qlo
339 \ https://forth-standard.org/standard/core/ne
340 \ = ( x1 x2 -- flag ) flag is true if and only if x1 is not bit-for-bit the same as x2
351 \ https://forth-standard.org/standard/core/Equal
352 \ = x1 x2 -- flag test x1=x2
359 XOR #-1,TOS \ 1 flag Z = 1
364 \ https://forth-standard.org/standard/core/Uless
365 \ U< u1 u2 -- flag test u1<u2, unsigned
368 SUB @PSP+,TOS \ 2 u2-u1
372 AND #0,TOS \ 1 flag Z = 1
379 \ ------------------------------------------------------------------------------
381 \ ------------------------------------------------------------------------------
382 \ THEN and BEGIN compile nothing
383 \ DO compile one word
384 \ IF, ELSE, AGAIN, UNTIL, WHILE, REPEAT, LOOP & +LOOP compile two words
385 \ LEAVE compile three words
388 \ https://forth-standard.org/standard/core/IF
389 \ IF -- IFadr initialize conditional forward branch
393 MOV &DP,TOS \ -- HERE
394 ADD #4,&DP \ compile one word, reserve one word
395 MOV #QFBRAN,0(TOS) \ -- HERE compile QFBRAN
396 ADD #2,TOS \ -- HERE+2=IFadr
401 [UNDEFINED] THEN [IF]
402 \ https://forth-standard.org/standard/core/THEN
403 \ THEN IFadr -- resolve forward branch
404 CODE THEN \ immediate
405 MOV &DP,0(TOS) \ -- IFadr
411 [UNDEFINED] ELSE [IF]
412 \ https://forth-standard.org/standard/core/ELSE
413 \ ELSE IFadr -- ELSEadr resolve forward IF branch, leave ELSEadr on stack
414 CODE ELSE \ immediate
415 ADD #4,&DP \ make room to compile two words
418 MOV W,0(TOS) \ HERE+4 ==> [IFadr]
420 MOV W,TOS \ -- ELSEadr
425 [UNDEFINED] DEFER! [IF]
426 \ https://forth-standard.org/standard/core/DEFERStore
427 \ Set the word xt1 to execute xt2. An ambiguous condition exists if xt1 is not for a word defined by DEFER.
428 CODE DEFER! \ xt2 xt1 --
429 MOV @PSP+,2(TOS) \ -- xt1=CFA_DEFER xt2 --> [CFA_DEFER+2]
436 \ https://forth-standard.org/standard/core/IS
439 \ DEFER DISPLAY create a "do nothing" definition (2 CELLS)
440 \ inline command : ' U. IS DISPLAY U. becomes the runtime of the word DISPLAY
441 \ or in a definition : ... ['] U. IS DISPLAY ...
442 \ KEY, EMIT, CR, ACCEPT and WARM are examples of DEFERred words
444 \ as IS replaces the PFA value of any word, it's a TO alias for VARIABLE and CONSTANT words...
447 IF POSTPONE ['] POSTPONE DEFER!
453 [UNDEFINED] >BODY [IF]
454 \ https://forth-standard.org/standard/core/toBODY
455 \ >BODY -- addr leave BODY of a CREATEd word\ also leave default ACTION-OF primary DEFERred word
462 \ =============================================================================
477 \ https://forth-standard.org/standard/core/HOLDS
478 \ Adds the string represented by addr u to the pictured numeric output string
479 \ compilation use: <# S" string" HOLDS #>
480 \ free chars area in the 32+2 bytes HOLD buffer = {26,23,2} chars with a 32 bits sized {hexa,decimal,binary} number.
481 \ (2 supplementary bytes are room for sign - and decimal point)
487 BEGIN SUB #1,X \ 1 src-1
489 U>= WHILE SUB #1,Y \ 1 dst-1
493 MOV @IP+,PC \ 4 15 words
496 TLV_ORG 4 + @ $81F3 U<
497 $81EF TLV_ORG 4 + @ U<
498 = [IF] ; MSP430FR2xxx|MSP430FR4xxx subfamilies without hardware_MPY
501 CODE F/ \ Q15.16 / Q15.16 --> Q15.16 result
504 MOV @PSP+,X \ DVDhi --> REMlo
506 MOV @PSP,Y \ DVDlo --> DVDhi
509 XOR TOS,S \ DVDhi XOR DVRhi --> S keep sign of result
510 AND #-1,X \ DVD < 0 ?
511 S< IF XOR #-1,Y \ INV(DVDlo)
512 XOR #-1,X \ INV(DVDhi)
513 ADD #1,Y \ INV(DVDlo)+1
514 ADDC #0,X \ INV(DVDhi)+C
515 THEN AND #-1,TOS \ DVR < 0 ?
516 S< IF XOR #-1,R6 \ INV(DVRlo)
517 XOR #-1,TOS \ INV(DVRhi)
518 ADD #1,R6 \ INV(DVRlo)+1
519 ADDC #0,TOS \ INV(DVRhi)+C
521 \ don't uncomment lines below !
522 \ ------------------------------------------------------------------------
523 \ UD/MOD DVDlo DVDhi DVRlo DVRhi -- REMlo REMhi QUOTlo QUOThi
524 \ ------------------------------------------------------------------------
525 \ MOV 4(PSP),T \ DVDlo
526 \ MOV 2(PSP),Y \ DVDhi
527 \ MOV #0,X \ REMlo = 0
528 \ MOV #0,W \ REMhi = 0
529 MOV #32,R5 \ init loop count
530 BW1 CMP TOS,W \ 1 REMhi = DVRhi ?
531 0= IF CMP R6,X \ 1 REMlo U< DVRlo ?
533 U>= IF SUB R6,X \ 1 no: REMlo - DVRlo (carry is set)
534 SUBC TOS,W \ 1 REMhi - DVRhi
536 BW2 ADDC R7,R7 \ 1 RLC quotLO
537 ADDC R4,R4 \ 1 RLC quotHI
538 SUB #1,R5 \ 1 Decrement loop counter
539 0< ?GOTO FW1 \ 2 out of loop if count<0
540 ADD T,T \ 1 RLA DVDlo
541 ADDC Y,Y \ 1 RLC DVDhi
542 ADDC X,X \ 1 RLC REMlo
543 ADDC W,W \ 1 RLC REMhi
544 U< ?GOTO BW1 \ 2 15~ loop
545 SUB R6,X \ 1 REMlo - DVRlo
546 SUBC TOS,W \ 1 REMhi - DVRhi
548 GOTO BW2 \ 2 16~ loop
550 \ MOV X,4(PSP) \ REMlo
551 \ MOV W,2(PSP) \ REMhi
552 \ ADD #4,PSP \ skip REMlo REMhi
553 MOV R7,0(PSP) \ QUOTlo
555 POPM #4,R7 \ restore R4 to R7
556 \ MOV @IP+,PC \ end of UD/MOD
557 \ ------------------------------------------------------------------------
558 BW1 AND #-1,S \ clear V, set N; QUOT < 0 ?
559 S< IF XOR #-1,0(PSP) \ INV(QUOTlo)
560 XOR #-1,TOS \ INV(QUOThi)
561 ADD #1,0(PSP) \ INV(QUOTlo)+1
562 ADDC #0,TOS \ INV(QUOThi)+C
566 \ F#S Qlo Qhi u -- Qhi 0 convert fractional part Qlo of Q15.16 fixed point number
569 MOV 2(PSP),X \ -- Qlo Qhi u X = Qlo
570 MOV @PSP,2(PSP) \ -- Qhi Qhi u
571 MOV X,0(PSP) \ -- Qhi Qlo u
572 PUSHM #2,TOS \ save TOS,IP
573 MOV #0,S \ -- Qhi Qlo x
574 BEGIN PUSH S \ R-- limit IP count
575 MOV &BASEADR,TOS \ -- Qhi Qlo base
577 UM* \ u1 u2 -- RESlo REShi
578 HI2LO \ -- Qhi RESlo digit
580 CMP #10,TOS \ digit to char
583 MOV @RSP+,S \ R-- limit IP
584 MOV.B TOS,HOLDS_ORG(S) \ -- Qhi RESlo char char to string
586 CMP 2(RSP),S \ count=limit ?
588 POPM #2,TOS \ restore IP,TOS
589 MOV #0,0(PSP) \ -- Qhi 0 len
590 SUB #2,PSP \ -- Qhi 0 x len
591 MOV #HOLDS_ORG,0(PSP) \ -- Qhi 0 addr len
592 GOTO BW3 \ jump HOLDS
595 \ unsigned multiply 32*32 = 64
596 \ don't use S reg (keep sign)
599 PUSHM #4,R7 \ 6 save R7 ~ R4 regs
600 MOV 4(PSP),IP \ 3 MDlo
601 MOV 2(PSP),T \ 3 MDhi
605 MOV #0,4(PSP) \ 3 RESlo=0
606 MOV #0,2(PSP) \ 3 REShi=0
607 MOV #0,R6 \ 1 RESLO=0
608 MOV #0,R7 \ 1 RESHI=0
609 MOV #1,X \ 1 BIT TEST REGlo
610 MOV #0,Y \ 1 BIT TEST2 REGhi
612 0<> IF BIT X,W \ 2+1 TEST ACTUAL BIT MRlo
613 ELSE BIT Y,TOS \ 2+1 TEST ACTUAL BIT MRhi
615 0<> IF ADD IP,4(PSP) \ 2+3 IF 1: ADD MDlo TO RESlo
616 ADDC T,2(PSP) \ 3 ADDC MDhi TO REShi
617 ADDC R4,R6 \ 1 ADDC MDLO TO RESLO
618 ADDC R5,R7 \ 1 ADDC MDHI TO RESHI
619 THEN ADD IP,IP \ 1 (RLA LSBs) MDlo *2
620 ADDC T,T \ 1 (RLC MSBs) MDhi *2
621 ADDC R4,R4 \ 1 (RLA LSBs) MDLO *2
622 ADDC R5,R5 \ 1 (RLC MSBs) MDHI *2
623 ADD X,X \ 1 (RLA) NEXT BIT TO TEST
624 ADDC Y,Y \ 1 (RLA) NEXT BIT TO TEST
625 U>= UNTIL MOV R6,0(PSP) \ 2+2 IF BIT IN CARRY: FINISHED 32 * 16~ (average loop)
626 MOV R7,TOS \ 1 high result in TOS
627 POPM #4,R7 \ 6 restore R4 to R7
632 CODE F* \ s15.16 * s15.16 --> s15.16 result
634 XOR TOS,S \ 1s15 XOR 2s15 --> S keep sign of result
635 BIT #$8000,2(PSP) \ MD < 0 ?
636 0<> IF XOR #-1,2(PSP)
642 DABS UDM* \ -- RES0 RES1 RES2 RES3
645 MOV @PSP+,TOS \ -- RES0 RES1 RES2
646 MOV @PSP+,0(PSP) \ -- RES1 RES2
647 GOTO BW1 \ goto end of F/ to process sign of result
650 [ELSE] \ hardware multiplier
652 CODE F/ \ Q15.16 / Q15.16 --> Q15.16 result
657 PUSHM #4,R7 \ 6 PUSHM R7 to R4
658 MOV @PSP+,R6 \ 2 DVRlo
659 MOV @PSP+,X \ 2 DVDhi --> REMlo
660 MOV #0,W \ 1 REMhi = 0
661 MOV @PSP,Y \ 2 DVDlo --> DVDhi
662 MOV #0,T \ 1 DVDlo = 0
664 XOR TOS,S \ 1 DVDhi XOR DVRhi --> S keep sign of result
665 AND #-1,X \ 1 DVD < 0 ?
666 S< IF XOR #-1,Y \ 1 INV(DVDlo)
667 XOR #-1,X \ 1 INV(DVDhi)
668 ADD #1,Y \ 1 INV(DVDlo)+1
669 ADDC #0,X \ 1 INV(DVDhi)+C
670 THEN AND #-1,TOS \ 1 DVR < 0 ?
671 S< IF XOR #-1,R6 \ 1 INV(DVRlo)
672 XOR #-1,TOS \ 1 INV(DVRhi)
673 ADD #1,R6 \ 1 INV(DVRlo)+1
674 ADDC #0,TOS \ 1 INV(DVRhi)+C
675 THEN MOV #32,R5 \ 2 init loop count
676 BW1 CMP TOS,W \ 1 REMhi = DVRhi ?
678 CMP R6,X \ 1 REMlo U< DVRlo ?
681 SUB R6,X \ 1 no: REMlo - DVRlo (carry is set)
682 SUBC TOS,W \ 1 REMhi - DVRhi
684 BW2 ADDC R7,R7 \ 1 RLC quotLO
685 ADDC R4,R4 \ 1 RLC quotHI
686 SUB #1,R5 \ 1 Decrement loop counter
687 0< ?GOTO FW1 \ 2 out of loop if count<0
688 ADD T,T \ 1 RLA DVDlo
689 ADDC Y,Y \ 1 RLC DVDhi
690 ADDC X,X \ 1 RLC REMlo
691 ADDC W,W \ 1 RLC REMhi
692 U< ?GOTO BW1 \ 2 19~ loop
693 SUB R6,X \ 1 REMlo - DVRlo
694 SUBC TOS,W \ 1 REMhi - DVRhi
696 GOTO BW2 \ 2 16~ loop
697 FW1 AND #-1,S \ 1 clear V, set N; QUOT < 0 ?
698 S< IF XOR #-1,R7 \ 1 INV(QUOTlo)
699 XOR #-1,R4 \ 1 INV(QUOThi)
700 ADD #1,R7 \ 1 INV(QUOTlo)+1
701 ADDC #0,R4 \ 1 INV(QUOThi)+C
702 THEN MOV R7,0(PSP) \ 3 QUOTlo
703 MOV R4,TOS \ 1 QUOThi
704 POPM #4,R7 \ 6 restore R4 to R7
708 \ F#S Qlo Qhi u -- Qhi 0 convert fractionnal part of Q15.16 fixed point number
711 MOV 2(PSP),X \ -- Qlo Qhi u X = Qlo
712 MOV @PSP,2(PSP) \ -- Qhi Qhi u
713 MOV X,0(PSP) \ -- Qhi Qlo u
714 MOV TOS,T \ T = limit
716 BEGIN MOV @PSP,&MPY \ Load 1st operand
717 MOV &BASEADR,&OP2 \ Load 2nd operand
718 MOV &RES0,0(PSP) \ -- Qhi RESlo x low result on stack
719 MOV &RES1,TOS \ -- Qhi RESlo REShi high result in TOS
720 CMP #10,TOS \ digit to char
723 MOV.B TOS,HOLDS_ORG(S) \ -- Qhi RESlo char char to string
725 CMP T,S \ count=limit ?
726 0= UNTIL MOV #0,0(PSP) \ -- Qhi 0 REShi
727 MOV T,TOS \ -- Qhi 0 limit
728 SUB #2,PSP \ -- Qhi 0 x len
729 MOV #HOLDS_ORG,0(PSP) \ -- Qhi 0 addr len
730 GOTO BW3 \ jump HOLDS
733 CODE F* \ signed s15.16 multiplication --> s15.16 result
734 MOV 4(PSP),&MPYS32L \ 5 Load 1st operand
735 MOV 2(PSP),&MPYS32H \ 5
736 MOV @PSP,&OP2L \ 4 load 2nd operand
738 ADD #4,PSP \ 1 remove 2 cells
740 \ NOP2 \ 2 wait 8 cycles after write OP2L before reading RES1
746 [THEN] \ hardware multiplier
748 CODE F.N \ ( f n -- ) display a Q15.16 number with n digits after comma
749 MOV TOS,T \ T = #digits
752 PUSHM #3,IP \ R-- IP sign #digit
754 <# DABS \ -- uQlo uQhi R-- IP sign #digit
755 R> F#S \ -- uQhi 0 R-- IP sign
756 $2C HOLD \ $2C = char ','
758 R> SIGN #> \ -- addr len R-- IP
763 \ https://forth-standard.org/standard/double/Dless
764 \ flag is true if and only if d1 is less than d2
769 BW1 CMP TOS,T \ 1 d1H - d2H
770 MOV #0,TOS \ 1 -- false_flag by default
771 S< IF MOV #-1,TOS \ 2 -- true_flag if d1H < d2H
773 0= IF CMP S,W \ 1 -- false_flag d1L - d2L
774 S< IF MOV #-1,TOS \ 1 -- true_flag if (d1H = d2H) & (d1L < d2L)
784 MOV @PSP+,TOS \ TOS=d1H
789 CODE S2F \ ( s -- f ) Signed number to fixed point
795 : F2S \ ( f -- s ) Fixed point to signed number (rounded)
796 SWAP $8000 AND IF 1 + THEN ;
798 : DMIN \ ( d1 d2 -- d_min ) Minimum of double number (also for fixed-point)
800 D< IF 2DROP ELSE 2NIP THEN
803 : DMAX \ ( d1 d2 -- d_max ) Maximum of double number (also for fixed-point)
805 D> IF 2DROP ELSE 2NIP THEN
808 : DRANGE \ ( d_val d_min d_max -- d_val ) Make sure a double number is in range
812 : RANGE \ ( s_val s_min s_max -- s_val ) Make sure a number is in range
816 : F.000 3 F.N ; \ Output fixed point value
818 \ Setup variables for pid control
819 2VARIABLE KP \ Proportionnal coeff, scaled to input range.
820 2VARIABLE KI \ integral coeff, in second
821 2VARIABLE KD \ derivative coeff, in second
822 VARIABLE SETPOINT \ setpoint, same scale as input
824 VARIABLE SAMPLE_TIME \ sampling interval in ms
825 VARIABLE OUT_MAX \ output max limit (--> 20 mA)
826 VARIABLE OUT_MIN \ output min limit (--> 4 mA)
827 VARIABLE OUT-OVERRIDE \ output override (auto mode if -1)
829 \ Working variables while pid is running
830 VARIABLE SET-VAL \ current setpoint
831 VARIABLE INPUT_PREV \ last seen input
832 2VARIABLE I_SUM \ cummulative i error
834 VARIABLE DEBUG \ PID compute state
840 \ =============================================================================
841 \ Main PID - internal definitions (do not call manually)
842 \ inputs and outputs are 16 bits numbers
843 \ PID parameters and PID compute are Q15.16 numbers.
845 : CALC-P \ ( f_error -- f_correction ) Calculate proportionnal output
846 KP 2@ F* \ fetch k-value and scale error
847 ?DEBUG IF ." Pval:" 2DUP F2S .
852 : CALC-I \ ( f_error -- f_correction ) Calculate integral output
853 KI 2@ F* \ apply ki factor
854 I_SUM 2@ F+ \ sum up with running integral error
857 DRANGE \ cap inside output range
858 2DUP I_SUM 2! \ update running integral error
859 ?DEBUG IF ." Ival:" 2DUP F2S .
863 : CALC-D \ ( s_is -- f_correction ) Calculate differential output
864 \ actually use "derivative on input", not on error
865 INPUT_PREV @ - \ substract last input from current input
866 S2F KD 2@ F* \ make fixed point, fetch kd factor and multiply
867 ?DEBUG IF ." Dval:" 2DUP F2S .
871 : PID_COMPUTE \ ( s_is -- s_corr ) Do a PID calculation, return duty-cycle
872 \ CR ." SET:" SET-VAL @ . ." IS:" DUP . \ DEBUG
873 \ feed error in p and i, current setpoint in d, sum up results
874 DUP DUP SET-VAL @ SWAP - S2F \ ( s_is s_is f_error )
875 2DUP CALC-P \ ( s_is s_is f_error f_p )
876 2SWAP CALC-I F+ \ ( s_is s_is f_p+i )
877 ROT CALC-D F- \ ( s_is f_p+i+d ) \ substract! derivate on input - not error
879 F2S \ ( s_is s_corr )
880 ?DEBUG IF ." OUT:" DUP .
882 SWAP INPUT_PREV ! \ Update INPUT_PREV for next run
883 OUT_MIN @ OUT_MAX @ RANGE \ Make sure we return something inside PWM range
884 ?DEBUG IF ." PWM:" DUP .
888 \ =============================================================================
889 \ Main PID - external interface
891 : SET \ ( s -- ) Change setpoint on a running pid
894 : TUNING \ ( f_kp f_ki f_kd -- ) Change tuning-parameters on a running pid
895 \ depends on sampletime, so fetch it, move to fixed-point and change unit to seconds
896 \ store on return stack for now
897 SAMPLE_TIME @ S2F 1000,0 F/ 2>R \
899 2R@ F/ KD 2! \ translate from 1/s to the sampletime
900 2R> F* KI 2! \ translate from 1/s to the sampletime
904 \ To use in a *reverse acting system* (bigger output value **reduced**
905 \ input value make sure `kp`, `ki` and `kd` are **all** negative.
906 \ Starts pid in manual mode (no setpoint set!). Set setpoint and call auto
907 \ to start the control loop.
908 : PID-INIT \ ( f_kp f_ki f_kd s_sampletime s_outmin s_outmax -- )
913 0 OUT-OVERRIDE ! \ Make sure we're in manual mode
914 CR ." PID initialized - kp:" KP 2@ F.000 ." ki:" KI 2@ F.000 ." kd:" KD 2@ F.000
917 \ Returns calculated PID value or override value if in manual mode
918 : PID \ ( s_is -- s_corr )
919 OUT-OVERRIDE @ -1 = IF \ we're in auto-mode - do PID calculation
921 ELSE \ manual-mode! store input, return override value
922 CR ." SET:" SET-VAL @ . ." IS:" DUP .
928 : MANUAL \ ( s -- ) Override output - switches PID into *manual mode*
932 : AUTO \ ( -- ) Switch back to auto-mode after manual mode
933 OUT-OVERRIDE @ -1 <> IF \ only do something if we'r in override mode
934 \ store current output value as i to let it run smoothly
936 OUT_MIN @ OUT_MAX @ RANGE \ Make sure we return something inside PWM range
937 S2F I_SUM 2! \ init I_SUM
941 : AUTOHOLD \ ( -- ) Bring PID back to auto-mode after a manual override
942 INPUT_PREV @ SET-VAL ! \ Use last input as setpoint (no bumps!)
947 \ \ ******************************\
949 \ \ ******************************\
951 \ \ ... \ insert here your background task
954 \ CALL &RXON \ comment this line to disable TERMINAL_INPUT
956 \ \ ******************************\
957 \ \ here start all interrupts \
958 \ \ ******************************\
959 \ \ here return all interrupts \
960 \ \ ******************************\
963 \ \ ******************************\
965 \ ------------------------------\
966 CODE STOP \ stops multitasking, must to be used before downloading app
967 \ ------------------------------\
971 \ ------------------------------\
972 CODE APP_INIT \ this routine completes the init of system, i.e. FORTH + this app.
973 \ ------------------------------\
977 \ ------------------------------\
978 CODE START \ this routine replaces WARM and SLEEP default values by these of this application.
979 \ ------------------------------\
980 \ MOV #SLEEP,X \ replace default background process SLEEP
981 \ MOV #BACKGROUND,2(X) \ by RC5toLCD BACKGROUND
982 \ MOV #WARM,X \ replace default WARM
983 \ MOV #APP_INIT,2(X) \ by RC5toLCD APP_INIT
984 \ MOV X,PC \ then execute it