Which is the best practice for using interrupt? - arduino

The goal is to record and accumulate total interval of high pulse with smallest interval up to 1 milliseconds. I have seen and made 2 different code, first one calculate interval in loop function and other calculate interval in ISRs. Here is the code:
Code 1
volatile boolean state;
volatile uint16_t start;
volatile uint16_t end;
uint16_t interval;
uint16_t totalinterval;
void setup () {
Serial.begin (9600);
attachInterrupt (digitalPinToInterrupt(2), pulse, CHANGE);
}
void loop () {
if (end) {
interval = end - start;
totalinterval += interval;
end = 0;
}
}
void pulse () {
if (state)
end = micros ();
else
start = micros ();
state = !state;
}
Code 2
volatile uint16_t start;
volatile uint16_t end;
uint8_t pinstate;
uint16_t interval;
uint16_t totalinterval;
void setup () {
Serial.begin (9600);
attachInterrupt(digitalPinToInterrupt(2), pulse, CHANGE);
}
void loop {
}
void pulse () {
pinstate = digitalRead(2);
if (pinstate == LOW) {
falling ();
}
else {
rising ();
}
}
void falling () {
start = micros();
}
void rising () {
end = micros();
cli ();
calculateinterval ();
sei ();
}
void calculateinterval () {
interval = end - start;
totalinterval =+ interval;
interval = 0;
}
my main concern is which code can catch up to read every micros of end and start if I add more code line? For example to control servo, arithmetic operation, data logging, etc. Also, should I disable all interrupt while calculate interval in code 1?

Related

Send and read the button state via CAN bus using Arduino

I'm intending to read the change of button input using 2 separate Arduino that connected via CAN bus (MP2515). The transmitter will connect to button with internal pulldown resistor, that pin will act as external interrupt. My reference is coming from here. By not assign any value to data frame (canMsg1 and canMsg2 in the code below), is that enough for the receiver to understand the input pin state?
The origin code using digitalRead(pin) to read and later write state of the button by single Arduino.
transmitter of CAN massage
#include <SPI.h>
#include <mcp2515.h>
struct can_frame canMsg1;
struct can_frame canMsg2;
MCP2515 mcp2515(10);
int incPin(2);
int decPin(3);
unsigned long current_time = 0;
unsigned long previous_time = 0;
void setup() {
Serial.begin(9600);
SPI.begin();
mcp2515.reset();
mcp2515.setBitrate(CAN_500KBPS, MCP_8MHZ);
mcp2515.setNormalMode();
canMsg1.can_id = 0xAA;
canMsg1.can_dlc = 1;
canMsg2.can_id = 0xBB
canMsg2.can_dlc = 1;
pinMode(incPin, INPUT_PULLUP);
pinMode(decnPin, INPUT_PULLUP);
attachInterrupt(incpPin, inc, FALLING);
attachInterrupt(decPin, dec, FALLING);
}
void loop() {}
void inc() {
current_time = millis();
if (current_time - previous_time > 200) { //debouncing for 0.2s
mcp2515.sendMessage(&canMsg1);
}
previous_time = current_time;
}
void dec() {
current_time = millis();
if (current_time - previous_time > 200) { //debouncing for 0.2s
mcp2515.sendMessage(&canMsg2);
}
previous_time = current_time;
}
receiver/reader of CAN massage
#include <SPI.h>
#include <mcp2515.h>
struct can_frame canMsg1;
struct can_frame canMsg2;
MCP2515 mcp2515(10);
int pos = 0;
int up;
int down;
void setup() {
Serial.begin(9600);
SPI.begin();
mcp2515.reset();
mcp2515.setBitrate(CAN_500KBPS, MCP_8MHZ);
mcp2515.setNormalMode();
}
void loop() {
if (mcp2515.readMessage(&canMsg1) == MCP2515::ERROR_OK) { //read CAN increment button message
if (canMsg1.can_id==0xAA) {
up = canMsg1.data[0];
if (up == LOW) {
pos++;
} else {}
}
}
if (mcp2515.readMessage(&canMsg2) == MCP2515::ERROR_OK) { //read CAN decrement button message
if (canMsg2.can_id==0xBB) {
down = canMsg2.data[0];
if (down == LOW) {
pos--;
} else {}
}
}
}
You are right, your CAN events do not require any data. But then, why do you set can_dlc to 1? No data is 0 bytes.
You can try this:
Get rid of:
struct can_frame canMsg1; // these only hog global memory for no practical use.
struct can_frame canMsg2;
Change your interrupt routines to:
void inc() {
current_time = millis();
if (current_time - previous_time > 200) { //debouncing for 0.2s
mcp2515.beginPacket(0xAA);
mcp2515.endPacket();
}
previous_time = current_time;
}
void dec() {
current_time = millis();
if (current_time - previous_time > 200) { //debouncing for 0.2s
mcp2515.beginPacket(0xBB);
mcp2515.endPacket();
}
previous_time = current_time;
}
I could not figure out what canMsg7 and canMsg8 were. Nor why you'd need several message structures in global memory to receive one CAN message at a time... I really don't think that's necessary. Since your packets have no data, receiving gets streamlined as well:
int loop() {
if (mcp2515.parsePacket() != 0) { // != 0 => we have fully received a packet.
switch (mcp2515.packetId()) {
case 0xAA:
// inc switch was pressed
// ...
break;
case 0xBB:
// dec switch was pressed
// ...
break;
}
}
}

Pausing a loop to run another in Arduino

I'm working on a circuit that has two separate 4-bit binary counters with LEDs. I press a button and one counter begins counting to 15 in binary. I press a second button and the first counter pauses where it is and the second group of LEDs begin counting to 15 in binary. I got both counters working, but I can't get the first group to pause and the second to begin. I've tried using if statements with a boolean flag, but it messes up the first group of LEDs. How can I get ledPins1[] to pause when button2 is pressed, then resume when ledPins2[] finish?
int ledPin1[] = {2,3,4,5};
int ledPin2[] = {7,8,9,10};
int button1 = 11;
int button2 = 12;
boolean button1Last = LOW;
boolean button1Current = LOW;
boolean button2Last = LOW;
boolean button2Current = LOW;
void setup()
{
pinMode(button1, INPUT);
pinMode(button2, INPUT);
for(int i=0; i<4; i++)
{
pinMode(ledPin1[i], OUTPUT);
}
for(int i=0; i<4; i++)
{
pinMode(ledPin2[i], OUTPUT);
}
}
boolean waitForButtonPush1 (boolean lastStartSwitchState1)
{
boolean currentStartSwitchState1 = digitalRead(button1);
if(lastStartSwitchState1 != currentStartSwitchState1) delay(20);
currentStartSwitchState1 = digitalRead(button1);
return currentStartSwitchState1;
}
boolean waitForButtonPush2 (boolean lastStartSwitchState2)
{
boolean currentStartSwitchState2 = digitalRead(button2);
if(lastStartSwitchState2 != currentStartSwitchState2) delay(20);
currentStartSwitchState2 = digitalRead(button2);
return currentStartSwitchState2;
}
void loop()
{
button1Current = waitForButtonPush1(button1Last);
if(button1Last == LOW && button1Current == HIGH)
{
for (byte counter =0;counter<=15; counter++)
{
displayBinary(counter);
delay(500);
}
}
button2Current = waitForButtonPush2(button2Last);
if(button2Last == LOW && button2Current == HIGH)
{
for (byte counter =0;counter<=15; counter++)
{
displayBinary2(counter);
delay(500);
}
}
}
void displayBinary(byte numToShow)
{
for (int i =0;i<4;i++)
{
if (bitRead(numToShow, i)==1)
{
digitalWrite(ledPin1[i], HIGH);
}
else
{
digitalWrite(ledPin1[i], LOW);
}
}
}
void displayBinary2(byte numToShow)
{
for (int i =0;i<4;i++)
{
if (bitRead(numToShow, i)==1)
{
digitalWrite(ledPin2[i], HIGH);
}
else
{
digitalWrite(ledPin2[i], LOW);
}
}
}
Welcome to the world of embedded devices!
Getting a small microprocessor to do several things at the same time is a bit tricky.
The key is to never block. No calls to delay(), no sending large buffers on the serial port at 9600 bauds in one go, etc...
There are some simple techniques to do it, one of the most commonly used is finite state machines.
Let's analyse your app a bit.
2 similar dssplay counters, with delay
2 buttons, buttons usually need to be debounced, that also involves a delay.
Some code, for you to tinker with:
// ****************************
// pinout
static const byte ledPin1[] = { 2, 3, 4, 5 };
static const byte ledPin2[] = { 7, 8, 9, 10 };
constexpr byte button1 = 11; // using constexpr for these saves 2 bytes of RAM.
constexpr byte button2 = 12;
// ****************************
// Counter data
static constexpr unsigned int led_delay = 500; // 500 ms, for all counters.
// constexpr ?? arduino supports c++17. Not all features in the main .ino
// module and all features in .cpp modules.
// Hint: you could have a member variable in the structure below for delay,
// this would allow for counters running at different speeds, or add buttons
// to increase/decrease speed.
// we have only 2 states, but you could add more, like running
// backwards, or run a different chase pattern maybe?
enum class led_counter_state : byte
{
stopped,
running,
};
struct led_counter_data_t
{
led_counter_state state; // STATE
byte counter; // counter current value
unsigned int timestamp; // used for timing.
const byte* leds; // LED pins.
};
static led_counter_data_t led_counter[2];
void led_display_init()
{
for (byte i = 0; i < 2; ++i)
{
led_counter[i].state = led_counter_state::stopped;
led_counter[i].counter = 0;
led_counter[i].timestamp = 0;
}
led_counter[0].leds = ledPin1;
led_counter[1].leds = ledPin2;
}
// ****************************
// LED cotrrol
static void leds_display_value(const led_counter_data_t& cntr)
{
for (byte i = 0, val = cntr.counter; i < 4; ++i, val >>= 1)
digitalWrite(cntr.leds[i], val & 0x01);
}
static void leds_control(led_counter_data_t& cntr)
{
const auto now = (unsigned int)millis(); // keep track of time.
switch(cntr.state)
{
default: // something is wrong.. stop.
cntr.state = led_counter_state::stopped;
// fall through ...
case led_counter_state::stopped:
return; // if not running, do nothing
case led_counter_state::running:
if (now - cntr.timestamp >= led_delay) // check delay
{
if (++cntr.counter > 15) // advance counter.
cntr.counter = 0;
leds_display_value(cntr); // show value.
cntr.timestamp = now; // keep track of time.
}
break;
}
}
static void leds_start(led_counter_data_t& cntr)
{
if (cntr.state != led_counter_state::stopped)
return;
cntr.state = led_counter_state::running;
if (++cntr.counter > 15) // advance counter.
cntr.counter = 0;
led_display_value(cntr); // show value.
cntr.timestamp = (unsigned int)millis();
}
static void leds_stop(led_counter_data_t& cntr)
{
cntr.state = led_counter_state::stopped;
}
// ****************************
// switch inputs data
static constexpr byte switch_debounce_delay = 30; // 30ms is a good value for
// debouncing
struct switch_data_t
{
byte sw1_state : 1; // no need to waste more than 1 bit per switch
byte sw2_state : 1;
byte timestamp; // we'll only count to 30 ms, so 1 byte timestamp will do
};
static switch_data_t switch_data;
// ****************************
// switch inputs code
static void control_inputs()
{
const auto now = (byte)millis();
if (now - switch_data.timestamp < switch_debounce_delay)
return;
switch_data.timestamp = now;
// All switch control logic is regrouped here, and isolated
// form other control code, this makes the logic easier to
// write, read, and debug.
bool b = digitalRead(button1);
if (b & !switch_data.sw1_state) // button was pushed right now.
{
if (led_counter[0].state == led_counter_state::stopped)
{
leds_start(led_counter[0]); // start counter 1
leds_stop(led_counter[1]); // stop counter 2
}
else
{
leds_stop(led_counter[0]); // stop counter 1
}
}
switch_data.sw1_state = b;
b = digitalRead(button2);
if (b & !switch_data.sw2_state) // button was pushed right now.
{
if (led_counter[1].state == led_counter_state::stopped)
{
leds_start(led_counter[1]); // start counter 2
leds_stop(led_counter[0]); // stop counter 1
}
else
{
leds_stop(led_counter[1]); // stop counter 2
}
}
switch_data.sw2_state = b;
}
// ****************************
void setup()
{
pinMode(button1, INPUT);
pinMode(button2, INPUT);
for (byte i = 0; i < 4; ++i)
{
digitalWrite(ledPin1[i], LOW);
pinMode(ledPin1[i], OUTPUT);
digitalWrite(ledPin2[i], LOW);
pinMode(ledPin2[i], OUTPUT);
}
led_display_init();
}
// ****************************
// The goal, always, is to exit loop() as fast as possible, so
// everything will run smoothly, and appear to run simultaneously.
void loop()
{
control_inputs();
leds_control(led_counter[0]);
leds_control(led_counter[1]);
}
I do not have an arduino with me, so I did not comppile nor ran this, but it should be pretty close. Let me know if you're having issues or have any questions.

Can i somehow add millis to this code somewhere in the buttons or in the modes?

i need to add millis in my code but i dont know exactly where should be ok to add millis or where is it acctually needed.Because i have used only delay till now can you help me and tell me where can millis be implemented in this piece of code.If it is possible to add millis somewhere in or after the button modes or switch case because it would be nicer if the modes changed properly because for now their changing either when the button is pressed normal or somtimes you need to pres the button twice to change to the next mode.I would apriciate all the help.
const int BUTTON_SWITCH = 8;
const int BUTTON_ALARM = 9;
const int KNOB = A0;
const int TEMP = A1;
const int tempRES = 10000; // the resistance of the NTC at 25'C is 10k ohm
const int NTC_MATERIAL_CONSTANT = 3950;
const int RED_LED = 4;
const int BUZZER = 3;
int index = 1;
double value;
int state = 0;
unsigned long time_now = 0;
int period = 1000;
char incomingOption;
#include "Display.h"
void setup() {
Serial.begin(9600);
pinMode(BUTTON_SWITCH, INPUT_PULLUP);
pinMode(BUTTON_ALARM, INPUT_PULLUP);
pinMode(RED_LED, OUTPUT);
}
float get_temperature()
{
float temperature, resistance;
int value;
value = analogRead(TEMP);
resistance = (float)value * tempRES / (1024 - value); // Calculate resistance
/* Calculate the temperature according to the following formula. */
temperature = 1 / (log(resistance / tempRES) / NTC_MATERIAL_CONSTANT + 1 / 298.15) - 273.15;
return temperature;
}
void loop() {
if (digitalRead(BUTTON_SWITCH) == LOW) { // button for switching the 3 modes
index = index + 1;
}
if (digitalRead(BUTTON_ALARM) == LOW) { // button for the alarm
displayAlarm();
}
if (Serial.available()) {
// Read entire buffer up to newline character
// Since on the C# side, serialPort1.WriteLine appends a newline character
String respond = Serial.readStringUntil('\n');
if (respond == "RESET") {
digitalWrite(RED_LED, LOW);
digitalWrite(BUZZER, LOW);
}
}
if (index > 3) { // when the code is on the last mode press the button to turn back to the first mode
index = 1;
}
get_temperature();
float celcius;
celcius = get_temperature();
if(celcius<16 || celcius>27){ // if the temperature becomes less than 16 degrees or goes higher than 27 degrees turn on the alarm
displayAlarm();
}
switch (index) {
case 1: displayTime(); break; // switch between the 3 different modes
case 2: displayTemp(); break;
case 3: displayAngle(); break;
}
}
void displayTime() {
float timer = Serial.parseFloat(); // take the current time from the c# application and display it on the arduino board
Display.show(timer);
}
void displayTemp() {
float celcius;
celcius = get_temperature(); // mode for displaying the current temperature
Display.show(celcius);
}
void displayAngle() {
int value = analogRead(KNOB); // read and save analog value from the potentionmeter
value = map(value, 0, 1023, 0, 30); // Map value 0-1023 to 0-30
Display.show(value);
}
void displayAlarm() {
Serial.println("Alarm");
digitalWrite(RED_LED, HIGH);
tone(BUZZER, 1500, 700);
}

