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

Go to full version