一些学习成果的分享。

智能小车快速精确定位设计与实现

2020-2021学年第一学期《电子综合实验》课程设计代码
  本次实验主要在STC89C52RC单片机和智能小车上进行,实现了让小车定点停车并根据前方障碍距离进行调整、警报等功能,同时进行计时、学号显示,实现代码类型为C语言。

具体要求:
(1)程序运行后,在8位数码管的最右边2位显示自己的学号,并启动超声波测距,将距离值显示在最左边4位(xxx.x cm) ;
(2)设置一个按键,当按下该按键时,蜂鸣器响0.1秒(按键提示音),启动小车直线前进(速度可自行设定档位),同时启动“秒表计时器”,作为小车运行时间计时,并在数码管最右边3位显示 时间(要求定时中断实现),显示从“00.0”秒开始,最大到“99.9” 秒;
(3)小车行驶过程中,在最左边4位数码管上实时显示距离障碍物的测距结果,精度为0.1cm。
(4)设定小车距离目标位置12.5±0.3cm处停止,通过速度调节和前进后退等方式使小车精确定位在目标范围,若小于目标距离12.2cm,则声光报警,即用一个发光二极管指示灯闪烁(亮0.1s、灭0.9s);蜂鸣器持续报警(低电平驱动);若大于目标距离12.2cm,则撤销声光报警。当小车最后停止在目标范围内时,停止运行时间计时。
代码如下

#include <reg52.h>

//定义驱动引脚
sbit Echo = P2^0;    //Echo
sbit Trig = P2^1;    //Trig

//定义I/O接口
sbit PWM_IN1 = P1^2;     // 高电平1:左电机后退(反转)
sbit PWM_IN2 = P1^3;     // 高电平1:左电机前进(正转)

sbit PWM_IN3 = P1^6;     // 高电平1:右电机前进(正转)    
sbit PWM_IN4 = P1^7;     // 高电平1:右电机后退(反转)

sbit PWM_EN1 = P1^4;     // 高电平1:使能左电机 
sbit PWM_EN2 = P1^5;     // 高电平1:使能右电机.

sbit BUZZER = P2^3;    //有源蜂鸣器驱动端口,低电平输出响
sbit LED = P1^0;    //定义LED由P1.0控制
sbit KEY1 = P3^4;        //定义按键K1,启动键

//智能小车数码管显示电路
sbit LED_seg_control = P2^6;       //数码管段选信号
sbit LED_dig_control = P2^7;       //数码管位选信号

//定义PWM最大级数,也就是调节直流电机的速度等级
#define SPEED_MAX        40

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

//定义PWM级数,分为0~SPEED_MAX-1级
unsigned char Speed_L;        //左电机转速调节(调节PWM的一个周期SPEED_MAX*1ms时间内,左电机正转时间:Speed_L*1ms)
unsigned char Speed_R;        //右电机转速调节(调节PWM的一个周期SPEED_MAX*1ms时间内,右电机正转时间:Speed_R*1ms)

bit timer_start_flag = 0;    //定义标志位,可用于程序中工作状态的指示
unsigned int v;       //变速时间指示
unsigned int juli;       //变速时间指示

//定义显示缓冲区(由定时中断程序自动扫描)
unsigned char DispBuf[8];



//软件定时器
bit SWTR;    //软件定时器运行标志
bit SWTF;    //软件定时器溢出标志
unsigned int SWTV;    //软件定时器定时值

bit  Counter_overflag = 0;        //T0定时器溢出标志

unsigned long Rang = 0;

code unsigned char Tab[] =
    {//定义0123456789AbCdEF的数码管字型数据,其他显示字符需自行计算,如‘-’的字形数据为0x40
        0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,
        0x7F,0x6F,0x77,0x7C,0x39,0x5E,0x79,0x71
    };
//============================//
void DispClear();
void Timer0Init();
void Timer1Init();
void Delay(unsigned int t);
void SysInit();
void StartModule();
unsigned int Conut();
void DispRang();
unsigned char KeyScan();
    
/*
函数:KeyScan()
功能:键盘扫描
返回:扫描到的键值
*/
unsigned char KeyScan()
{
    unsigned char k;

    k = '\0';
    if ( KEY1 == 0 ) k = '0';        //k1 表示开始
    
    return k;
}

    
    
//============================//
/*
函数:T0INTSVC()
功能:定时器T0的中断服务函数
*/
    
void T0INTSVC() interrupt 1          //T0中断用来定时器溢出,超过测距范围
  {
    Counter_overflag = 1;             //中断溢出标志
        Echo = 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 char high = 0;
    static unsigned char ms1=0;
    static unsigned char sec=0;
    static unsigned char HQX=0;
    TH1 = 0xFC;
    TL1 = 0x66;                    //可以将FA24换成0000,降低扫描速度,观察和理解动态扫描
    TR1 = 1;

    LED_dig = 0xFF;                //消隐(通过控制8位数码管的位控制端,即COM端,让其全不亮)
    LED_dig_control = 1;
    LED_dig_control = 0;

    LED_seg = DispBuf[n];    //更新扫描显示数据
    LED_seg_control = 1;
    LED_seg_control = 0;

    LED_dig = ~com[n];            //重新显示
    LED_dig_control = 1;
    LED_dig_control = 0;
    
    n++;                         //指向下一位扫描显示
    n &= 0x07;

//========模拟一个软件定时器===========//
    if ( SWTR )
    {
        if ( --SWTV == 0 ) SWTF = 1;
    }
    //控制电机
    if(timer_start_flag)
    {
        high++;
        v++;
        ms1++;                        //1ms加1
        HQX++;
        if(HQX<=100) 
            BUZZER = 0;
        if(HQX>100)
        {
            HQX--;
            BUZZER=1;
        }
        if(ms1 == 100)
        {
            ms1 = 0;            //0.1s到
            if(PWM_EN1 ==1)
            {
            sec ++;                    //0.1秒加1
            
            DispBuf[0] = Tab[sec % 10];            //查表取出显示数字对应的段码,存入显示缓冲器数组
            DispBuf[1] = Tab[sec / 10 % 10] | 0x80;     // 该位带小数点显示
            DispBuf[2] = Tab[sec / 100];
            }
        }
        if(v == 1000)//计时1s
        {
            v=0;
        }
        if ( high >= SPEED_MAX ) 
            high = 0;                //PWM波的周期为:SPEED_MAX*1ms = 32ms

        if(juli >= 500)
        {
            BUZZER = 1;
            LED = 1;
            PWM_EN1 = 1;
            PWM_EN2 = 1;            //电机有效
            if ( high < 30 )        //PWM波高电平时间:(Speed_L)*1ms
            {
                PWM_IN1 = 0;
                PWM_IN2 = 1;         //左电机的正转
            }
            else                    //PWM波低电平时间:(SPEED_MAX-Speed_L)*1ms
            {
                PWM_IN1 = 0;
                PWM_IN2 = 0;         //左电机的停转
            }
    
            if ( high < 30 )        //PWM波高电平时间:Speed_R*1ms
            {
                PWM_IN3 = 1;
                PWM_IN4 = 0;        //右电机的正转        
            }
            else                    //PWM波低电平时间:(SPEED_MAX-Speed_R)*1ms
            {
                PWM_IN3 = 0;
                PWM_IN4 = 0;        //右电机的停转    
            }
        }
        if(juli >= 300 && juli < 500)
        {
            BUZZER = 1;
            LED = 1;
            PWM_EN1 = 1;
            PWM_EN2 = 1;            //电机有效
            if ( high < 15 )        //PWM波高电平时间:(Speed_L)*1ms
            {
                PWM_IN1 = 0;
                PWM_IN2 = 1;         //左电机的正转
            }
            else                    //PWM波低电平时间:(SPEED_MAX-Speed_L)*1ms
            {
                PWM_IN1 = 0;
                PWM_IN2 = 0;         //左电机的停转
            }
    
            if ( high < 15 )        //PWM波高电平时间:Speed_R*1ms
            {
                PWM_IN3 = 1;
                PWM_IN4 = 0;        //右电机的正转        
            }
            else                    //PWM波低电平时间:(SPEED_MAX-Speed_R)*1ms
            {
                PWM_IN3 = 0;
                PWM_IN4 = 0;        //右电机的停转    
            }
        }
        if(juli > 128 && juli < 300)
        {
            BUZZER = 1;
            LED = 1;
            PWM_EN1 = 1;
            PWM_EN2 = 1;            //电机有效
            if ( high < 4 )        //PWM波高电平时间:(Speed_L)*1ms
            {
                PWM_IN1 = 0;
                PWM_IN2 = 1;         //左电机的正转
            }
            else                    //PWM波低电平时间:(SPEED_MAX-Speed_L)*1ms
            {
                PWM_IN1 = 0;
                PWM_IN2 = 0;         //左电机的停转
            }
    
            if ( high < 4 )        //PWM波高电平时间:Speed_R*1ms
            {
                PWM_IN3 = 1;
                PWM_IN4 = 0;        //右电机的正转        
            }
            else                    //PWM波低电平时间:(SPEED_MAX-Speed_R)*1ms
            {
                PWM_IN3 = 0;
                PWM_IN4 = 0;        //右电机的停转    
            }
        }
        if(juli <= 128 && juli >=122)
        {
            BUZZER = 1;
            LED = 1;
            PWM_IN1 = 0;
            PWM_IN2 = 0;
            PWM_IN3 = 0;
            PWM_IN4 = 0;
            PWM_EN1 = 0;
            PWM_EN2 = 0;
        }
        if(juli < 122)
        {
            BUZZER = 0;
            if(v > 100)
                LED = 1;
            else
                LED = 0;
            PWM_EN1 = 1;
            PWM_EN2 = 1;            //电机有效
            if ( high < 8 )        //PWM波高电平时间:(Speed_L)*1ms
            {
                PWM_IN1 = 1;
                PWM_IN2 = 0;         //左电机的反转
            }
            else                    //PWM波低电平时间:(SPEED_MAX-Speed_L)*1ms
            {
                PWM_IN1 = 0;
                PWM_IN2 = 0;         //左电机的停转
            }
    
            if ( high < 8 )        //PWM波高电平时间:Speed_R*1ms
            {
                PWM_IN3 = 0;
                PWM_IN4 = 1;        //右电机的反转        
            }
            else                    //PWM波低电平时间:(SPEED_MAX-Speed_R)*1ms
            {
                PWM_IN3 = 0;
                PWM_IN4 = 0;        //右电机的停转    
            }
        }
    }
}

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

