前言
想出一个关于Protues仿真的系列,回想起在大学时做课程设计,大多数是用这个来做,这个系列就围绕一些常用的外设或者说课程设计题目来进行对以前知识的回顾,温故而知新,同时也希望能对新手读者的学习有所帮助,我一般在文章最后都会给出所有的源代码,如果懒得复制的话,想要所有的工程文件,仿真文件,那就给我点赞赏吧
使用单片机来制作一个简易的计算器,实现两个整数的加减乘除等运算功能。首先,需要LCD1602来显示计算的公式,以及计算的结果。其次需要一个矩阵键盘来输入数据。
这个电路也不是很难画,我们先把仿真电路画好,尽量画漂亮一些:
下面来先把LCD1602给点亮。
1602是字符型的液晶屏,字符型LCD的引脚定义都是差不多的,在网上也能轻易找到,我在网上找了一个比较清晰的表格,1602的引脚定义如图:
然后我们就是要知道如何去控制这个LCD,对于LCD1602的驱动程序,最重要的就是两个函数了,一个是发数据函数,一个是发命令函数,然后再寻找相关手册,查看如何写命令,写数据。
查看LCD1602的手册可知:RS是命令/数据选择引脚,接单片机的一个IO扣,若RS为低电平,则为命令模式,反之则为数据模式;RW是读/写选择引脚,若RW为低电平时,则为写入,若RW为高电平时,则为读取;E脚为执行写入命令/数据的使能引脚,也就是说,每写入一次命令或数据,需要使能此引脚才会执行。
那么写入命令/数据的程序就很容易写出来了:
写命令:
//--------------------------------------------// 写命令函数//--------------------------------------------void write_com(char com)
{
RS=0; //命令 DATA_PORT=com;
delay_ms(5); //延时 EN=1;
delay_ms(5); //延时 EN=0;
}
写数据:
//--------------------------------------------
// 写数据函数
//--------------------------------------------
void write_data(char dat)
{
RS=1; //数据
DATA_PORT=dat; //把数据总到P口
delay_ms(5); //延时
EN=1;
delay_ms(5); //延时
EN=0; //关使能
}
然后就要了解显示字符的位置怎么判断,LCD1602第一行的首地址对应0c80,,第二行的首地址对应0xc0。
在LCD1602的初始化程序中需要配置LCD1602的一些配置指令,就是那些显示屏的配置指令,是否显示光标,地址是否自加等等配置,按照自己的需求进行配置即可,之后将光标移到最开始的位置,方便之后的操作。我的如下:
//--------------------------------------------
// 1602初始化
//--------------------------------------------
void lcd_init()
{
EN=0;
LCDRW=0;
RS=0;
write_com(0x36); //set text home address
write_com(0x38);
write_com(0x0c); //开显示,不显示光标 0x0f为开显示,开光标
write_com(0x06); //写一个字符后地址加一
write_com(0x01); //显示清零,数据指针清零
write_com(0x80); //第一行
}
这些写还是不太方便的,因为驱动1602我还是需要写很多,先要写入显示地址,再要一个字符一个字符显示,这样操作起来不是很方便,因此,我又编写了一个函数,也就是直接显示字符串,然后如果在第一行显示,就写入1,在第二行显示就写入2,程序如下:
//--------------------------------------------
// 打印函数函数
// 第一个参数为要打印的字符串 第二个参数为要显示的行
//--------------------------------------------
void print_string(char *str,uchar line){
int i;
/@@*如果line参数为1则光标移到第一行 如果为2则移到第二行*/
if(line == 1)
write_com(0x80); //第一行
else if(line == 2)
write_com(0xc0); //第二行
for(i=0;str[i]!=0;i++)
{
write_data(str[i]);
}
}
这样用这个函数操作起来,就简单多了,先声明一个数字,然后借助C语言的sprintf函数,将字符串与要显示的数字一起转化到数字里。具体sprintf的用法请自行百度,我这里举几个例子:
显示字符串:
void main()
{
char LCD_STR_LINE[10];
sprintf((char*)LCD_STR_LINE,"Hello");
lcd_init(); //lcd初始化
print_string(LCD_STR_LINE,1); //在第一行显示字符串
while(1)
{
}
}
效果如图:
显示一个变量:
void main()
{
char LCD_STR_LINE[10];
float a = 0;
a = 1.1 + 2.2 + 3.3;
sprintf((char*)LCD_STR_LINE,"a = %f",a);
lcd_init(); //lcd初始化
print_string(LCD_STR_LINE,1); //在第一行显示字符串
while(1)
{
}
}
注意我的写法:
效果如图:
不知道你们感觉如何,我是觉得这样的函数封装比较方便。
然后我们把LCD的接口进行封装,然后写成.c与.h文件的方式,这样避免在主文件那里程序繁多,而且可以进行模块化封装,方便以后直接移植驱动。写好之后将文件添加到工程即可。
在1602做好之后,便是编写矩阵键盘的驱动。其实矩阵键盘无非也就是循环扫描,然后记住按键消抖即可。这部分的资料很好找,又比较简单,我就不再赘述。
同样的,对于按键检测,也进行文件的封装,方便以后移植,循环使用:
这里我编写了一个按键扫描函数,函数的功能就是,矩阵键盘,左上角第一个按键被按下时,返回0,第一行第二个按键被按下时返回1,依次类推,没有按键按下时,返回16。
然后我们试一下把按键检测与LCD1602结合起来,按下哪个,在LCD上就显示它的编号,代码如下:
int key_flag; //按键标志
void main()
{
char LCD_STR_LINE[10];
lcd_init(); //lcd初始化
print_string(LCD_STR_LINE,1);
while(1)
{
key_flag = key_scan(); //按键检测 没有按键被按下时 返回值为16
if(key_flag!=16) //有按键被按下
{