Project 2 - Measuring distance of an object using ultrasonic sensor (HC-SR04)
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#define TRIGGER_PIN PD2
#define ECHO_PIN PD3
#define SOUND_SPEED 0.034 // Speed of sound in cm/µs
volatile uint16_t pulse_width = 0;
volatile uint8_t measurement_complete = 0;
void timer1_init(void) {
// Configure Timer1 for Input Capture
TCCR1A = 0; // Normal mode
TCCR1B = (1 << ICES1) | (1 << CS10); // Capture on rising edge, no prescaling
TIMSK = (1 << ICIE1); // Enable Input Capture Interrupt
}
void usart_init(uint16_t ubrr) {
// Set baud rate
UBRRH = (unsigned char)(ubrr >> 8);
UBRRL = (unsigned char)ubrr;
// Enable receiver and transmitter
UCSRB = (1 << RXEN) | (1 << TXEN);
// Set frame format: 8 data bits, 1 stop bit
UCSRC = (1 << URSEL) | (1 << UCSZ1) | (1 << UCSZ0);
}
void usart_transmit(unsigned char data) {
// Wait for empty transmit buffer
while (!(UCSRA & (1 << UDRE)));
// Put data into buffer, sends the data
UDR = data;
}
void trigger_ultrasonic(void) {
// Send a 10µs pulse to the trigger pin
PORTD |= (1 << TRIGGER_PIN);
_delay_us(10);
PORTD &= ~(1 << TRIGGER_PIN);
}
int main(void) {
// Initialize USART
usart_init(51); // 9600 baud rate for 8 MHz clock
// Set TRIGGER_PIN as output and ECHO_PIN as input
DDRD |= (1 << TRIGGER_PIN);
DDRD &= ~(1 << ECHO_PIN);
// Initialize Timer1
timer1_init();
// Enable global interrupts
sei();
while (1) {
if (measurement_complete) {
// Calculate distance
float distance = (pulse_width * SOUND_SPEED) / 2; // Distance = (Pulse width in µs * Speed of sound) / 2 (round trip)
// Convert distance to string and send via USART
char buffer[20];
snprintf(buffer, sizeof(buffer), "Distance: %.2f cm\r\n", distance);
for (char *ptr = buffer; *ptr; ptr++) {
usart_transmit(*ptr);
}
measurement_complete = 0; // Reset flag
}
// Trigger ultrasonic sensor every 1 second
trigger_ultrasonic();
_delay_ms(1000);
}
return 0;
}
// Input Capture Interrupt Service Routine
ISR(TIMER1_CAPT_vect) {
static uint16_t start_time = 0;
if (TCCR1B & (1 << ICES1)) {
// Rising edge: Capture the time
start_time = ICR1;
TCCR1B &= ~(1 << ICES1); // Switch to capture on falling edge
} else {
// Falling edge: Capture the time and calculate pulse width
pulse_width = ICR1 - start_time;
TCCR1B |= (1 << ICES1); // Switch back to capture on rising edge
measurement_complete = 1; // Set measurement complete flag
}
}