【51单片机练习1——超声波测距+LCD12864显示】

PS:这是人生中的第一篇博客,记录了我人生中的第一次实习工作。与其说是博客文章,这些更像是一个小白的流水账日记,可能会有很多错误和有待优化的地方,希望各位大佬可以多多提出。如有问题可以私信联系。
行文过程中,欲说尽心中无限事,感慨万千。不过各位“道友”,很高兴见到你们,我们来日方长!
话不多说接下来就是正文内容。。。

任务需求

2018年安徽省机器人大赛单片机与嵌入式系统应用技能竞赛试题

需求1:开机后LCD12864屏幕第一行显示"DFZBJQ",第二行显示四位数字,并自下而上滚动,3秒后停止滚动。
需求2:应用超声波传感器实现距离采集,并在LCD12864显示屏上显示。
需求3:能够实现最远最近距离存储、查阅以及清除。

硬件环境介绍

1.开发套件介绍

本练习所采用的的开发套件为单片机与嵌入式系统竞赛实训平台。
单片机嵌入式系统竞赛实训平台
单片机与嵌入式竞赛实训平台拥有丰富的板载资源,一共分为公共资源去和四个实训场景,实训场景分别为智慧农业、智能音箱、智能小车以及工业互联网。在每个场景中都有丰富的传感器以及执行器,例如温湿度传感器、超声波测距传感器、步进电机以及直流电机等。
同时,该竞赛实训平台提供了三种不同的核心板——STC51核心板、STM21核心板以及FPGA核心板,可以通过核心板的插拔实现不同单片机核心的切换,大大提高利用率。
该实训平台的场景切换只需要拨动核心板上的拨码开关到对应的编码即可实现元器件的链接和切换,不需要再使用杜邦线进行连接,更加美观简洁。
由于本人是嵌入式学习的小白一枚,因此是从最简单的STC51单片机开始,因此所使用的为STC51核心板。

2.单片机介绍

STC51核心板的核心芯片采用的是宏晶科技的STC15W4K56S4,板载256Kb外置SRAM存储器以及2个100P BTB高速连接器用于和底板连接。
在本实训平台中所使用的晶振值为24MHz,串口测试波特率为115200.
STC51核心板

3.所需元器件介绍

3.1 LCD12864液晶显示屏

LCD12864液晶显示屏是一种具有4位/8位并行、2线或3线串行多种接口方式的点阵图形液晶
显示模块,这里使用的是8位并行总线驱动。
引脚连接:
数据端(D0~7) --------------------> P0
RS --------------------------------------> P20
RW -------------------------------------> P21
EN --------------------------------------> P22
电路原理图如下:
12864电路原理图
对于LCD12864的所有操作可以总结为4中:

  1. 读忙状态:读忙的同时读出指针地址内容,在初始化之后对LCD12864的每次读写都要进行忙检测。
  2. 写命令:写地址也是写指令,所有的指令可以查看下方指令集。
  3. 写数据:操作对象为DDRAM(数据显示RAM)、CGRAM(字符发生RAM)和GDRAM(图形显示RAM)。
  4. 读数据:操作对象与写数据一致
3.1.1 LCD12864引脚介绍

LCD12864一共拥有20个引脚,引脚号以及引脚说明如下图所示,其中我们需要重点关注的是RS、R/W、EN以及DB0~7这几个引脚。
12864引脚说明

3.1.2 LCD12864时序图
  1. 写时序:
    读数据
    2.读时序:
    在这里插入图片描述
3.1.3 LCD12864常用指令集

指令集分为基本指令集和扩展指令集,在使用对应的指令集前需要先写入对应指令,表明后续的所有指令都是该类指令。使用基本指令集时写指令(0x30),使用扩展指令集时写指令(0x34)

  • 基本指令集(RE = 0)
    • 清屏指令(0x01)
    • 回车指令(0x02/0x03)
    • 显示开、关光标(0x0c)
  • 扩展指令集(RE = 1)
3.2 超声波控制模块

本实训平台所使用的超声波模块是以CS100A芯片为主的自研模块,其具有两个控制端口——TRIG和ECHO,TRIG端口用于触发测距,至少需要给10us的高电平信号,接着模块自动发送8个40KHz的方波,自动检测是否有信号返回。如果有信号返回,通过ECHO输出一个高电平,高电平持续的时间就是超声波从发射到返回的时间。
超声波测距时序图:
在这里插入图片描述

由此可以得出:测试距离 = (高电平时间 * 声速(340m/s))/ 2
本次实验超声波控制模块与单片机引脚:
Trig ----------------------> P15
Echo --------------------->P34
超声波模块外围电路图:
超声波外围电路图

3.3 4x4矩阵键盘

4x4矩阵键盘电路原理图:
4x4矩阵键盘原理图
引脚分配图:
引脚分配图

硬件连接示意图

硬件连接示意图

总体程序流程图

程序流程图

实现代码

1. 需求1:LCD12864滚动显示实现

实现思路及代码:

1. 初始化LCD12864屏幕
/*******************************************************************************
* 函 数 名         : LCD12864_Init
* 函数功能		   : 初始化LCD12864
* 输    入         : 无
* 输    出         : 无
*******************************************************************************/
void LCD12864_Init()
{
	LCD12864_WriteCmd(0x30);  //选择基本指令操作
	LCD12864_WriteCmd(0x0c);  //显示开,关光标
	LCD12864_WriteCmd(0x01);  //清除LCD12864的显示内容
}
2. 使用for循环完成三次位置变换,每次延迟1S,结束for循环后再次显示(一定记得在循环中清屏哦)
for(i; i>0; i--)
{
	LCD12864_ShowStr(i - 1, 0, "DCFZBJQ");
	LCD12864_ShowStr(i, 0, "8957");
	//i--;
	Delay_ms(1000);
	LCD12864_WriteCmd(0x01);
}
LCD12864_ShowStr(2, 0, "DCFZBJQ");
LCD12864_ShowStr(3, 0, "8957");

2. 需求2:LCD12864显示实时显示超声波测距实现

  1. 初始化LCD12864和超声波模块(LCD初始化见上方)
TRIG_IO = 1;
Delay10us();												//@24.000MHz,10微秒
TRIG_IO = 0;
Timer_Count_0 = 0;
while(ECHO_IO == 0 && Timer_Count_0 < 50);					//500微秒超时
Timer_Count_0 = 0;
while(ECHO_IO == 1 && Timer_Count_0 < 2000);				//20毫秒超时
  1. 根据公式计算距离,公式为:距离(毫米)=时间(ms)*速度(340mm/ms)/2,及时通过定时器0完成
Num_Distance = (int)((float)Timer_Count_0 / 100 * 340 / 2);	//计算距离:距离(毫米)=时间(ms)*速度(340mm/ms)/2
void Timer0_Int (void) interrupt 1
{
	Timer_Count_0++;	
}
  1. 显示数据
LCD12864_WriteCmd(0x01);
LCD12864_ShowStr(0, 0, "Distance:");
sprintf((char *)buf,"%d",Num_Distance);
LCD12864_ShowStr(0,5,&buf);

3. 需求3:记录并显示有效最大值最小值并实现清除

  1. 创建三个变量s(交换变量,用于临时存储距离值),max(最大值),min(最小值)。同时将min赋初值为测试输出最大值2187,max赋值为0。并且在主循环中获取距离值的时候不断进行比较更新。
int min = 2187, max = 0, s;
s = Num_Distance;
if(s > max)
	max = s;
if(s < min)
	min = s;
  1. 在屏幕上显示数据
LCD12864_ShowStr(1, 0, "Min:");
sprintf((char *)buf1,"%d",min);
LCD12864_ShowStr(1,2,&buf1);
		
LCD12864_ShowStr(1, 4, "Max:");
sprintf((char *)buf2,"%d",max);
LCD12864_ShowStr(1,6,&buf2);
  1. 按键扫描,判断清除键是否按下,按下后将max和min两个变量恢复初始值
void IO_KeyScan(void)
{
	unsigned char j;
	j = IO_KeyState1;	//保存上一次状态

	P7 = 0xf0;	//X低,读Y
	IO_KeyDelay();
	IO_KeyState1 = P7 & 0xf0;

	P7 = 0x0f;	//Y低,读X
	IO_KeyDelay();
	
	IO_KeyState1 |= (P7 & 0x0f);
	
	IO_KeyState1 ^= 0xff;	//取反
	
	if(j == IO_KeyState1)	//连续两次读相等
	{
		j = IO_KeyState;
		IO_KeyState = IO_KeyState1;
		if(IO_KeyState != 0)	//有键按下
		{
			F0 = 0;
			if(j == 0)	F0 = 1;	//第一次按下
			else if(j == IO_KeyState)
			{
				if(++IO_KeyHoldCnt >= 20)	//1秒后重键
				{
					IO_KeyHoldCnt = 18;
					F0 = 1;
				}
			}
			if(F0)
			{
				j = T_KeyTable[IO_KeyState >> 4];
				if((j != 0) && (T_KeyTable[IO_KeyState& 0x0f] != 0)) 
				{
					KeyCode = (unsigned char)(((j - 1) * 4 )+ (T_KeyTable[IO_KeyState & 0x0f]) );	//计算键码,17~32  + 16
				}
					
			}
		}
		else	
			IO_KeyHoldCnt = 0;
	}
	P7 = 0xff;	
	if(KeyCode>0)
		{
			serial_one_send_number(KeyCode);
			serial_one_send_string("按键\r\n");
			switch(KeyCode)
			{
				case 1: 
				{
					serial_one_send_string("查询最近值\r\n");
					RememberMaxMin();
					
					break;
				}
				case 2: {CleanMaxMin();break;}
			}
			KeyCode=0;
		}	
}
void CleanMaxMin()
{
	LCD12864_WriteCmd(0x01);
	serial_one_send_string("清除最大最小值\r\n");	//串口输出测试
	max = 0;
	min = 2187;
	Delay_ms(100);
}

总结

在完成这个练习之后,第一个感想,我在学校里真的学了51单片机吗?
哦~原来是以前做的太简单了,全程只有一个功能,难怪那会儿不知天高地厚,因为自己掌握了单片机的精髓,结果就这么一个练习就让我费劲心思。
第二个感想,对于单片机的基础知识一定要扎实扎实再扎实,所有的高端操作就离不开最简单的“烹饪方式”,对于定时器、中断以及串口的认识只停留在知道且了解的境地是不够的,我们要做的是将单片机上的基础资源和功能可以了然于胸,这些虽不是什么高深的功法,但没有这些,后续任何高深的操作都会让你“走火入魔”。
第三个感想,精进编程水平的精髓在于多敲!多敲!多敲!以及提前构思好代码流程。由于一段时间没有触碰到C语言以及单片机,初上手一股陌生感油然而生,许多方法和语法都会和其他语言弄混(鄙人还在写Java),因此导致这第一个练习让我踌躇不前,一度怀疑自己是否适合做这个,但是在捋清楚思路和流程后,一步一步的实现和改正bug,最终让我实现了这个功能。
最后,各位,一定要好好学习!天天向上!俺们下一篇“水文”见!

  • 12
    点赞
  • 44
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
以下是51单片机超声波测距用LCD1602显示距离的程序: ``` #include <reg52.h> #include <intrins.h> #define LCD_Data_Port P0 // LCD数据端口 #define LCD_RS P2_0 // LCD RS引脚 #define LCD_RW P2_1 // LCD RW引脚 #define LCD_EN P2_2 // LCD EN引脚 sbit Trig = P1^0; // 超声波发射引脚 sbit Echo = P1^1; // 超声波接收引脚 void LcdWriteCmd(unsigned char cmd); void LcdWriteData(unsigned char dat); void LcdInit(); void DelayUs(unsigned int us); void DelayMs(unsigned int ms); void main() { unsigned int distance; char str[16]; LcdInit(); // 初始化LCD while(1) { // 发送超声波信号 Trig = 1; DelayUs(10); Trig = 0; // 等待接收超声波信号 while(!Echo); TR0 = 1; // 开始计时 // 等待超声波信号结束 while(Echo); TR0 = 0; // 停止计时 // 计算距离 distance = TH0 * 256 + TL0; distance = distance / 58; // 显示距离 sprintf(str, "Distance: %dcm", distance); LcdWriteCmd(0x80); // 设置光标位置为第一行第一列 for(int i=0; i<16; i++) { if(str[i] == '\0') break; LcdWriteData(str[i]); } DelayMs(200); // 等待200ms } } void LcdInit() { LcdWriteCmd(0x38); // 8位数据总线,2行显示,5x7点阵字符 LcdWriteCmd(0x0c); // 开显示,不显示光标,不闪烁 LcdWriteCmd(0x06); // 光标右移,字符不移动 LcdWriteCmd(0x01); // 清屏 } void LcdWriteCmd(unsigned char cmd) { LCD_RS = 0; LCD_RW = 0; LCD_Data_Port = cmd; LCD_EN = 1; _nop_(); _nop_(); LCD_EN = 0; } void LcdWriteData(unsigned char dat) { LCD_RS = 1; LCD_RW = 0; LCD_Data_Port = dat; LCD_EN = 1; _nop_(); _nop_(); LCD_EN = 0; } void DelayUs(unsigned int us) { unsigned int i, j; for(i=0; i<us; i++) { for(j=0; j<12; j++); } } void DelayMs(unsigned int ms) { unsigned int i, j; for(i=0; i<ms; i++) { for(j=0; j<12000; j++); } } ``` 注意,此程序需要使用定时器来计算超声波信号的回波时间。在程序中,使用了Timer0来计时。同时,需要注意LCD的引脚连接和初始化的顺序。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值