Forum > Embedded - AVR
Reset AVR from FPC code, possible ?
(1/1)
pascalbythree:
Does anybody know weither there is a freepascal command on AVR to reset it? instead of reset button? So it starts at the begin again? Greets, Wouter
d.ioannidis:
Hi,
I don't know if a reset command exists but you can achieve what you want using the watchdog functionality.
PS: In atmega4809 you can reset the MCU using a single CCP write and SWRR write after you disable interrupts .
EDIT: Added Microchip(Atmel) knowledge article reference
regards,
ccrause:
--- Quote from: d.ioannidis on November 21, 2022, 12:12:19 pm --- I don't know if a reset command exists but you can achieve what you want using the watchdog functionality.
PS: In atmega4809 you can reset the MCU using a single CCP write and SWRR write after you disable interrupts .
EDIT: Added Microchip(Atmel) knowledge article reference
--- End quote ---
The above description is the correct way to reset an AVR (including re-initializing hardware registers).
However, here is a shortcut way to restart code execution (without re-initializing hardware registers, so e.g. pins will retain their current values):
--- Code: Pascal [+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i < x.length; i++) { x[i].style.maxHeight='none'; x[i].style.height = Math.min(x[i].clientHeight+15,306)+'px'; x[i].style.resize = "vertical";}};} ---var _start: record end; external name '_START'; begin asm // clear status register out 0x3f, r1 {$if defined(FPC_FLASHSIZE) and (FPC_FLASHSIZE>8192)} jmp _start {$else} rjmp _start {$endif} end;end.
d.ioannidis:
Hi,
I had some free time so I wrote a watchdog unit, which works like Microchip's AVR watchdog timer ( actually I ported only the atmega328p part ), specific for atmega328p ( it was fun ;) ) :
--- Code: Pascal [+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i < x.length; i++) { x[i].style.maxHeight='none'; x[i].style.height = Math.min(x[i].clientHeight+15,306)+'px'; x[i].style.resize = "vertical";}};} ---unit watchdog; {$mode ObjFPC} interface const WDTO_15MS = 0; WDTO_30MS = 1; WDTO_60MS = 2; WDTO_120MS = 3; WDTO_250MS = 4; WDTO_500MS = 5; WDTO_1S = 6; WDTO_2S = 7; //WDTO_4S = 8; //WDTO_8S = 9; procedure wdt_enable(const Timeout: byte);procedure wdt_disable;procedure wdt_reset; implementation var bWdtEnable: byte = (1 << WDCE) or (1 << WDE); procedure wdt_enable_internal(const Timeout: byte); assembler;asm LDS r25, bWdtEnable; IN r0, 0x3f; CLI; WDR; STS WDTCSR, r25; OUT 0x3f, r0; STS WDTCSR, r24;end; procedure wdt_enable(const Timeout: byte);var tmp: byte;begin if (Timeout and $08) <> 0 then tmp := (1 << WDP+5) or (1 << WDE) or (Timeout and $07) else tmp := $00 or (1 << WDE) or (Timeout and $07); wdt_enable_internal(tmp);end; procedure wdt_disable; assembler; nostackframe;asm LDS r25, bWdtEnable; IN r0, 0x3f; CLI; WDR; LDS r24, WDTCSR; OR r24, r25; STS WDTCSR, r24; STS WDTCSR, r1; OUT 0x3f, r0;end; procedure wdt_reset; assembler; nostackframe;asm WDR;end; end.
Tested in an arduino and it works ( for me anyway ;) ) .
If you use it you'll need to reset the MCUSR register on start ( MCUSR := 0 ) .
In the following demo code, the "blink" (PB5) led starts off for 2 seconds, then we enter the loop and its starts blinking for ~8 seconds. After the first ~4 seconds, the watchdog is enabled with a 2 seconds timeout and for the last ~4 seconds we reset the timeout every blink cycle ( ~400ms < timeout) so no mcu reset. When it exits the loop the led is changed on and because we don't reset watchdog timeout after ~2 seconds, the watchdog resets the mcu.
--- Code: Pascal [+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i < x.length; i++) { x[i].style.maxHeight='none'; x[i].style.height = Math.min(x[i].clientHeight+15,306)+'px'; x[i].style.resize = "vertical";}};} ---program watchdog_demo; uses delay, watchdog; const PB5 = 1 shl 5; var pinport: byte absolute PORTB; pindir: byte absolute DDRB; i: byte = 0; begin MCUSR := 0; wdt_disable; pindir := pindir or PB5; pinport := pinport and not PB5; // LED off delay_ms(2000); // delay 1 second repeat pinport := pinport or PB5; // LED on delay_ms(200); // delay 200 ms pinport := pinport and not PB5; // LED off delay_ms(200); // delay 200 ms Inc(i); If i = 10 then wdt_enable(WDTO_2S); if (i > 10) and (i <= 20) then wdt_reset; until i > 20; pinport := pinport or PB5; // LED on end.
So for your specific case, all you need to do to reset the mcu from software, is something like this :
--- Code: Pascal [+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i < x.length; i++) { x[i].style.maxHeight='none'; x[i].style.height = Math.min(x[i].clientHeight+15,306)+'px'; x[i].style.resize = "vertical";}};} --- procedure ResetMCU;begin wdt_enable(WDTO_15MS or WDTO_1S or ....whatever)end; ....... Somewhere else in the code ........ if blabla then ResetMCU; .......
EDIT: The delay unit in the demo is from ccrauce's repo .
regards,
d.ioannidis:
Hi,
added the missing WDTO_4S and WDTO_8S options and some minor enhancements ....
--- Code: Pascal [+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i < x.length; i++) { x[i].style.maxHeight='none'; x[i].style.height = Math.min(x[i].clientHeight+15,306)+'px'; x[i].style.resize = "vertical";}};} ---unit watchdog; {$mode ObjFPC}{$macro on} interface const WDTO_15MS = 0; WDTO_30MS = 1; WDTO_60MS = 2; WDTO_120MS = 3; WDTO_250MS = 4; WDTO_500MS = 5; WDTO_1S = 6; WDTO_2S = 7; WDTO_4S = 8; WDTO_8S = 9; procedure wdt_enable(const Timeout: byte);procedure wdt_disable;procedure wdt_reset; implementation const WDP3 = 5; {$define _WD_PS3_MASK := (1 << WDP3)}{$define _WD_CONTROL_REG := WDTCSR}{$define _WD_CHANGE_BIT := WDCE} {$define _WD_WDE := (1 << WDE)}{$define _WD_CHANGE_ENABLE := (1 << _WD_CHANGE_BIT) or (1 << WDE)} var WD_CONTROL_REG: byte absolute _WD_CONTROL_REG; WD_PS3_MASK: byte absolute _WD_PS3_MASK; WD_CHANGE_ENABLE: byte absolute _WD_CHANGE_ENABLE; WD_WDE: byte absolute _WD_WDE; procedure wdt_enable(const Timeout: byte); assembler;asm LDI r25, WD_CHANGE_ENABLE; MOV r1, r24; ANDI r24, 7; ORI r24, WD_WDE; SBRC r1, 3; ORI r24, WD_PS3_MASK; EOR r1, r1; IN r0, 0x3F; CLI; WDR; STS WD_CONTROL_REG, r25; OUT 0x3F, r0; STS WD_CONTROL_REG, r24;end; procedure wdt_disable; assembler; nostackframe;asm IN r0, 0x3F; CLI; WDR; LDI r24, WD_CONTROL_REG; ORI r24, WD_CHANGE_ENABLE; STS WD_CONTROL_REG, r24; STS WD_CONTROL_REG, r1; OUT 0x3F, r0;end; procedure wdt_reset; assembler; nostackframe;asm WDR;end; end.
Also forgot to mention ( quoting from avr sources ) :
--- Quote --- Note that for newer devices (ATmega88 and newer, effectively any
AVR that has the option to also generate interrupts), the watchdog
timer remains active even after a system reset (except a power-on
condition), using the fastest prescaler value (approximately 15
ms). It is therefore required to turn off the watchdog early
during program startup, the datasheet recommends a sequence like
the following:
\code
#include <stdint.h>
#include <avr/wdt.h>
uint8_t mcusr_mirror __attribute__ ((section (".noinit")));
void get_mcusr(void) \
__attribute__((naked)) \
__attribute__((section(".init3")));
void get_mcusr(void)
{
mcusr_mirror = MCUSR;
MCUSR = 0;
wdt_disable();
}
\endcode
Saving the value of MCUSR in \c mcusr_mirror is only needed if the
application later wants to examine the reset source, but in particular,
clearing the watchdog reset flag before disabling the
watchdog is required, according to the datasheet.
--- End quote ---
Hope it helps !
EDIT: Some minor changes ...
regards,
Navigation
[0] Message Index