Hallo,
ich versuche gerade, den Watchdog auf dem AtMega2560 zu benutzen. Dafür
habe ich mir ein paar Routinen geschrieben. _enableWatchdog und
_disableWatchdog verändern die Prescalerbits nicht.
1 | ; void _setTimeout(unsigned char r24);
| 2 | _setTimeout:
| 3 | wdr
| 4 | push r16
| 5 | push r17
| 6 | lds r16, 0x60 ; read WDT register
| 7 | andi r16, 0xD8 ; clear WDP bits
| 8 | or r16, r24 ; set timeout from parameter, leave old bits
| 9 | ldi r17, 0x10 | 0x08 ; load WDE and WDCE
| 10 | sts 0x60, r17 ; start WDT manipulation
| 11 | sts 0x60, r16 ; write new WDT content
| 12 | pop r17
| 13 | pop r16
| 14 | ret
| 15 |
| 16 | _enableWatchdog:
| 17 | push r16
| 18 | lds r16, 0x60 ; load content of WDTCSR
| 19 | ori r16, 0x08 ; enable WDE and WDCE
| 20 | sts 0x60, r16 ; write back to WDTCSR
| 21 | pop r16
| 22 | ret
| 23 |
| 24 | _disableWatchdog:
| 25 | wdr ; reset WDT
| 26 | push r16
| 27 | push r17
| 28 | lds r16, 0x60 ; load WDTCSR content
| 29 | mov r17, r16 ; one copy is also needed
| 30 | ori r16, 0x10 | 0x18 ; set WDCE and WDE, but leave WDP bits
| 31 | andi r17, ~(0x10 | 0x08); clear WDCE and WDE bit, but leave WDP bits
| 32 | sts 0x60, r16 ; write to WDTCSR to begin watch dog manipulation
| 33 | sts 0x60, r17 ; disable watchdog
| 34 | pop r17
| 35 | pop r16
| 36 | ret
|
Das klappt auch so weit innerhalb des Programms. Ich kann den Prescaler
setzen und den Watchdog beliebig oft aktivieren und deaktivieren. Wenn
er allerdings zuschlägt und der Controller neu startet, dann
funktioniert _disableWatchdog nicht mehr und der Watchdog schlägt sofort
wieder zu, obwohl das eigentlich hiermit abgestellt werden sollte:
1 | __attribute__((naked)) __attribute__((section(".init1"))) void initWatchdog()
| 2 | {
| 3 | _disableWatchdog();
| 4 | }
|
Die Routine wird auch zum richtigen Zeitpunkt angesprungen, denn wenn
ich in initWatchdog einen Aufruf zu _setTimeout mit einem beliebigen
Prescalerwert einfüge, dann wird der Watchdog nach einem Watchdog-Reset
auch abgestellt.
Hat jemand eine Idee, warum ich nach einem Watchdog-Reset unbedingt die
Prescalerbits setzen muss?
Danke
Richard
Richard W. schrieb:
> Wenn
> er allerdings zuschlägt und der Controller neu startet, dann
> funktioniert _disableWatchdog nicht mehr
Und warum nimmst Du nicht einfach die Funktionen aus wdt.h statt Dich
mit Assembler abzuquälen?
Peter
Hallo Peter,
das Löschen des MCUSR-Registers hat scheinbar keinen Einfluss. Ich muss
explizit irgendein Prescalerbit beschreiben.
Warum nicht die wdt.h? Mein Watchdogtreiber soll sich in ein bestehendes
Framework integrieren, welches keinerlei Abhängigkeit zu den
AVR-Bibliotheken aufweist. Ich hab's jetzt in Hochsprache gebracht, da
muss ich nur noch eine einzige Zeile Assembler schreiben. So ungefähr
siehts aus:
1 | class Watchdog
| 2 | {
| 3 |
| 4 | public:
| 5 | enum WatchdogBits
| 6 | {
| 7 | WDP0 = 0x01, WDP1 = 0x02, WDP2 = 0x04, WDE = 0x08, WDCE = 0x10, WDP3 = 0x20, WDIE = 0x40, WDIF = 0x80
| 8 | };
| 9 | /**
| 10 | * Timeout timings in milliseconds
| 11 | */
| 12 | enum Timeout
| 13 | {
| 14 | NONE = 0x10,
| 15 | MS_16 = 0,
| 16 | MS_32 = WDP0,
| 17 | MS_64 = WDP1,
| 18 | MS_125 = WDP0 + WDP1,
| 19 | MS_250 = WDP2,
| 20 | MS_500 = WDP2 + WDP0,
| 21 | MS_1000 = WDP2 + WDP1,
| 22 | MS_2000 = WDP2 + WDP1 + WDP0,
| 23 | MS_4000 = WDP3,
| 24 | MS_8000 = WDP3 + WDP0
| 25 | };
| 26 |
| 27 | static void setTimeout(Timeout timeout);
| 28 | static void enable();
| 29 | static void disable();
| 30 | static void reset();
| 31 |
| 32 | private:
| 33 | static void inline _wdr();
| 34 | };
| 35 |
| 36 | /**
| 37 | * Helper to access registers
| 38 | */
| 39 | struct WatchdogRegisters
| 40 | {
| 41 | volatile unsigned char WDTCSR;
| 42 |
| 43 | WatchdogRegisters* operator ->()
| 44 | {
| 45 | return reinterpret_cast<WatchdogRegisters*> (WD_BASE);
| 46 | }
| 47 | };
| 48 |
| 49 | /**
| 50 | * Function that will disable the watchdog immediately after boot.
| 51 | */
| 52 | void initWatchdog();
| 53 |
| 54 | }
| 55 |
| 56 | void Watchdog::setTimeout(Timeout timeout)
| 57 | {
| 58 | InterruptLock lock;
| 59 | uint8 content = WatchdogRegisters()->WDTCSR;
| 60 | content &= ~(WDP3 | WDP2 | WDP1 | WDP0);
| 61 | content |= timeout;
| 62 | WatchdogRegisters()->WDTCSR |= WDCE | WDE;
| 63 | WatchdogRegisters()->WDTCSR = content;
| 64 | }
| 65 |
| 66 | void Watchdog::enable()
| 67 | {
| 68 | InterruptLock lock;
| 69 | WatchdogRegisters()->WDTCSR |= WDCE | WDE;
| 70 | WatchdogRegisters()->WDTCSR |= WDE;
| 71 | }
| 72 |
| 73 | void Watchdog::disable()
| 74 | {
| 75 | InterruptLock lock;
| 76 | _wdr();
| 77 | WatchdogRegisters()->WDTCSR |= WDCE | WDE;
| 78 | WatchdogRegisters()->WDTCSR &= ~WDE;
| 79 | }
| 80 |
| 81 | void Watchdog::reset()
| 82 | {
| 83 | _wdr();
| 84 | }
| 85 |
| 86 | void Watchdog::_wdr()
| 87 | {
| 88 | asm volatile ("wdr"::);
| 89 | }
| 90 |
| 91 | __attribute__((naked)) __attribute__((section(".init1"))) void initWatchdog()
| 92 | {
| 93 | Watchdog::disable();
| 94 | Watchdog::setTimeout(Watchdog::MS_125); // without setting prescaler watchdog can not be disabled
| 95 | }
|
Sapperlot!
Tatsächlich muss ich das MCUSR-Register zurücksetzen, ich habs nur
anscheinend nicht richtig gemacht.
So ungefähr funktioniert's jetzt ganz prima und ohne dass ich mich mit
Assembler abquälen muss ;-) Vielen Dank für die Hilfe.
1 | class Watchdog
| 2 | {
| 3 |
| 4 | public:
| 5 | enum WatchdogBits
| 6 | {
| 7 | WDP0 = 0x01, WDP1 = 0x02, WDP2 = 0x04, WDE = 0x08, WDCE = 0x10, WDP3 = 0x20, WDIE = 0x40, WDIF = 0x80
| 8 | };
| 9 | /**
| 10 | * Timeout timings in milliseconds
| 11 | */
| 12 | enum Timeout
| 13 | {
| 14 | MS_16 = 0,
| 15 | MS_32 = WDP0,
| 16 | MS_64 = WDP1,
| 17 | MS_125 = WDP0 + WDP1,
| 18 | MS_250 = WDP2,
| 19 | MS_500 = WDP2 + WDP0,
| 20 | MS_1000 = WDP2 + WDP1,
| 21 | MS_2000 = WDP2 + WDP1 + WDP0,
| 22 | MS_4000 = WDP3,
| 23 | MS_8000 = WDP3 + WDP0
| 24 | };
| 25 |
| 26 | static void setTimeout(Timeout timeout);
| 27 | static void enable();
| 28 | static void disable();
| 29 | static void reset();
| 30 |
| 31 | private:
| 32 | static void inline _wdr();
| 33 | };
| 34 |
| 35 | /**
| 36 | * After a reset caused by the watchdog, we have to disable it immediately.
| 37 | */
| 38 | void initWatchdog();
| 39 |
| 40 |
| 41 |
| 42 | enum RegisterBits
| 43 | {
| 44 | PORF = 0x01, EXTRF = 0x02, BORF = 0x04, WDRF = 0x08
| 45 | };
| 46 |
| 47 | struct Registers
| 48 | {
| 49 | volatile uint8 dummy0[0x54]; // 0x00..0x53
| 50 | volatile uint8 MCUSR; // 0x54
| 51 | volatile uint8 MCUCR; // 0x55
| 52 | volatile uint8 dummy1[10];
| 53 | volatile uint8 WDTCSR; // 0x60
| 54 |
| 55 | Registers* operator->()
| 56 | {
| 57 | return reinterpret_cast<Registers*> (0);
| 58 | }
| 59 | };
| 60 |
| 61 |
| 62 |
| 63 | void Watchdog::setTimeout(Timeout timeout)
| 64 | {
| 65 | InterruptLock lock;
| 66 | uint8 content = Registers()->WDTCSR | WDCE ;
| 67 | content &= ~(WDP3 | WDP2 | WDP1 | WDP0);
| 68 | content |= timeout;
| 69 | Registers()->WDTCSR |= WDCE | WDE;
| 70 | Registers()->WDTCSR = content;
| 71 | }
| 72 |
| 73 | void Watchdog::enable()
| 74 | {
| 75 | InterruptLock lock;
| 76 | Registers()->WDTCSR |= WDCE | WDE;
| 77 | Registers()->WDTCSR |= WDE;
| 78 | }
| 79 |
| 80 | void Watchdog::disable()
| 81 | {
| 82 | InterruptLock lock;
| 83 | _wdr();
| 84 | uint8 disabled = Registers()->WDTCSR & (~WDE);
| 85 | Registers()->WDTCSR |= WDCE | WDE;
| 86 | Registers()->WDTCSR = disabled;
| 87 | }
| 88 |
| 89 | void Watchdog::reset()
| 90 | {
| 91 | _wdr();
| 92 | }
| 93 |
| 94 | void Watchdog::_wdr()
| 95 | {
| 96 | asm volatile ("wdr"::);
| 97 | }
| 98 |
| 99 | __attribute__((naked)) __attribute__((section(".init1"))) void initWatchdog()
| 100 | {
| 101 | Registers()->MCUSR &= ~WDRF;
| 102 | Watchdog::disable();
| 103 | }
|
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
|