Hi.
I am trying to create a program that with count the time taken for 4 pulses and then convert that to to RPM and store it in a variable for use later.
I have tried using INT0 to capture the 4 pulse and record the time taken but when i add in the code to show the result on serial monitor it seems to take a while to show the correct rpm for a pulse at 66Hz.
Here is the code I have. Please feel free to tell me any stupid mistakes. I'm still learning.
I'm testing with a NANO but will be moving to a ATTINY85
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include <SwitecX12.h>
//#define F_CPU 8000000UL
#define TIMER_INTERVAL_MS 200
#define PULSE_THRESHOLD 4
#define RPM_THRESHOLD 2000
#define AVERAGE_COUNT 5
const int STEPS = 265 * 12;
// Define X12 control pins
const int A_STEP = 8;
const int A_DIR = 9;
const int RESET = 11;
SwitecX12 motor1(STEPS, A_STEP, A_DIR);
volatile uint16_t pulse_count = 0;
volatile uint8_t pulse_flag = 0;
volatile uint16_t rpm = 0;
volatile uint16_t rpm_average = 0;
uint16_t rpm_buffer[AVERAGE_COUNT] = { 0 };
uint8_t rpm_index = 0;
// Function prototypes
void timer1_init();
void int0_init();
void uart_init();
void uart_transmit(char data);
void uart_print(const char* str);
void uart_print_number(uint16_t num);
// Timer1 compare match interrupt (200 ms interval)
ISR(TIMER1_COMPA_vect) {
// Calculate RPM (pulses in 200ms * 5 * 60 = pulses per minute)
rpm = (pulse_count * (60.0 / 0.66644) / 1.5); // Convert to RPM
pulse_count = 0; // Reset pulse count
// Store RPM in the buffer for averaging
rpm_buffer[rpm_index] = rpm;
rpm_index = (rpm_index + 1) % AVERAGE_COUNT;
// Calculate average RPM
uint16_t sum = 0;
for (uint8_t i = 0; i < AVERAGE_COUNT; i++) {
sum += rpm_buffer[i];
}
rpm_average = sum / AVERAGE_COUNT;
// Set LED on if average RPM is above threshold
if (rpm_average > RPM_THRESHOLD) {
PORTB |= (1 << PORTB5); // Turn LED on
} else {
PORTB &= ~(1 << PORTB5); // Turn LED off
}
int motorPosition = map(rpm_average, 0, 5000, 0, STEPS); // Adjust the steps according to your motor's range
motor1.stepTo(motorPosition);
motor1.update();
}
// External Interrupt on INT0 (pin PD2)
ISR(INT0_vect) {
pulse_count++;
// Set flag every 4 pulses
if (pulse_count % PULSE_THRESHOLD == 0) {
pulse_flag = 1;
}
}
void timer1_init() {
// Configure Timer1 for CTC mode with 200ms interval
TCCR1B = (1 << WGM12) | (1 << CS11) | (1 << CS10); // CTC mode, prescaler 64
OCR1A = (uint16_t)((F_CPU / (64 * 1000)) * TIMER_INTERVAL_MS / 1000 - 1); // 200ms interval
TIMSK1 |= (1 << OCIE1A); // Enable Timer1 compare match A interrupt
}
void int0_init() {
// Configure INT0 to trigger on rising edge
EICRA |= (1 << ISC01) | (1 << ISC00); // Rising edge on INT0
EIMSK |= (1 << INT0); // Enable external interrupt INT0
}
void uart_init() {
// Set baud rate to 9600
uint16_t ubrr_value = F_CPU / 16 / 9600 - 1;
UBRR0H = (uint8_t)(ubrr_value >> 8);
UBRR0L = (uint8_t)ubrr_value;
UCSR0B = (1 << TXEN0); // Enable transmitter
UCSR0C = (1 << UCSZ01) | (1 << UCSZ00); // 8-bit data
}
void uart_transmit(char data) {
while (!(UCSR0A & (1 << UDRE0)))
; // Wait for empty transmit buffer
UDR0 = data; // Send data
}
void uart_print(const char* str) {
while (*str) {
uart_transmit(*str++);
}
}
void uart_print_number(uint16_t num) {
char buffer[10];
itoa(num, buffer, 10);
uart_print(buffer);
}
void setup() {
// Configure PB5 as output for LED
DDRB |= (1 << DDB5);
DDRB |= (1 << DDB3);
PORTB |= (1 << PORTB3);
motor1.zero();
motor1.stepTo(STEPS);
motor1.stepTo(0);
}
int main(void) {
// Initialize Timer1, INT0, and UART
timer1_init();
int0_init();
uart_init();
setup();
// Enable global interrupts
sei();
while (1) {
// Check if the pulse flag is set
if (pulse_flag) {
pulse_flag = 0; // Reset flag
// Print RPM to serial
uart_print("RPM: ");
uart_print_number(rpm_average);
uart_print("\r\n");
}
}
}