I2C between RPI and Arduino using Processing

Posted here is my code for my RPi master and Arduino slave project. I have an analog sensor connected to the Arduino and i am reading this data with Processing on the RPi. I am using Processing because I intend on generating a graph and waveform with the data. The code below seems to work, however, any slight movement of setup "disconnects" the slave device because I get the following message. "The device did not respond. Check the cabling and whether you are using the correct address." I have narrowed the problem down and found out that it always disconnects at the i2c.read();function. My question is whether there is some type of break function so that when this does happen processing moves on and tries again in the next iteration? Or if it is stuck in a loop is it waiting for some signal from the slave device? Does anyone have any suggestions on how to approach this?
Processing Code
import processing.io.*;
I2C i2c;
int val = 0;
void setup()
{
i2c = new I2C(I2C.list()[0]);
}
void draw ()
{
if (I2C.list() != null)
{
i2c.beginTransmission(0x04);
i2c.write(8);
byte[] in = i2c.read(1);
int accel = in[0];
println (accel);
}
}
Arduino Code
#include <Wire.h>
#define SLAVE_ADDRESS 0x04
int number = 5;
int state = 0;
const int zInput = A0;
int zRawMin = 493;
int zRawMax = 530;
float acceleration;
int accel;
void setup() {
analogReference(EXTERNAL);
pinMode(13,OUTPUT);
Serial.begin(9600); // start serial for output
Wire.begin(SLAVE_ADDRESS); // join i2c bus with address #8
Wire.onReceive(receiveData); // register event
Wire.onRequest(sendData);
Serial.println ("Ready");
}
void loop() {
int zRaw = ReadAxis (zInput);
acceleration = map (float(zRaw), float (zRawMin), float(zRawMax), -9.81, 9.81);
accel = int(acceleration);
//delay(100);
}
void receiveData(int byteCount)
{
while (0 < Wire.available())
{ // loop through all but the last
number = Wire.read(); // receive byte as a character
//Serial.print("data received");
Serial.println(number); // print the character
if (number==1)
{
if (state == 0)
{
digitalWrite(13,HIGH);
state = 1;
}
else
{
digitalWrite(13,LOW);
state = 0;
}
}
}
}
void sendData()
{
Wire.write(accel);
}
int ReadAxis(int axisPin)
{
long reading = 0;
int raw = analogRead(axisPin);
return raw;
}
Looks like a solution might be to use a try/catch block courtesy of #Kevin Workman. It works well for what I need it to do thx.
here is the updated Processing code.
import processing.io.*;
I2C i2c;
int val = 0;
void setup()
{
i2c = new I2C(I2C.list()[0]);
}
void draw ()
{
if (I2C.list() != null)
{
i2c.beginTransmission(0x04);
i2c.write(8);
try
{
byte[] in = i2c.read(1);
}
catch(Exception e)
{
i2c.endTransmission();
}
int accel = in[0];
println (accel);
}
}

quadrature encoder using photo interrupts

I a trying to program an encoder using photo interrupts and a 7474 d flip flop. I believe my wiring is correct. It is simply wiring the photo interrupt to the arduino and having the pin out going through the 7474. Also one of the wires goes to pin 4 to digital read.
Below is my code. For some reason I can't get the photo interrupts to read the turn of the wheel.
const int clock = 0; //pin 2 is interrupt 0
const int dirPin = 4; //the number of the LED pin
//const int ledPin = 13;
int count = 0;
int dir = 0;
//int clockA = 0;
void setup(){
pinMode(dirPin, INPUT);
Serial.begin(9600);
attachInterrupt(clock, program, RISING);
}
void loop()
{
delay(50);
}
void program()
{
dir = digitalRead(dirPin);
if (dir == HIGH)
{
count ++;
}
else
{
count --;
}
Serial.println(count*30);
}
Don't put the Serial.println() in the interrupt handler. Inside a handler other interrupts are disabled. Do as little work as possible and return. The handler should just update the count and can mark a flag to tell the main loop to process.
int count = 0;
int newcounts = 0;
void loop() {
delay(100);
if (newcounts) {
Serial.println(count*30);
newcounts = 0
}
}
void program() {
// ... update count
newcounts = 1;
}

Resources