Realtidsprogrammering En introduktion Implementering (med exempel från PIC)
Utan timing Periodtid varierar beroende på funktionernas exekveringstid. Specificera endast maxtid ( Worst case) och eventuellt mintid ( Best case). För garanterad mintid kan en funktion utgöras av en fördröjningsloop. Realtidsprog. implementering - Anders Arvidsson 2
Utan timing while(1){ PWM = knapptest(); PORTB = BinBCD(PWM); RA2 = PWM_styr(PWM); delay(); // Evig loop // Läs och behandla indata // Presentera // Agera // Eventuell fördröjningsloop Anm: Exekvering får ej stanna i någon funktion, se nästa sida. Realtidsprog. implementering - Anders Arvidsson 3
Kodstruktur Vilken kod fungerar i realtid? while(1) { while (knapp==0) // Väntar tills knapp är tryckt continue; while(1) { if (knapp ==1 && knappold ==0) // Detekterar att knappens Knapptryckt = 1; // tillstånd har gått från 0 till 1 Led ^=1; while (knapp==1) continue; // Väntar tills knapp är släppt Knappold = knapp; if (Knapptryckt == 1) { Led ^=1; Knapptryckt = 0; Realtidsprog. implementering - Anders Arvidsson 4
Med timing start Vid hård realtid får Worst case aldrig överstiga tiden från start av timer tills timern räknat ut. Periodtiden blir då konstant. Vid mjuk realtid kan timern startas med en kortare tid nästa varv om det förra varvet tog lite för lång tid. (Ladda om timern genom subtraktion istället för att ladda med en konstant). wait Realtidsprog. implementering - Anders Arvidsson 5
Med timing while(1){ start_timer(periodtid); PWM = knapptest(); PORTB = BinBCD(PWM); RA2 = PWM_styr(PWM); // Evig loop // Starta timern // Läs och behandla indata // Presentera // Agera idle(); // Vänta på att timern räknat ut // Parallellt med det kan en // bakgrundstask utföras Anm: Funktionerna får tillsammans inte ta längre tid än periodtid Realtidsprog. implementering - Anders Arvidsson 6
Tidmätning Timer 0 Timer 8 bitar, räknar ständigt upp Kan slöas ner av prescaler max 256 ggr => upp till 65536 cykler Använder intern klocka eller extern pinne Kan fungera som räknare Realtidsprog. implementering - Anders Arvidsson 7
Timer Se option_reg (skriv bara option, i C) för konfigurering. Realtidsprog. implementering - Anders Arvidsson 8
Timer Exempelkod void init_timer(void) { PSA = 0; // Koppla prescaler till TMR0 PS0 = 0; // Prescaler satt till PS1 = 1; // dela med 128 PS2 = 1; T0CS = 0; // Väljer intern oscillator till timer void start_timer(void) { TMR0 = timervalue; T0IF = 0; // Laddar om timer // (256-timervalue)*prescaler*1us + omladdningstid void wait_timer(void) { while(!t0if) continue; // Väntar på att timer räknar ut (CPU är idle ) Realtidsprog. implementering - Anders Arvidsson 9
Med timing, funktioner med lång exekv. tid start switch() Om en funktion tar för lång tid kan den ibland delas upp i två (eller fler) delar där ena delen körs ena varvet, andra delen nästa. Denna metod används när interrupt inte bör användas men någon funktion har behöver kortare periodtid än den långa funktionen kan möta. wait Realtidsprog. implementering - Anders Arvidsson 10
Exekveringstid Kompilatorns och kodens inverkan Tänk på att kompilatorn kan generera helt olika kod vid små förändringar i syntaxen. Studera den genererade assemblerkoden och bedöm om något tar onödigt mycket kod/exekveringstid. Testa då en omskrivning. I MPLAB syns den genererade koden under View Disassembly Listing. Realtidsprog. implementering - Anders Arvidsson 11
Exekveringstid Kompilatorns och kodens inverkan ui16duty = ui16duty + ui8ipartoffs - 128; Adress Hexkod Asm 0B2 1283 BCF 0x3, 0x5 0B3 086B MOVF 0x6b, W 0B4 00FB MOVWF 0x7b 0B5 01FC CLRF 0x7c 0B6 0854 MOVF 0x54, W 0B7 07FB ADDWF 0x7b, F 0B8 1803 BTFSC 0x3, 0 0B9 0AFC INCF 0x7c, F 0BA 0855 MOVF 0x55, W 0BB 07FC ADDWF 0x7c, F 0BC 3080 MOVLW 0x80 0BD 077B ADDWF 0x7b, W 0BE 00D4 MOVWF 0x54 0BF 30FF MOVLW 0xff 0C0 1803 BTFSC 0x3, 0 0C1 3000 MOVLW 0 0C2 077C ADDWF 0x7c, W 0C3 00D5 MOVWF 0x55 ui16duty = ui16duty + ui8ipartoffs; 0B2 1283 BCF 0x3, 0x5 0B3 086B MOVF 0x6b, W 0B4 07D4 ADDWF 0x54, F 0B5 1803 BTFSC 0x3, 0 0B6 0AD5 INCF 0x55, F ui16duty = ui16duty - 128; 0B7 3080 MOVLW 0x80 0B8 07D4 ADDWF 0x54, F 0B9 1C03 BTFSS 0x3, 0 0BA 03D5 DECF 0x55, F Koden genererar 9 rader asm Koden genererar 18 rader asm (= 18 Word) Realtidsprog. implementering - Anders Arvidsson 12
Exekveringstid Kompilatorns och kodens inverkan if ((ui8goalpwmin) > (ui8pwmin + 1)) 1DE 00FB MOVWF 0x7b 1DF 01FC CLRF 0x7c 1E0 0843 MOVF 0x43, W 1E1 00FD MOVWF 0x7d 1E2 01FE CLRF 0x7e 1E3 3001 MOVLW 0x1 1E4 07FD ADDWF 0x7d, F 1E5 1803 BTFSC 0x3, 0 1E6 0AFE INCF 0x7e, F 1E7 087E MOVF 0x7e, W 1E8 3A80 XORLW 0x80 1E9 00FF MOVWF 0x7f 1EA 087C MOVF 0x7c, W 1EB 3A80 XORLW 0x80 1EC 027F SUBWF 0x7f, W 1ED 1D03 BTFSS 0x3, 0x2 1EE 29F1 GOTO 0x1f1 1EF 087B MOVF 0x7b, W 1F0 027D SUBWF 0x7d, W 1F1 1C03 BTFSS 0x3, 0 1F2 2A09 GOTO 0x209 if ((ui8goalpwmin) > (INT8U)(ui8PwmIn + 1)) 1DE 0A43 INCF 0x43, W 1DF 00FD MOVWF 0x7d 1E0 0844 MOVF 0x44, W 1E1 027D SUBWF 0x7d, W 1E2 1C03 BTFSS 0x3, 0 1E3 29E8 GOTO 0x1e8 Koden genererar 6 rader asm INT8U är genom typedef satt till en unsigned char typedef unsigned char INT8U; (ui8pwmin + 1) betraktas nu inte längre av kompilatorn som en signed i jämförelsen. Koden genererar 21 rader asm Realtidsprog. implementering - Anders Arvidsson 13
Med timing, verifiera marginal (alternativ 1) start switch() Debug = 1; Använd en ledig pinne (Debug) för att verifiera exekveringstiden. Sätt t ex pinnen vid loopens början, nollställ i vänterutinen. Trigga på stigande flank och mät på oscilloskop med persist (ej radering av föregående svep). Kontrollera att det alltid finns marginal oavsett vad programmet gör. Debug = 0; wait Realtidsprog. implementering - Anders Arvidsson 14
Med timing, verifiera marginal (alternativ 2) start switch() I program med små marginaler kan det vara enklare att mäta tiden programmet vistas i vänterutinen. Kom ihåg att subtrahera minsta tiden vänterutinen själv tar vid beräkning av tidsmarginal. Debug = 1; Kontrollera också frekvensen på debugpinnen. Ändras denna tar exekveringen för lång tid. Ge felindikering (t ex tänd en LED) om timern redan räknat ut när programmet går in i wait. wait Debug = 0; Realtidsprog. implementering - Anders Arvidsson 15
Interrupt Avbrott = preemptive schemaläggning Realtidsprog. implementering - Anders Arvidsson 16
Med Avbrott ISR Större delen av programmet körs som en bakgrundstask. Avbrottsrutinen (ISR) kör med viss periodicitet (triggad av timern) och/eller vid vissa händelser. Övriga programmet kan köras med timing som i tidigare exempel, synkronisera med en flagga från ISR eller använd en andra timer. Realtidsprog. implementering - Anders Arvidsson 17
Avbrott Exempelkod void interrupt timer(void){ // timer = funktionens namn // interrupt = lägg funktionen på int_handling(); // adress 4 something_hard(); i main() while(1){ // Evig loop something_soft(); idle(); // Vänta på nästa interrupt // = Synk Realtidsprog. implementering - Anders Arvidsson 18
Interrupt Källor Flaggor IE = Interrupt Enable IF = Interrupt Flag Realtidsprog. implementering - Anders Arvidsson 19
Konfigurera Interrupt Exempelkod void interrupt timer(void) { Flag = 1; TMR0 = timervalue; T0IF = 0; // Visa att int skett // Sätt timervalue med #define void init(void) { PSA = 0; // Använd prescaler till TMR0 PS0 = 1; // Prescaler satt till dela med 64 PS1 = 0; PS2 = 1; T0CS = 0; // Väljer intern oscillator T0IE = 1; // Timer0 Interrupt Enable GIE = 1; // Global Interrupt Enable Realtidsprog. implementering - Anders Arvidsson 20
Implementera schemaläggning Robot från föreläsning om realtidsprinciper. Realtidsprog. implementering - Anders Arvidsson 21
Struktur Switch switch (CykCnt) { // Var 10:e ms case 0:{ // 0 ms K(); // Krocksensor S(); // Styr robot H(); // Hjulräknare break; case 1: case 5: { // 10, 50 ms K(); break; case 2: case 4: { // 20, 40 ms K(); S(); break; case 3: { // 30 ms K(); H(); break; default: { CykCnt=0; CykCnt=CykCnt++; If (CykCnt == 6) CykCnt=0; Realtidsprog. implementering - Anders Arvidsson 22
Utan timing Realtidsprogrammering Periodtid varierar beroende på funktionernas exekveringstid. Specificera endast maxtid ( Worst case) och eventuellt mintid ( Best case). För garanterad mintid kan en funktion utgöras av en fördröjningsloop. En introduktion Implementering (med exempel från PIC) Realtidsprog. implementering - Anders Arvidsson 2 Utan timing while(1){ // Evig loop Kodstruktur Vilken kod fungerar i realtid? PWM = knapptest(); // Läs och behandla indata while(1) { while(1) { PORTB = BinBCD(PWM); // Presentera while (knapp==0) // Väntar tills knapp är tryckt continue; if (knapp ==1 && knappold ==0) // Detekterar att knappens Knapptryckt = 1; // tillstånd har gått från 0 till 1 RA2 = PWM_styr(PWM); delay(); // Agera // Eventuell fördröjningsloop Led ^=1; while (knapp==1) // Väntar tills knapp är släppt continue; Knappold = knapp; if (Knapptryckt == 1) { Led ^=1; Knapptryckt = 0; Anm: Exekvering får ej stanna i någon funktion, se nästa sida. Realtidsprog. implementering - Anders Arvidsson 3 Realtidsprog. implementering - Anders Arvidsson 4 Med timing start wait Vid hård realtid får Worst case aldrig överstiga tiden från start av timer tills timern räknat ut. Periodtiden blir då konstant. Vid mjuk realtid kan timern startas med en kortare tid nästa varv om det förra varvet tog lite för lång tid. (Ladda om timern genom subtraktion istället för att ladda med en konstant). Med timing while(1){ start_timer(periodtid); PWM = knapptest(); PORTB = BinBCD(PWM); RA2 = PWM_styr(PWM); // Evig loop // Starta timern // Läs och behandla indata // Presentera // Agera idle(); // Vänta på att timern räknat ut // Parallellt med det kan en // bakgrundstask utföras Anm: Funktionerna får tillsammans inte ta längre tid än periodtid Realtidsprog. implementering - Anders Arvidsson 5 Realtidsprog. implementering - Anders Arvidsson 6 1
Tidmätning Timer 0 Timer Timer 8 bitar, räknar ständigt upp Kan slöas ner av prescaler max 256 ggr => upp till 65536 cykler Använder intern klocka eller extern pinne Kan fungera som räknare Se option_reg (skriv bara option, i C) för konfigurering. Realtidsprog. implementering - Anders Arvidsson 7 Realtidsprog. implementering - Anders Arvidsson 8 Timer Exempelkod Med timing, funktioner med lång exekv. tid void init_timer(void) { PSA = 0; // Koppla prescaler till TMR0 PS0 = 0; // Prescaler satt till PS1 = 1; // dela med 128 PS2 = 1; T0CS = 0; // Väljer intern oscillator till timer void start_timer(void) // Laddar om timer { TMR0 = timervalue; // (256-timervalue)*prescaler*1us + omladdningstid T0IF = 0; start switch() Om en funktion tar för lång tid kan den ibland delas upp i två (eller fler) delar där ena delen körs ena varvet, andra delen nästa. Denna metod används när interrupt inte bör användas men någon funktion har behöver kortare periodtid än den långa funktionen kan möta. void wait_timer(void) { while(!t0if) continue; // Väntar på att timer räknar ut (CPU är idle ) wait Realtidsprog. implementering - Anders Arvidsson 9 Realtidsprog. implementering - Anders Arvidsson 10 Exekveringstid Kompilatorns och kodens inverkan Tänk på att kompilatorn kan generera helt olika kod vid små förändringar i syntaxen. Studera den genererade assemblerkoden och bedöm om något tar onödigt mycket kod/exekveringstid. Testa då en omskrivning. I MPLAB syns den genererade koden under View Disassembly Listing. Exekveringstid Kompilatorns och kodens inverkan ui16duty = ui16duty + ui8ipartoffs - 128; Adress Hexkod Asm 0B2 1283 BCF 0x3, 0x5 0B3 086B MOVF 0x6b, W 0B4 00FB MOVWF 0x7b 0B5 01FC CLRF 0x7c 0B6 0854 MOVF 0x54, W 0B7 07FB ADDWF 0x7b, F 0B8 1803 BTFSC 0x3, 0 0B9 0AFC INCF 0x7c, F 0BA 0855 MOVF 0x55, W 0BB 07FC ADDWF 0x7c, F 0BC 3080 MOVLW 0x80 0BD 077B ADDWF 0x7b, W 0BE 00D4 MOVWF 0x54 0BF 30FF MOVLW 0xff 0C0 1803 BTFSC 0x3, 0 0C1 3000 MOVLW 0 0C2 077C ADDWF 0x7c, W 0C3 00D5 MOVWF 0x55 Koden genererar 18 rader asm (= 18 Word) ui16duty = ui16duty + ui8ipartoffs; 0B2 1283 BCF 0x3, 0x5 0B3 086B MOVF 0x6b, W 0B4 07D4 ADDWF 0x54, F 0B5 1803 BTFSC 0x3, 0 0B6 0AD5 INCF 0x55, F ui16duty = ui16duty - 128; 0B7 3080 MOVLW 0x80 0B8 07D4 ADDWF 0x54, F 0B9 1C03 BTFSS 0x3, 0 0BA 03D5 DECF 0x55, F Koden genererar 9 rader asm Realtidsprog. implementering - Anders Arvidsson 11 Realtidsprog. implementering - Anders Arvidsson 12 2
Exekveringstid Kompilatorns och kodens inverkan if ((ui8goalpwmin) > (ui8pwmin + 1)) 1DE 00FB MOVWF 0x7b 1DF 01FC CLRF 0x7c 1E0 0843 MOVF 0x43, W 1E1 00FD MOVWF 0x7d 1E2 01FE CLRF 0x7e 1E3 3001 MOVLW 0x1 1E4 07FD ADDWF 0x7d, F 1E5 1803 BTFSC 0x3, 0 1E6 0AFE INCF 0x7e, F 1E7 087E MOVF 0x7e, W 1E8 3A80 XORLW 0x80 1E9 00FF MOVWF 0x7f 1EA 087C MOVF 0x7c, W 1EB 3A80 XORLW 0x80 1EC 027F SUBWF 0x7f, W 1ED 1D03 BTFSS 0x3, 0x2 1EE 29F1 GOTO 0x1f1 1EF 087B MOVF 0x7b, W 1F0 027D SUBWF 0x7d, W 1F1 1C03 BTFSS 0x3, 0 1F2 2A09 GOTO 0x209 Koden genererar 21 rader asm if ((ui8goalpwmin) > (INT8U)(ui8PwmIn + 1)) 1DE 0A43 INCF 0x43, W 1DF 00FD MOVWF 0x7d 1E0 0844 MOVF 0x44, W 1E1 027D SUBWF 0x7d, W 1E2 1C03 BTFSS 0x3, 0 1E3 29E8 GOTO 0x1e8 Koden genererar 6 rader asm INT8U är genom typedef satt till en unsigned char typedef unsigned char INT8U; (ui8pwmin + 1) betraktas nu inte längre av kompilatorn som en signed i jämförelsen. Realtidsprog. implementering - Anders Arvidsson 13 Med timing, verifiera marginal (alternativ 1) Debug = 1; Använd en ledig pinne (Debug) för start att verifiera exekveringstiden. Sätt t ex pinnen vid loopens början, nollställ i vänterutinen. switch() Trigga på stigande flank och mät på oscilloskop med persist (ej radering av föregående svep). Kontrollera att det alltid finns marginal oavsett vad programmet gör. wait Debug = 0; Realtidsprog. implementering - Anders Arvidsson 14 Med timing, verifiera marginal (alternativ 2) start switch() I program med små marginaler kan det vara enklare att mäta tiden programmet vistas i vänterutinen. Kom ihåg att subtrahera minsta tiden vänterutinen själv tar vid beräkning av tidsmarginal. Kontrollera också frekvensen på debugpinnen. Ändras denna tar exekveringen för lång tid. Ge felindikering (t ex tänd en LED) om timern redan räknat ut när programmet går in i wait. Debug = 1; Interrupt Avbrott = preemptive schemaläggning wait Debug = 0; Realtidsprog. implementering - Anders Arvidsson 15 Realtidsprog. implementering - Anders Arvidsson 16 Med Avbrott Avbrott Exempelkod ISR void interrupt timer(void){ // timer = funktionens namn // interrupt = lägg funktionen på int_handling(); // adress 4 Större delen av programmet körs som en bakgrundstask. Avbrottsrutinen (ISR) kör med viss periodicitet (triggad av timern) och/eller vid vissa händelser. Övriga programmet kan köras med timing som i tidigare exempel, synkronisera med en flagga från ISR eller använd en andra timer. something_hard(); i main() while(1){ something_soft(); idle(); // Evig loop // Vänta på nästa interrupt // = Synk Realtidsprog. implementering - Anders Arvidsson 17 Realtidsprog. implementering - Anders Arvidsson 18 3
Interrupt Källor Konfigurera Interrupt Exempelkod void interrupt timer(void) { Flag = 1; TMR0 = timervalue; T0IF = 0; // Visa att int skett // Sätt timervalue med #define Flaggor IE = Interrupt Enable IF = Interrupt Flag void init(void) { PSA = 0; // Använd prescaler till TMR0 PS0 = 1; // Prescaler satt till dela med 64 PS1 = 0; PS2 = 1; T0CS = 0; // Väljer intern oscillator T0IE = 1; // Timer0 Interrupt Enable GIE = 1; // Global Interrupt Enable Realtidsprog. implementering - Anders Arvidsson 19 Realtidsprog. implementering - Anders Arvidsson 20 Implementera schemaläggning Robot från föreläsning om realtidsprinciper. Struktur Switch switch (CykCnt) { // Var 10:e ms case 0:{ // 0 ms K(); // Krocksensor S(); // Styr robot H(); // Hjulräknare break; case 1: case 5: { // 10, 50 ms K(); break; case 2: case 4: { // 20, 40 ms K(); S(); break; case 3: { // 30 ms K(); H(); break; default: { CykCnt=0; CykCnt=CykCnt++; If (CykCnt == 6) CykCnt=0; Realtidsprog. implementering - Anders Arvidsson 21 Realtidsprog. implementering - Anders Arvidsson 22 4