Using COMPA and COMPB correctly

Hi,
I am struggeling using CAMPA and COMPB correctly. I would like to start an action by an external interrupt on PIN2 (UNO) and start Timer1. After TIMER1 has started it shall use COMPA and COMPB to start certain actions, but NOT resetting the TIMER1. That reset shall happen later on special command at a later point in time. The script should enable and disable the LED13 on the UNO board. Here is the code I tried to start with COMPA and COMPB, but the LED won't go off...

// avr-libc library includes
#include <avr/io.h>
#include <avr/interrupt.h>

#define LEDPIN 13
int timer;
void setup()
{
  
  pinMode(LEDPIN, OUTPUT);
  cli();                    // disable global interrupts
  pinMode(2, INPUT);        // Enable Pin2 as input to interrupt
  EIMSK |= (1 << INT0);     // Enable external interrupt INT0  (see table 13-2-2 in Atmel 328 spec)
  EICRA |= (1 << ISC01);    // Trigger INT0 on rising edge (see table 13-1 in Atmel 328 spec)
  EICRA |= (1 << ISC00);    // Trigger INT0 on riding edge (see table 13-1 in Atmel 328 spec)

  // initialize Timer1
  // ZERO Timer1
  TCCR1A = 0;               // set entire TCCR1A register to 0
  TCCR1B = 0;               // same for TCCR1B

  // set compare match register to desired timer count:
  OCR1A = 15624;            // turn on CTC mode:initial value changed in loop
  OCR1B = 30000;            // turn on CTC mode:initial value changed in loop
  TCCR1B |= (1 << WGM12);   // Set CS10 and CS12 bits for 1024 prescaler:
  TCCR1B |= (1 << CS10);    // prescaler 1024
  TCCR1B |= (1 << CS12);    // prescaler 1024
  TIMSK1 |= (1 << OCIE1A);  // enable timer compare interrupt:
  TIMSK1 |= (1 << OCIE1B);  // enable timer compare interrupt:
  sei();                    // Enable global interrupts
}

void loop()
{}
ISR(TIMER1_COMPA_vect){digitalWrite(LEDPIN, HIGH);}
ISR(TIMER1_COMPB_vect){digitalWrite(LEDPIN, LOW);}

I don't see an ISR for INT0 (Pin 2).

You are also setting the Waveform Generator Mode to 4 (only WGM12 set) which resets the timer to 0 when it reaches the value in OCR1A. You will never reach the value in OCR1B so that interrupt will never occur. You probably want Mode 0 where the timer counts up to FFFF and then re-starts at 0. After you reset the timer the LED should come on about a second later and stay on for 0.92 seconds. The who cycle will then repeat every about every 4.2 seconds.

Hi johnwasser,
I improved my sketch and the compA and compB are working. I let the LED on pin13 switch on (compA) and off (compB) which works well.

What is missing/ not working is:

  • External interrupt on INT0 on rising flag
  • this interrupt starts the timer1 starting at zero
  • stopping the timer1 with compB

Is that possible?

// avr-libc library includes
#include <avr/io.h>
#include <avr/interrupt.h>
#define LEDPIN 13
// int timer;
// unsigned int counter;

void setup()
{
  Serial.begin(115200);
  pinMode(LEDPIN, OUTPUT);
  pinMode(2, INPUT);        // Enable Pin2 as input to interrupt

  cli();                    // disable global interrupts
  EIMSK |= (1 << INT0);     // Enable external interrupt INT0  (see table 13-2-2 in Atmel 328 spec)
  EICRA |= (1 << ISC00);    // Trigger INT0 on rising edge (see table 13-1 and 13-2 in Atmel 328 spec)
  EICRA |= (1 << ISC01);    // Trigger INT0 on rising edge (see table 13-1 and 13-2 in Atmel 328 spec)


    // set compare match register to desired timer count:
  OCR1A = 100;            // COMPA
  OCR1B = 50000;          // COMPB

  TCCR1B = 0;               // set entire  TCCR1B register to 0
  TCCR1B |= (1 << CS12);    // prescaler 256- see table 16-5

//***Not shiure if this is right...
  TCCR1A = 0;               // set entire TCCR1A register to 0
  TCCR1A |= (1 << COM1B0);    // Set OC1A/OC1B on Compare Match (Set output to high level).- see table 16-1
  TCCR1A |= (0 << COM1B1);    // Set OC1A/OC1B on Compare Match (Set output to high level).- see table 16-1
  TCCR1A |= (1 << COM1B0);    // Set OC1A/OC1B on Compare Match (Set output to high level).- see table 16-1
  TCCR1A |= (0 << COM1B1);    // Set OC1A/OC1B on Compare Match (Set output to high level).- see table 16-1
//***Not shiure if this is right...

  
  TIMSK1 |= (1 << OCIE1B);  // enable timer compare interrupt channel B:
  TIMSK1 |= (1 << OCIE1A);  // enable timer compare interrupt channel A:


  sei();                    // Enable global interrupts
  attachInterrupt(0, start, RISING);
}

void loop()
{
}

ISR(EXT_INT0_vect){    
//Reset Timer1
TCCR1A = 0;               // set entire TCCR1A register to 0
TCCR1B = 0;               // same for TCCR1B and setting timer1 to 0
}


void start()
{
//want to start the timer here

//TCNT1H=0; //To do a 16-bit write, the high byte must be written before the low byte
//TCNT1L=0; //To do a 16-bit write, the high byte must be written before the low byte

}

ISR(TIMER1_COMPA_vect){
  digitalWrite(LEDPIN, HIGH);
  Serial.println("NUN");
}

ISR(TIMER1_COMPB_vect){
  digitalWrite(LEDPIN, LOW);

 // digitalWrite(LEDPIN, !digitalRead(LEDPIN));
 Serial.println("AHA");
//want to stop the timer here
}

It looks to me like you are starting the timer in setup() and then calling start() which does nothing. The code in the ISR looks like it stops the timer by setting the control registers to 0.

Perhaps you should move the code that starts the timer into start() and call it from your external interrupt instead of from setup().

Then you can take the code the stops the timer, put it in a function called stop() and call it from the COMPB interrupt to stop the timer.

Hi,
the "attachInterrupt(0, start, RISING);" was a try to get the external interrupt running because I couldn't make it with real "C" :blush: . And the ISR(EXT_INT0_vect) function is basically the double cover to the attachInterrupt...but both doesn't work... :~

The code in the ISR looks like it stops the timer by setting the control registers to 0.

I wanted to put the counter at least to zero. I again read the datasheet and should have put a zero value to the TCNT1 register and not to TCCR1* control register , right?

Perhaps you should move the code that starts the timer into start() and call it from your external interrupt instead of from setup().

I did not know that I started the timer...I thought it would be running anyhow. How can I start and stop it?

soulid:

The code in the ISR looks like it stops the timer by setting the control registers to 0.

I wanted to put the counter at least to zero. I again read the datasheet and should have put a zero value to the TCNT1 register and not to TCCR1* control register , right?

Yes.

soulid:

Perhaps you should move the code that starts the timer into start() and call it from your external interrupt instead of from setup().

I did not know that I started the timer...I thought it would be running anyhow. How can I start and stop it?

The timer starts when you set the Clock Select Bits in TCCR1B to something other than 0.

Hi,
changed the code as proposed. There is "a" reaction to what I have written, but not as expected. The (red) Interrupt rising line is not very close to the (blue) trigger HIGH expected 20 ticks after. Changing the CompB from 100 to 10.000 (which is intended to increase pulseduration but keeping the rising edge at the same position) changes the rising edge position.
Here is the code and a oscilloscope screenshot (blue line is trigger and red line is interrupt reaction- time/ dev= 50mS):

#include <avr/io.h>
#include <avr/interrupt.h>
#define LEDPIN 13

void setup(){  
  pinMode(LEDPIN, OUTPUT);
  cli();                    // disable global interrupts
  TIMSK1 |= (1 << OCIE1A);  // enable timer compare interrupt channel A:
  TIMSK1 |= (1 << OCIE1B);  // enable timer compare interrupt channel B:
//  TCCR1A = 0;               // set entire TCCR1A register to 0
  TCCR1A = B11110000;      //see table 16-1
  sei();                    // Enable global interrupts
  attachInterrupt(0, start, RISING);
}

void loop(){
 OCR1A = 20;            // COMPA
 OCR1B = 40;          // COMPB
}

void start(){
  //want to start/ restart the timer here
  TCCR1B |= (1 << CS10);    // prescaler 1- see table 16-5 and (re)start timer1
}

ISR(TIMER1_COMPA_vect){
  digitalWrite(LEDPIN, HIGH);
}

ISR(TIMER1_COMPB_vect){
  digitalWrite(LEDPIN, LOW);
  TCCR1B = 0;  //stop timer here
}
}

COMPA and COMPB-compA20-compB100.png

COMPA and COMPB-compA20-compB10000.png

The pulses look OK to me.

100-20=80 80/16=5 microseconds. at 0.5 milliseconds (500 microseconds) per division the pulse would be about 1/100th of a division.

10000-20 = 9980. 9980/16=623 microseconds or a little over 1 division.

I suspect the long delay before triggering is a wrap-around of the timer. Try resetting more of the registers:

#include <avr/io.h>
#include <avr/interrupt.h>
#define LEDPIN 13

void setup(){  
  pinMode(LEDPIN, OUTPUT);
  cli();                    // disable global interrupts
  TCCR1A = 0;               // Clear TCCR1A
  TCCR1B = 0;               // Clear TCCR1B
  TCCR1C = 0;               // Clear TCCR1C
  TCNT1 = 0;                // Clear the timer
  TIMSK1 = 0;               // Clear the interrupt mask
  
  TIMSK1 |= (1 << OCIE1A);  // enable timer compare interrupt channel A:
  TIMSK1 |= (1 << OCIE1B);  // enable timer compare interrupt channel B:
  TCCR1A = B11110000;      // Set OC1A/OC1B on Compare Match, clear OC1A/OC1B at BOTTOM
  OCR1A = 20;              // COMPA at 20/16ths microseconds
  OCR1B = 40;              // COMPB at 40/16th microseconds

  sei();                    // Enable global interrupts
  attachInterrupt(0, start, RISING);
}

void loop(){
}

void start(){
  //want to start/ restart the timer here
  TCNT1 = 0;                // Clear the timer
  TCCR1B |= (1 << CS10);    // prescaler=1 16 MHz clock
}

ISR(TIMER1_COMPA_vect){
  digitalWrite(LEDPIN, HIGH);
}

ISR(TIMER1_COMPB_vect){
  digitalWrite(LEDPIN, LOW);
  TCCR1B = 0;  //stop timer here
}

Your code works! Jihaa! :grin:

I tried several values and found a "base delay" of abput 12.5us meaning that there is no Interupt control below that. From that value on the control works , but I can live with that.

Thank you for your brilliant help!

soulid:
Your code works! Jihaa! :grin:

I tried several values and found a "base delay" of abput 12.5us meaning that there is no Interupt control below that. From that value on the control works , but I can live with that.

Thank you for your brilliant help!

The digitalWrite() function takes a while and probably explains much of your 12.5 microsecond delay. You can get faster response using direct port access. On the UNO, Pin 13 is PORTB bit 5. (See: Arduino Pins - Google Drive )

To set it HIGH:

PORTB |= 0b00100000;

To set it LOW:

PORTB &= 0b11011111;

Opening a very old subject I got solved with the great help of johnwasser. :smiley:
After having COMPA and COMPB running well on Arduino UNO, I want to transfer this behavior to Arduino MEGA. :

  • Input interrupt pin is PIN2 (so timer 3, right?)
  • Output is PIN6 and 13

I am reading the 2560 datasheet but I am failing somewhere :confused:

In "start" interrupt, the timer TCNT3 is cleared and by setting the prescaler TCCR3B CS30 to "1" and to 16MHz, the timer3 is starting.
When meeting COMPA value the LEDs is intendet to be switched ON and with COMPB the LEDs shall switch OFF again.
The code works so far that it starts the "start()" section, but the COMPA and COMPB does not work.

Any idea why not?

//For Arduino Mega2560

#include <avr/io.h>
#include <avr/interrupt.h>
#define LEDPIN 13
#define LEDPIN1 6

//*********************************PIN für den Pickup*********************
const byte interruptPin = 2;

//**************************KALIBRIERTABELLE***************************************

void setup() {
  Serial.begin(57600);// start serial for output. Make sure you set your Serial Monitor to the same!
  pinMode(LEDPIN, OUTPUT);
  pinMode(LEDPIN1, OUTPUT);
  cli();                    // disable global interrupts
  TCCR3A = 0;               // Clear TCCR1A
  TCCR3B = 0;               // Clear TCCR1B
  TCCR3C = 0;               // Clear TCCR1C
  TCNT3 = 0;                // Clear the timer
  TIMSK3 = 0;               // Clear the interrupt mask

  TIMSK3 |= (1 << OCIE1A);  // enable timer compare interrupt channel A:
  TIMSK3 |= (1 << OCIE1B);  // enable timer compare interrupt channel B:
  TCCR3A = B11110000;      // Set OC1A/OC1B on Compare Match, clear OC1A/OC1B at BOTTOM
  OCR3A = 20;              // COMPA at 20/16ths microseconds
  OCR3B = 50;              // COMPB at 40/16th microseconds

  sei();                    // Enable global interrupts
  pinMode(interruptPin, INPUT_PULLUP);
  attachInterrupt(0, start, CHANGE);
}

void loop() {
    

}

void start() {
  //want to start/ restart the timer here
  TCNT3 = 0;                // Clear the timer
  TCCR3B |= (1 << CS30);    // prescaler=1 16 MHz clock
//digitalWrite(LEDPIN1, HIGH);
}

ISR(TIMER1_COMPA_vect) {
  digitalWrite(LEDPIN, HIGH);
  digitalWrite(LEDPIN1, HIGH);
}

ISR(TIMER1_COMPB_vect) {
  digitalWrite(LEDPIN, LOW);
  digitalWrite(LEDPIN1, LOW);
  TCCR3B = 0;  //stop timer here
}

Needed another 6 hrs to get this running. But here it is- the Mega answers the trigger with about +/- 2-4ys

Here is the code:

//For Arduino Mega2560

#include <avr/io.h>
#include <avr/interrupt.h>
#define LEDPIN 13
#define LEDPIN1 6
int ledState = LOW;             // ledState used to set the LED
int ledState1 = LOW;             // ledState used to set the LED
//*********************************PIN für den Pickup*********************
const byte interruptPin = 2;

//**************************KALIBRIERTABELLE***************************************

void setup() {
  Serial.begin(57600);// start serial for output. Make sure you set your Serial Monitor to the same!
  pinMode(LEDPIN, OUTPUT);
  pinMode(LEDPIN1, OUTPUT);
  cli();                    // disable global interrupts
  TCCR3A = 0;               // Clear TCCR1A
  TCCR3B = 0;               // Clear TCCR1B
  TCCR3C = 0;               // Clear TCCR1C
  TCNT3 = 0;                // Clear the timer
  TIMSK3 = 0;               // Clear the interrupt mask

  TIMSK3 |= (1 << OCIE3A);  // enable timer compare interrupt channel A:
  TIMSK3 |= (1 << OCIE3B);  // enable timer compare interrupt channel B:
  TCCR3A = B11110000;      // Set OC1A/OC1B on Compare Match, clear OC1A/OC1B at BOTTOM
  
  OCR3A = 6;              // COMPA 
  OCR3B = 15;              // COMPB 

  sei();                    // Enable global interrupts
  pinMode(interruptPin, INPUT_PULLUP);
  attachInterrupt(0, start, FALLING);
 
}

void loop() {
}

void start() {
  //want to start/ restart the timer here
  TCNT3 = 0;                // Clear the timer
  TCCR3B |= (1 << CS31)|(1 << CS30);    // prescaler=64
  TCCR3B |= (1 << WGM33)|(1 << WGM32);    // Enable CTC
 }

ISR(TIMER3_COMPA_vect) {
  PORTH = 0b00001000;
  }

ISR(TIMER3_COMPB_vect) {
  PORTH = 0b00000000;
  TCCR3B = 0;  //stop timer here
  }