1

I'm writing a program on attiny85 using interrupts. It is a simple program: - when rising edge is detected on PCINT0, drive DEBUG_PIN to HIGH - when TIMER1 counts up to OCR1A, drive DEBUG_PIN to LOW

Problem is that when program detects rising edge on PCINT0, it starts Timer1 comparator A and then immediately calls ISR(TIMER1_COMPA_vect). As an effect, on DEBUG_PIN there is only a little narrow spike instead of a nice square wave of length 50ms (checked it with oscilloscope).

Im using ARDUINO IDE. Here is the code:

/*
 * Movement detection using interrupts instead of linear approach
 * 
 * POS_TIMEOUT will be checked using COMPA on TIM0
 * IMP_TIMEOUT will be checked using COMPB on TIM0
 * LOOP_TIME will be chcecked using TIMER1 overflow
 * 
 * INT0_PIN_IN  (PB2) is the pin that receives the signal
 * INT0_PIN_OUT (PB4) is the pin used for external interrupt debugging
 * DEBUG_PIN    (PB3) is the pin used for debugging
 * STATUS_PIN   (PB1) - high when awake, low when asleep
*/

#include <avr/sleep.h> 
#include <avr/power.h>
#include <avr/wdt.h>
#include <avr/interrupt.h>
//#include "timers_init.h"

//Watch out! do not use the reset pin (PB5)!

#define INT0_PIN_IN     0
#define INT0_PIN_OUT    4
#define STATUS_PIN      1
#define DEBUG_PIN       3    
#define POS_TIMEOUT     50

#define PRE_1     0
#define PRE_2     1
#define PRE_4     2
#define PRE_8     3
#define PRE_16    4
#define PRE_32    5
#define PRE_64    6
#define PRE_128   7
#define PRE_256   8
#define PRE_512   9
#define PRE_1024  10
#define PRE_2048  11
#define PRE_4096  12
#define PRE_8192  13
#define PRE_16384 14


/*******MONITORING*VARIABLES*VOLATILES**************/
/* variable keeping last state of INT0 input */
volatile bool lastState = false;

/*************STATUS FLAGS**************************/
/* flags used for movement detection */
/* all flags are initiated as false */

volatile bool positiveFlag  = false;
volatile bool fallingEdge   = false;
volatile bool risingEdge    = false;
volatile bool positiveTimeoutCheck  = false;
volatile bool impulseTimeoutCheck = false;
/*************UTILS*********************************/
void timerOneCompADisable( void ){
  bitClear(TIMSK, OCIE1A);
}

void timerOneCompAInit( uint8_t compValue  ){
  //set comparatorA value
  OCR1A = compValue;
  //enable compA interrupts
  bitSet(TIMSK, OCIE1A);
}

/******************
 * INTERRUPTS!!!
******************/

/*
 * This interrupt is called every time PCINT0 input state changes
*/
ISR(PCINT0_vect){
  bool readPin = digitalRead( INT0_PIN_IN );

  //falling edge
  if( lastState && lastState != readPin ){
    fallingEdge = true;
    lastState = false;
  }
  //rising edge
  else if( !lastState && lastState != readPin ){
    risingEdge = true;
    lastState = true;
  }
}

//TIMER 1 COMPARE match A
ISR(TIMER1_COMPA_vect){
  positiveTimeoutCheck = false;
  digitalWrite( DEBUG_PIN, LOW );
  timerOneCompADisable();
}

/**********************
 * MAIN CODE!!!!!!!
**********************/

void setup(){

  /* Pin config */
  pinMode( INT0_PIN_IN, INPUT );
  pinMode( DEBUG_PIN, OUTPUT);

  /* Pin initialization */
  digitalWrite( DEBUG_PIN, LOW );

  /* Timers configuration */
  //prescaler is 8192 so one tick is ~1 ms
  TCCR1 |= (1<<CS13)|(1<<CS12)|(1<<CS11)|(0<<CS10);

  /* Interrupts initialization */
  bitSet( GIMSK, PCIE );
  bitSet( PCMSK, PCINT0 );
}

/* function resetting flags */
void resetFlags( void ){
   positiveFlag  = false;
   fallingEdge   = false;
   risingEdge    = false;
   positiveTimeoutCheck  = false;
}

int main(){
  cli();
  setup();

  /* Variables initialization */
  lastState = digitalRead( INT0_PIN_IN );
  resetFlags();
  sei();

  while(1){
      /* State machine */

      //if nothing is going on and rising edge is detected
      //reset everything and ignite positive flag
      if( risingEdge ){
        cli();
        resetFlags();
        positiveFlag = true;
        sei();
      } // if

      //INT0 is high, initiate positive timeout check
      else if( positiveFlag && !positiveTimeoutCheck ){
        cli();
        resetFlags();
        timerOneCompAInit( POS_TIMEOUT );
        positiveTimeoutCheck = true;
        digitalWrite( DEBUG_PIN, HIGH );
        TCNT1 = 0;
        sei();
      } //else if

  } //while

  return 0;
}

3 Answers 3

2

Thank you guys! All I had to do is disable TIMER1, reset it, set COMPA, reset OCF1A ("bitSet(TIFR,OCF1A)") and set prescaler before sei();.

Here is my final, working code:

#include <avr/sleep.h> 
#include <avr/power.h>
#include <avr/wdt.h>
#include <avr/interrupt.h>
//#include "timers_init.h"

//Watch out! do not use the reset pin (PB5)!

#define INT0_PIN_IN     0
#define INT0_PIN_OUT    4
#define STATUS_PIN      1
#define DEBUG_PIN       3    
#define POS_TIMEOUT     70

#define PRE_1     0
#define PRE_2     1
#define PRE_4     2
#define PRE_8     3
#define PRE_16    4
#define PRE_32    5
#define PRE_64    6
#define PRE_128   7
#define PRE_256   8
#define PRE_512   9
#define PRE_1024  10
#define PRE_2048  11
#define PRE_4096  12
#define PRE_8192  13
#define PRE_16384 14


/*******MONITORING*VARIABLES*VOLATILES**************/
/* variable keeping last state of INT0 input */
volatile bool lastState = false;

/*************STATUS FLAGS**************************/
/* flags used for movement detection */
/* all flags are initiated as false */

volatile bool positiveFlag  = false;
volatile bool fallingEdge   = false;
volatile bool risingEdge    = false;
volatile bool positiveTimeoutCheck  = false;
volatile bool impulseTimeoutCheck = false;
/*************UTILS*********************************/
void timerOneCompADisable( void ){
  bitClear(TIMSK, OCIE1A);
}

void timerOneCompAInit( uint8_t compValue  ){
  //set comparatorA value
  OCR1A = compValue;
  //enable compA interrupts
  bitSet(TIMSK, OCIE1A);
}

void timerOneDisable(){
  TCCR1 |= (0<<CS13)|(0<<CS12)|(0<<CS11)|(0<<CS10);
}

/******************
 * INTERRUPTS!!!
******************/

/*
 * This interrupt is called every time PCINT0 input state changes
*/
ISR(PCINT0_vect){
  bool readPin = digitalRead( INT0_PIN_IN );

  //falling edge
  if( lastState && lastState != readPin ){
    fallingEdge = true;
    lastState = false;
  }
  //rising edge
  else if( !lastState && lastState != readPin ){
    risingEdge = true;
    lastState = true;
  }
}

//TIMER 1 COMPARE match A
ISR(TIMER1_COMPA_vect){
  positiveTimeoutCheck = false;
  digitalWrite( DEBUG_PIN, LOW );
  TCNT1=0;
  timerOneCompADisable();
}

/**********************
 * MAIN CODE!!!!!!!
**********************/

void setup(){

  /* Pin config */
  pinMode( INT0_PIN_IN, INPUT );
  pinMode( DEBUG_PIN, OUTPUT);

  /* Pin initialization */
  digitalWrite( DEBUG_PIN, LOW );

  /* Timers configuration */
  //prescaler is 8192 so one tick is ~1 ms
  TCCR1 |= (1<<CS13)|(1<<CS12)|(1<<CS11)|(0<<CS10);

  /* Interrupts initialization */
  bitSet( GIMSK, PCIE );
  bitSet( PCMSK, PCINT0 );
}

/* function resetting flags */
void resetFlags( void ){
   positiveFlag  = false;
   fallingEdge   = false;
   risingEdge    = false;
   positiveTimeoutCheck  = false;
}

int main(){
  cli();
  setup();

  /* Variables initialization */
  lastState = digitalRead( INT0_PIN_IN );
  resetFlags();
  sei();

  while(1){
      /* State machine */

      //if nothing is going on and rising edge is detected
      //reset everything and ignite positive flag
      if( risingEdge ){
        cli();
        resetFlags();
        positiveFlag = true;
        sei();
      } // if

      //INT0 is high, initiate positive timeout check
      else if( positiveFlag && !positiveTimeoutCheck ){
        cli();
        resetFlags();
        timerOneDisable();
        timerOneCompAInit( POS_TIMEOUT );
        positiveTimeoutCheck = true;
        digitalWrite( DEBUG_PIN, HIGH );
        bitSet(TIFR,OCF1A);
        TCNT1 = 0;
        TCCR1 |= (1<<CS13)|(1<<CS12)|(1<<CS11)|(0<<CS10);
        sei();
      } //else if

  } //while

  return 0;
}
1

You should move the line TCNT1 = 0; from loop() into timerOneCompAInit() function:

void timerOneCompAInit( uint8_t compValue  ){
  TCNT1 = 0;
  //set comparatorA value
  OCR1A = compValue;
  //enable compA interrupts
  bitSet(TIMSK, OCIE1A);
}

Otherwise, during the timelapse between OCR1A = compValue; and TCNT1 = 0;, it is highly possible that TCNT1 reaches OCR1A and thus sets the OCF1A flag in TIFR, which will generate an interrupt and call ISR(TIMER1_COMPA_vect) immediately after sei() is called in loop().

The fact is Timer1 is always running since it has been enabled at setup(), hence TCNT1 is continuously changing from 0 to 255 and back to 0.

Hence in your current code, at the time you execute OCR1A = compValue;, TCNT1 may have any value between 0 and 255, if its value is >50 (POS_TIMEOUT), then an interrupt will be generated.

Roughly the probability for this interrupt to happen is at least 80% (i.e. (255-50)/255).

1

the code is a little too long for me to go through but sounds like this is a one-shot compare.

With that, you should turn off the timer1, clear the timer1 compa flag, set the compare match point forward (COMPA = target count if TCNT1 is reset, or COMPA = TCNT1 + target if TCNT1 is NOT reset), enable the timer1 compa interrupt, and start the timer1. All of that in the PCNT1 interrupt (where timer1 is armed).

Some of the actions can be structured in the timer1 initialization routine, or even after timer1 compa isr if you want to improve the code responsiveness.

Otherwise, your timer1 keeps running and once the counter passes COMPA, the flag is set, regardless of if the interrupt is set or not.

2
  • 1
    Man, youre great!!!! All I had to do was to: 1. Disable TIM1; 2. Reset TIM1; 3. Set COMPA 4.Enable TIM1 5.Enable interrupts . One more question: is there a way to disable timer (make it stop counting) without clearing its prescaler bits?
    – Em Ka
    Apr 22, 2017 at 14:57
  • there is no "prescaler counter" in the avrs so the answer is no (in general). in some cases, you can read the prescaler -> by externally switching the input to a gpio pin and flip that pin until the counter advances. But that doesn't seem to be the case here.
    – dannyf
    Apr 22, 2017 at 16:39

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.