文章目录[隐藏]
结合前面学习的流水灯、蜂鸣器、外部中断、超声波和OLED显示,实现51单片机平台的倒车雷达案例
1. 倒车雷达
倒车雷达(PDC,Parking Distance Control)全称叫“倒车防撞雷达”,也叫“泊车辅助装置”,是汽车泊车或者倒车时的安全辅助装置,由超声波传感器(俗称探头)、控制器和显示器(或蜂鸣器)等部分组成。在倒车时,帮助司机“看见”后视镜里看不见的东西,以声音或者更为直观的显示告知驾驶员周围障碍物的情况,解除了驾驶员泊车、倒车和起动车辆时前后左右探视所引起的困扰,并帮助驾驶员扫除了视野死角和视线模糊的缺陷,提高驾驶的安全性。
案例采用STC89C52单片机做为主控,通过高精度超声波测量距离,STC89C52单片机接收超声波的测量距离信号并处理得到数据,可实时显示在OLED上;
当测量距离小于安全距离时,借助蜂鸣器发出声音报警,也可使用流水灯作为距离检测辅助。
2. 功能模块回顾
2.1 流水灯
有3种方式可以控制板上流水灯
位输出方式:
void main()
{
while(1)
{
LED0=ON;
delay(200);
LED0=OFF;
LED1=ON;
delay(200);
LED1=OFF;
LED2=ON;
delay(200);
LED2=OFF;
LED3=ON;
delay(200);
LED3=OFF;
LED4=ON;
delay(200);
LED4=OFF;
LED5=ON;
delay(200);
LED5=OFF;
LED6=ON;
delay(200);
LED6=OFF;
LED7=ON;
delay(200);
LED7=OFF;
}
}
移位方式:
void main()
{
unsigned char i;//0-255
while(1)
{
P1=0xfe;//1111 1110
for(i=0;i<8;i++)
{
delay(200);
P1<<=1;//P1=P1<<1
P1=P1|0X01;//0000 0001
}
}
}
循环移位方式:
#include<intrins.h>
void main()
{
P1=0Xfe;//1111 1110
while(1)
{
delay(200);
P1=_crol_(P1,1); //循环移位函数 把P1寄存器内的数向左移1位
}
}
2.2 蜂鸣器多频率
改变for函数中蜂鸣器高低电平的时间间隔,可以实现蜂鸣器以多段频率鸣叫
unsigned char i;
for(i=20;i>0;i--)
{
BUZZER = ON;
delay_ms(20);
BUZZER = OFF;
delay_ms(20);
}
for(i=10;i>0;i--)
{
BUZZER = ON;
delay_ms(50);
BUZZER = OFF;
delay_ms(50);
}
2.3 按键检测
通过检测物理按键输入状态执行不同操作(需要加延时消抖)
if(KEY1 == 0)
{
delay_ms(10);
if(KEY1 == 0)
{
P1 = 0x00;
}
}
if(KEY2 == 0)
{
delay_ms(20);
if(KEY2 == 0)
{
P1 = 0xff;
}
}
2.4 外部中断
通过外部中断的方式来执行实时性操作
void EXIT_Init()
{
IT1 = 0; //设置外部中断1触发方式 0:低电平触发 1:下降沿触发
EX1 = 1; //使能外部中断1
EA = 1; //开启全局中断
}
void main()
{
P1 = 0xff;
EXIT_Init();
while(1);
}
//外部中断1服务函数
void EXint1() interrupt 2
{
delay_ms(10);
if(KEY1 == 0) P1 = ~P1; // 0xff 0x00
while(KEY1 == 0);
}
2.5 超声波测距
按超声波时序图编写超声波驱动代码(用定时器0做10us延时)
// 延时10us
void Delay10us()
{
TMOD |= 0x01; //16位定时器/计数器,TH0、TH1全用
TH0 = 0xFF;
TL0 = 0xF6;
TR0 = 1; //TR0为1时允许T0开始计数
while(!TF0); //当T0溢出时退出while
TF0 = 0; //TF0置0
}
float GetDistance(unsigned int time)
{
float distance;
distance = (float)time * 0.017; //cm
return distance;
}
unsigned int RunOnce()
{
unsigned int time;
//10us高电平发送触发信号
Trig = 0;
Trig = 1;
Delay10us();
Trig = 0;
//等待高电平信号接收
while(!Echo);
//T0清0重新计数(高电平持续时间)
TH0 = 0;
TL0 = 0;
TR0 = 1;
//等待高电平信号接收结束
while(Echo);
//关闭T0计数
TR0 = 0;
//高电平时间赋值,单位us
time = TH0*256 + TL0; // TH0<<8 | TL0
TH0 = 0;
TL0 = 0;
return time;
}
main函数中调用函数计算超声波检测距离
void main()
{
unsigned int time = 0;
float distance;
while(1)
{
time = RunOnce(); //计算超声波测距时 传感器接收到高电平的时间
distance = GetDistance(time);
}
}
用流水灯“展示”超声波距离
void RunLED(unsigned int LEDnum)
{
unsigned int RunNum = LEDnum;
switch(RunNum){
case 0:
P1 = 0x00;
delay_ms(20);
P1 = 0xff;
delay_ms(20);
break;
case 1: P1 = 0xfe;
break;
case 2: P1 = 0xfc;
break;
case 3: P1 = 0xf8;
break;
case 4: P1 = 0xf0;
break;
case 5: P1 = 0xe0;
break;
case 6: P1 = 0xc0;
break;
case 7: P1 = 0x80;
break;
case 8: P1 = 0x00;
break;
default:
P1 = 0x00;
break;
}
}
void main()
{
unsigned int time = 0;
float distance;
while(1)
{
time = RunOnce();
distance = GetDistance(time);
RunLED((int)(distance/10));
}
}
根据不同的超声波检测距离使蜂鸣器以不同频率发出声音
void main()
{
unsigned int time = 0;
float distance;
while(1)
{
time = RunOnce();
distance = GetDistance(time);
if(distance <= 10.0){
BUZZER = ON;
delay_ms(50);
BUZZER = OFF;
delay_ms(50);
}
else if((10.0 < distance) && (distance <= 20.0)){
BUZZER = ON;
delay_ms(100);
BUZZER = OFF;
delay_ms(100);
}
else if((20.0 < distance) && (distance <= 50.0)){
BUZZER = ON;
delay_ms(160);
BUZZER = OFF;
delay_ms(160);
}
}
}
2.6 OLED显示
需要调用到的显示函数如下:
数字显示函数
//x,y :起点坐标
//len :数字的位数
//size:字体大小
//mode:模式 0,填充模式;1,叠加模式
//num:数值(0~4294967295);
void OLED_ShowNum(u8 x,u8 y,u32 num,u8 len,u8 size2)
{
u8 t,temp;
u8 enshow=0;
for(t=0;t<len;t++)
{
temp=(num/oled_pow(10,len-t-1))%10;
if(enshow==0&&t<(len-1))
{
if(temp==0)
{
OLED_ShowChar(x+(size2/2)*t,y,' ');
continue;
}else enshow=1;
}
OLED_ShowChar(x+(size2/2)*t,y,temp+'0');
}
}
调用示例:
//参数: x坐标,y坐标,数值,位数,大小
OLED_ShowNum(0,0,2,1,8);
汉字显示函数(需取模)
//显示汉字
void OLED_ShowCHinese(u8 x,u8 y,u8 no)
{
u8 t,adder=0;
OLED_Set_Pos(x,y);
for(t=0;t<16;t++)
{
OLED_WR_Byte(Hzk[2*no][t],OLED_DATA);
adder+=1;
}
OLED_Set_Pos(x,y+1);
for(t=0;t<16;t++)
{
OLED_WR_Byte(Hzk[2*no+1][t],OLED_DATA);
adder+=1;
}
}
调用示例:
//参数 x坐标,y坐标,数组下标
OLED_ShowCHinese(8,0,0);
字符串显示函数
//显示一个字符号串
void OLED_ShowString(u8 x,u8 y,u8 *chr)
{
unsigned char j=0;
while (chr[j]!='\0')
{ OLED_ShowChar(x,y,chr[j]);
x+=8;
if(x>120){x=0;y+=2;}
j++;
}
}
调用示例:
OLED_ShowString(20,0,"Hello World");
3.案例编程
3.1 案例构思
各功能模块在倒车雷达的案例应用构思如下表
功能 | 应用 |
---|---|
流水灯 | 测距辅助 |
蜂鸣器 | 测距辅助 |
板载按键(KEY1除外) | 按键控流水灯/蜂鸣器 |
外部中断(KEY1) | 模式切换(按键模式/测距模式) |
超声波 | 障碍物距离检测 |
OLED显示 | 模式显示/超声波距离显示 |
3.2 汉字取模
将案例中所需要显示的汉字一起取模,然后把取模代码拷贝至工程汉字显示数组中需要显示的文字内容分别是“倒车雷达”,“测距模式”,“按键模式”
unsigned char code Hzk[][32]={
{0x84,0x84,0xFC,0x84,0x84,0x00,0xF8,0x00,0xFF,0x00,0x84,0x84,0xFC,0x84,0x84,0x00},
{0x10,0x30,0x1F,0x08,0x88,0x42,0x21,0x18,0x07,0x00,0x20,0x20,0x3F,0x20,0x20,0x00},/*"",0*/
{0x80,0x60,0xF8,0x07,0x04,0x64,0x5C,0xC4,0x64,0x44,0x00,0xF8,0x00,0xFF,0x00,0x00},
{0x00,0x00,0xFF,0x00,0x20,0x62,0x22,0x1F,0x12,0x12,0x00,0x4F,0x80,0x7F,0x00,0x00},/*"倒",1*/
{0x00,0x08,0x88,0x48,0x28,0x18,0x0F,0xE8,0x08,0x08,0x08,0x08,0x08,0x08,0x00,0x00},
{0x08,0x08,0x09,0x09,0x09,0x09,0x09,0xFF,0x09,0x09,0x09,0x09,0x09,0x08,0x08,0x00},/*"车",2*/
{0x20,0x18,0x0A,0xAA,0xAA,0xAA,0x0A,0xFE,0x0A,0xAA,0xAA,0xAA,0x0A,0x28,0x18,0x00},
{0x00,0x00,0xFE,0x92,0x92,0x92,0x92,0xFE,0x92,0x92,0x92,0x92,0xFE,0x00,0x00,0x00},/*"雷",3*/
{0x40,0x40,0x42,0xCC,0x00,0x10,0x10,0x10,0x90,0x7F,0x90,0x10,0x10,0x10,0x10,0x00},
{0x00,0x40,0x20,0x1F,0x20,0x48,0x44,0x42,0x41,0x40,0x40,0x41,0x42,0x4C,0x40,0x00},/*"达",4*/
{0x10,0x60,0x02,0x8C,0x00,0xFE,0x02,0xF2,0x02,0xFE,0x00,0xF8,0x00,0xFF,0x00,0x00},
{0x04,0x04,0x7E,0x01,0x80,0x47,0x30,0x0F,0x10,0x27,0x00,0x47,0x80,0x7F,0x00,0x00},/*"测",5*/
{0x00,0x3E,0x22,0xE2,0x22,0x3E,0x00,0xFE,0x22,0x22,0x22,0x22,0x22,0xE2,0x02,0x00},
{0x20,0x3F,0x20,0x1F,0x11,0x11,0x00,0x7F,0x44,0x44,0x44,0x44,0x44,0x47,0x40,0x00},/*"距",6*/
{0x10,0x10,0xD0,0xFF,0x90,0x14,0xE4,0xAF,0xA4,0xA4,0xA4,0xAF,0xE4,0x04,0x00,0x00},
{0x04,0x03,0x00,0xFF,0x00,0x89,0x4B,0x2A,0x1A,0x0E,0x1A,0x2A,0x4B,0x88,0x80,0x00},/*"模",7*/
{0x10,0x10,0x90,0x90,0x90,0x90,0x90,0x10,0x10,0xFF,0x10,0x10,0x11,0x16,0x10,0x00},
{0x00,0x20,0x60,0x20,0x3F,0x10,0x10,0x10,0x00,0x03,0x0C,0x10,0x20,0x40,0xF8,0x00},/*"式",8*/
{0x10,0x10,0x10,0xFF,0x90,0x20,0x98,0x88,0x88,0xE9,0x8E,0x88,0x88,0xA8,0x98,0x00},
{0x02,0x42,0x81,0x7F,0x00,0x00,0x80,0x84,0x4B,0x28,0x10,0x28,0x47,0x80,0x00,0x00},/*"按",9*/
{0x40,0x30,0xEF,0x24,0x24,0x80,0xE4,0x9C,0x10,0x54,0x54,0xFF,0x54,0x7C,0x10,0x00},
{0x01,0x01,0x7F,0x21,0x51,0x26,0x18,0x27,0x44,0x45,0x45,0x5F,0x45,0x45,0x44,0x00},/*"键",10*/
};
3.3 案例编程
如3.1案例构思的逻辑,编写程序
#include "REG51.h"
#include "delay.h"
#include "oled.h"
#define ON 0
#define OFF 1
sbit Trig = P0^0;
sbit Echo = P0^1;
sbit BUZZER = P0^7;
sbit EXint1 = P3^3;
sbit key2 = P3^4;
sbit key3 = P3^6;
sbit key4 = P3^7;
unsigned int ModeFlag = 0;
unsigned int ModeDispaly = 0;
// 延时10us
void Delay10us()
{
TMOD |= 0x01; //16位定时器/计数器,TH0、TH1全用
TH0 = 0xFF;
TL0 = 0xF6;
TR0 = 1; //TR0为1时允许T0开始计数
while(!TF0); //当T0溢出时退出while
TF0 = 0; //TF0置0
}
float GetDistance(unsigned int time)
{
float distance;
distance = (float)time * 0.017;
return distance;
}
unsigned int RunOnce()
{
unsigned int time;
//10us高电平发送触发信号
Trig = 0;
Trig = 1;
Delay10us();
Trig = 0;
//等待高电平信号接收
while(!Echo);
//T0清0重新计数(高电平持续时间)
TH0 = 0;
TL0 = 0;
TR0 = 1;
//等待高电平信号接收结束
while(Echo);
//关闭T0计数
TR0 = 0;
//高电平时间赋值,单位us
time = TH0*256 + TL0; // TH0<<8 | TL0
TH0 = 0;
TL0 = 0;
return time;
}
void EXIT_Init()
{
IT1 = 0;
EX1 = 1;
EA = 1;
}
void distance1_3()
{
BUZZER = ON;
delay_ms(50);
BUZZER = OFF;
delay_ms(50);
}
void distance4_5()
{
BUZZER = ON;
delay_ms(90);
BUZZER = OFF;
delay_ms(90);
}
void distance6_8()
{
BUZZER = ON;
delay_ms(150);
BUZZER = OFF;
delay_ms(150);
}
unsigned char last_num = 99;
void RunLED(unsigned int num)
{
if(num == last_num){
switch(num){
case 0:
P1 = 0x00;
BUZZER = ON;
delay_ms(20);
P1 = 0xff;
BUZZER = OFF;
delay_ms(20);
break;
case 1:
P1 = 0xfe;
distance1_3();
break;
case 2:
P1 = 0xfc;
distance1_3();
break;
case 3:
P1 = 0xf8;
distance1_3();
break;
case 4:
P1 = 0xf0;
distance4_5();
break;
case 5:
P1 = 0xe0;
distance4_5();
break;
case 6:
P1 = 0xc0;
distance6_8();
break;
case 7:
P1 = 0x80;
distance6_8();
break;
case 8:
P1 = 0x00;
distance6_8();
break;
default:
P1 = 0x00;
break;
}
}
last_num = num;
}
void main(void)
{
unsigned int time = 0;
float distance;
EXIT_Init(); //外部中断初始化
OLED_Init(); //oled屏幕初始化
OLED_Clear(); //oled清屏
OLED_ShowCHinese(16,0,1);
OLED_ShowCHinese(32,0,2);
OLED_ShowCHinese(48,0,3);
OLED_ShowCHinese(64,0,4);
OLED_ShowString(80,0,"demo");
while(1)
{
time = RunOnce();
distance = GetDistance(time);
if(ModeFlag){
if(ModeDispaly == 1)
{
OLED_ShowCHinese(0,2,5);
OLED_ShowCHinese(16,2,6);
OLED_ShowCHinese(32,2,7);
OLED_ShowCHinese(48,2,8);
OLED_ShowChar(64,2,':');
ModeDispaly = 0;
}
if(distance>100)
{
OLED_ShowString(0,4," ");
OLED_ShowNum(40,4,(int)distance/100,1,8);
}
else{
OLED_ShowString(0,4," ");
OLED_ShowString(40,4," ");
}
OLED_ShowNum(50,4,(int)distance/10,1,8);
OLED_ShowNum(60,4,(int)distance%10,1,8);
OLED_ShowString(68,4,"cm");
RunLED((int)distance/10);
}else{
if(ModeDispaly == 0)
{
OLED_ShowCHinese(0,2,9);
OLED_ShowCHinese(16,2,10);
OLED_ShowCHinese(32,2,7);
OLED_ShowCHinese(48,2,8);
OLED_ShowChar(64,2,':');
OLED_ShowString(0,4," ");
P1 = 0xff;
ModeDispaly = 1;
}
if(key2 == 0)
{
delay_ms(10);
if(key2 == 0)
{
unsigned int i;
P1 = 0xfe;
for(i=8;i>0;i--)
{
delay_ms(60);
P1 <<= 1;
}
P1 = 0x80;
for(i=7;i>0;i--)
{
delay_ms(60);
P1 >>= 1;
P1 |= 0x80;
}
}
}
if(key3 == 0)
{
delay_ms(10);
if(key3 == 0) BUZZER = ON;
}
if(key4 == 0)
{
delay_ms(10);
if(key4 == 0) BUZZER = OFF;
}
}
}
}
void exint() interrupt 2
{
delay_ms(10);
if(EXint1 == 0) ModeFlag = !ModeFlag;
while(EXint1 == 0);
}
3.4 案例运行效果
程序烧写成功后,上电运行,初始模式是按键模式
在按键模式下,除了key1作为外部中断1不能对其编程外,其他按键对应控制流水灯和蜂鸣器(key2执行1次双向流水灯,key3打开蜂鸣器,key4关闭蜂鸣器),这里蜂鸣器演示不出来,只演示key2的流水灯
按key1切换超声波测距模式,在<10cm的距离内,板载LED灯爆闪+蜂鸣器高频率响起
障碍物距离越远,LED点亮的个数随着增多,蜂鸣器的鸣叫频率也渐渐变缓
在测量距离中,OLED屏幕会一直显示超声波模块检测到的距离(单位:cm)
版权声明:本文为CSDN博主「GenCoder」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_36955622/article/details/122998820
暂无评论