81. High-Frequency Pulses Counter

Solving Approach,

  • We will count the number of pulses received with a maximum frequency of 8MHz (125 ns pulse width).
  • The short pulse duration makes External Interrupts unreliable for counting, as the controller may miss new pulses while serving the ISR.
  • To ensure accurate pulse counting, we will use a timer as a counter with an external clock source.
  • In this approach, the controller won’t be stuck in the ISR when a pulse occurs. The timer will automatically increment the counter without any interference from the microcontroller.
  • Timers in Arduino UNO:
    • Timer0: 8-bit
    • Timer1: 16-bit
    • Timer2: 8-bit
  • Chosen Timer: Timer1 (16-bit)
    • It can count up to 65,536 pulses (2^16) before overflowing.

Hardware Connection

Arduino UNO (Pulse Generator)

  • Connect the board to the PC using a USB cable to establish communication with the Serial Monitor.
  • Push Button Switch 
    • One terminal (NC) is connected to the Digital pin 12 using internal pullup.
    • The other terminal (NO) is connected to GND.

Arduino UNO (Pulse Counter)

  • Connect the board to the PC using a USB cable to establish communication with the Serial Monitor.
  • Push Button Switch 
    • One terminal (NC) is connected to the Digital pin 12 using internal pullup.
    • The other terminal (NO) is connected to GND.

The pulse generator produces pulses on digital pin 7, which is connected to digital pin 5 of the pulse counter. Digital pin 5 serves as the input for the external clock source to 

Firmware

  • Pulse Counting
    • Timer1 is configured as a counter to count external pulses connected to the T1 pin (Arduino pin 5).
    • The counter increments on the rising edge of each pulse.
    • An overflow interrupt is used to track when the counter exceeds its 16-bit limit (65,536).

Code  


#define SWITCH_PIN 12
#define DEBOUNCE_DELAY 50              // debounce delay 

bool last_button_state = 1;            // Previous button state (1: not pressed, 0: pressed)
bool current_button_state = 1;         // Current button state
unsigned long last_debounce_time = 0;  // Timestamp of the last button state change

uint8_t overflowFlag = 0;

void setup() {
  Serial.begin(115200);
  pinMode(SWITCH_PIN, INPUT_PULLUP);
  // Configure Timer1 as a counter
  TCCR1A = 0x00; // Normal mode
  TCCR1B = 0x07; // External clock source on T1 pin, rising edge
  TIMSK1 |= B00000001;  // Enable Timer Overflow Interrupt
  TCNT1 = 0;     // Initialize counter to 0
}

void loop() {

 if (is_debounced_press(SWITCH_PIN)) {
  uint32_t pulseCount = (overflowFlag*65536) + TCNT1;
  Serial.println("Number of Pulses: ");
  Serial.println(pulseCount);
  TCNT1 = 0;
  overflowFlag = 0;
}
}

bool is_debounced_press(int button_pin) {
  
  int reading = digitalRead(button_pin);
  

  // If the button state has changed, reset the debounce timer
  if (reading != last_button_state) {
    last_debounce_time = millis();
  }
  last_button_state = reading;
  // If the button state is stable for more than 50 msec the debounce delay, update the state.
  if ((millis() - last_debounce_time) > DEBOUNCE_DELAY) {
    if (reading != current_button_state) {
      current_button_state = reading;
      if (current_button_state == 0) {
        return true;  // valid press detected
      }
    }
  }
  return false;  // No valid press detected
}

ISR(TIMER1_OVF_vect)
{
  overflowFlag++;
}

Code Explanation 

  1. Setup
    • Serial communication is initialized.
    • Timer1 is configured as a counter.
    • The button pin is set as an input with the internal pull-up resistor enabled.
  2. Loop
    • The is_debounced_press() the function checks for a debounced button press.
    • If a debounced press is detected:
      • The total pulse count is calculated using the formula: (overflowFlag * 65536) + TCNT1.
      • The pulse count is printed to the Serial Monitor.
      • The Timer1 counter (TCNT1) and overflow flag (overflowFlag) are reset to 0.
  3. Interrupt Service Routine (ISR)
    • The ISR(TIMER1_OVF_vect) function increments  overflowFlag each time Timer1 overflows.

Timer Configuration and Calculations

  1. Timer1 Setup
    • TCCR1A = 0x00: Timer1 is set to normal mode (no PWM or waveform generation).
    • TCCR1B = 0x07: Timer1 is configured to use an external clock source (T1 pin) and increment on the rising edge of the input signal.
    • TIMSK1 |= B00000001: The Timer1 overflow interrupt is enabled. This interrupt triggers when the counter overflows (i.e., exceeds 65,536).
    • TCNT1 = 0: The Timer1 counter is initialized to 0.
  2. Overflow Handling
    • Timer1 is a 16-bit counter, so it can count up to 65,535 (0xFFFF) before overflowing.
    • When an overflow occurs, the ISR(TIMER1_OVF_vect) interrupt service routine is triggered, and the overflow flag is incremented.
    • The total pulse count is calculated as:
      • Total Pulses = (overflowFlag * 65536) + TCNT1.
      • Example 
        1. overflowFlag = 2 (2 overflows occurred).
        2. TCNT1 = 1234 (current counter value).
      • Total Pulses = (2 * 65536) + 1234 

= 131072 + 1234.

132306.

Button Debouncing Logic

  1. Debounce Mechanism
    • Read the button state using digitalRead(SWITCH_PIN).
    • If the button state changes, reset the debounce timer (last_debounce_time).
    • The button state is stable if it stays the same for at least 50 milliseconds (DEBOUNCE_DELAY).
  2. Valid Press Detection
    • A valid press is detected when the button state is stable and changes from HIGH (1) to LOW (0).
    • On a valid press, the pulse count is calculated and displayed.

 

 

Output

Hardware Setup

 

 

Serial Monitor Output

 

 

Video

 

 

Submit Your Solution

Note: Once submitted, your solution goes public, helping others learn from your approach!