Aktykuły które nie zmieściły się w żadnej kategorii

Rejestry MCUCSR , MCUSR w procesorach Atmel, jego funkcje i wykorzystanie.

Z doświadczenia wiem że każdy z procesorów nawet najlepszego producenta popełnia błędy i
nie zależnie od tego czy nasz algorytm jest napisany dobrze czy źle. Szukanie takich błędów jest czasochłonne i frustrujące. Uruchomienie watchdoga i sprawdzenie rejestru MCU Status Register może znacznie przyśpieszyć lokalizację błędu. Pamięć programu flash podczas programowania procesor jest zapisywane po czym weryfikowana ale to nie daje nam 100% pewności że pamięć w czasie pracy nie będzie popełniała błędów. Niejednokrotnie spotykam się z taką sytuacją że wgrany program do procesora w 0.01% przypadków nie działa poprawnie, co wskazuje że 100 procesów na 1’000’000 jest uszkodzonych fabrycznie. Takie błędy zauważyłem w procesorach Atmel i STM. Przypuszczam że w procesorach innych producentów sytuacja wygląda podobnie.

W tym artykule skupię się na procesorach firmy Atmel.

Rejestr w Atmega328
Atmega328

Rejestr w Atmega8
Atmega8

Rejestr w Atmega32
Atmega32

Rejestr w Atmega128
Atmega128

Rejestr w Atmega1284
Atmega1284

Bit 3 – WDRF : Flaga resetowania od watchdog
Bit 2 – BORF : Flaga resetowania od spadku zasilania
Bit 1 – EXTRF : Flaga resetowania , reset zewnątrzy
Bit 0 – PORF : Flaga resetu po włączeniu zasilania

Do czego możemy wykorzystać tą informację i na ile będzie nam potrzebna w dalszym pisaniu programu.

Uruchomienie watchdoga w procesorze jest wskazane a nawet można powiedzieć wymagane. Zabezpiecza nasz algorytm przed zawieszeniem się lub nie prawidłowym działanie. Układ nadzoru jak stwierdzi że procesor działa nie prawidłowo to go zresetuje. Oczywiście w takim przypadku należy pamiętać że procesor wszystkie nasze zmienne ustawi do wartości domyślnych tak jak to zawarliśmy w swoim algorytmie.
Przed utratą danych możemy się zabezpieczyć wpisując je do pamięci eeprom procesora lub deklarując zmienne w sekcji .noinit. Umieszczenie danych w tej sekcji sprawia że kompilator ich nie inicjuje ( inicjacja zmiennych jest przeprowadzona przez kompilator i polega na wpisaniu wartości zero do każdej zmiennej, operacja ta jest przeprowadzona przed main-em ).
Zmienne zadeklarowane w sekcji noinit przechowują swoje dane do chwili całkowitej utraty zasilania.

Prosty przykład wykorzystania tej sekcji.

  1. // Atmega128
  2. //////// sekcja noinit, dane poza resetem
  3. volatile uint8_t sek    __attribute__ ((section (".noinit")));
  4. volatile uint8_t min    __attribute__ ((section (".noinit")));
  5. volatile uint8_t god    __attribute__ ((section (".noinit")));
  6. volatile uint8_t rMCUCSR;
  7. volatile uint8_t resety    __attribute__ ((section (".noinit")));
  8. int main(void
  9.     rMCUCSR = MCUCSR;
  10.     if ((rMCUCSR & rPORF) ==1 ) 
  11.       {  // Power reset 
  12.          resety=0;
  13.          sek =0;
  14.          min =0;
  15.          god =0;
  16.       }
  17.     initUsart();
  18.     rsInt(rMCUCSR)  rsPrint("=MCUCSR\n\r");
  19.     MCUCSR = 0;
  20.     rsInt(++resety)  rsPrint("=resety\n\r");
  21.     while(1)
  22.     {
  23.        sek++; 
  24.        if (sek==60) { sek=0; min++;}
  25.        if (min==60) { min=0; god++;}
  26.        if (god==24) { god=0;}
  27.        if (god<=9) rsSend(' ');rsInt(god);rsSend(':');
  28.        if (min<=9) rsSend('0');rsInt(min);rsSend(':');
  29.        if (sek<=9) rsSend('0');rsInt(sek);rsPrint("\n\r");  
  30.        _delay_ms(1000);
  31.     } 
  32. }
