1

I'm trying to control an Arduino Barebone from an arduino with relays. Basically when the attiny starts it should switch on the arduino for 4 seconds. The Arduino sends a signal back to a digital pin of the attiny and as long as it is set to high the attiny should leave it working. If it is low the attiny should switch the relay of.

the problem is some changes to the code make the interrupt reset the attiny (which would switch of the arduino) othertimes- in this case. the interrupt doesn't work at all.

#include <avr/sleep.h>
#include <avr/wdt.h>
#include <avr/io.h>
#include <avr/interrupt.h>

#ifndef cbi
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#endif
#ifndef sbi
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
#endif

int pinBoard = 0;
bool boardOn = false;
bool startBoard_flag = false;
int pinBoardReturn = 3;

volatile boolean f_wdt = 1;

void setup() {
  cli();//disable interrupts during setup
  pinMode(pinBoard, OUTPUT);
  pinMode(pinBoardReturn, INPUT);
  setup_watchdog(9);
  cbi(ADCSRA, ADEN);                   // switch Analog to Digitalconverter OFF
  // cli();//disable interrupts during setup
  for (int i = 0; i < 10; i++) {
    digitalWrite(pinBoard, HIGH);
    delay(100);
    digitalWrite(pinBoard, LOW);
    delay(100);
  }

  PCMSK |= (1 << PCINT1); //sbi(PCMSK,INTERRUPTPIN) also works but I think this is more clear // tell pin change mask to listen to pin2 /pb3 //SBI
  GIMSK |= (1 << PCIE);   // enable PCINT interrupt in the general interrupt mask //SBI

  startBoard();

  DDRB &= ~(1 << DDB1); //cbi(DDRB, DATADIRECTIONPIN);//  set up as input  - pin2 clear bit  - set to zero
  PORTB |= (1 << PB1); //cbi(PORTB, PORTPIN);// disable pull-up. hook up pulldown resistor. - set to zero
}


void startBoard() {
  digitalWrite(pinBoard, HIGH);
  boardOn = true;
  startBoard_flag = false;
  cli();                                  // Disable interrupts
  delay(4000);
}

void shutdownBoard() {
  digitalWrite(pinBoard, LOW);
  boardOn = false;
}

void loop() {
  if (f_wdt == 1) { // wait for timed out watchdog / flag is set when a watchdog timeout occurs
    f_wdt = 0;     // reset flag
    if (startBoard_flag) {
      startBoard();
    }
    bool boardReturn = digitalRead(pinBoardReturn);
    if (boardOn && !boardReturn) {
      shutdownBoard();
    }
    system_sleep();
  }
}

//****************************************************************
// set system into the sleep state
// system wakes up when wtchdog is timed out
void system_sleep() {

  // GIMSK |= _BV(PCIE);                     // Enable Pin Change Interrupts
  // PCMSK |= _BV(PCINT3);                   // Use PB3 as interrupt pin

  set_sleep_mode(SLEEP_MODE_PWR_DOWN);    // replaces above statement
  sleep_enable();                         // Sets the Sleep Enable bit in the MCUCR Register (SE BIT)
  sleep_cpu();                            // sleep

  // PCMSK &= ~_BV(PCINT3);                  // Turn off PB3 as interrupt pin
  sleep_disable();                        // Clear SE bit

  // sei();                                  // Enable interrupts
}

//****************************************************************
// 0=16ms, 1=32ms,2=64ms,3=128ms,4=250ms,5=500ms
// 6=1 sec,7=2 sec, 8=4 sec, 9= 8sec
void setup_watchdog(int ii) {

  byte bb;
  int ww;
  if (ii > 9 ) ii = 9;
  bb = ii & 7;
  if (ii > 7) bb |= (1 << 5);
  bb |= (1 << WDCE);
  ww = bb;

  MCUSR &= ~(1 << WDRF);
  // start timed sequence
  WDTCR  |= (1 << WDCE) | (1 << WDE);
  // set new watchdog timeout value
  WDTCR  = bb;
  WDTCR  |= _BV(WDIE);
}
//****************************************************************
// Watchdog Interrupt Service / is executed when  watchdog timed out
ISR(WDT_vect) {
  f_wdt = 1; // set global flag
}