/*
函数:Timer0Init()
功能:定时器T0示初始化,用于获取超声波作用时间
*/
void Timer0Init()
{
    
    EA = 0;
    TH0 = 0;        
    TL0 = 0;
    ET0 = 1;
    EA = 1;
}

/*
函数:Timer1Init()
功能:定时器T1示初始化,用于计时和数码管扫描
*/
void Timer1Init()
{
    
    EA = 0;
    TH1 = 0xFC;        //1ms定时初值
    TL1 = 0x66;
    TR1 = 1;
    ET1 = 1;
    EA = 1;
}


void Delay(unsigned int t)
{
    SWTV = t;    //软件定时器赋初值
    SWTR = 1;    //启动软件定时器
    while ( !SWTF );    //等待溢出
    SWTR = 0;    //停止软件定时器
    SWTF = 0;    //清除溢出标志
}

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

    TMOD = 0x11;    //设置定时器T0为16位定时器
    DispClear();    //初始为全灭
    Timer0Init();    //定时器T0初始化
    Timer1Init();    //定时器T1初始化
}

/*
函数:StartModule()
功能:启动模块,采用 IO 触发测距,给至少10us 的高电平信号;
*/
void  StartModule()
{
    unsigned char i;
    Trig = 1;                                //启动一次模块
    for(i = 0;i < 10;i ++);                    //超声波启动延迟10us以上;
    Trig = 0;
}
/*
函数:Conut()
功能:超声波高电平脉冲宽度计算函数,采用查询模式
说明:t = (12 / 11.0592)us*(TH0 * 256 + TL0) = 1.1*(TH0 * 256 + TL0)
*/
 unsigned int Conut()
{
    unsigned long Rang_time = 0;        //T0定时器合并数值及测距值
    
    TR0 = 1;          //开启计数
    while(Echo);    //当Echo为1计数并等待
    TR0 = 0;            //关闭计数
    Rang_time = TH0 * 256 + TL0;     //Rang_time*1.085单位是微秒
    TH0 = 0;
    TL0 = 0;
    
    //(Rang_time * 1.1 / 1000000) * (344 / 2) * 1000 = Rang_time * 11 * 344 / 20000
    //假设环境温度20摄氏度,考虑到车头与超声波模块间距,进行10毫米的校正。(试验中自行修正!)
    Rang_time = (Rang_time * 11 * 344 ) / 20000 + 10;     //单位是毫米
    return ((unsigned int)Rang_time);
}