Po starcie podłączeniu zasilania zauważymy w oknie terminala poniższą informację.

5=MCUCSR
1=resety
0:00:01
0:00:02


Procesor ustawiła dwie flagi PORF -sygnalizując włączenie zasilania i BORF – sygnalizując za niskie napięcie. Wywołanie resetu sprzętowe spowoduje ustawienie tylko flagi EXTRF.

2=MCUCSR
2=resety
0:04:16


Zmienna resety umieszczona w sekcji .noinit przechowuje informację ile razy program startował od początku.
  1. // Atmega128
  2. //////// sekcja noinit, dane poza resetem
  3. volatile uint8_t resety __attribute__ ((section (".noinit")));
  4. volatile uint8_t sek    __attribute__ ((section (".noinit")));
  5. volatile uint8_t min    __attribute__ ((section (".noinit")));
  6. volatile uint8_t god    __attribute__ ((section (".noinit")));
  7. volatile uint8_t rMCUCSR;
  8. volatile uint8_t bWDRF  __attribute__ ((section (".noinit")));
  9. volatile uint8_t bBORF  __attribute__ ((section (".noinit"))); 
  10. volatile uint8_t bEXTRF __attribute__ ((section (".noinit"))); 
  11. volatile uint8_t bPORF  __attribute__ ((section (".noinit")));
  12. int main(void
  13.     uint16_t petla, i;
  14.     uint8_t  bUDR1;
  15.     rMCUCSR = MCUCSR;
  16.     if ((rMCUCSR & (1<<PORF)) ==1 ) 
  17.       {  // Power reset
  18.          resety = 0;
  19.          sek    = 0;
  20.          min    = 0;
  21.          god    = 0;
  22.          bWDRF  = 0;
  23.          bBORF  = 0;
  24.          bEXTRF = 0;
  25.          bPORF  = 0;
  26.       }
  27.     if ((rMCUCSR & (1<<WDRF)) == (1<<WDRF) )   bWDRF++;
  28.     if ((rMCUCSR & (1<<BORF)) == (1<<BORF) )   bBORF++;
  29.     if ((rMCUCSR & (1<<EXTRF)) == (1<<EXTRF) ) bEXTRF++;
  30.     if ((rMCUCSR & (1<<PORF)) == (1<<PORF) )   bPORF++;
  31.     initUsart();
  32.     rsPrint("MCUCSR ");rsInt(rMCUCSR)  
  33.     rsPrint(" od WDRF , BORF , EXTRF , PORF ");
  34.     rsInt(bWDRF);rsSend(' ');
  35.     rsInt(bBORF);rsSend(' ');
  36.     rsInt(bEXTRF);rsSend(' ');
  37.     rsInt(bPORF);rsPrint("\n\r");
  38.     rsPrint("Reset =");rsInt(++resety);rsPrint("\n\r");
  39.     MCUCSR = 0;
  40.     wdt_enable( WDTO_1S );
  41.     while(1)
  42.     {
  43.        sek++; 
  44.        if (sek==60) { sek=0; min++;}
  45.        if (min==60) { min=0; god++;}
  46.        if (god==24) { god=0;}   
  47.        if (god<=9) rsSend(' ');rsInt(god);rsSend(':');
  48.        if (min<=9) rsSend('0');rsInt(min);rsSend(':');
  49.        if (sek<=9) rsSend('0');rsInt(sek);rsPrint("\n\r");  
  50.        wdt_reset();  // zeruj licznik watchdoga
  51.        _delay_ms(900);
  52.     } 
  53. }

MCUCSR 2 od WDRF , BORF , EXTRF , PORF 11 1 4 1
Reset =16
0:01:01
0:01:02