ISR(PCINT_VECTOR) {
  for (int i = 0; i < 4; i++) {
    digitalWrite(pinBoard, HIGH);
    delay(1000);
    digitalWrite(pinBoard, LOW);
    delay(1000);
  }
  startBoard_flag = true;
  f_wdt = 1; // set global flag
}
4
  • 2
    Sequence like "cli(); delay(...);" is not a very good idea as delay() depends on Timer interrupts. Feb 24, 2016 at 13:59
  • @MikaelPatel Is it? I believe it's just a loop with "NOP" instructions? Wait, this: forum.arduino.cc/index.php?topic=46452.0 - tells me it uses milis, which does use a timer I believe! (AVR delay functions didn't use timers).
    – Paul
    Feb 24, 2016 at 18:27
  • @Paul Might a well check the source code. Do you have a link to the ATtiny core that you are using? Let's check delay(). Feb 24, 2016 at 19:08
  • So i would rather have a counter for the watchdog in order to wait for the specified time?
    – tinkerly
    Feb 24, 2016 at 19:28

1 Answer 1

4

Disassembling the delay code indicates it uses micros which needs interrupts on to work.

void delay(unsigned long ms)
{
 27a:   cf 92           push    r12
 27c:   df 92           push    r13
 27e:   ef 92           push    r14
 280:   ff 92           push    r15
 282:   cf 93           push    r28
 284:   df 93           push    r29
 286:   6b 01           movw    r12, r22
 288:   7c 01           movw    r14, r24
    uint16_t start = (uint16_t)micros();
 28a:   d3 df           rcall   .-90        ; 0x232 <micros>
 28c:   eb 01           movw    r28, r22

    while (ms > 0) {
 28e:   c1 14           cp  r12, r1
 290:   d1 04           cpc r13, r1
 292:   e1 04           cpc r14, r1
 294:   f1 04           cpc r15, r1
 296:   79 f0           breq    .+30        ; 0x2b6 <delay+0x3c>
        yield();
 298:   ea d0           rcall   .+468       ; 0x46e <yield>
        if (((uint16_t)micros() - start) >= 1000) {
 29a:   cb df           rcall   .-106       ; 0x232 <micros>
 29c:   6c 1b           sub r22, r28
 29e:   7d 0b           sbc r23, r29
 2a0:   68 3e           cpi r22, 0xE8   ; 232
 2a2:   73 40           sbci    r23, 0x03   ; 3
 2a4:   a0 f3           brcs    .-24        ; 0x28e <delay+0x14>
            ms--;
 2a6:   81 e0           ldi r24, 0x01   ; 1
 2a8:   c8 1a           sub r12, r24
 2aa:   d1 08           sbc r13, r1
 2ac:   e1 08           sbc r14, r1
 2ae:   f1 08           sbc r15, r1
            start += 1000;
 2b0:   c8 51           subi    r28, 0x18   ; 24
 2b2:   dc 4f           sbci    r29, 0xFC   ; 252
 2b4:   ec cf           rjmp    .-40        ; 0x28e <delay+0x14>
        }
    }
}
 2b6:   df 91           pop r29
 2b8:   cf 91           pop r28
 2ba:   ff 90           pop r15
 2bc:   ef 90           pop r14
 2be:   df 90           pop r13
 2c0:   cf 90           pop r12
 2c2:   08 95           ret

That appears to confirm that the code in wiring.c is indeed what is being used on the ATtiny85 core:

void delay(unsigned long ms)
{
    uint16_t start = (uint16_t)micros();

    while (ms > 0) {
        yield();
        if (((uint16_t)micros() - start) >= 1000) {
            ms--;
            start += 1000;
        }
    }
}

Turning interrupts off and then doing a delay is a Bad IdeaTM.


So i would rather have a counter for the watchdog in order to wait for the specified time?

Using the watchdog timer is certainly one way of waiting for 4 seconds.

2
  • Excellent effort and description. Feb 25, 2016 at 5:51
  • +1 for the Bad Idea(TM). I like it! The rest of the answer deserves it too of course.
    – jfpoilpret
    Feb 25, 2016 at 12:29

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.