课堂作业四

发布时间 2023-10-17 11:38:18作者: LYoungH

 

 

/*
    功能:超声波测距实验。
    功能模块:HC-SR04,测距范围:2cm to 450cm
    数码管显示:xxx.x,单位:cm
    计算公式如下:
    s = v*t/2 = (340m/s)*t(s)/2 = (340um/us)*t(us)/2 = (0.34mm/us)*t(us)/2 = (0.034cm/us)*t(us)/2;;
    s(cm) = t(us)*0.017(cm/us).
    t is the received Echo high level time from the UltracsonicModule HC-SR04, 
    it messured by T0 timer, t=(12/11.0592)us*(TH0,TL0), initial (TH0,TL0)=0x00 00
    s(cm) =0.0184*(TH0,TL0),    (TH0,TL0)max = 24456 for s =450cm
    UltracsonicModule HC-SR04 VCC(+5V) connect to VCC; 
    Gnd connct to GND; 
    Trig connect to P3.3; 
    Echo connect to P3.2.

*/


#include <reg52.h>

//定义驱动引脚
sbit Echo = P3^2;    //Echo
sbit Trig = P3^3;    //Trig

#define LED_seg    P0            //8位数码管的段码和位码驱动通过P0端口锁存 
#define LED_dig    P0

//定义显示缓冲区(由定时中断程序自动扫描)
unsigned char DispBuf[8];
unsigned char RangeBuf[3];
//智能小车数码管显示电路管脚定义
sbit SS = P2^6;       //数码管段选信号
sbit CS = P2^7;       //数码管位选信号


bit Counter_overflag = 0;        //T0定时器溢出标志
bit Echo_Over = 0;                     //超声波测距完成标志,无论收到回波或没有收到,总要置位一次
unsigned char cnt = 0;            //测量次数,每次加一
unsigned int Range = 0;
unsigned long Echo_time = 0;        //T0定时器合并数值
unsigned int sec = 60;
int Range_mean;
bit flag_display = 1;
unsigned int count = 0;
code unsigned char Tab[] =
    {//定义0123456789AbCdEF的数码管字型数据,其他显示字符需自行计算,如‘-’的字形数据为0x40
        0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,
        0x7F,0x6F,0x77,0x7C,0x39,0x5E,0x79,0x71
    };
//============================//
/*
函数:EX0INTSVC()
功能:外部中断0中断服务程序
用途:为避免定时中断干扰测距的准确性,采用外部中断触发后立即停止计时
        外部中断0优先级高于定时中断,程序尽量短,避免过多干扰定时中断
*/
void EX0INTSVC() interrupt 0
{
    TR0 = 0;                    //停止计时
    Echo_time = TH0 * 256 + TL0;     //读取定时器0计数值
    TH0 = 0;                     //清除定时器0计数寄存器,为下次计数做准备
    TL0 = 0;
    Echo_Over = 1;         //表示本次超声波测距完成,可以启动下次的测量
    EX0 = 0;                    //关闭外部中断,否则会马上引起下一次中断
}
//============================//
/*
函数:T0INTSVC()
功能:定时器T0的中断服务函数
用途:若超过测距范围,长时间无法收到回波,
        已经启动的T0中断会计数溢出,利用定时器溢出标志来判断
*/
void T0INTSVC() interrupt 1          
{
    TR0 = 0;                //停止计时
    Counter_overflag = 1;             //中断溢出标志,未收到回波
    Echo_Over = 1;    //表示本次超声波测距完成,可以启动下次的测量
    EX0 = 0;                //关闭外部中断
}
/*
函数:T1INTSVC()
功能:定时器T1的中断服务函数
*/
void T1INTSVC() interrupt 3    //定时器1的中断号为:3
{
    code unsigned char com[] = {0x80,0x40,0x20,0x10,0x08,0x04,0x02,0x01};    //显示位的端口控制字节
    static unsigned char n = 0;                        //n: 扫描显示位计数,0-7
    static unsigned int counter_1ms = 0; 
//================数码管定时扫描驱动显示===============    
    TR1 = 0;
    TH1 = 0xFC;
    TL1 = 0x66;                        //可以将FC66换成0000,降低扫描速度,观察和理解动态扫描
    TR1 = 1;
    P0 = 0xFF;        //消隐
    CS = 1;
    CS = 0;
    P0 = DispBuf[n];    //更新扫描显示数据
    SS = 1;
    SS = 0;
    P0 = ~com[n];    //重新显示
    CS = 1;
    CS = 0;
    n++;                         //指向下一位扫描显示
    n &= 0x07;
//====================================================
    counter_1ms ++;
    if ( counter_1ms == 100 )
    {
        counter_1ms = 0;
        count = count + 1;
        if ( count == 4 )
        {
            count = 0;
            flag_display = 1;
        }
        else
        {
            flag_display = 0;
        }
        if ( Range_mean > 150 )
        {
            sec ++;
        }
        else if ( Range_mean < 120 )
        {
            sec --;
        }
        DispBuf[7] = Tab[sec / 100] ;
        DispBuf[6] = Tab[sec /10 % 10] | 0x80;
        DispBuf[5] = Tab[sec % 10];
        if ( sec == 119)
        {
            sec = 0;
        }
        else if ( sec ==0 )
        {
            sec = 119;
        }
    }
}


/*
函数:DispClear()
功能:清除数码管的所有显示
*/
void DispClear()
{
    unsigned char i;
    for ( i=0; i<8; i++ )
    {
        DispBuf[i] = 0x00;    //i值代表数码管的位数,可以在后面的程序观察是左起还是右起,0x00可以关闭数码管显示
    }
}

/*
函数:Delay()
功能:延时
说明:
    晶振频率为11.0592MHz
    延时长度 = 1ms * t
*/
void Delay(unsigned int t)
{
    unsigned int us_ct;
    for (;t > 0;t --)        //执行代码消耗CPU时间
        for (us_ct = 113;us_ct > 0;us_ct --);    
}


/*
函数:SysInit()
功能:系统初始化
*/
void SysInit()
{

    DispClear();    //初始化显示缓存
    TMOD = 0x11;    //设置定时器T0为16位定时器,定时器T1为16位定时器
    EA = 0;                //关闭总中断,待初始化结束后再打开
//======定时计数器T0初始化,用于获取超声波作用时间,若定时溢出,则超出测距范围
    TH0 = 0;        
    TL0 = 0;
    ET0 = 1;
//======定时计数器T1初始化,用于获取1ms定时中断=====
    TH1 = 0xFC;        //设置定时器1的初值: 0xFC66,对应定时时间1ms
    TL1 = 0x66;
    ET1 = 1;            //使能定时器T1中断
    TR1 = 1;            //启动定时器T1
//======定时计数器T0和T1初始化完毕=================    
    EX0 = 0;            //关闭外部中断
    IT0 = 0;            //外部中断0采用电平触发模式,低电平出发
    EA = 1;                //使能总中断
}

/*
函数:StartModule()
功能:启动模块,采用 IO 触发测距,给至少10us 的高电平信号;
*/
void  StartModule()
{
    unsigned char i;
    Trig = 1;                              //启动一次模块
    for(i = 0;i < 5;i ++);                    //超声波启动延迟10us以上;
    Trig = 0;
}
/*
函数:Range_Display()
功能:超声波距离显示函数
说明:若超出距离则显示“- - - -*/
 void Range_Display()
{
//    cnt ++;        //每测距一次,数值加一,用于测试
//    DispBuf[7] = Tab[cnt / 100]; 
//    DispBuf[6] = Tab[cnt / 10 % 10];
//    DispBuf[5] = Tab[cnt % 10];
    
    //超出测量范围或者障碍物太近反射角度太小导致无法收到回波,都显示“- - - -if((Range >= 4000) || Counter_overflag == 1)
    {     
        Counter_overflag = 0;
        DispBuf[0] = 0x40;          
        DispBuf[1] = 0x40;
        DispBuf[2] = 0x40;              
        DispBuf[3] = 0x40;
    }
    //按照HC-SR04的指标,大致工作在2cm—450cm的范围内,与产品质量和反射面相关
    else        //显示数据单位:厘米
    {
        
        DispBuf[0] = Tab[Range_mean % 10];          
        DispBuf[1] = Tab[Range_mean / 10 % 10] | 0x80;
        DispBuf[2] = Tab[Range_mean / 100 % 10];              
        DispBuf[3] = Tab[Range_mean / 1000];
    }
}
/*
函数:Timer_Count()
功能:超声波高电平脉冲宽度计算函数
备注:采用查询模式
说明:超声波模块在等待回波的时候,经常被定时中断打断,导致回波到达时间测量不及时,干扰了超声波测量精度
改进的办法是分别加入下面两条语句(暂时屏蔽,取消屏蔽可以用来对照)
    TR1 = 0;        //暂停定时器T1计数,相当于关闭定时中断T1
  TR1 = 1;        //重启定时器T1计数,相当于打开定时中断T1
随之带来什么新的问题呢?分析产生的原因。
*/
unsigned int  Timer_Count()        
{
    
    TR0 = 1;          //开启计数
    EX0 = 1;            //开启外部中断
    while(!Echo_Over);    //等待回波,当Echo_Over为1,表明收到回波或超出测距范围
    
    Echo_Over = 0;            //清除Echo_Over,准备下一次测距
    //程序到这里就已经得到了超声波的响应计数值,结果存在变量Echo_time内,Echo_time * 1.1us得到响应时间
    //假设环境温度26.5摄氏度,根据Echo_time的值自行计算测距的长度(单位:毫米)并替换下面的定值表达式
    //注意:必须用定点运算,精度为毫米!
    Range = Echo_time * 11 / 10 * 334 / 1000 /2;     //单位是毫米

    return (unsigned int)Range;
}
void main()
{
    
    SysInit();        //定时器初始化
    
//    DispBuf[7] = Tab[1];           //在8位数码管上显示“12345678”字样
//    DispBuf[6] = Tab[2];
//    DispBuf[5] = Tab[3];
//    DispBuf[4] = Tab[4];
//    DispBuf[3] = Tab[5];
//    DispBuf[2] = Tab[6];
//    DispBuf[1] = Tab[7];
//    DispBuf[0] = Tab[8];
//    Delay(2000);                    
    DispClear();                //清除显示(数码管上的显示内容全部熄灭
    //DispBuf[7] = Tab[cnt / 100]; 
    //DispBuf[6] = Tab[cnt / 10 % 10];
    //DispBuf[5] = Tab[cnt % 10];
    while(1)
    {
        Echo = 1;            //IO口读入之前输出1
        TR1 = 0;            //关闭定时器T1,避免打断超声波启动和上升沿捕获
        ET1 = 0;            //关闭定时器T1中断,时间很短,影响较小
        StartModule();        //启动模块
        while(!Echo);
        ET1 = 1;            //使能定时器T1中断
        TR1 = 1;            //启动定时器T1
    Range = Timer_Count();        //超声波高电平脉冲宽度计算函数
        RangeBuf[cnt] = Range;
        cnt = ( cnt + 1 ) % 3; 
        Range_mean = ( RangeBuf[0] + RangeBuf[1] + RangeBuf[2] )/3 ;
        if( flag_display )
            Range_Display();                    //超声波距离显示
//        Delay(200);                            //每隔0.2秒读取一次并显示一次测量数据,便于视觉观察
    }

}