I'm adding a rev counter to the spindle motor on a cnc machine using an IR distance sensor.
Code:-
#include <U8x8lib.h>
#include <util/atomic.h> // this library includes the ATOMIC_BLOCK macro.
U8X8_SSD1306_128X64_NONAME_HW_I2C u8x8(/* reset=*/ U8X8_PIN_NONE);
// Timer Counter and compare values
const uint16_t t1_load = 0;
const uint16_t t1_comp = 31250;
// Pins
const byte led_pin = PB5;
volatile int Int_On_2 = false;
volatile byte Rev_Count = 0;
volatile float rpmfloat = 0;
volatile unsigned int rpm = 0;
void setup() {
attachInterrupt(digitalPinToInterrupt(2), RPMCnt, LOW);
Serial.begin(9600);
PORTB ^= (1 << led_pin);
TCCR1A = 0;
// Set CTC mode
TCCR1B &= ~(1 << WGM13);
TCCR1B |= (1 << WGM12);
// Set prescaler t0 256
TCCR1B |= (1 << CS12);
TCCR1B &= ~(1 << CS11);
TCCR1B &= ~(1 << CS10);
// Reset Timer1 and set compare value
TCNT1 = t1_load;
OCR1A = t1_comp;
// Eable Timer1 compare interupt
TIMSK1 = (1 << OCIE1A);
Rev_Count = 0;
// Enable global interupts
//sei();
u8x8.begin();
u8x8.setFont(u8x8_font_profont29_2x3_f);
}
void loop() {
u8x8.setCursor(1, 0);
ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
// code with interrupts blocked (consecutive atomic operations will not get interrupted)
Serial.print(Int_On_2);
rpm = round(rpmfloat * 120);
}
u8x8.print(rpm);
u8x8.print(Int_On_2);
delay(500);
}
ISR(Timer1_COMPA_vect) {
// Triggers every 500 ms
PORTB ^= (1 << led_pin); // flip the led
// ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
// code with interrupts blocked (consecutive atomic operations will not get interrupted)
rpmfloat = Rev_Count;
Rev_Count = 0;
// }
}
void RPMCnt () {
// Triggers every Infrared reflection detected
// ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
// code with interrupts blocked (consecutive atomic operations will not get interrupted)
Rev_Count += 1;
Int_On_2 = 2;
//}
}
RPM when displayed is always zero and when I trigger the sensor by hand Int_On_" displays 2 ONCE only and then goes back to zero. Int_On_2 = 2 is the ONLY instruction that changes this global. so once a trigger is detected it should stay at 2. I add Int_On_" to help me track down why rpm is always zero but to no avail. atomic block does not cure the problem.
The LOW interrupt trigger will constantly retrigger the interrupt while the pin is LOW - you want FALLING I think. LOW is of very specialized use during sleep states I believe as it is the only interrupt trigger that works when clocking is turned off for the GPIO pads.
Triggering the interrupt when the sensor is LOW does not sound right. It's usual to trigger the interrupt on an edge: RISING, FALLING or CHANGE (which means either RISING or FALLING).
Thanks for the reply. I just tried FALLING and with the same result. I'll maybe put a scope of the sensor to make sure I am getting a square wave. However this would not explain why Int_On_2 does not stay at 2 after the first trigger.
is it possible that at some point your program is crashing, reseting the processor and resetting Int_On_2 back to 0 - try putting a Serial print at the start of setup() so you know when the program starts executing
It is normally due to interference pick up. This is common when motors are involved.
Try and use a separate power supply for any motors and beef up your supply decoupling.
If the motors only turn one way, which seems likely as it is a CNC machine then make sure you have a fly back diode across the motor.
// Eable Timer1 compare interupt
TIMSK1 = (1 << OCIE1A); This is the culprit!!!!!
Test done with motor not running but I take the point of motor noise. I didn't even have to manually trigger the sensor. Processor resets with this in line.
Note: This is equivalent to: PINB = 1 << led_pin;
This is a special feature of the PINx registers on the AVR processors. If you write a 1 into any bit(s) it will toggle the corresponding PORTx bit(s). This saves a read/modify/write cycle.
That made it burst into life. Thank you. Strange that my compiler (1.8.16) did not error this. I've just changed compiler warnings to ALL but it still did not complain when wrong case.