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;
}
}
}
}