W powyższej tabelce widać końcowy efekt działania drugiego przykładu.
Widać wyraźnie że procesor był 14 razy zresetowany prze watchdoga i 4 razy przez reset zewnętrzy. Powyższy przykład jest oparty na pamięci ram procesora która jest ulotna. Zapisywanie tych danych do pamięci eeprom procesora przy diagnostyce dlaczego urządzenie przestało działać, wskaże nam kierunek poszukiwania przyczyn awarii.

Kilka słów o bibliotece WDT

#include <avr/wdt.h>

wdt_reset(); // zerowanie licznika w watchdogu
wdt_enable( value ); // włączenie watchdoga z wartością
wdt_disable(); // wyłączenie watchdoga


Watchdog po włączeniu działa stale poza naszym programem ( do momentu wyłączenia )
musimy tak pisać program by polecenie wdt_reset(); zdążyło wyzerować zawartość watchdoga w przeciwny razie watchdog zresetuje nam procesor.

Przy starcie procesor powinniśmy rozpoznawać rodzaj startu, czy jest to normalny start czy spowodowany watchdogiem. Przy resecie od watchdoga raczej nie powinniśmy tracić zgromadzonych danych i po określeniu w którym momencie zadania nastąpił reset powinniśmy wznowić pracę procesora.

Wersja końcowa z użyciem pamięci EEPROM procesora.

  1. void eLicznikiRST(void)
  2. {
  3.     rsPrint("\n\r Reset od WDRF , BORF , EXTRF , PORF ");
  4.     rsInt(eeprom_read_byte(&eWDRF));rsSend(' ');
  5.     rsInt(eeprom_read_byte(&eBORF));rsSend(' ');
  6.     rsInt(eeprom_read_byte(&eEXTRF));rsSend(' ');
  7.     rsInt(eeprom_read_byte(&ePORF));rsPrint("\n\r");
  8.     rsPrint(" Reset =");rsInt(eeprom_read_byte(&eRST));rsPrint("\n\r");
  9. }
  10. int main(void
  11.     rMCUCSR = MCUCSR;
  12.     if ((rMCUCSR & (1<<PORF)) ==1 ) 
  13.       {  // Power reset
  14.          sek    = 0;
  15.          min    = 0;
  16.          god    = 0;
  17.       }
  18.     if ((rMCUCSR & (1<<WDRF)) == (1<<WDRF) )   
  19.        eeprom_write_byte(&eWDRF,(eeprom_read_byte(&eWDRF)+1));
  20.     if ((rMCUCSR & (1<<BORF)) == (1<<BORF) )   
  21.        eeprom_write_byte(&eBORF,(eeprom_read_byte(&eBORF)+1)); 
  22.     if ((rMCUCSR & (1<<EXTRF)) == (1<<EXTRF) ) 
  23.        eeprom_write_byte(&eEXTRF,(eeprom_read_byte(&eEXTRF)+1)); 
  24.     if ((rMCUCSR & (1<<PORF)) == (1<<PORF) )   
  25.        eeprom_write_byte(&ePORF,(eeprom_read_byte(&ePORF)+1)); 
  26.     initUsart();
  27.     rsPrint(" MCUCSR ");rsInt(rMCUCSR)  
  28.     eeprom_write_byte(&eRST,(eeprom_read_byte(&eRST)+1)); 
  29.     eLicznikiRST();

Procedury transmisji usart do powyższych przykładów, przez uart1.
  1. void rsSend( unsigned char data )
  2. {
  3.      while ( !( UCSR1A & (1<<UDRE1)) );
  4.      UDR1 = data;
  5. }
  6. void rsPrint(char text[]) 
  7.    unsigned int i; 
  8.    i = 0; 
  9.    while ( text[i] != 0 ) 
  10.    { 
  11.       rsSend ( text[i] ); 
  12.       i++; 
  13.    } 
  14. void rsInt( long num )
  15. {
  16.      ltoa( num , bTemp, 10);
  17.      rsPrint( bTemp );
  18. }


Dasej (C) 2019 : 5095