Atmega128a 和HC-SR04 做超声波测距

业余研究嵌入式,现在这方面还是个门外汉,买了块Atmega128a的开发版和HC-SR04,慢慢跟着入门,今天做了个测距程序,一些知识点记录下来,怕以后给忘了。
晶震:8MHz
管脚连接: PD5接的HC-SR04的Trig,PD4接的HC-SR04的Echo
代码里的printf是我自己实现的一个版本,往串口输出,方便上位机测试

#include<avr/io.h>
#include<avr/interrupt.h>
#include "rs232.h"

int lastValue, value;//分别记录上升沿和下降沿时的计数器值
enum {
    READY,
    STARTED,
    COUNTING
}stage;

void initIO()
{
     DDRD = (1<<PD5);//PD5设置为输出管脚
     PORTD = 0x0;
}
void initTimer()
{
    //初始化16位记时器
    TCNT1 = 0;
    TCCR1A |= 0x00;
    TCCR1B |= 0x42;//使用普通模式(WGM),8分频(CS),使用上升沿触发输入捕捉(ICES1)
    TIMSK = 0x20;//设置TICIE1为1,设置输入捕捉中断使能信号
}

//输入捕捉中断程序
ISR(TIMER1_CAPT_vect)
{
    if(TCCR1B & 0x40)//捕捉到了上升沿
    {
        lastValue = ICR1;//记录上升沿的时候的计数器值
        stage = COUNTING;//设置状态为正在记录
        TCCR1B &= ~0x40;//让记时器捕捉下降沿
    }
    else
    {
        value = ICR1;//记录下降沿的时候的计数器值
        stage = READY;//标记为结束
        TCCR1B |= 0x40;//让记时器捕捉上升沿
    }
}
void delay(int n)
{
    int i, j, k;
    for(k = 0; k < n; k++)
    for(i = 0; i < 100;i++)
        for(j = 0; j < 500;j++)
            asm("nop");

}
void send()
{
    unsigned char k;
    stage = STARTED;
    lastValue = value = 0;
    PORTD |= (1<<PD5);//将Trig信号设置为高电平一段时间后还原成低电瓶,这样会让超声波模块发送超声波并接受超声波
    for(k= 0; k< 100;k++)
        asm("nop");
    PORTD &= ~(1<<PD5);
    while(stage != READY);//等待接收结束
    int time;
    if(value < lastValue)//需要处理计数器向上溢出的情况,求出正确的时间差
        time = 0xffff - lastValue + 1 + value;
    else//没有溢出,直接求时间差
        time = value - lastValue;
    int distance = time / 58;//计算距离,单位cm
    printf("done, distance = %dcm\r\n", distance);//输出结果
}

//禁用看门狗
void disableWatchdog()
{
     asm("wdr");
     WDTCR = (1 << WDCE) | (1<< WDE);
     WDTCR = 0;//按照Atmega128a手册说法,关闭开门狗必须连续执行这三个代码才能正确关闭。
}
void main(void)
{
     disableWatchdog();
     initIO();
     initTimer();
     USART0_Init();         //波特率9600 初始化串口
     SREG = 0x80;
     asm("sei");
     printf("\r\nSystem started, pause for 1s %d\r\n", MCUCSR);
     delay(10);
     while(1)
     {
         printf("Send trig...");
         send();
         delay(80);
     }
}

今天困扰我的地方有几个知识点:
1)配饰TIMSK后导致系统执行玩第一个send后就自动复位,于是我试图关闭看门狗,但是关闭后还是自动复位,于是我用MCUCSR寄存器来调试复位原因,MCUCSR值是PORF|EXTRF,分别表示上电复位和外部复位,最初怀疑是电路板没接好,调试好久才发现我启用的是Timer1输入捕捉中断,而代码里原来是定义的Timer0溢出中断,因此中断向量表里输入捕捉中断那里指向的是0,导致发生中断的时候复位了
2)Magic Number 58,这个值是在HC-SR04资料里的51例子里看到的,但是解释得不够合理,然后在研究了AVR 16位定时器的普通模式后终于算出来,晶震是8MHz,8分频后,Timer1的时钟信号频率只有8MHz/8=1MHz,因此周期只有1/1MHz=1e-06,声音速度是344米,Echo高电平持续时间包括了发送和接受,因此距离 D=COUNT * 1e-06s * 344m/s /2 = COUNT/(58.13953488372093cm),最初推倒这个系数的时候,看书上说时钟记时器要超过TOP才会触发一次中断,16位Timer1的TOP为0xFFFF,所以我想着这个TOP是否也需要加入计算,这个也耗费了不少时间,最后才明白这个可以无视。

接下来研究Zigbee看怎么搞


Last modified on 2013-04-13