1
\$\begingroup\$

I am using an ATmega328P MCU with an 16 MHz external oscillator. I need to measure distance of Ultrasonic Sensor HC-SR04 using the ATmega328P Timer 1 Input Capture interrupt. I have to measure the Echo pulse time between the rising and falling edges. Then send the calculated distance via UART. I tried to simulate on Proteus, but it doesn't work. Can anybody give me some direction how to do it? . I'm using the CodeVisionAVR IDE. Here's my code:

#include <mega328p.h> #include <stdio.h> #include <delay.h> #define Trigger PORTB.0 #define Echo PINB.1 // Declare your global variables here unsigned int distance; unsigned long echoStart, echoEnd, time; void setup() { DDRB = (0<<DDB1) | (1<<DDB0); DDRC = (1<<DDC2) | (1<<DDC0); //Timer 1 initialization TCCR1A = 0; TCCR1B = (1 << ICES1) | (1 << CS10); //rising edge, no prescaler TIFR1 = (1<<ICF1); TIMSK1 = (1 << ICIE1); } void initUSART() { // USART initialization // Communication Parameters: 8 Data, 1 Stop, No Parity // USART Receiver: Off // USART Transmitter: On // USART0 Mode: Asynchronous // USART Baud Rate: 9600 UCSR0A=(0<<RXC0) | (0<<TXC0) | (0<<UDRE0) | (0<<FE0) | (0<<DOR0) | (0<<UPE0) | (0<<U2X0) | (0<<MPCM0); UCSR0B=(0<<RXCIE0) | (1<<TXCIE0) | (0<<UDRIE0) | (0<<RXEN0) | (1<<TXEN0) | (0<<UCSZ02) | (0<<RXB80) | (0<<TXB80); UCSR0C=(0<<UMSEL01) | (0<<UMSEL00) | (0<<UPM01) | (0<<UPM00) | (0<<USBS0) | (1<<UCSZ01) | (1<<UCSZ00) | (0<<UCPOL0); UBRR0H=0x00; UBRR0L=0x67; } interrupt [TIM1_CAPT] void timer1_capt_isr(void) { TCCR1B = 0x41; //Rising edge, no prescaler while((TIFR1&(1<<ICF1)) == 0); echoStart = (ICR1H << 8) | ICR1L; TIFR1 = (1<<ICF1); //Clear ICF flag TCCR1B = 0x01; //Falling edge, no prescaler while ((TIFR1&(1<<ICF1)) == 0); echoEnd = (ICR1H << 8) | ICR1L; //Take value of capture register TIFR1 = (1<<ICF1); // Clear ICF flag } void main() { setup(); initUSART(); // Global enable interrupts #asm("sei") while (1) { Trigger = 1; delay_us(10); Trigger = 0; time = echoEnd - echoStart; distance = time / 58; printf("Distance: %2d\r\n", distance); delay_ms(500); } } 
\$\endgroup\$
1
  • \$\begingroup\$ [...]how to do it? The method you outline is the proper approach...measure the pulse width of ECHO with a microcontroller's timer...time from ECHO's rising edge to ECHO's falling edge. \$\endgroup\$ Commented Apr 10, 2023 at 13:56

1 Answer 1

1
\$\begingroup\$

Check the datasheet of your sensor, normally there's a max distance it can measure (let's say 4m). Within this max distance decide the range you want to measure. (let's say 1-3m). Calculate the round trip time for sound for this distance: 2-6m @ 343m/s -> 5.8ms - 17.5ms. Ok, so we know that.

Let's assume you want to measure with 16 bit precision. Then the max counter value of 65535 can not be exceeded in 17.5ms. The min time for 1 count is 17.5ms / 65535 = 0.267us. Max counter frequency is 1/ans = 3.74MHz. If your counter is faster than 3.74MHz, it will count so fast that your 16 bit variable will overflow before the sound bounces back from 3m. 1MHz clock would be nice, but you don't have 1:16 prescaler. Use 1:64, that would give you 250KHz, that's 4us/count giving you 1.37mm precision.

So set up your counter with the 1:64 prescaler, the input capture is fine. Pulse your trigger thing, reset counters. When your interrupt triggers, check your counter value. That value * 4 is your rround trip time in us. The rest is about sending it wherever you want to send it.

I think the main difficulty is calculating this ^, to have an idea what kind of values you are dealing with.

Now as I'm thinking about it, you could use 1:8 prescaler for 2MHz counter, that overflows in 32.768ms, that will do too. Then you have 0.17mm precision. Realistically speaking this is not gonna be useful, but don't let it stop you.

\$\endgroup\$

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.