From Arduino 101: Timers and Interrupts. Timer0: Timer0 is a 8-bit timer. In the Arduino world timer0 is been used for the timer functions, like delay, millis and micros. If you change Timer0 registers, this may influence the Arduino timer function. So you should know what you are doing. Timer1: Timer1 is a 16-bit timer.

Nano Every - Interrupt Handling - Rotary Encoder #every#arduino#nano#rotary

#9734

Hi all -
Here is a programming difference between the Arduino Nano and Nano Every you will need if you use pin change interrupts. The setup is slightly different on the Every and the rotary encoder pins (2 and 3) use different interrupt vectors on the Every which means you need two ISRs rather than one. The following code can be used and will work on both the Nano and the Every - it detects the board and compiles differentially based on whether you have an Uno/Nano or Every.
//
//
#define Every 1
#define _BOARDTYPE Nano
#if defined(ARDUINO_AVR_NANO_EVERY)
#endif
#define _BOARDTYPE Uno
#endif
///////////////////////////////////////////////////////////
// Interrupt service routine, called on encoder movement //
// +1 for Clockwwise //
// Ignore intermediate values //
///////////////////////////////////////////////////////////
#if _BOARDTYPE != Every
unsigned char result = encoder.process();
encoder_count++;
encoder_count--;
}
#else
#define PA0_CLEAR_INTERRUPT_FLAG PORTA.INTFLAGS &= PIN0_bm
#define PF5_CLEAR_INTERRUPT_FLAG PORTF.INTFLAGS &= PIN5_bm
ISR(PORTA_PORT_vect) {
PA0_CLEAR_INTERRUPT_FLAG;
if (result DIR_CW) {
} else if (result DIR_CCW) {
}
unsigned char result = encoder.process();
if (PF5_INTERRUPT)
encoder_count++;
encoder_count--;
#endif
//*******************Setup Interrupt Service Routine********************
void setupEncoderISR() {
// Set up for the rotary encoder interrupts
PCICR |= (1 << PCIE2);
#endif
PORTA.PIN0CTRL |= PORT_PULLUPEN_bm | PORT_ISC_BOTHEDGES_gc;
PORTF.PIN5CTRL |= PORT_PULLUPEN_bm | PORT_ISC_BOTHEDGES_gc;
sei();

#9773

Thanks for this Dean. Is this meant to be used in the main sketch before the void setup?
#9784

The simple answer to your question is yes - this all goes before the main loop.
But there are a few other bits you need to make it work You need an Encoder library(or code to read the pins). You need to call setupEncoderISR() from your main setup() routine. The interrupt routine I supplied just keeps a count +! for rotate step clockwise, -1 for rotate step counter clockwise. From your main loop you need to determine what you want to do with that. If your encoder has a push button and you want to check that you'll need code in your loop to do that as well.
I've attached a sketch that I used to test this. Quick wireup on a breadboard - the Encoder A/B gets wired to pins 2 and 3 and ground goes to ground. The Encoder button goes to pin A3 and ground - change RotaryTest.h if you wire the button to a different pin. Open a debug window and the sketch will report encoder changes as you turn the knob. Each time you press the button it will rotate through a set of values that I use to adjust the tuning increment.
73 and good luck, let me know if you get it working.
Dean
KK4DAS
#10077

Dean,
Thanks for the code. It's helping with converting my uBITX sketch from the Nano to the Every.
When you say that the encoder is on pins 2 and 3, what do you mean? I have a uBITX v6, and the rotary encoder is on A0 and A1, the pushbutton is A2 (and the PTT is A3). Currently, my Nano sketch catches all four inputs with a single pin change interrupt.
Also, your code above calls an method encoder.process() which doesn't seem to be included.
73 and thanks,
Mark, N8ME
#10088

On Thu, Jan 7, 2021 at 06:00 PM, Mark Erbaugh wrote:
Also, your code above calls an method encoder.process() which doesn't seem to be included.
Hi Mark -
encoder.process returns DIR_CW, DIR_CCW, or DIR_NONE based on encoder movement. It is part of a small encoder library that I use. You should be able to replace encoder.process with whatever code you use to read the encoder. My build is not a uBitx and uses pins D2 and D3 (the external interrupt pins on the Nano) for the encoder. You should be able to adapt it the interrupt code for whatever pins you want. The code I posted should be a good roadmap for creating code that will compile and run on either the Nano or the Every.
For completeness, my test sketch is attached. The test sketch demonstrates reading the encoder and how I use it to adjust frequency. No display is needed - it prints results on the serial monitor.
73,
Dean
KK4DAS
#10089
Edited

Hi
Unfortunately I don't have an Arduino Nano Every here. But I will be surprised if you can't use the code I use in the RFzero https://www.rfzero.net/tutorials/rotary-encoder/ which also works directly on a Nano and Uno - prego!
Of course if you skip the Arduino attachInterrupt function abstraction things become much more MCU specific. But using the attachInterrupt makes the code much more portable in the Arduino world.
Bo
www.rudius.net/oz2m :: www.rfzero.net
#10093

On Sat, Jan 9, 2021 at 10:33 AM, Bo, OZ2M wrote:
But using the attachInterrupt makes the code much more portable in the Arduino world.
Hi Bo - yes - attachinterrupt is more portable, but the limitation is that it only works on specific digital pins. On the Nano or Uno - the only pins it works on are 2 and 3. If you want interrupts on other pins you're pretty much stuck with using Pin Change Interrupts at the lower level. There are some Pin Change Interrupt libraries out there that attempt to hide the hardware details but I haven't used any of them.
73,
Dean
KK4DAS
#10097

On the Nano or Uno - the only pins it works on are 2 and 3.
Dean:
Are you sure? It's true that external interrupts only work on pins 2 and 3 for both, but I'm pretty sure that software interrupts work on all digital pins.
Jack, W8TEE
toggle quoted messageShow quoted text
On Saturday, January 9, 2021, 3:45:04 PM EST, Dean Souleles <dsouleles@...> wrote:

On Sat, Jan 9, 2021 at 10:33 AM, Bo, OZ2M wrote:
But using the attachInterrupt makes the code much more portable in the Arduino world.
Hi Bo - yes - attachinterrupt is more portable, but the limitation is that it only works on specific digital pins. If you want interrupts on other pins you're pretty much stuck with using Pin Change Interrupts at the lower level. There are some Pin Change Interrupt libraries out there that attempt to hide the hardware details but I haven't used any of them.
73,
Dean
KK4DAS
#10099

Hello
Indeed there are some restrictions to the attachInterrupt function https://www.arduino.cc/reference/en/language/functions/external-interrupts/attachinterrupt/ However, the need for interrupt may be a bit overrated. As shown at https://www.rfzero.net/tutorials/rotary-encoder/ scanning is also possible and used specifically in the RFzero Signal Generator: https://www.rfzero.net/examples/signal-generator/ that can do a lot of things without missing a beat.
Bo
www.rudius.net/oz2m :: www.rfzero.net
#10106

Hi

This may not be relevant but I have been working on a rig that was designed with a Nano v2 and I could only source Nano v3 boards. There are pin differences between the variants.

Here is a link with some info.

Jim G4EQX

toggle quoted messageShow quoted text
On 09/01/2021 21:51, jjpurdum via groups.io wrote:
On the Nano or Uno - the only pins it works on are 2 and 3.
Dean:
Are you sure? It's true that external interrupts only work on pins 2 and 3 for both, but I'm pretty sure that software interrupts work on all digital pins.
Jack, W8TEE
On Saturday, January 9, 2021, 3:45:04 PM EST, Dean Souleles <dsouleles@...> wrote:

On Sat, Jan 9, 2021 at 10:33 AM, Bo, OZ2M wrote:
But using the attachInterrupt makes the code much more portable in the Arduino world.
Hi Bo - yes - attachinterrupt is more portable, but the limitation is that it only works on specific digital pins. If you want interrupts on other pins you're pretty much stuck with using Pin Change Interrupts at the lower level. There are some Pin Change Interrupt libraries out there that attempt to hide the hardware details but I haven't used any of them.
73,
Dean
KK4DAS
#10122

On Sat, Jan 9, 2021 at 04:51 PM, jjpurdum wrote:
Are you sure? It's true that external interrupts only work on pins 2 and 3 for both, but I'm pretty sure that software interrupts work on all digital pins.
Hi Jack,
Aarduino interrupt nomenclature is endlessly confusing. The attachinterrupt function in only for external interrupts - specified per board. On advantage of the Every over the Nano or the Uno is that every digital pin is an externa interrupt pin. Bo posted the link to the documentation so I won't repeat it - but to satisfy myself I tried different pins for encoder A & B when using attachinterrupt and, sure enough, it only works on the external interrupt pins. But if you setup the registers properly you can use pin change interrupts on any pin - but you have to be careful because multiple pins share interrupt vectors so you need to add code if it is possible for more than one pin to trigger the interrupt.
73,
Dean
KK4DAS
#10127

On Sat, Jan 9, 2021 at 05:23 PM, Bo, OZ2M wrote:
as shown at https://www.rfzero.net/tutorials/rotary-encoder/ scanning is also possible
Thank you for the reference Bo. Unfortunately some of our Nano based sketches don't quite keep up and have been known to miss or have very inconsistent encoder reading when polling. Using interrupts clears it right up and tuning is now smooth and reliable.
The RFzero looks like a very nice board - with the M0, and SI5351 and GPS well integrated. I had not heard of it before. It is a little pricey for our kit radios - it is close to 1/2 the price of a uBitx V6. That said, it looks like a very cool project board. Is it your design?
73
Dean
KK4DAS
#10132

Hello Dean
Yes the RFzero is a project I have done with three other OZ-stations. The original idea was to make a low cost 6 m beacon driver for the Synchronized Beacon Project since the Next Generation Beacons project https://rudius.net/oz2m/ngnb for OZ7IGY is overkill and a lot more expensive but also in a very different performance category. So to get the volume up we decided to rub it in Arduino. Subsequently a lot of other applications were identified from a cheap GPSDO over WSPR on 23 cm to 10 GHz QO-100 dual LO and 10 GHz beacon driver if you can live with the phase noise. A lot of effort has been put into the design and construction, e.g. four layer immersion gold PCB and good RF design techniques applied for best RF performance while observing the cost. There are already to many bad Si5351A RF designs around. The RFzero is professionally assembled by a Danish EMS.
The KH6HME/B beacon on 2 m uses a RFzero.
Bo
www.rudius.net/oz2m :: www.rfzero.net

Here’s a simple Arduino Tachometer project for measuring and displaying speed of rotation. You can find similar projects everywhere on the web but this one has some unique features. First off, the idea is based on a square type inductive proximity sensor SN04-N from RiKo. The SN04-N is a low cost, stable, and waterproof inductive proximity sensor with a normally-open (N/O) NPN transistor output, surprisingly with an internal pull-up resistor that’s not clearly mentioned in the datasheet!

Example

Hardware: This project requires three key hardware parts as listed below.

  1. Interrupts can generally enabled or disabled with the function interrupts or noInterrupts. By default in the Arduino firmware interrupts are enabled. Interrupt masks are enabled / disabled by setting or clearing bits in the Interrupt mask register (TIMSKx). When an interrupt occurs, a flag in the interrupt flag register (TIFRx) is been set.
  2. Interrupts on Arduino. Arduino Interrupts work in a similar way. For example, if you are waiting for a user to press a push button, you can either monitor the button at a high frequency, or use interrupts. With interrupts, you’re sure that you won’t miss the trigger. The monitoring for Arduino Interrupts is done by hardware, not software.
  3. The ATmega328P instruction set does have a Branch If Interrupt Enabled (BRIE) instruction. This is the command you would use to initiate a software interrupt. Unfortunately, I do not see an equivalent command in the Arduino command reference.
Interrupt
  • Inductive Proximity Sensor (SN04-N)
  • Arduino (Rev3)
  • Arduino LCD Shield (DFR0009)

As can see in the below diagram, the hardware frame-up is extremely simple, and calls only one bare I/O of Arduino (Pin 2) to complete the entire setup. Pin 2 of Arduino Uno, used here as the ‘Pulse Input’ pin, is its ‘Interrupt 0’ pin that enables us to run some code only when a level change happens there. The second interrupt – ‘Interrupt 1’ of Uno is on Pin 3. Just to recap, interrupts are a nice way to make the system more tractable to time tender tasks. They also helps to free up the main loop to look at the primary task in the system. For more details, check out this documentation on interrupts – https://www.arduino.cc/en/Reference/attachInterrupt

Software

The code strictly follows the common Arduino Sketch style and uses the standard interrupt 0 on pin 2 (D2). As you can see in the code, the interrupt triggers on ‘falling’ edge of the input pulse. Copy the below code in your Arduino IDE software to program your Arduino.

In the code, the interrupt will increment a variable each time the signal at INT0 is falling, and interrupts are disabled during the computation time. Take note on the code line ” uint32_t rpm = rev * 60000 / (millis() – measureTime);” as it’s tailored for interrupts occurred once per revolution. You can modify this line to suit your specific ‘sensor and target’ setup. The given code works better only if the RPM is greater than 60 as at least one rotation per second must happen for accurate RPM display.

Rotational Speed Monitor

As stated, an inductive proximity sensor is used for rotational speed monitoring here by counting relevant pulses in a non-contact way. Sensing range of SN04-N is 4mm but a distance (gap) close to 3.2mm is recommended between the stationary head (proximity sensor) and the orbiting target (ferrous object).

The entire setup must be powered from a regulated 5V dc supply. First of all, route the ground lead of the power supply to the ground rail (GND) of Arduino, and to the ground wire (blue) of SN04-N. Similarly extend the positive lead to the regulated dc input (5V) pin of Arduino, and to the positive wire (brown) of SN04-N. Finally, connect the output (black) of SN04-N to D2 (INT0) of Arduino (refer next figure). For short-time experiments, powering up the Arduino through its dc input jack (or Vin) by a 9-12V unregulated dc supply lets you take the regulated 5V output from Arduino board to run the inductive proximity sensor (be careful).

According to “Automation Insights” blog (https://automation-insights.blog), the best target in general for an inductive proximity sensor is a flat piece of ferrous metal. Other non-ferrous target materials can still be detected by an inductive sensor (of course with lesser sensitivity), but ferrous steel is a proven target material with a cheerful sensing range.

Interrupts

Don’t Kill Your Arduino! Somebody might wonder why I wanted to run SN04-N sensor from a 5V regulated dc supply instead of the recommended 10-30V dc input. It’s because I noticed that output (black) of the SN04-N sensor (what I got from an online seller is a cheap one, may be another clone) is internally tied to its unregulated positive rail through a 10K pull-up resistor. So its output will give the same inputted positive voltage in idle state and that will kill the Arduino. Although with a little trick it’s easy to drop down the output voltage to TTL level, my laziness forced me to try a 5V regulated dc supply input for SN04-N, and it worked.

More Interrupts?

Arduino Serial Interrupt Example

If you want one more sensor in the same setup, luckily Arduino Uno has 2 external interrupts, so you can try the same strategy as in the code, i.e. just make another interrupt function for the second sensor and attach it on interrupt 1 (pin 3). Thankfully, using the “PinChange Interrupt” library, we can configure any pin on Arduino an interrupt pin. As always you can go through the external link for more details https://www.brainy-bits.com/make-any-arduino-pin-an-interrupt-pin/

Arduino Software Interrupt Timer

And, here’s a useful documentation especially for those interested to learn more about Arduino RPM measurement techniques http://elimelecsarduinoprojects.blogspot.com/2013/06/measure-rpms-arduino.html