First C program in Arduino IDE has interrupt error

Go To Last Post
11 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

This is my very first C program using Arduino IDE running in Win10.

I'm learning C programming on my own with books and Youtube videos.

The learning curve is quite steep.

My Arduino Nano clone was previously uploaded with example sketch "Blink".

I'm trying a test program to toggle the user LED at 2 second intervals using
Timer1 interrupts. None of the IDE examples have interrupts so I don't know
how to tell the IDE about interrupts. I've tried various forms of the ISR statement
but always an error.

 

The following is my program code:

  // Arduino NANO, 5V, 16 MHz, oldbootloader
  // This test program toggles the user LED every two seconds
  // using Timer1 Interrupts
  
#include "Arduino.h"
  
void setup() {
  // put your setup code here, to run once:
  DDRB |= (1<<5);       //PORTB.5 as output, is user LED
  CLKPR = 0x80;         //clk prescaler change enable
  CLKPR = 0x06;          //set prescaler 64
                        //new cpu clk = 16MHz/64 = 250,000Hz
     //
  TIMSK1 = 0x01;        //enable Timer1 overflo interrupts 
  TCCR1B = 0x02;        //Timer1 prescaler = 8; start Timer1
        //Timer1 clk = 250,000Hz/8 = 31,250Hz
        //Timer1 will overflow in 65535/31250 = 2.09 sec
  sei();               //enable global interrupts       
}

void loop() {
  // put your main code here, to run repeatedly:
  while (1);               //wait here for interrupts           
}
void ISR Timer1_OVF_vect()     //ISR when TIMER1 overflows
{
  PORTB ^=  (1<<5);        //toggle PORTB.5, the user LED
}

 

 

A typical compile error is:

sketch_dec30a_tog2:25:10: error: expected initializer before 'Timer1_OVF_vect'

 void ISR Timer1_OVF_vect()    //ISR when TIMER1 overflows

 

Of course, I have no idea what the correct form of the ISR statement should be.

Suggestions greatly appreciated.

 

This topic has a solution.

Skier

Last Edited: Tue. Dec 31, 2019 - 05:35 PM
This reply has been marked as the solution. 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The function signature should be:
ISR(TIMER1_OVF_vect)

Edit: fixed typo

/Jakob Selbing

Last Edited: Mon. Dec 30, 2019 - 10:50 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

More generally you probably need to read this page in the user manual of the compiler you are using..

 

https://www.nongnu.org/avr-libc/...

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Here is an example using interrupts with a rotary encoder, you may find it helpful.

/*
   ROTARY ENCODER KY-040 on Arduino UNO    -   May 2014

  WIRING INFORMATION
  ===================
  Connect CLK to Pin 2 on Arduino Board  (CLK is Data Output 1 of KY-040)
  Connect DT  to Pin 3 on Arduino Board  (DT is Data Output 2 of KY-040)
  Connect SW  to Pin 4 on Arduino Board  (Switch - goes LOW when pressed)
  Connect GND to ground
  Connect +   to +5V  (this will pull up CLK and DT with 10 KiloOhm resistors)
  ----------------------------------------------------------------------------
  Connect a 0,47µ capacitor from ground to CLK   (debouncing)
  Connect a 0,47µ capacitor from ground to DT    (debouncing)
  Connect a 10 KiloOhm resistor from +5V to SW (no integrated pullup for SW !!)
  ----------------------------------------------------------------------------
  It is better NOT to use internal pull-up resistors on the Arduino, instead
  use the integrated pull-ups of KY-040 (this requires "+" to be connected to 5V).
  You can check if your version of the KY-040 has pull-up resistors on the bottom
  side ouf the printed circuit board.
  If not, use internal pull-ups from Arduino or external pull-ups.
  -----------------------------------------------------------------------------
  In the stopping positions the KY-040 has always HIGH signals on both CLK and DT.
  When you turn the encoder from one position to another, either CLK or DT goes LOW
  before the other signal goes LOW as well.
  The signal that goes LOW first determines if the encoder is turned left or right.
  Once you reach the next stopping position both signals will be HIGH again.

  If you press the push button, the current count can be reset to ZERO.

  For faster response you might increase the speed of the serial connection.
  (Make sure, that the Serial Monitor is also set to a higher speed,
   otherwise you will get no output).

  My rotary encoder came from china for 2 dollars, and after some debugging i found out, that it sometimes
  behaves strange (e.g. it gives a signal to the SW pin - although I did not press the button.)
  So for serious projects invest a dollar more and buy a quality product.
  ----------------------------------------------------------------------------------
*/
volatile boolean TurnDetected;
volatile boolean up;

const int PinCLK = 2;                 // Used for generating interrupts using CLK signal
const int PinDT = 3;                  // Used for reading DT signal
const int PinSW = 4;                  // Used for the push button switch

// Interrupt service routine is executed when a HIGH to LOW transition is detected on CLK
void isr0 ()  {
  up = (digitalRead(PinCLK) == digitalRead(PinDT));
  TurnDetected = true;
}



void setup ()  {
  pinMode(PinCLK, INPUT);
  pinMode(PinDT, INPUT);
  pinMode(PinSW, INPUT);
  attachInterrupt (0, isr0, CHANGE); // interrupt 0 is always connected to pin 2 on Arduino UNO
  Serial.begin (9600);
  Serial.println("Start");
}

void loop ()  {
  static long virtualPosition = 0;  // without STATIC it does not count correctly!!!
  
  if (!(digitalRead(PinSW)) && (virtualPosition != 0)) {  // check if pushbutton is pressed

    virtualPosition = 0;            // if YES, then reset counter to ZERO
    Serial.print ("Reset = ");      // Using the word RESET instead of COUNT here to find out a buggy encoder

    Serial.println (virtualPosition);
  }

  if (TurnDetected)  {       // do this only if rotation was detected
    if (up)
      virtualPosition++;
    else
      virtualPosition--;
    TurnDetected = false;          // do NOT repeat IF loop until new rotation detected
    Serial.print ("Count = ");
    Serial.println (virtualPosition);
  }
}

Jim

 

Click Link: Get Free Stock: Retire early! PM for strategy

share.robinhood.com/jamesc3274
get $5 free gold/silver https://www.onegold.com/join/713...

 

 

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks for the solution. Other ISRs are probably expressed in the same form.

Skier

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

That's why I gave the link in #3.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

If your importing Arduino sketches, interrupts are handled as call backs using the attachInterrupt() function.

The ISR then becomes just an ordinary subroutine.   See example sketch.

Jim

 

 

Click Link: Get Free Stock: Retire early! PM for strategy

share.robinhood.com/jamesc3274
get $5 free gold/silver https://www.onegold.com/join/713...

 

 

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

attachInterrupt() in Arduino is only for setting up callbacks on ext-ints isn't it? I don't think it's generic for all ISRs
.
EDIT yes this seems to confirm my understanding...
.
https://www.arduino.cc/reference/en/language/functions/external-interrupts/attachinterrupt/

Last Edited: Tue. Dec 31, 2019 - 07:59 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:
attachInterrupt() in Arduino is only for setting up callbacks on ext-ints isn't it? I don't think it's generic for all ISRs

You are correct as always! 

For pin change int see PCattachInterrupt()

 

Jim

#include "pins_arduino.h"
/*
* an extension to the interrupt support for arduino.
* add pin change interrupts to the external interrupts, giving a way
* for users to have interrupts drive off of any pin.
* Refer to avr-gcc header files, arduino source and atmega datasheet.
*/

/*
* Theory: all IO pins on Atmega168 are covered by Pin Change Interrupts.
* The PCINT corresponding to the pin must be enabled and masked, and
* an ISR routine provided.  Since PCINTs are per port, not per pin, the ISR
* must use some logic to actually implement a per-pin interrupt service.
*/

/* Pin to interrupt map:
* D0-D7 = PCINT 16-23 = PCIR2 = PD = PCIE2 = pcmsk2
* D8-D13 = PCINT 0-5 = PCIR0 = PB = PCIE0 = pcmsk0
* A0-A5 (D14-D19) = PCINT 8-13 = PCIR1 = PC = PCIE1 = pcmsk1
*/

volatile uint8_t *port_to_pcmask[] = {
	&PCMSK0,
	&PCMSK1,
	&PCMSK2
};

static int PCintMode[24];

typedef void (*voidFuncPtr)(void);

volatile static voidFuncPtr PCintFunc[24] = {
	NULL };

volatile static uint8_t PCintLast[3];

/*
* attach an interrupt to a specific pin using pin change interrupts.
*/
void PCattachInterrupt(uint8_t pin, void (*userFunc)(void), int mode) {
	uint8_t bit = digitalPinToBitMask(pin);
	uint8_t port = digitalPinToPort(pin);
	uint8_t slot;
	volatile uint8_t *pcmask;

	// map pin to PCIR register
	if (port == NOT_A_PORT) {
		return;
	}
	else {
		port -= 2;
		pcmask = port_to_pcmask[port];
	}

	// -- Fix by Baziki. In the original sources it was a little bug, which cause analog ports to work incorrectly.
	if (port == 1) {
		slot = port * 8 + (pin - 14);
	}
	else {
		slot = port * 8 + (pin % 8);
	}
	// --Fix end
	PCintMode[slot] = mode;
	PCintFunc[slot] = userFunc;
	// set the mask
	*pcmask |= bit;
	// enable the interrupt
	PCICR |= 0x01 << port;
}

void PCdetachInterrupt(uint8_t pin) {
	uint8_t bit = digitalPinToBitMask(pin);
	uint8_t port = digitalPinToPort(pin);
	volatile uint8_t *pcmask;

	// map pin to PCIR register
	if (port == NOT_A_PORT) {
		return;
	}
	else {
		port -= 2;
		pcmask = port_to_pcmask[port];
	}

	// disable the mask.
	*pcmask &= ~bit;
	// if that's the last one, disable the interrupt.
	if (*pcmask == 0) {
		PCICR &= ~(0x01 << port);
	}
}

// common code for isr handler. "port" is the PCINT number.
// there isn't really a good way to back-map ports and masks to pins.
static void PCint(uint8_t port) {
	uint8_t bit;
	uint8_t curr;
	uint8_t mask;
	uint8_t pin;

	// get the pin states for the indicated port.
	curr = *portInputRegister(port+2);
	mask = curr ^ PCintLast[port];
	PCintLast[port] = curr;
	// mask is pins that have changed. screen out non pcint pins.
	if ((mask &= *port_to_pcmask[port]) == 0) {
		return;
	}
	// mask is pcint pins that have changed.
	for (uint8_t i=0; i < 8; i++) {
		bit = 0x01 << i;
		if (bit & mask) {
			pin = port * 8 + i;
			// Trigger interrupt if mode is CHANGE, or if mode is RISING and
			// the bit is currently high, or if mode is FALLING and bit is low.
			if ((PCintMode[pin] == CHANGE
						|| ((PCintMode[pin] == RISING) && (curr & bit))
						|| ((PCintMode[pin] == FALLING) && !(curr & bit)))
					&& (PCintFunc[pin] != NULL)) {
				PCintFunc[pin]();
			}
		}
	}
}


SIGNAL(PCINT0_vect) {
	PCint(0);
}
SIGNAL(PCINT1_vect) {
	PCint(1);
}
SIGNAL(PCINT2_vect) {
	PCint(2);
}

volatile long ticktocks = 0;
long i = 0;

void tick(void) {
	ticktocks++;
}

void tock(void) {
	ticktocks--;
}

void setup()
{
	Serial.begin(9600);
	pinMode(4, INPUT);
	pinMode(5, INPUT);
	delay(3000);
	PCattachInterrupt(4, tick, CHANGE);
	PCattachInterrupt(5, tock, CHANGE);
}

void loop() {
	i++;
	delay(1000);
	Serial.print(i, DEC);
	Serial.print(" ");
	Serial.println(ticktocks);
	if (i > 256) {
		PCdetachInterrupt(4);
		PCdetachInterrupt(5);
	}
}

 

Click Link: Get Free Stock: Retire early! PM for strategy

share.robinhood.com/jamesc3274
get $5 free gold/silver https://www.onegold.com/join/713...

 

 

 

 

Last Edited: Tue. Dec 31, 2019 - 08:16 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

But his example was using a timer ISR. AFAIK Arduino does not have callback registration for timer ISRs?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The key to using Arduino is to make everything work as simply as possible: not to look for ways to make things difficult and then puzzle out why the AVR is not behaving correctly.  And "simple" in Arduinoland means using the standardized Arduino methods and techniques to handle the hardware.   Program/sketch timing is reserved to be handled by the Timer0.   There is a function called millis() which returns the number of milliseconds that the program has been running.  It's a 32-bit unsigned long variable, which means that it overflows only once every 15 days or so.

 

Create a new unsigned long variable, called ,say,  twoSeconds.    Assign the end trigger point for your event by adding the event length to the current millis() reading:  twoSeconds = millis() +2000;

  Now in the loop(), compare the current millis() reading to the end trigger point found in the updated twoSeconds variable:  if (millis() > twoSeconds) { do what needs to be done; }

 

 

Last Edited: Thu. Jan 2, 2020 - 05:40 PM