void DispRang(unsigned int F_rang)
{
    if((F_rang >= 4000) || Counter_overflag == 1)            //超出测量范围
    {     
        Counter_overflag = 0;
        DispBuf[4] = 0x40;          //显示“-”
        DispBuf[5] = 0x40;
        DispBuf[6] = 0x40;              
        DispBuf[7] = 0x40;
    }
    else        //显示数据单位:厘米
    {
        DispBuf[4] = Tab[F_rang % 10];          
        DispBuf[5] = Tab[F_rang / 10 % 10] + 0x80;
        DispBuf[6] = Tab[F_rang / 100 % 10];              
        DispBuf[7] = Tab[F_rang / 1000];
    }
}


void main()
{
    unsigned int Mem_rang[3];
    unsigned char Mem_Numb = 0;
    unsigned char k;
    DispClear();    //初始为全灭
    SysInit();        //定时器初始化
    timer_start_flag = 0;
    
    PWM_EN1 = 1;
    PWM_EN2 = 1;            //电机有效
    
    DispBuf[1] = Tab[0];           //在8位数码管上显示学号后两位,例如12号
    DispBuf[0] = Tab[8];
    
    //这里将读取两次测距结果作为第一次求均值的备用数据
    Echo = 1;                    //IO口读入之前输出1
    StartModule();        //启动模块
    while(Echo != 1);
    Mem_rang[1] = Conut();            //超声波高电平脉冲宽度计算函数
    Echo = 1;                    //IO口读入之前输出1
    StartModule();        //启动模块
    while(Echo != 1);
    Mem_rang[2] = Conut();

    while(1)
    {
        Echo = 1;                  //IO口读入之前输出1
        StartModule();        //启动模块
        while(Echo != 1);

        Mem_rang[Mem_Numb] = Conut();
        Mem_Numb ++;
        if (Mem_Numb == 3)Mem_Numb = 0;
        Rang = (Mem_rang[2] + Mem_rang[1] + Mem_rang[0]) / 3;    
    juli = Rang;    
        DispRang(Rang);
        
        Delay(200);                //每隔0.2秒读取一次并显示一次测量数据
        k = KeyScan();        //键盘扫描
        if ( k != '\0' ) 
        {
            timer_start_flag = 1;
            PWM_EN1 = 1;
            PWM_EN2 = 1;
            for (;;)
            {
                Delay(50);
                if ( KeyScan() == '\0' ) break;
            }
        }
    }
}

添加新评论