简体   繁体   English

使用超声波和 ATMEGA32 进行距离测量

[英]Distance measurement using Ultrasonic and ATMEGA32

I'm working on a Distance measurement program using an AVR microcontroller.我正在使用 AVR 微控制器开发距离测量程序。 I use a 16x2 LCD and an ultrasonic sensor along with ATMEGA32A.我使用 16x2 LCD 和超声波传感器以及 ATMEGA32A。 I wrote a code to display the distance from the Ultrasonic HC-SR04 on the LCD screen, but it gives me false readings, it increases the distance when the object is very near and vice versa.我写了一个代码来在 LCD 屏幕上显示与超声波 HC-SR04 的距离,但它给了我错误的读数,当物体非常近时它会增加距离,反之亦然。 I just want an accurate reading.我只是想要一个准确的读数。

Ultrasonic datasheet ATMEGA32A Datasheet 超声波数据表ATMEGA32A 数据表在此处输入图像描述

#include <avr/io.h>
#include <avr/interrupt.h>
#include <MrLcd/MrLCDmega32.h>
#define F_CPU 1000000
#include <util/delay.h>
#include <stdlib.h>
#define  Trigger_pin    PD0 /* Trigger pin */


static volatile int pulse = 0;
static volatile int i = 0;

int main(void)
{
    
    Initialise();
    DDRD = 0b11111011;
    _delay_ms(50);

    GICR |= 1<<INT0;
    MCUCR |= 1<<ISC00;
    
    int16_t count_a = 0;
    char show_a[16];

    sei();
    
    while(1)
    {
        PORTD |= (1<<Trigger_pin);
        _delay_us(10);

        PORTD &= ~(1<<Trigger_pin);
        count_a = pulse/58;

        Send_A_String("Distance Sensor");
        GoToMrLCDLocation(1,2);
        Send_A_String("Distance=");
        itoa(count_a,show_a,10);
        Send_A_String(show_a);
        Send_A_String(" ");
        GoToMrLCDLocation(13,2);
        Send_A_String("cm");
        GoToMrLCDLocation(1,1);
    }
}

ISR(INT0_vect)
{
    if(i == 1)
    {
        TCCR1B = 0;
        pulse = TCNT1;
        TCNT1 = 0;
        i = 0;
    }

    if(i==0)
    {
        TCCR1B |= 1<<CS10;
        i = 1;
    }
}

I tried to change the trigger pin definition and define it in the code itself but still no progress.我试图更改触发器引脚定义并在代码本身中定义它,但仍然没有进展。

Update: I changed a bit more in the code but I'm getting hex values when the distance is more than 9, for example, 10 is being displayed as 1e.更新:我在代码中做了更多更改,但当距离大于 9 时,我得到了十六进制值,例如,10 显示为 1e。

This is for initialise function这是用于初始化功能

void Initialise(void)
{        
    DataDir_MrLCDsControl|=1<<LightSwitch|1<<ReadWrite|1<<BipolarMood;  //these information will go towards the LCD
        
    _delay_ms(15);                              // Wait for the LCD to start
    
    Send_A_Command(0x01);   // to clear the screen
    _delay_ms(2);
    Send_A_Command(0x38);   // TO tell LCD about 8 data lines
    _delay_us(50);
    Send_A_Command(0b00001110); //Some cursor command
    _delay_us(50);
}

You are sending pulses at a very rapid rate (determined solely by the display update time), and they are asynchronous to the time/counter reset.您正在以非常快的速率发送脉冲(仅由显示更新时间决定),并且它们与时间/计数器重置异步。 You have no idea which pulse triggered the interrupt and it did not start at the same time as the timer.您不知道哪个脉冲触发了中断,并且它没有与定时器同时启动。

I would suggest that you reset the counter at the start of the pulse, and capture the counter value on interrupt.我建议您在脉冲开始时重置计数器,并在中断时捕获计数器值。 When the time has exceeded the maximum range, send a new pulse:当时间超过最大范围时,发送一个新的脉冲:

First define some constants:首先定义一些常量:

#define PULSES_PER_CMx100 (F_CPU * 100 / 68600)
#define MAX_RANGE_CM 300
#define MAX_RANGE_COUNT ((MAX_RANGE_CM * PULSES_PER_CMx100) / 100)

Then your measure/display loop might look like:然后你的测量/显示循环可能看起来像:

    pulse = 1 ; // dummy start
    GICR &= ~(1<<INT0) ;  // Disable INT0

    for(;;)
    {
        // Ready for new measurement?...
        if( pulse != 0 )
        {
            // Send pulse and reset timer
            PORTD |= (1<<Trigger_pin) ;
            pulse = 0 ;
            TCNT1 = 0 ;
            _delay_us(10);
            PORTD &= ~(1<<Trigger_pin) ;

            // Wait for echo pulse interrupt...
            GIFR |= 1<<INTF0;  // Clear INT0 pending flag
            GICR |= 1<<INT0 ;  // Enable INT0
        }
        else  // When measurement available...
        {
            int distance_cm = pulse * 100 / PULSES_PER_CMx100 ;

            // display distance
            ...
        }

        // If out of range, timeout, send a new pulse
        if( TCNT1 > MAX_RANGE_COUNT )
        {
            // Force a new pulse to be triggered
            pulse = 1 ;
        }
    }

And the ISR:和情报服务侦察:

ISR(INT0_vect)
{
    pulse = TCNT1;       // Capture time on interrupt
    GICR &= ~(1<<INT0) ; // Disable further interrupts
}

Now bear in mind that that method will take measurements as fast as possible and since you are displaying them for human reading, that is rather unnecessary.现在请记住,该方法将尽可能快地进行测量,并且由于您正在显示它们以供人类阅读,因此这是不必要的。 You might simply put a delay in the loop - making the pulse timeout unnecessary, or better you could take the mean of multiple measurements to get a more robust measurement, or use a moving average window, with outlier rejection.您可以简单地在循环中放置一个延迟 - 使脉冲超时变得不必要,或者更好的是您可以采用多次测量的平均值以获得更可靠的测量,或者使用移动平均窗口,并拒绝离群值。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM