Okay so you aren't able to pass a variable into in ISR. This is causing problems for me. I'm using a rotary encoder, and I need it to be connected to an interrupt pin and running a ISR. When using this method, no pulses are ever skipped and the knob works great.
Currently I have the ISR setup like this:
void vhISR()
{
// this checks the PINE register for a 1 or 0 on the 5th bit (or 6th)
// I'm bit shifting the result so that variable is either true or false for comparison, not 16/0 or 32/0
rotCurrentA = (PINE & B00010000) >> 4;
rotB = (PINE & B00100000) >> 5;
if (rotCurrentA != rotLastA)
{
if (rotCurrentA != rotB) // cw
{
voltageHigh += knobResolution;
rotLastA = rotCurrentA;
}
else // ccw
{
voltageHigh -= knobResolution;
rotLastA = rotCurrentA;
}
}
}
The code simply varies voltageHigh by 0.1V increments. The problem is this: I need to do this same 0.1 variations on several other variables, depending on which one I select. I need to increase/decrease a voltageLow, a timer, and a resistance, all that need +/- 0.1 increments (but on different ranges. For instance, voltLow is somewhere between 0.1 & 5, and VH is somewhere between 6 & 10). Normally what I would do is just pass the variable I needed by reference into the function and have the function increment/decrement that variable....but an ISR is not a normal function and you cannot pass a variable into it.
How do I get around this? I'm not sure if that's enough information to go off of let me know.
2 Answers 2
I would follow Delta_G's advice, and write an ISR that counts the steps and does nothing more. As a general rule, you want to do as little as possible within an ISR. I would even avoid floating point and just count the raw number of steps:
// Count of rotational steps updated in the ISR. Must be unsigned in
// order to avoid signed overflow, which is undefined behavior.
volatile uint16_t rotation_count;
void vhISR()
{
static uint8_t rotLastA;
uint8_t rotCurrentA = (PINE >> 4) & 1;
uint8_t rotB = (PINE >> 5) & 1;
if (rotCurrentA != rotLastA)
return;
if (rotCurrentA != rotB) // cw
rotation_count++;
else // ccw
rotation_count--;
rotLastA = rotCurrentA;
}
// Return the number of rotational steps since the last call. Should be
// called often enough to avoid overflowing an int16_t.
int16_t get_rotation()
{
static uint16_t last_count;
// Avoid a data race: rotation_count should not be modified while we
// are reading it.
noInterrupts();
uint16_t count_copy = rotation_count;
interrupts();
int16_t delta = count_copy - last_count; // this is signed!
last_count = count_copy;
return delta;
}
Note that the raw count is an unsigned number which can roll over modulo
216. The value returned by get_rotation()
, however, is a
signed number which, owing to the rules of modular arithmetic, is
immune to the rollover of the raw count. As long as you call
get_rotation()
often enough (more often than every 32767 steps),
the returned value will always be correct.
Then, in your main program, you can use this to update whatever variable you want:
const float knobResolution = 0.1;
float voltageHigh, voltageLow, resistance;
// The variable that the user has selected for changing.
enum {VOLTAGE_HIGH, VOLTAGE_LOW, RESISTANCE} selection;
void loop()
{
// Set a pointer to the selected variable.
float *selected_var;
switch (selection) {
case VOLTAGE_HIGH:
selected_var = &voltageHigh;
break;
case VOLTAGE_LOW:
selected_var = &voltageLow;
break;
case RESISTANCE:
selected_var = &resistance;
break;
}
// Update the selected variable.
*selected_var += get_rotation() * knobResolution;
}
Note here that the selection
enum is probably redundant with
selected_var
. You could use the latter to keep track of the user
selection, then you would not need the switch
/case
.
-
This is awesome! Thank you for typing it all out for me. I'll have to mull this over soon and implement once I understand everything. Thanks!AJ_Smoothie– AJ_Smoothie06/29/2023 14:25:19Commented Jun 29, 2023 at 14:25
Okay so you aren't able to pass a variable into in ISR ...
Not as such, because the ISR is triggered by hardware, and therefore you don't pass arguments to it.
However there is nothing stopping you having a global variable which the ISR can access (preferably declared volatile
) so the ISR can change its behaviour depending on what the global variable holds.
switch
statement based on a global variable with the current selected setting?