[SOLVED] WDT interrupt not executing on 2313, just resetting chip

I am trying to get this script working on an ATTINY2313

#include <stdint.h>
#include <avr/interrupt.h>
#include <avr/wdt.h>

volatile byte TC0sample;
volatile unsigned int TC1sample;
boolean sample_waiting = false;

void setup() {
  Serial.begin(115200);
  wdt_enable(WDTO_30MS);
  Serial.println("TC0,TC1");
}

void loop()
{
  if (sample_waiting) {
    sample_waiting = false;
    Serial.print(TC0sample);
    Serial.print(",");
    Serial.print(TC1sample);
  }
}

// Watchdog Timer Interrupt Service Routine
ISR(WDT_vect)
{
  TC0sample = TCNT0;
  TC1sample = TCNT1;
  sample_waiting = true;
}

I am using the tiny core and first I burn the bootloader for the ATTINY2313 @8MHz, then I upload the code using an AVRISP MKII Instead of transmitting the expected timer values it appears to be resetting the chip. I can tell this since it keeps rerunning the setup() function about every 30 milliseconds. This script works fine on the other normal arduino's I have tested as well as the tinyX5 and tinyX4 I have tested.

Sample output on serial monitor:

TC0,TC1
TC0,TC1
TC0,TC1
TC0,TC1
TC0,TC1
TC0,TC1
TC0,TC1
TC0,TC1
TC0,TC1
...

I have reviewed the datasheet and it appears consistent with the other chips I have tested with this script. Any ideas?

My gut tells me you are running out of SRAM. Try adding F-macro calls...

Serial.println( F( "TC0,TC1" ) );

Thanks I replaced both Serial.print statements that had a string constant with an encloseing F("..."). Same behavior. Odd.

Two things...

  1. The name of the vector is different: WDT_OVERFLOW_vect (versus WDT_vect).

  2. Apparently, the stuff in wdt.h does not include a way to enable an interrupt; only a way to enable a reset.

This seems to work...

#include <stdint.h>
#include <avr/interrupt.h>
#include <avr/wdt.h>

volatile byte TC0sample;
volatile unsigned int TC1sample;
boolean sample_waiting = false;

void setup() {
  Serial.begin(115200);

  noInterrupts();

  // Clear the WDRF flag
  MCUSR &= ~(1 << WDRF);

  // Restart the watchdog
  wdt_reset();

  // Prepare to configure the watchdog
  _WD_CONTROL_REG = (1 << _WD_CHANGE_BIT) | (1 << WDE);
  // Configure the watchdog: clear the interrupt flag; enable interrupts; 30ms
  _WD_CONTROL_REG = (1 << WDIF) | (1 << WDIE) | (1 << WDP0);

  interrupts();

  Serial.println(F("TC0,TC1"));
}

void loop()
{
  if (sample_waiting) {
    sample_waiting = false;
    Serial.print(TC0sample);
    Serial.print(F(","));
    Serial.print(TC1sample);
    Serial.println();
  }
}

// Watchdog Timer Interrupt Service Routine
ISR(WDT_OVERFLOW_vect)
{
  TC0sample = TCNT0;
  TC1sample = TCNT1;
  sample_waiting = true;
}

Note: wdt_enable does not generate correct code. The watchdog initialization has to be performed with interrupts disabled. Interrupts are re-enabled one instruction too early...
~~ b0: 29 e0 ldi r18, 0x09 ;~~
~~ b2: 88 e1 ldi r24, 0x18 ;~~
~~ b4: 90 e0 ldi r25, 0x00 ;~~
~~ b6: 0f b6 in r0, 0x3f ; r0 = SREG~~
~~ b8: f8 94 cli ; disable interrupts~~
~~ ba: a8 95 wdr ; reset watchdog~~
~~ bc: 81 bd out 0x21, r24 ; WDTCSR = WDCE WDE~~
~~ be: 0f be out 0x3f, r0 ; restore interrupts (OOPS!)~~
~~ c0: 21 bd out 0x21, r18 ; WDTCSR = WDE WDP0~~

Interrupts are re-enabled one instruction too early

Nah. Interrupts actually don't get enabled till one instruction after the instruction that changes the status bits.
At least, that's what gcc assumes (the documentation only implies this when it defines the behavior of the RETI instruction), and it hasn't been declared wrong yet.

Oh crud! I forgot that. Sorry about the false alarm.

You have to get the ISR spelling right. For example, this compiles:

ISR (But_Does_It_Get_Goats_Blood_Out)
  {
  Serial.println ("no");
  }
  
void setup () {}
void loop () {}

However I don't believe the Atmega supports that But_Does_It_Get_Goats_Blood_Out interrupt.

Ok, I have been attempting to get this to work, using the above advice and I am not getting behavior that indicates the ISR is being executed.

Here is my latest code

#include <stdint.h>
#include <avr/interrupt.h>
#include <avr/wdt.h>
#include <util/atomic.h>

volatile byte TC0sample;
volatile unsigned int TC1sample;
volatile boolean sample_waiting = false;

void setup() {
  Serial.begin(115200);

  noInterrupts();

  // Clear the WDRF flag
  MCUSR &= ~(1 << WDRF);

  // Restart the watchdog
  wdt_reset();

  // Prepare to configure the watchdog
  _WD_CONTROL_REG = (1 << _WD_CHANGE_BIT) | (1 << WDE);
  // Configure the watchdog: clear the interrupt flag; enable interrupts;
  _WD_CONTROL_REG = (1 << WDIF) | (1 << WDIE);

  interrupts();

  Serial.println(F("TC0,TC1"));
}

void loop()
{
  if (sample_waiting) {
    ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
    {
      sample_waiting = false;
      Serial.print(TC0sample);
      Serial.print(F(","));
      Serial.print(TC1sample);
      Serial.println();
    }
  }
}

// Watchdog Timer Interrupt Service Routine
ISR(WDT_OVERFLOW_vect)
{
  TC0sample = TCNT0;
  TC1sample = TCNT1;
  sample_waiting = true;
}

and a sample of the output being produced...

TC0,TC1
ç,TC0,TC1
ü,TC0,TC1
ê,TC0,TC1
ç,TC0,TC1
ç,TC0,TC1
é,TC0,TC1
ô,TC0,TC1

and looking at the assembly dump, it appears the isr vector is being defined...

Disassembly of section .text:

00000000 <__vectors>:
   0:   18 c0           rjmp    .+48            ; 0x32 <__ctors_end>
   2:   3a c0           rjmp    .+116           ; 0x78 <__bad_interrupt>
   4:   39 c0           rjmp    .+114           ; 0x78 <__bad_interrupt>
   6:   38 c0           rjmp    .+112           ; 0x78 <__bad_interrupt>
   8:   37 c0           rjmp    .+110           ; 0x78 <__bad_interrupt>
   a:   36 c0           rjmp    .+108           ; 0x78 <__bad_interrupt>
   c:   0a c3           rjmp    .+1556          ; 0x622 <__vector_6>
   e:   fd c0           rjmp    .+506           ; 0x20a <__vector_7>
  10:   33 c0           rjmp    .+102           ; 0x78 <__bad_interrupt>
  12:   32 c0           rjmp    .+100           ; 0x78 <__bad_interrupt>
  14:   31 c0           rjmp    .+98            ; 0x78 <__bad_interrupt>
  16:   30 c0           rjmp    .+96            ; 0x78 <__bad_interrupt>
  18:   2f c0           rjmp    .+94            ; 0x78 <__bad_interrupt>
  1a:   2e c0           rjmp    .+92            ; 0x78 <__bad_interrupt>
  1c:   2d c0           rjmp    .+90            ; 0x78 <__bad_interrupt>
  1e:   2c c0           rjmp    .+88            ; 0x78 <__bad_interrupt>
  20:   2b c0           rjmp    .+86            ; 0x78 <__bad_interrupt>
  22:   2a c0           rjmp    .+84            ; 0x78 <__bad_interrupt>
  24:   64 c0           rjmp    .+200           ; 0xee <__vector_18>

...

000000ee <__vector_18>:
  }
}

// Watchdog Timer Interrupt Service Routine
ISR(WDT_OVERFLOW_vect)
{
  ee:   1f 92           push    r1
  f0:   0f 92           push    r0
  f2:   0f b6           in      r0, 0x3f        ; 63
  f4:   0f 92           push    r0
  f6:   11 24           eor     r1, r1
  f8:   8f 93           push    r24
  fa:   9f 93           push    r25
  TC0sample = TCNT0;
  fc:   82 b7           in      r24, 0x32       ; 50
  fe:   80 93 72 00     sts     0x0072, r24
  TC1sample = TCNT1;
 102:   8c b5           in      r24, 0x2c       ; 44
 104:   9d b5           in      r25, 0x2d       ; 45
 106:   90 93 74 00     sts     0x0074, r25
 10a:   80 93 73 00     sts     0x0073, r24
  sample_waiting = true;
 10e:   81 e0           ldi     r24, 0x01       ; 1
 110:   80 93 75 00     sts     0x0075, r24
}
 114:   9f 91           pop     r25
 116:   8f 91           pop     r24
 118:   0f 90           pop     r0
 11a:   0f be           out     0x3f, r0        ; 63
 11c:   0f 90           pop     r0
 11e:   1f 90           pop     r1
 120:   18 95           reti

I'm fairly certain there is not enough SRAM to run the program. I can't even get a stripped down version to run. The Standard Core Serial really was not meant to be used on such a tiny processor.

I have something that works but you will have to have an Arduino (compatible) or Teensy board in the middle.

Well, I have plenty of spare Arduinos hanging around, along with a bus pirate, presuming your thinking of SPI or I2C transfer using the USI. Example code would be helpful, I haven't done much with either bus.

The Standard Core Serial really was not meant to be used on such a tiny processor.

I thought the OP said that they were using the "tiny core" (but never exactly WHICH "tiny core." That might help.)

He did. The t2313 Serial in the Tiny Core is unmodified from the Arduino 0022 core. Serial's various buffers and vtable# take a huge chunk of the t2313's memory.

Tiny Debug Serial may work well in lieu of Hardware Serial. I'll test it tonight...

# I assume the vtable is stored in SRAM. I have not actually checked.

http://code.google.com/p/arduino-tiny has serial rx buffer size dependent on RAMEND, and no tx buffer...
OTOH, tn2313 only has 128 bytes of RAM, total...

I think Print::printNumber is pushing the application over the limit...
http://code.google.com/p/arduino-tiny/source/browse/trunk/hardware/tiny/cores/tiny/Print.cpp#192

...yup...
2 + 2 + 2 + 18 + 104 = 128

That's all available SRAM and I think I left out a few things.

@wanderson, you have several choices...

• Wait for me to send source code and instructions for the thing I mentioned in Reply #8. I think I can get to it this evening. It's a home brew bit-bang replacement for Serial that is fast and very small. I know for a fact it works for this application. The drawback is that it requires interrupts to be disabled while sending which may interfere with WDT_OVERFLOW_vect (the same potential problem exists with Tiny Debug Serial).

• Reduce the receive buffer size in HardwareSerial.cpp.

• Send the data in binary.

Any preference?

Thanks for the guidance. Given the information, I went ahead and dug up some simple serial transmission code I worked on in my pre-arduino days and used that to replace the Serial class. It worked a treat!

#include <stdlib.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/wdt.h>
#include <util/atomic.h>

volatile byte TC0sample;
volatile unsigned int TC1sample;
volatile boolean sample_waiting = false;
char xmitString[6];
const char EOS='\0';

void USART_TX(char data)
{
  while (!(UCSRA & (1 << UDRE))) ;
  UDR = data;
}

void setup() {
  UBRRH = 0;
  UBRRL = 12;   // Set baud to 38400 at 8MHz clock
  UCSRB = (1<<TXEN);
  UCSRC = (3<<UCSZ0);
  noInterrupts();
  // Clear the WDRF flag
  MCUSR &= ~(1 << WDRF);
  // Restart the watchdog
  wdt_reset();
  // Prepare to configure the watchdog
  _WD_CONTROL_REG = (1 << _WD_CHANGE_BIT) | (1 << WDE);
  // Configure the watchdog: clear the interrupt flag; enable interrupts;
  _WD_CONTROL_REG = (1 << WDIF) | (1 << WDIE);
  interrupts();
}

void loop()
{
  char *ptr;
  if (sample_waiting) {
    ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
    {
      sample_waiting = false;
      itoa(TC0sample, xmitString, 10);
      ptr = xmitString;
      while (*ptr != EOS)
        USART_TX(*ptr++);
      USART_TX(',');
      itoa(TC1sample, xmitString, 10);
      ptr = xmitString;
      while (*ptr != EOS)
        USART_TX(*ptr++);
      USART_TX('\n');
    }
  }
}

// Watchdog Timer Interrupt Service Routine
ISR(WDT_OVERFLOW_vect)
{
  TC0sample = TCNT0;
  TC1sample = TCNT1;
  sample_waiting = true;
}

Excellent!

I have a suggestion. Don't tell anyone but, because the data flow is in one direction, with a minor change, it is actually safe to eliminate the critical section...

if (sample_waiting) {
ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
{
sample_waiting = false;
...
sample_waiting = false;
}

ISR(WDT_OVERFLOW_vect)
{
if ( ! sample_waiting )
{
TC0sample = TCNT0;
TC1sample = TCNT1;
sample_waiting = true;
}
}

After finally convincing me of the need for the "critical" section, you go and demonstrate it isn't needed (in this case)! :slight_smile: