Hello, I am very new to messing with Interrupts and Timers, and I'm having trouble getting this particular code to work. I'm trying to program Arduino interrupt PCINT2 to Pin 10 so that when the push button is pressed, the Timer 1 will be enabled. It must produce a delay of 2 second, and after 2 seconds, the LED must turn ON. Timer 1 will then count another 3 seconds, and after that, the LED will turn OFF. Additionally, the microcontroller must be in the possible lowest power sleep mode otherwise.
Note that the circuit has a push switch from 3.3V to Pin 10 with a 1KΩ shunt resistor, and an LED in series with a 220Ω resistor connected to Pin 13. Below is a semi-accurate picture, though the button is placed across the divide in the center of the breadboard so that the button actually opens and closes the circuit and the GND column was moved from the red + column to the blue - column:
Yes, that is all, and I do need to use Interrupts and Timers as I want to understand how to use them. And I am aware that the sleep mode isn't particularly useful as its stated use; it is just another thing I wanted to mess with. As such, any help in figuring this out will be appreciated.
You don't need interrupts or timers to solve your problem. This is done using the millis function and can be done with a dozen lines of code.... instead of your too long and too complicated sketch
I am fully aware that I don't need to. I've already run an alternate program that uses millis() to make sure the circuit was working properly, but now I want to mess with Interrupts and Timers to see how to use them properly in the code.
Yes, I have found a few tutorials (like this one which helped me a lot on understanding the basics of the syntax and the hardware elements), and I am fully aware that this is an entirely unreasonable usage of Interrupts. But its just me messing around with it so I can begin to figure them out. I'm not an Arduino beginner; I'm just a beginner at using Interrupts and Timers.
Start by using "#include <avr/io.h>" instead of your pile of #defines. That way a typo can't produce unexpected results.
The bit values are always 0 to 7 so you always have to use 1<<val or, better, the macro _BV(val) to get a bit mask.
Here is my best first guess at how I would do it. Rather than making the ISR's do work, I take advantage of the fact that the interrupts will wake the processor from sleep. The loop() function will sleep and then look at the state of the world when it is awakened.
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/sleep.h>
volatile boolean Timer1Overflowed = false;
void setup()
{
Serial.begin(9600); // Initialize serial monitor, only for debug
delay(200);
// Set the Pin 13 as output
DDRB |= _BV(DDB5); // pinMode(13, OUTPUT);
PORTB &= ~_BV(PORTB5); // digitalWrite(13, LOW);
DDRB &= ~_BV(DDB2); // pinMode(10, INPUT);
// Turn off Timer1 for now
cli(); // noInterrupts();
TCCR1A = 0; // Set to Mode Normal
TCCR1B = 0; // Stop timer
TIMSK1 = _BV(TOIE1); // Turn ON timer overflow interrupt
sei(); // interrupts();
// Control regs for PCINT
PCICR |= _BV(PCIE0); // Enable PCIE0 (PortB)
PCMSK0 |= _BV(PCINT2); // PCINT2 = Pin 10 (pin 2 of PORTB)
}
// ISR for pin change on PORTB
ISR(PCINT0_vect)
{
// No need to do anything here since the interrupt will
// allow loop() to continue and check for a button
// press.
}
ISR(TIMER1_OVF_vect)
{
Timer1Overflowed = true;
TCCR1B = 0; // Stop Timer1
}
enum States
{
WAITING_FOR_PUSHBUTTON,
DELAYING_TWO_SECONDS,
LED_ON_FOR_3_SECONDS
} State = WAITING_FOR_PUSHBUTTON;
void loop()
{
// Make local copy of volative variables
cli(); // noInterrupts();
boolean T1Overflow = Timer1Overflowed;
Timer1Overflowed = false;
sei(); // interrupts();
switch (State)
{
case WAITING_FOR_PUSHBUTTON:
if (PINB & _BV(PINB2)) // if (digitalRead(10) == HIGH)
{
// Set Timer1 to overflow in 2 seconds
TCNT1 = 0x10000 - (F_CPU * 2ul) / 1024ul; // 2 seconds at prescale 1024
TCCR1B = _BV(CS12) | _BV(CS10); // Start timer at Prescale = 1024
// Clear overflow flag
State = DELAYING_TWO_SECONDS;
}
break;
case DELAYING_TWO_SECONDS:
if (T1Overflow)
{
// Turn on LED
PORTB |= _BV(DDB5); //digitalWrite(13, HIGH);
// Set Timer1 to overflow in 3 seconds
TCNT1 = 0x10000 - (F_CPU * 3ul) / 1024ul; // 3 seconds at prescale 1024
TCCR1B = _BV(CS12) | _BV(CS10); // Start timer at Prescale = 1024
// Clear overflow flag
State = LED_ON_FOR_3_SECONDS;
}
break;
case LED_ON_FOR_3_SECONDS:
if (Timer1Overflowed)
{
// Turn off LED
PORTB &= ~_BV(DDB5); // digitalWrite(13, LOW);
// Wait for next button press
State = WAITING_FOR_PUSHBUTTON;
}
break;
}
// Use an appropriate sleep mode
// Turn off the millis() (TIMER0) interrupts during sleep
uint8_t Timer0Interrupts = TIMSK0;
TIMSK0 = 0; // All TIMER0 interrupts disabled
set_sleep_mode(SLEEP_MODE_PWR_SAVE); // Set sleep mode to power save
sleep_mode();
TIMSK0 = Timer0Interrupts; // re-enable TIMER0 interrupts
}
Thank you all for the help. I was able to figure it out eventually. The biggest problem was, of course, syntax errors and assignment errors that led to some of the required registers not being declared and assigned values properly (most notably PCICR and PCMSK0). So, I fixed them and added a better way to mess with the sleep mode and got the following code that works:
#include <avr/sleep.h>
char tick = 0; // Describes the state of the LED
void setup() {
cli(); // Clear global interrupt
// Set the Pin 13 as output and 10 as input
DDRB |= (1 << DDB5);
DDRB &= ~(1 << DDB2);
// Control regs for Timer 1, disable timer unless sw press
TCCR1A = 0; // Set to Mode Normal
TCCR1B = 0; // Disable timer
TIMSK1 = (1 << TOIE1); // Turn ON timer overflow mask
// Control regs for PCINT
PCICR = (1 << PCIE0); // Enable PCINT0 (PortB)
PCMSK0 = (1 << PCINT2); // PCINT0 = Pin 10 of Digital port
//Serial.begin(9600); // Initialize serial monitor, only for debug
SMCR = B00000101; // Enable sleep and set Power Down Sleep Mode
sei(); // Set global Interrupt
}
// ISR for pin change interrupt capture, set Timer 1 for 2 sec count
ISR(PCINT0_vect) {
// Set the LED to OFF
PORTB &= ~(1 << DDB5);
// Clock cycle needed = 2 * 16,000,000 / 1024 = 31,250
// CNT value = 65,535 - 31,250 = 34,285
TCNT1 = 34285; // set value for 2 sec count
TCCR1B = (1 << CS12) | (1 << CS10); // Prescale to 1024, start timer, 16E6/1024 = 15,625 Hz
}
ISR(TIMER1_OVF_vect) {
// First time, LED will be ON; second time, LED will be OFF
if (tick == 0) {
PORTB |= (1 << DDB5); // Turn ON pin 13
// Clock cycle needed = 3 * 16,000,000 / 1024 = 46,875
// CNT value = 65,535 - 46,875 = 18,660
TCNT1 = 18660; // 3 sec count
tick = 1;
} else {
PORTB &= ~(1 << DDB5); // Turn OFF pin 13
TCCR1B = 0; // Disable timer
tick = 0;
sleep_cpu(); // Proper Sleep Mode
}
}
void loop() {
}