【金善愚】 单片机应用实践——基础篇 笔记整理
课程视频 :https://space.bilibili.com/483942191/channel/collectiondetail?sid=144001
仿真软件:Proteus 8.13
安装链接:https://pan.baidu.com/s/1-1fscykdvulV60xA4Hygaw?pwd=xeob
代码软件:Keil μvision V5.14.2
安装链接:https://pan.baidu.com/s/1MRdG2EkxORr6M6XH-n5ang?pwd=l2rm
文章目录
一、点亮LED 灯
1.仿真原理图
电阻 RES
电容 CAP
晶振 crystal
按键 button
2.代码
//包含单片机的头文件,建立软件和硬件的联系
#include<reg51.h>
//sbit LED0 = P1^0; //位操作,取P1的0位命名为LED
//sbit LED1 = P1^1;
//sbit LED2 = P1^2;
//sbit LED3 = P1^3;
void main()
{
while(1) //保持循环执行
{
P1 = 0xF0; //P1.7 - P1.0 赋值 1111_0000
//LED0 = 0;
//LED1 = 0;
//LED2 = 0;
//LED3 = 0; //P1其他位默认为1
}
}
2.软硬件关联
(1)生成HEX文件
a.点击图示的快捷图标
b.生成
c.编译
d.文件位置
(2)仿真中关联代码
a.双击单片机,找到HEX文件位置,关联代码
(3)开始仿真
二、LED 闪烁
1.基本数据类型
2.代码
(1)用循环语句方式
#include<reg5l.h>
void main ( )
{
unsigned int i;//无符号整形0~65535
while (1)
{
i =20000;
P1 = 0xFO;//1111 0000
while ( i--)
{
}
i = 20000;
P1 = 0x0F;//0000 1111
while ( i--);
//for(i = 0 ; i<50000;i++) //用for语句
}
}
(2)用延时函数方式
#include<reg51.h>
void delay();//声明函数
void main()
{
while(1)
{
P1 = 0xf0;
delay();
P1 = 0x0f;
delay();
}
}
void delay()
{
unsigned int ms = 500000;
while(ms--)
{
}
}
(3)用带参数的延时函数
/****************************************
功 能:带有参数的延时函数实现小灯的闪烁
时 间:****/**/**
****************************************/
#include<reg51.h>
//声明引脚
sbit LED = P1^7;
//函数声明
void DelayXms(unsigned int xms);
/****************************************
函数名: main
功 能: 主函数
参 数: 无
返回值: 无
****************************************/
void main()
{
while(1)
{
LED = 0;
DelayXms(1000);//调用函数
LED = 1;
DelayXms(1000);
}
}
/****************************************
函数名: DelayXms
功 能: 延时函数
参 数: unsigned int
返回值: 无
****************************************/
void DelayXms(unsigned int xms)
{
unsigned int i,j; // 0 -65535 0000H - FFFFH
for(i = xms;i>0;i--)
{
for(j = 124;j>0;j--)
{
}
}
}
2.调试
(1)设置
晶振和仿真
(2)进入调试模式
(3)时间等状态
(4)分步调试
①运行到鼠标所在位置
②单步运行
③进入函数
④运行全部(直至断点)
⑤设置断点
二、LED 流水灯
1.用简单顺序语句的方法实现
#include<reg51.h>
void DelayXms(unsigned int xms); //函数声明,或者将延迟函数放在main()函数前面,否则会报错
void main()
{
while(1)
{
P1 = 0xfe; //1111 1110
DelayXms(1000); //调用函数
P1 = 0xfd; //1111 1101
DelayXms(1000);
P1 = 0xfb; //1111 1011
DelayXms(1000);
P1 = 0xf7; //1111 0111
DelayXms(1000);
P1 = 0xef; //1110 1111
DelayXms(1000);
P1 = 0xdf; //1101 1111
DelayXms(1000);
P1 = 0xbf; //1011 1111
DelayXms(1000);
P1 = 0x7f; //0111 1111
DelayXms(1000);
}
}
//延时函数
void DelayXms(unsigned int xms)
{
unsigned int i,j; // 0 -65535 0000H - FFFFH
for(i = xms;i>0;i--)
for(j = 124;j>0;j--);
}
2.用数组的方法实现(多文件)
1.创建工程目录下新建4个文件夹,并修改设置下的存放路径
Listings 放中间文件
Output 放输出HEX等文件
Project 放工程文件
Source 放 .c、 .h文件
2.创建模块的 .c文件并创建其对应的.h文件
- .c文件负责函数的定义及变量的定义
- .h文件负责函数的声明及变量的声明(不允许赋初值)以及常量和I/O口的宏定义
(1) delay.c
#include"delay.h" //""头文件会在当前目录下查找。< >头文件一般是系统头文件,会在安装目录下寻找
/*************************************
函数名:DelayXms
功 能:毫秒级延时函数
参 数: unsigned int(1 - 65535)
返回值:无
***********************************/
void DelayXms(unsigned int xms)
{
unsigned int i,j; // 0 -65535 0000H - FFFFH
for(i = xms;i>0;i--)
for(j = 124;j>0;j--);
}
(2) delay.h
#ifndef __DELAY_H__
#define __DELAY_H__
void DelayXms(unsigned int xms); //声明加分号
#endif
2.创建main函数的 .c文件,可直接引用模块头文件使用
main.c
#include<reg51.h>
#include"delay.h"
unsigned char code LEDBUF[] = {0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf,0x7f};
//创建数组,常量可存放在ROM中,添加关键词code。变量可存放在RAM中
void main()
{
unsigned char cnt;//unsigned char 类型范围是0 -255
while(1)
{
for (cnt = 0; cnt<8;cnt++)
{
P1 = LEDBUF[cnt];
DelayXms(1000);
}
}
}
3.用库函数的方法实现
1._crol_函数
_crol_函数功能:将 c 进行b位左位移,并将值以unsigned char类型返回
//文档定义
#include <intrins.h>
unsigned char _crol_ (
unsigned char c, /* character to rotate left */
unsigned char b); /* bit positions to rotate */
函数返回类型为unsigned char,函数的两个形参也是unsigned char类型。
c 为要被进行 位左移 的形式参数
b 为要进行的 位移数
注释:向左循环移位时,从左边出去会从右边重新补入
2._cror_函数
与_crol_函数类似,区别只是进行的是右位移
注释:向右循环移位时,从右边出去会从左边重新补入
3.main.c 代码
#include<reg51.h>
#include <intrins.h>
#include"delay.h"
void main()
{
unsigned char temp = 0xfe; //1111 1110
while(1)
{
P1 = temp;
temp = _crol_(temp,1);
DelayXms(1000);
}
}
4.用移位运算符的方法实现
1.位运算符 及 位表达式
⚠️注意:位表达式c<<1的值是0011 1100B,而c的值并未改变,仍是1001 1110B。移出补0。
2.代码
(1)main.c 用移位+补位方法
#include<reg51.h>
#include <intrins.h>
#include"delay.h"
void main()
{
unsigned char temp = 0xfe; //1111 1110
while(1)
{
P1 = temp; // 输出
DelayXms(1000); // 延时
if (temp & 0x80) // 判断temp最高位是否为1
{
temp = temp<<1; // 左移
temp = temp|1; // 如果左移之前temp最高位为1,则在temp的最低位补1
}
else
{
temp<<=1; // 左移
}
}
}
(2)main.c 用反向方法
#include<reg51.h>
#include <intrins.h>
#include"delay.h"
void main()
{
unsigned char temp = 0x1; //0000 0001
unsigned char i;
while(1)
{
P1 = ~(temp<<i++);
DelayXms(1000);
if (8 == i) i=0;
}
}
三、蜂鸣器
1.蜂鸣器工作电路
图中 D4为 续流二极管:
一种配合电感性负载使用的二极管,当电感性负载的电流有突然的变化或减少时,电感二端会产生突变电压,可能会破坏其他元件。配合续流二极管时,其电流可以较平缓地变化,避免突波电压的发生。
2.仿真原理图
3.代码
#include<reg51.h>
#include"delay.h"
sbit Sound = P3^7;
void main()
{
unsigned int i;
while(1)
{
for(i = 0;i<100;i++)
{
Sound = ~Sound;
DelayXms(1);
}
for(i = 0;i<100;i++)
{
Sound = ~Sound;
DelayXms(5);
}
}
}
四、数码管
1.静态显示
(1)仿真原理图
数码管seg
CC-共阴
CA-共阳
(2)代码
#include<reg51.h>
void main()
{
while(1)
{
P2 = 0xc0;
}
}
2.动态显示
(1)仿真原理图
(2)代码
#include<reg51.h>
#include"delay.h"
void DelayXms();
unsigned char code leddata[] = { //数码管的段码表
0x3F, //0
0x06, //1
0x5B, //2
0x4F, //3
0x66, //4
0x6D, //5
0x7D, //6
0x07, //7
0x7F, //8
0x6F //9
};
unsigned char LEDBuf[] = {0,3,0,5};//数据缓存区
void main()
{
while(1)
{
P2 = 0xfe;//1111 1110 第一个数码管的位选打开,其他的关掉,打开第一个
P0 = leddata[LEDBuf[0]];
DelayXms(10);
P2 = 0xfd;//1111 1101 第一个数码管的位选关掉,同时打开第二个数码管的位选
P0 = leddata[LEDBuf[1]];
DelayXms(10);
P2 = 0xfb;//1111 1011 第二个数码管的位选关掉,同时打开第三个数码管的位选
P0 = leddata[LEDBuf[2]];
DelayXms(10);
P2 = 0xf7;//1111 0111 第三个数码管的位选关掉,同时打开第四个数码管的位选
P0 = leddata[LEDBuf[3]];
DelayXms(10);
}
}
(3)模块化
display.h
#ifndef __DISPLAY_H__
#define __DISPLAY_H__
#include <reg51.h>
#include "delay.h"
//宏定义
#define GPIO_DIG P0 //段码IO
#define GPIO_PLACE P2 //位选IO
#define N 4 //数码管的个数
//变量声明
unsigned char code leddata[];
extern unsigned char LEDBuf[]; //定义成外部变量
//函数声明
void Display();
#endif
display.c (if语句实现)
#include "display.h"
#include "delay.h"
unsigned char LEDBuf[] = {0,6,2,3};//数据缓存区
unsigned char code PLACE_CODE[] = {0xfe,0xfd,0xfb,0xf7};//数码管位选信号
//数码管的段码表
unsigned char code leddata[] = {
0x3F, //0
0x06, //1
0x5B, //2
0x4F, //3
0x66, //4
0x6D, //5
0x7D, //6
0x07, //7
0x7F, //8
0x6F, //9
0x77, //A 10
0x7C, //B 11
0x39, //C 12
0x5E, //D 13
0x79, //E 14
0x71, //F 15
0x76, //H 16
0x38, //L 17
0x37, //N 18
0x3E, //U 19
0x73, //P 20
0x5C, //O 21
0x40, //- 22
0x00, //熄灭 23
};
void Display()
{
//static unsigned char i = 0; //静态变量,只在第一次初始化有效
unsigned char i;
//1.送位选
GPIO_PLACE = PLACE_CODE[i];
//2.送段码
GPIO_DIG = leddata[LEDBuf[i]];
//3.延时 1ms <10ms
DelayXms(10);
//4.消隐
GPIO_DIG = 0x00;
i++;
if (N == i)
i = 0;
}
display.c (switch case 语句实现)
void Display()
{
//static unsigned char i = 0; //静态变量,只在第一次初始化有效
unsigned char i;
//1.送位选
//2.送段码
//3.延时 1ms <10ms
//4.消隐
switch(i)
{
case 0:
GPIO_PLACE = leddata[LEDBuf[0]];
GPIO_PLACE = PLACE_CODE[0];
DelayXms(10);
GPIO_DIG = 0x00;
i++;
break;
case 1:
GPIO_PLACE = leddata[LEDBuf[1]];
GPIO_PLACE = PLACE_CODE[1];
DelayXms(10);
GPIO_DIG = 0x00;
i++;
break;
case 2:
GPIO_PLACE = leddata[LEDBuf[2]];
GPIO_PLACE = PLACE_CODE[2];
DelayXms(10);
GPIO_DIG = 0x00;
i++;
break;
case 3:
GPIO_PLACE = leddata[LEDBuf[3]];
GPIO_PLACE = PLACE_CODE[3];
DelayXms(10);
GPIO_DIG = 0x00;
i = 0;
break;
default:break;
}
}
main.c
#include <reg51.h>
#include "display.h"
#include "delay.h"
void main()
{
while(1)
{
Display();
}
}
3.应用层程序开发
项目1:数码管开机初始显示 - - - -,正常运行时显示4567
main.c
#include <reg51.h>
#include "display.h"
#include "delay.h"
void main()
{
unsigned int i;
for(i = 0;i<500;i++)
{
Display();
}
while(1)
{
LEDBuf[0] = 4; //调用前修改底层参数值
LEDBuf[1] = 5;
LEDBuf[2] = 6;
LEDBuf[3] = 7;
Display();
}
}
项目2:显示任意4位十进制数
main.c
#include <reg51.h>
#include "display.h"
#include "delay.h"
void main()
{
unsigned int i; //0 - 65535
unsigned int num = 1995; //十进制数
for(i = 0;i<500;i++)
{
Display();
}
while(1)
{
LEDBuf[0] = num/1000;
LEDBuf[1] = num/100%10;
LEDBuf[2] = num/10%10;
LEDBuf[3] = num%10;
Display();
}
}
项目3:逐渐加1
#include <reg51.h>
#include "display.h"
#include "delay.h"
void main()
{
unsigned int i; //0 - 65535
unsigned int num = 1995; //十进制数
for(i = 0;i<500;i++)
{
Display();
}
while(1)
{
LEDBuf[0] = num/1000;
LEDBuf[1] = num/100%10;
LEDBuf[2] = num/10%10;
LEDBuf[3] = num%10;
for(i = 0;i<50;i++) //延时
{
Display();
}
num++;
if (num >2000)
num = 1994;
}
}
项目4:
1、开机时显示闪烁的8888
2、正常工作时,需要分时显示2个参数,大概每隔2S切换显示一个参数
3、 参数1的范围是0-3
参数2的范围是0-99
中间用 - 隔开
main.c
#include <reg51.h>
#include "display.h"
#include "delay.h"
//定义变量
unsigned char wnd; //窗口变量
unsigned char parm1 = 23; //参数变量
unsigned char parm2 = 49;
unsigned char parm3 = 62;
unsigned char parm4 = 81;
void main()
{
//开机初始化 "- - - -"
unsigned int i;
for(i = 0;i<500;i++)
{
Display();
}
//正常显示变化
while(1)
{
switch(wnd)
{
case 0:
LEDBuf[0] = 0; // "0"
LEDBuf[1] = 22; // "-"
LEDBuf[2] = parm1/10; //parm1的十位
LEDBuf[3] = parm1%10; //parm1的个位
for(i = 0;i<500;i++) //延时显示
{
Display();
}
wnd++;
break;
case 1:
LEDBuf[0] = 1; // "1"
LEDBuf[1] = 22; // "-"
LEDBuf[2] = parm2/10; //parm2的十位
LEDBuf[3] = parm2%10; //parm2的个位
for(i = 0;i<500;i++) //延时显示
{
Display();
}
wnd++;
break;
case 2:
LEDBuf[0] = 2; // "2"
LEDBuf[1] = 22; // "-"
LEDBuf[2] = parm3/10; //parm3的十位
LEDBuf[3] = parm3%10; //parm3的个位
for(i = 0;i<500;i++) //延时显示
{
Display();
}
wnd++;
break;
case 3:
LEDBuf[0] = 3; // "3"
LEDBuf[1] = 22; // "-"
LEDBuf[2] = parm4/10; //parm4的十位
LEDBuf[3] = parm4%10; //parm4的个位
for(i = 0;i<500;i++) //延时显示
{
Display();
}
wnd = 0;
break;
default:break;
}
}
}
main.c(分结构)
#include <reg51.h>
#include "display.h"
#include "delay.h"
//定义变量
unsigned char wnd; //窗口变量
unsigned char parm1 = 23; //参数变量
unsigned char parm2 = 49;
unsigned char parm3 = 62;
unsigned char parm4 = 81;
void dis_Service();
void main()
{
//开机初始化 "- - - -"
unsigned int i;
for(i = 0;i<500;i++)
{
Display();
}
//正常显示变化
while(1)
{
dis_Service();
}
}
void dis_Service()
{
unsigned int i;
switch(wnd)
{
case 0:
LEDBuf[0] = 0; // "0"
LEDBuf[1] = 22; // "-"
LEDBuf[2] = parm1/10; //parm1的十位
LEDBuf[3] = parm1%10; //parm1的个位
for(i = 0;i<500;i++) //延时显示
{
Display();
}
wnd++;
break;
case 1:
LEDBuf[0] = 1; // "1"
LEDBuf[1] = 22; // "-"
LEDBuf[2] = parm2/10; //parm2的十位
LEDBuf[3] = parm2%10; //parm2的个位
for(i = 0;i<500;i++) //延时显示
{
Display();
}
wnd++;
break;
case 2:
LEDBuf[0] = 2; // "2"
LEDBuf[1] = 22; // "-"
LEDBuf[2] = parm3/10; //parm3的十位
LEDBuf[3] = parm3%10; //parm3的个位
for(i = 0;i<500;i++) //延时显示
{
Display();
}
wnd++;
break;
case 3:
LEDBuf[0] = 3; // "3"
LEDBuf[1] = 22; // "-"
LEDBuf[2] = parm4/10; //parm4的十位
LEDBuf[3] = parm4%10; //parm4的个位
for(i = 0;i<500;i++) //延时显示
{
Display();
}
wnd = 0;
break;
default:break;
}
}
五、独立按键
1.原理
1.扫描方式
(1)程序扫描
(2)定时扫描
(3)中断扫描
2.按键时序
2.仿真原理图
3.代码(实现按键 小灯亮灭交替)
key.h
#ifndef __KEY_H__
#define __KEY_H__
#include <reg51.h>
#include "key.h"
//按键定义
sbit KEY1 = P3^0;
sbit KEY2 = P3^1;
sbit KEY3 = P3^2;
sbit KEY4 = P3^3;
//函数定义
unsigned char Key_Scan();
#endif
key.c(while方法)
#include "key.h"
#include "delay.h"
unsigned char Key_Scan()
{
unsigned char KeyNum = 0;
if(!KEY1) //1.判断按键
{
DelayXms(10); //2.延时消抖 时间一般5-10ms
if(!KEY1) //3.再次判断
{
KeyNum = 1; //4.键值软件编码
}while(!KEY1); //5.松手等待
}
return KeyNum; //6.返回键值
}
key.c(不占用cpu方法)
#include "key.h"
#include "delay.h"
short flag = 0;
unsigned char Key_Scan()
{
unsigned char KeyNum = 0;
if (KEY1 != 0) // 按键没有按下的时候
{
flag = 0; // 标志位清零
}
if (KEY1 == 0) // 按键按下
{
DelayXms(10);
if (KEY1 == 0) // 消除抖动后再次判断按键是否按下
{
// 如果按键没有一直按着就执行 如果标志位为1说明还没有松手
if (flag == 0)
{
KeyNum = 1;
}
flag = 1; // 将标志位置1锁住 表示已经按下
}
}
return KeyNum;
}
main.c
#include <reg51.h>
#include "key.h"
#include "delay.h"
void main()
{
while(1)
{
if(Key_Scan() == 1)
{
P1 = ~P1;
DelayXms(4);
}
}
}
4.代码(实现数码管显示键值)
main.c
#include <reg51.h>
#include "key.h"
#include "delay.h"
#include "display.h"
void main()
{
LEDBuf[0] = 23;
LEDBuf[1] = 23;
LEDBuf[2] = 23;
LEDBuf[3] = 0;
while(1)
{
switch(Key_Scan())
{
case 0: break;
case 1: LEDBuf[3] = 1;break;
case 2: LEDBuf[3] = 2;break;
default:break;
}
Display();
}
}
5.代码(实现按键加减1)
key.h
#ifndef __KEY_H__
#define __KEY_H__
#include <reg51.h>
#include "key.h"
//按键定义
sbit KEY1 = P3^0;
sbit KEY2 = P3^1;
sbit KEY3 = P3^2;
sbit KEY4 = P3^3;
//函数定义
unsigned char Key_Scan();
#endif
key.c
#include "key.h"
#include "delay.h"
unsigned char Key_Scan()
{
unsigned char KeyNum = 0;
KEY1 = 1;
KEY2 = 1;
KEY3 = 1;
KEY4 = 1;
if(!KEY1||!KEY2||!KEY3||!KEY4)
{
DelayXms(10);
if(!KEY1||!KEY2||!KEY3||!KEY4)
{
if(!KEY1)
KeyNum = 1;
if(!KEY2)
KeyNum = 2;
if(!KEY3)
KeyNum = 1;
if(!KEY4)
KeyNum = 4;
}while(!KEY1||!KEY2||!KEY3||!KEY4);
}
return KeyNum;
}
main.c
#include <reg51.h>
#include "key.h"
#include "delay.h"
#include "display.h"
void Key_Service();
unsigned int NumCnt;
void main()
{
while(1)
{
LEDBuf[0] = NumCnt/1000;
LEDBuf[1] = NumCnt/100%10;
LEDBuf[2] = NumCnt/10%10;
LEDBuf[3] = NumCnt%10;
Key_Service();
Display();
}
}
void Key_Service()
{
switch(Key_Scan())
{
case 0: break;
case 1: NumCnt++;if(NumCnt>9999) NumCnt=0; break;
case 2: NumCnt--;if(NumCnt>9999) NumCnt=9999;break;
case 3: NumCnt = 0;break;
case 4: NumCnt = 623;break;
default:break;
}
}
6.代码(实现按键流水灯)
功能:按键控制流水灯
P3.0~P3.3接四个独立按键分别对应Start、Stop、Up、Down;
P1口接八个发光二极管
Start“启动键”,首次按压Start可产生“自上向下”的流水灯运动;
Stop“停止键”,按压Stop可终止流水灯的运动;
Up、Down“方向键”,分别产生“自上向下”和“自下向上”运动。
#include <reg51.h>
#include <intrins.h>
#include "key.h"
#include "delay.h"
bit Startflag = 0;
bit UpDownflag = 1;
unsigned char temp = 0xfe;
void Key_Service();
void main()
{
while(1)
{
Key_Service();
}
}
void Key_Service()
{
switch(Key_Scan())
{
case 1: Startflag = 1;break; //启动键
case 2: Startflag = 0;break; //停止键
case 3: UpDownflag = 1;break; //Down
case 4: UpDownflag = 0;break; //Up
default:break;
}
if(Startflag)
{
P1 = temp;
if(UpDownflag)
{
temp = _crol_(temp,1);
}
else
{
temp = _cror_(temp,1);
}
DelayXms(1000);
}
}
7.代码(实现多窗口参数调整)
显示功能:
窗口1P1 XX参数1)
窗口2P2 XX(参数2)
窗口3P3 XX(参数3)
窗口4 P4 XX(参数4
按键功能:
KEY1 窗口切换
KEY2 数据加一
KEY3 数据减一
KEY4 数据清零
main.c
#include <reg51.h>
#include <intrins.h>
#include "key.h"
#include "delay.h"
#include "display.h"
//定义变量
unsigned char wnd = 0; //窗口变量
unsigned char parm1 = 23; //参数变量
unsigned char parm2 = 49;
unsigned char parm3 = 62;
unsigned char parm4 = 81;
void dis_Service();
void key_Service();
void main()
{
//开机初始化 "- - - -"
unsigned int i;
for(i = 0;i<500;i++)
{
Display();
}
//正常显示变化
while(1)
{
dis_Service();
key_Service();
Display();
}
}
void dis_Service()
{
switch(wnd)
{
case 0:
LEDBuf[0] = 0; // "0"
LEDBuf[1] = 22; // "-"
LEDBuf[2] = parm1/10; //parm1的十位
LEDBuf[3] = parm1%10; //parm1的个位
break;
case 1:
LEDBuf[0] = 1; // "1"
LEDBuf[1] = 22; // "-"
LEDBuf[2] = parm2/10; //parm2的十位
LEDBuf[3] = parm2%10; //parm2的个位
break;
case 2:
LEDBuf[0] = 2; // "2"
LEDBuf[1] = 22; // "-"
LEDBuf[2] = parm3/10; //parm3的十位
LEDBuf[3] = parm3%10; //parm3的个位
break;
case 3:
LEDBuf[0] = 3; // "3"
LEDBuf[1] = 22; // "-"
LEDBuf[2] = parm4/10; //parm4的十位
LEDBuf[3] = parm4%10; //parm4的个位
break;
default:break;
}
}
void key_Service()
{
switch(Key_Scan())
{
case 1: wnd++;if(wnd > 3) wnd = 0;break;
case 2:
switch(wnd)
{
case 0: parm1++;if(parm1>99) parm1=0;break;
case 1: parm2++;if(parm2>99) parm2=0;break;
case 2: parm3++;if(parm3>99) parm3=0;break;
case 3: parm4++;if(parm4>99) parm4=0;break;
default:break;
}
break;
case 3:
switch(wnd)
{
case 0: parm1--;if(parm1>99) parm1=99;break;
case 1: parm2--;if(parm2>99) parm2=99;break;
case 2: parm3--;if(parm3>99) parm3=99;break;
case 3: parm4--;if(parm4>99) parm4=99;break;
default:break;
}
break;
case 4:
switch(wnd)
{
case 0: parm1=0;break;
case 1: parm2=0;break;
case 2: parm3=0;break;
case 3: parm4=0;break;
default:break;
}
break;
default:break;
}
}
六、矩阵按键
1.行列扫描方式
实现按键时,窗口显示按键对应的编号
1.原理图
2.代码
delay.h
#ifndef __DELAY_H__
#define __DELAY_H__
void DelayXms(unsigned int xms); //声明
#endif
delay.c
#include "delay.h" //""头文件会在当前目录下查找
/*************
函数名:DelayXms
功 能:毫秒级延时函数
参 数:unsigned int (1 - 65535)
返回值:无
***************/
void DelayXms(unsigned int xms)
{
unsigned int i,j; // 0 -65535 0000H - FFFFH
for(i = xms;i>0;i--)
for(j = 124;j>0;j--);
}
key.h
#ifndef __KEY_H__
#define __KEY_H__
#include <reg51.h>
#include "key.h"
//按键定义
sbit KEYIN1 = P2^0;
sbit KEYIN2 = P2^1;
sbit KEYIN3 = P2^2;
sbit KEYIN4 = P2^3;
sbit KEYOUT1 = P2^4;
sbit KEYOUT2 = P2^5;
sbit KEYOUT3 = P2^6;
sbit KEYOUT4 = P2^7;
//函数定义
unsigned char Key_Scan();
#endif
key.c
#include "key.h"
#include "delay.h"
unsigned char Key_Scan()
{
unsigned char KeyNum = 22;
//检测第一列
KEYIN1 = 1; KEYIN2 = 1; KEYIN3 = 1; KEYIN4 = 1;
KEYOUT1 = 0;KEYOUT2 = 1;KEYOUT3 = 1;KEYOUT4 = 1;
if(!KEYIN1||!KEYIN2||!KEYIN3||!KEYIN4)
{
DelayXms(10);
if(!KEYIN1||!KEYIN2||!KEYIN3||!KEYIN4)
{
if(!KEYIN1)
KeyNum = 1;
if(!KEYIN2)
KeyNum = 2;
if(!KEYIN3)
KeyNum = 3;
if(!KEYIN4)
KeyNum = 4;
}while(!KEYIN1||!KEYIN2||!KEYIN3||!KEYIN4);
}
//检测第二列
KEYIN1 = 1; KEYIN2 = 1; KEYIN3 = 1; KEYIN4 = 1;
KEYOUT1 = 1;KEYOUT2 = 0;KEYOUT3 = 1;KEYOUT4 = 1;
if(!KEYIN1||!KEYIN2||!KEYIN3||!KEYIN4)
{
DelayXms(10);
if(!KEYIN1||!KEYIN2||!KEYIN3||!KEYIN4)
{
if(!KEYIN1)
KeyNum = 5;
if(!KEYIN2)
KeyNum = 6;
if(!KEYIN3)
KeyNum = 7;
if(!KEYIN4)
KeyNum = 8;
}while(!KEYIN1||!KEYIN2||!KEYIN3||!KEYIN4);
}
//检测第三列
KEYIN1 = 1; KEYIN2 = 1; KEYIN3 = 1; KEYIN4 = 1;
KEYOUT1 = 1;KEYOUT2 = 1;KEYOUT3 = 0;KEYOUT4 = 1;
if(!KEYIN1||!KEYIN2||!KEYIN3||!KEYIN4)
{
DelayXms(10);
if(!KEYIN1||!KEYIN2||!KEYIN3||!KEYIN4)
{
if(!KEYIN1)
KeyNum = 9;
if(!KEYIN2)
KeyNum = 10;
if(!KEYIN3)
KeyNum = 11;
if(!KEYIN4)
KeyNum = 12;
}while(!KEYIN1||!KEYIN2||!KEYIN3||!KEYIN4);
}
//检测第四列
KEYIN1 = 1; KEYIN2 = 1; KEYIN3 = 1; KEYIN4 = 1;
KEYOUT1 = 1;KEYOUT2 = 1;KEYOUT3 = 1;KEYOUT4 = 0;
if(!KEYIN1||!KEYIN2||!KEYIN3||!KEYIN4)
{
DelayXms(10);
if(!KEYIN1||!KEYIN2||!KEYIN3||!KEYIN4)
{
if(!KEYIN1)
KeyNum = 13;
if(!KEYIN2)
KeyNum = 14;
if(!KEYIN3)
KeyNum = 15;
if(!KEYIN4)
KeyNum = 16;
}while(!KEYIN1||!KEYIN2||!KEYIN3||!KEYIN4);
}
return KeyNum;
}
display.h
#ifndef __DISPLAY_H__
#define __DISPLAY_H__
#include <reg51.h> //定义P0、P2口
#include "delay.h"
//宏定义
#define GPIO_DIG P0 //段码IO 【P0】,按需更改
#define GPIO_PLACE P3 //位选IO 【P3】,按需更改
#define N 4 //数码管的个数
//变量声明
unsigned char code leddata[];
extern unsigned char LEDBuf[]; //定义成外部变量
//函数声明
void Display();
#endif
display.c
#include "display.h"
#include "delay.h"
/*******************************
函数名:Display
功 能:数码管段选
参 数:无【变量定义中修改】
返回值:无
*********************************/
//要显示的数 以及 位选【按需修改】
unsigned char LEDBuf[] = {22,22,22,22};//数据缓存区【要显示的数字,在leddata[]中的序号】
unsigned char code PLACE_CODE[] = {0xfe,0xfd,0xfb,0xf7};//数码管位选信号
//数码管的段码表
unsigned char code leddata[] = {
0x3F, //0
0x06, //1
0x5B, //2
0x4F, //3
0x66, //4
0x6D, //5
0x7D, //6
0x07, //7
0x7F, //8
0x6F, //9
0x77, //A 10
0x7C, //B 11
0x39, //C 12
0x5E, //D 13
0x79, //E 14
0x71, //F 15
0x76, //H 16
0x38, //L 17
0x37, //N 18
0x3E, //U 19
0x73, //P 20
0x5C, //O 21
0x40, //- 22
0x00, //熄灭 23
};
//函数【不动】
void Display()
{
static unsigned char i; //不给初始值,否则会错
//1.送位选
GPIO_PLACE = PLACE_CODE[i];
//2.送段码
GPIO_DIG = leddata[LEDBuf[i]];
//3.延时 1ms <10ms
DelayXms(10);
//4.消隐
GPIO_DIG = 0x00;
//下一个数码管
i++;
if (N == i)
i = 0;
}
main.c
#include<reg51.h>
#include "key.h"
#include "delay.h"
#include "display.h"
void main()
{
while(1) //保持循环执行
{
LEDBuf[0] = 23;
LEDBuf[1] = 23;
LEDBuf[2] = 23;
LEDBuf[3] = Key_Scan();
// switch(Key_Scan())
// {
// case 1: LEDBuf[3] = 1;break;
// case 2: LEDBuf[3] = 2;break;
// case 3: LEDBuf[3] = 3;break;
// case 4: LEDBuf[3] = 4;break;
// case 5: LEDBuf[3] = 5;break;
// case 6: LEDBuf[3] = 6;break;
// case 7: LEDBuf[3] = 7;break;
// case 8: LEDBuf[3] = 8;break;
// case 9: LEDBuf[3] = 9;break;
// case 10: LEDBuf[3] = 0;break;
// case 11: LEDBuf[3] = 10;break;
// case 12: LEDBuf[3] = 11;break;
// case 13: LEDBuf[3] = 12;break;
// case 14: LEDBuf[3] = 13;break;
// case 15: LEDBuf[3] = 14;break;
// case 16: LEDBuf[3] = 15;break;
// default:break;
// }
Display();
}
}
2.线反转法
只更改上述的 key.c 和 key.h
key.h
#ifndef __KEY_H__
#define __KEY_H__
#include <reg51.h>
#include "key.h"
//宏定义 keyIO口
#define KEYPORT P2
//函数定义
unsigned char Key_Scan();
#endif
key.c
#include "key.h"
#include "delay.h"
unsigned char Key_Scan()
{
unsigned char temp,temp2;
static unsigned char KeyNum = 22;
KEYPORT = 0xf0; //4行做输出,4列做输入,输出“0”,输入写“1”
temp = KEYPORT; //读P2 IO口
if(temp != 0xf0) //判断是否有键按下
{
DelayXms(10);
if(temp != 0xf0) //再次判断是否有键按下(消抖)
{
temp = KEYPORT & 0xf0; //取高四位确定列号
KEYPORT = 0x0f; //4列做输出,4行做输入,输出“0”,输入写“1”
temp2 = KEYPORT & 0x0f; //读入并取低四位确定行号
}
}
switch(temp + temp2)//或运算
{
case 0xee : KeyNum = 1;break;
case 0xed : KeyNum = 2;break;
case 0xeb : KeyNum = 3;break;
case 0xe7 : KeyNum = 4;break;
case 0xde : KeyNum = 5;break;
case 0xdd : KeyNum = 6;break;
case 0xdb : KeyNum = 7;break;
case 0xd7 : KeyNum = 8;break;
case 0xbe : KeyNum = 9;break;
case 0xbd : KeyNum = 0;break;
case 0xbb : KeyNum = 10;break;
case 0xb7 : KeyNum = 11;break;
case 0x7e : KeyNum = 12;break;
case 0x7d : KeyNum = 13;break;
case 0x7b : KeyNum = 14;break;
case 0x77 : KeyNum = 15;break;
}
return KeyNum;
}
七、独立按键中断
1.实现LED亮灭
1.原理图
2.代码
main.c
#include<reg51.h>
sbit LED = P1^0;
void EX0_Init(void);
void main()
{
EX0_Init();
while(1)
{
}
}
//外部中断0 初始化
void EX0_Init(void)
{
//1.设置触发方式 外部中断0
IT0 = 1;//下降沿触发 (TCON)
EX0 = 1;//允许中断0(IE)
EA = 1; //中断总开关
}
/**********************************************************************
1.中断服务函数 一定是一个没有返回值的函数
2.中断服务函数 一定是没有参数的函数
3.中断服务函数 函数名的后面要跟关键字interrupt
4.interrupt n,n的取值是0 - 4,代表中断向量入口地址,8*n + 0003H ,5个中断源
(0003H - INT0[中断0]
000BH - T0[定时器0]
0013H - INT1[中断1]
001BH - T1[定时器1]
0023H - ES[串行口])
5.中断服务函数 是不能被主程序或其他程序所调用的
6.n 的后面跟 using m ,m的取值是 0 - 3,代表工作寄存器组
***********************************************************************/
void EX0_ISR(void) interrupt 0
{
LED = ~LED;
}
2.实现流水灯
main.c
#include <reg51.h>
#include <intrins.h>
#include "delay.h"
unsigned char run = 0;//默认停止流水灯
unsigned char dir = 1;//默认正向流水灯
void EX0_Init(void);
void main()
{
unsigned char temp = 0xfe;
EX0_Init();
while(1)
{
if(run)
{
P1 = temp;
if(dir)
{
temp = _crol_(temp,1);
}
else
{
temp = _cror_(temp,1);
}
DelayXms(100);
}
}
}
//外部中断0 初始化
void EX0_Init(void)
{
IT0 = 1;
EX0 = 1;
EA = 1;
}
//外部中断函数
void EX0_ISR(void) interrupt 0
{
switch(P2 & 0X0F) //P2低四位
{
case 0x0e: run = 1;break;
case 0x0d: run = 0;break;
case 0x0b: dir = 1;break;
case 0x07: dir = 0;break;
default:break;
}
}
3.实现数码管变数
#include <reg51.h>
#include "delay.h"
#include "display.h"
//函数声明
void EX0_Init(void);
void EX1_Init(void);
Dis_Service();
//全局变量
unsigned int Num = 0;
void main()
{
unsigned int i;
EX0_Init();
EX1_Init();
EA = 1;//中断总开关
for(i = 0;i<500;i++)
{
Display();
}
while(1)
{
Dis_Service();
Display();
}
}
Dis_Service()
{
LEDBuf[0] = Num/1000;
LEDBuf[1] = Num/100%10;
LEDBuf[2] = Num/10%10;
LEDBuf[3] = Num%10;
}
//外部中断0 初始化
void EX0_Init(void)
{
IT0 = 1; //下降沿触发
EX0 = 1;
}
//外部中断1 初始化
void EX1_Init(void)
{
IT1 = 1; //下降沿触发
EX1 = 1;
}
//外部中断0函数
void EX0_ISR(void) interrupt 0
{
DelayXms(10);
if(INT0 == 0)
{
Num ++;
if(Num > 9999)
Num = 0;
}
}
//外部中断1函数
void EX1_ISR(void) interrupt 2
{
DelayXms(10);
if(INT1 == 0)
{
Num --;
if(Num > 9999)
Num = 9999;
}
}
4.中断优先级
main.c
#include <reg51.h>
#include "INT.h"
void main()
{
PX1 = 1; //中断1设置为高优先级
EX0_Init();
EX1_Init();
EA = 1;
while(1)
{
}
}
INT.h
#ifndef __INT_H__
#define __INT_H__
#include <reg51.h>
sbit LED0 = P1^0;
sbit LED1 = P1^1;
void EX0_Init(void);
void EX1_Init(void);
#endif
INT.c
#include "INT.h"
#include "delay.h"
//外部中断0 初始化
void EX0_Init(void)
{
//1.设置触发方式 外部中断0
IT0 = 1;//下降沿触发 (TCON)
EX0 = 1;//允许中断(IE)
}
//外部中断1 初始化
void EX1_Init(void)
{
//1.设置触发方式 外部中断1
IT1 = 1;//下降沿触发 (TCON)
EX1 = 1;//允许中断(IE)
}
//外部中断0的函数
void EX0_ISR(void) interrupt 0
{
LED0 = ~LED0;
DelayXms(1000);
}
//外部中断1的函数
void EX1_ISR(void) interrupt 2
{
LED1 = ~LED1;
DelayXms(1000);
}
八、定时/计数器
1.实现LED闪烁(查询方式、短定时)
main.c
#include <reg51.h>
sbit LED = P1^0;
void timer0_Iint();
void main()
{
timer0_Iint();
while(1)
{
if(TF0 ==1) //查询溢出位
{
TF0 = 0;//查询不会自动清零
//重新赋初值
TH0 = (65536 - 50000)/256; //对十进制取高8位
TL0 = (65536 - 50000)%256; //对十进制取低8位
LED = ~LED;
}
}
}
void timer0_Iint()
{
//1、设置TMOD —— 确定工作方式:定时or计数; 哪一种工作方式:工作方式1; GATE = 0;
TMOD = 0x01; //GATE C/T M1 M0 GATE C/T M1 M0 xxxx 0001
//2、初值的计算(50ms 12M)
TH0 = (65536 - 50000) / 256; //对十进制取高8位
TL0 = (65536 - 50000) % 256; //对十进制取低8位
//3、打开定时/计数器(TCON的TR0位)
TR0 = 1;//启动定时计数器
}
2.实现LED流水灯(查询方式、长定时)
#include <reg51.h>
#include <intrins.h>
void timer0_Iint();
unsigned char Cnt;
unsigned char temp = 0xfe;
void main()
{
timer0_Iint();
while(1)
{
if(TF0 ==1)
{
TF0 = 0;//查询不会自动清零
//重新赋初值
TH0 = (65536 - 50000)/256; //对十进制取高8位
TL0 = (65536 - 50000)%256; //对十进制取低8位
//1s = 1000ms/20ms = 50次
Cnt ++;
if(Cnt >= 50)
{
Cnt = 0;
P1 = temp;
temp = _crol_(temp,1);
}
}
}
}
void timer0_Iint()
{
//1、设置TMOD——确定工作方式:定时or计数;哪一种工作方式:工作方式1;GATE = 0;
TMOD = 0x01;//GATE C/T M1 M0 GATE C/T M1 M0 xxxx 0001
//2、初值的计算(50ms 12M)
TH0 = (65536 - 50000)/256; //对十进制取高8位
TL0 = (65536 - 50000)%256; //对十进制取低8位
//3、打开定时/计数器(TCON的TR0位)
TR0 = 1;//启动定时计数器
}
3. 实现LED闪烁(中断方式 - 短定时)
#include <reg51.h>
void Timer0Init(void);
sbit LED = P1^0;
void main()
{
Timer0Init();
while(1)
{
}
}
void Timer0Init(void) //50毫秒 @12.0O0MHz
{
TMOD &= 0xF0; //设置定时器模式 GATE C/T M1 MO(保留高四位,低四位清零)
TMOD |= 0x01; //设置定时器模式(设置定时器0)
TL0 = 0xB0; //设置定时初值低四位(65536 - 50000)%256
TH0 = 0x3C; //设置定时初值高四位(65536 - 50000)/256
TF0 = 0; //清除TFO标志
ET0 = 1; //打开中断
EA = 1;
TR0 = 1; //定时器0开始计时
}
//定时器0中断函数
void timer0_ISR(void) interrupt 1
{
TL0 = 0xB0; //设置定时初值低四位(65536 - 50000)%256
TH0 = 0x3C; //设置定时初值高四位(65536 - 50000)/256
LED =~LED;
}
4. 实现LED闪烁(中断方式 - 长定时)
#include <reg51.h>
void Timer0Init(void);
sbit LED = P1^0;
unsigned char Cnt;
void main()
{
Timer0Init();
while(1)
{
}
}
void Timer0Init(void) //50毫秒@12.0O0MHz
{
TMOD &= 0xF0; //设置定时器模式 GATE C/T M1 MO (保留高四位,低四位清零)
TMOD |= 0x01; //设置定时器模式(设置定时器0)
TL0 = 0xB0; //设置定时初值低四位(65536 - 50000)%256
TH0 = 0x3C; //设置定时初值高四位(65536 - 50000)/256
TF0 = 0; //清除TFO标志
ET0 = 1; //打开中断
EA = 1;
TR0 = 1; //定时器0开始计时
}
//定时器0中断函数
void timer0_ISR(void) interrupt 1
{
TR0 = 0;
TL0 = 0xB0; //设置定时初值低四位(65536 - 50000)%256
TH0 = 0x3C; //设置定时初值高四位(65536 - 50000)/256
Cnt ++;
if(Cnt >= 10)
{
LED =~LED;
Cnt = 0;
}
TR0 = 1;
}
5. 实现数码管刷新(中断方式)
main.c
#include <reg51.h>
#include "display.h"
void Timer0Init(void);
void main()
{
Timer0Init();
while(1)
{
}
}
void Timer0Init(void) //1毫秒 @12.0O0MHz
{
TMOD &= 0xF0; //设置定时器模式 GATE C/T M1 MO (保留高四位,低四位清零)
TMOD |= 0x01; //设置定时器模式(设置定时器0)
TL0 = 0x18; //设置定时初值低四位
TH0 = 0xFC; //设置定时初值高四位
TF0 = 0; //清除TFO标志
ET0 = 1; //打开中断
EA = 1;
TR0 = 1; //定时器0开始计时
}
//定时器0中断函数
void timer0_ISR(void) interrupt 1
{
TR0 = 0;
Display();
TL0 = 0x18; //设置定时初值低四位
TH0 = 0xFC; //设置定时初值高四位
TR0 = 1;
}
display.h
#ifndef __DISPLAY_H__
#define __DISPLAY_H__
#include <reg51.h>
//宏定义
#define GPIO_DIG P0 //段码IO
#define GPIO_PLACE P2 //位选IO
#define N 4 //数码管的个数
//变量声明
unsigned char code leddata[];
extern unsigned char LEDBuf[]; //定义成外部变量
//函数声明
void Display();
#endif
display.c
#include "display.h"
unsigned char LEDBuf[] = {0,6,2,3};//数据缓存区
unsigned char code PLACE_CODE[] = {0xfe,0xfd,0xfb,0xf7};//数码管位选信号
//数码管的段码表
unsigned char code leddata[] = {
0x3F, //0
0x06, //1
0x5B, //2
0x4F, //3
0x66, //4
0x6D, //5
0x7D, //6
0x07, //7
0x7F, //8
0x6F, //9
0x77, //A 10
0x7C, //B 11
0x39, //C 12
0x5E, //D 13
0x79, //E 14
0x71, //F 15
0x76, //H 16
0x38, //L 17
0x37, //N 18
0x3E, //U 19
0x73, //P 20
0x5C, //O 21
0x40, //- 22
0x00, //熄灭 23
};
void Display()
{
static unsigned char i = 0; //静态变量,只在第一次初始化有效
//1.送位选
//2.送段码
//3.延时 1ms <10ms
//4.消隐
switch (i)
{
case 0:
GPIO_DIG = 0x00;
GPIO_PLACE = leddata[LEDBuf[0]];
GPIO_PLACE = PLACE_CODE[0];
i++;
break;
case 1:
GPIO_DIG = 0x00;
GPIO_PLACE = leddata[LEDBuf[1]];
GPIO_PLACE = PLACE_CODE[1];
i++;
break;
case 2:
GPIO_DIG = 0x00;
GPIO_PLACE = leddata[LEDBuf[2]];
GPIO_PLACE = PLACE_CODE[2];
i++;
break;
case 3:
GPIO_DIG = 0x00;
GPIO_PLACE = leddata[LEDBuf[3]];
GPIO_PLACE = PLACE_CODE[3];
i = 0;
break;
default:break;
}
}
九、串口通信
1.发送一帧数据(查询方式)
#include<reg51.h>
#include "delay.h"
void UartInit(void);
void main()
{
UartInit();
while(1)
{
SBUF = 0x88; //10位异步串口通信 0 88H 1
//SBUF = 'a';
//回车换行
SBUF = '\r';
SBUF = '\n';
while(!TI);
TI = 0;
DelayXms(1000);
}
}
void UartInit(void) //4800bps@11.0592MHz
{
SCON = 0x50; //8位数据,可变波特率 SM0 SM1 SM2 REN TB8 RB8 TI RI
TMOD &= 0x0F; //设置定时器模式 0 1 0 1 0 0 0 0
TMOD |= 0x20; //设置定时器模式
TL1 = 0xFA; //设置定时初始值
TH1 = 0xFA; //设置定时重载值
ET1 = 0; //禁止定时器中断
TR1 = 1; //定时器1开始计时
}
2.发送一串数据(查询方式)
uart.h
#ifndef __UART_H__
#define __UART_H__
#include <reg51.h>
void UartInit(void);
void sendByte(unsigned char dat);
void sendString(unsigned char *dat) ;
#endif
uart.c
#include "uart.h"
//串口初始化
void UartInit(void) //9600bps@11.0592MHz
{
PCON &= 0x7F; //波特率不倍速
SCON = 0x50; //8位数据,可变波特率
TMOD &= 0x0F; //设置定时器模式
TMOD |= 0x20; //设置定时器模式
TL1 = 0xFD; //设置定时初始值
TH1 = 0xFD; //设置定时重载值
ET1 = 0; //禁止定时器中断
TR1 = 1; //定时器1开始计时
}
//循环中的串口函数
void sendByte(unsigned char dat)
{
SBUF = dat;
while(!TI);
TI = 0;
}
void sendString(unsigned char *dat)
{
while(*dat != '\0')
{
sendByte(*dat++);
}
}
main.c
#include<reg51.h>
#include "delay.h"
#include "uart.h"
void main()
{
UartInit();
while(1)
{
//sendByte(0x37);
//回车换行
//sendByte('\r');
//sendByte('\n');
sendString("Hello World! \r\n");
DelayXms(2000);
}
}
3.接收一帧数据(查询方式)
1.方法1
#include <reg51.h>
//初始化串口 (设置串口,开启串口中断)
void init_uart(void)
{
SCON = 0x50; // SCON: 方式 1, 8-bit, 允许接收数据
TMOD |= 0x20; // TMOD: 设置定时器1工作在方式2, 8-bit 自动重装
TH1 = 0xFD; // TH1: 初始值为0xFD 波特率:9600 晶振频率:11.0592MHz
TL1 = 0x0;
ET1 = 0; //禁止定时器中断
TR1 = 1; // TR1: 开启定时器1
EA = 1; //打开总中断
ES = 1; //打开串口中断
}
// 发送一个字节数据
void uart_send_byte(unsigned char dat)
{
SBUF = dat; // 将数据送到发送缓冲寄存器SBUF,一位一位的发送
while(!TI); // 等待发送完毕 (发送完毕TI硬件置1)
TI = 0;// 将TI清零,表示可以发送下一字节数据。
}
// 发送字符串
void uart_send_str(unsigned char *s)
{
while(*s != '\0')// '\0':字符串结束标志
{
uart_send_byte(*s);// 发送1个字节数据,1个字符占8位,1字节
s++;// 指向下一个字符
}
}
// 串口中断处理函数 (串口接收到数据,发送数据完毕都可以引起串口中断)
void uart_interrupt(void) interrupt 4 //也叫串行中断服务程序
{
unsigned char recv_data;// 用来存放接收到的数据
if(RI) //接收数据(1字节)完毕,RI会被硬件置1
{
RI = 0; // 将 接收中断标志位 清零(让串口可以继续接收数据)
recv_data = SBUF; //读取接收到的数据,并存放到data
SBUF = recv_data;
}
if(TI)// 发送数据(1字节)完毕
{
TI = 0;// 将 发送中断标志位 清零(让串口可以继续发送数据)
}
}
void main(void)
{
init_uart();// 初始化串口
while(1)// 主循环不做任何动作。(死循环)
{}
}
2.方法2
uart.h
#ifndef __UART_H__
#define __UART_H__
extern unsigned char recv_data;
extern unsigned char recv_flag;
void uart_init(void);
void uart_send_byte(unsigned char dat);
void uart_send_str(unsigned char *s);
#endif
uart.c
#include <reg51.h>
#include "uart.h"
unsigned char recv_data;// 用来存放接收到的数据
unsigned char recv_flag = 0;
//初始化串口 (9600 11.0592MHz)
void uart_init(void)
{
SCON = 0x50;
TMOD |= 0x20;
TH1 = 0xFD;
TL1 = 0x0;
ET1 = 0;
TR1 = 1;
EA = 1;
ES = 1;
}
// 发送一个字节数据
void uart_send_byte(unsigned char dat)
{
SBUF = dat;
while(!TI);
TI = 0;
}
// 发送字符串
void uart_send_str(unsigned char *s)
{
while(*s != '\0')
{
uart_send_byte(*s++);
}
}
void uart_interrupt(void) interrupt 4
{
if(RI)
{
RI = 0;
recv_data = SBUF;
recv_flag = 1;
}
if(TI)
{
TI = 0;
}
}
main.c
#include <reg51.h>
#include "uart.h"
void main(void)
{
uart_init();// 初始化串口
while(1)
{
if(recv_flag == 1)
{
recv_flag = 0;
SBUF = recv_data +1;
}
}
}
十、LCD1602液晶显示
1.分析时序
(1)读操作时序
(2)写操作时序
(3)基本操作时序
操作 | 输入 | 输出 |
---|---|---|
读状态 | RS=L,RW=H,E=H | DO~D7 = 状态字 |
写指令 | RS=L,RW=L,DO~D7=指令码,E=高脉冲 | 无 |
读数据 | RS=H,RW=H,E=H | DO~D7 = 数据 |
写数据 | RS=H,RW=L,DO~D7=数据,E=高脉冲 | 无 |
(4)初始化过程(复位过程)
1 - 延时15ms
2 - 写指令38H(不检测忙信号)
3 - 延时5ms
4 - 写指令38H(不检测忙信号)
5 - 延时5ms
6 - 写指令38H(不检测忙信号)
7 - 以后每次写指令、读/写数据操作之前均需检测忙信号
8 - 写指令38H:显示模式设置
9 - 写指令08H:显示关闭
10 - 写指令01H:显示清屏
11 - 写指令06H:显示光标移动设置
12 - 写指令0CH:显示开及光标设置
2.显示字符串 仿真
1.硬件仿真图
2.软件
LCD1602.h
#ifndef __LCD1602_H__
#define __LCD1602_H__
#include <reg51.h>
//IO接口声明
#define LCD1602_DB P0
sbit LCD1602_RS = P2^5;
sbit LCD1602_RW = P2^6;
sbit LCD1602_EN = P2^7;
//LCD1602指令
//显示模式设置指令
//DB7 DB6 DB5 DB4 DB3 DB2 DB1 DB0
// 0 0 1 DL N F * *
// DL = 1,8位数据接口; DL = 0,4位数据接口。
// N = 1,两行显示; N = 0,单行显示。
// F = 1,5*10点阵字符;F = 0,5*8点阵字符
#define LCD_MODE_PIN8 0x38 //8位数据接口,两行,5*8点阵
#define LCD_MODE_PIN4 0x28 //4位数据接口,两行,5*8点阵
#define LCD_SCREEN_CLR 0x01 //清屏
#define LCD_CURSOR_RST 0x02 //光标复位
//显示开关控制指令
#define LCD_DIS_CUR_BLK_ON 0x0F//显示开,光标开,光标闪烁
#define LCD_DIS_CUR_ON 0x0E//显示开,光标开,光标不闪烁
#define LCD_DIS_ON 0x0C//显示开,光标关,光标不闪烁
#define LCD_DIS_OFF 0x08//显示关,光标关,光标不闪烁
//显示模式控制
#define LCD_CURSOR_RIGHT 0x06//光标右移,显示不移动
#define LCD_CURSOR_LEFT 0x04//光标左移,显示不移动
#define LCD_DIS_MODE_LEFT 0x07//操作后,AC自增,画面平移。
#define LCD_DIS_MODE_RIGHT 0x05//操作后,AC自减,画面平移。
//光标、显示移动指令
#define LCD_CUR_MOVE_LEFT 0x10//光标左移
#define LCD_CUR_MOVE_RIGHT 0x14//光标右移
#define LCD_DIS_MOVE_LEFT 0x18//显示左移
#define LCD_DIS_MOVE_RIGHT 0x1C//显示右移
//函数声明
void LCDReadBF();
void LCDWriteCmd(unsigned char cmd);
void LCDWriteData(unsigned char dat);
void LCDInit();
void LCDShowStr(unsigned char x,unsigned y,unsigned char *str);
// CGRAM 地址设置,character generator RAM (0~3FH)
// DB7 DB6 DB5 DB4 DB3 DB2 DB1 DB0
// 0 1 A5 A4 A3 A2 A1 A0
// DDRAM 地址设置
// DB7 DB6 DB5 DB4 DB3 DB2 DB1 DB0
// 1 A6 A5 A4 A3 A2 A1 A0
//
// N = 0,单行显示 —— A6 ~ A0 = 00H ~ 4FH
// N = 1,首行 —— A6 ~ A0 = 00H ~ 27H
// 次行 —— A6 ~ A0 = 40H ~ 67H
// 读BF、AC值(BF:Busy Flag)
// RS R/W DB7 DB6 DB5 DB4 DB3 DB2 DB1 DBO
// 0 1 BF A6 A5 A4 A3 A2 A1 A0
// 写数据
// RS R/W DB7 DB6 DB5 DB4 DB3 DB2 DB1 DBO
// 1 0 BF A6 A5 A4 A3 A2 A1 A0
// 写命令
// RS R/W DB7 DB6 DB5 DB4 DB3 DB2 DB1 DBO
// 0 0 BF A6 A5 A4 A3 A2 A1 A0
#endif
LCD1602.c
#include "LCD1602.h"
//忙检测(读忙信号)
void LCDReadBF()
{
unsigned char state;
unsigned char i;
LCD1602_DB = 0xFF;//IO口置1,做输入
LCD1602_RS = 0;
LCD1602_RW = 1;
do
{
LCD1602_EN = 1;
state = LCD1602_DB;
LCD1602_EN = 0;
//忙信号一直为1,认为显示屏错误,直接退出
i++;
if(i>50)
break;
}
while(state & 0x80);//最高位为1等待
}
//写命令
void LCDWriteCmd(unsigned char cmd)
{
LCDReadBF();//等待忙检测,不忙再进制写操作
LCD1602_RS = 0;
LCD1602_RW = 0;
LCD1602_DB = cmd;
LCD1602_EN = 1;
LCD1602_EN = 0;
}
//写数据
void LCDWriteData(unsigned char dat)
{
LCDReadBF();//等待忙检测,不忙再进制写操作
LCD1602_RS = 1;
LCD1602_RW = 0;
LCD1602_DB = dat;
LCD1602_EN = 1;
LCD1602_EN = 0;
}
//LCD的初始化
void LCDInit()
{
LCDWriteCmd(LCD_MODE_PIN8); //显示模式设置。8位数据,两行,5*8点阵
LCDWriteCmd(LCD_DIS_ON); //显示开,光标关,光标不闪烁
LCDWriteCmd(LCD_CURSOR_RIGHT); //光标右移,显示不移动
LCDWriteCmd(LCD_SCREEN_CLR); //清屏
}
//显示字符串
void LCDShowStr(unsigned char x,unsigned y,unsigned char *str)
{
//设置DDRAM,确定字符位置
if(y == 0)
{
LCDWriteCmd(0x80 | x); //DDRAM最高位为1
}
else
{
LCDWriteCmd(0x80 | (0x40 + x)); //次行地址从0x40开始
}
while(*str != '\0')
{
LCDWriteData(*str++);
}
}
main.c
#include <reg51.h>
#include "delay.h"
#include "LCD1602.h"
void main()
{
unsigned char str1[] = "Hello LCD 1602";
unsigned char str2[] = "123456789ABCDEF";
LCDInit();
DelayXms(10);
LCDShowStr(0,0,str1);
LCDShowStr(0,1,str2);
while(1);
}
3.仿真结果
3.显示自定义字符串 仿真(显示汉语 “一” )方法一
1.自定义的第几个字符其对应CGRAM地址的关系式是
序号 | CGRAM | CGROM |
---|---|---|
1 | 0x40 - 0x47 | 显示代码 0x00 |
2 | 0x48 - 0x4F | 显示代码 0x01 |
3 | 0x50 - 0x57 | 显示代码 0x02 |
4 | 0x58 - 0x5F | 显示代码 0x03 |
5 | 0x60 - 0x67 | 显示代码 0x04 |
6 | 0x68 - 0x6F | 显示代码 0x05 |
7 | 0x70 - 0x77 | 显示代码 0x06 |
8 | 0x78 - 0x7F | 显示代码 0x07 |
2.代码
LCD1602.h
#ifndef __LCD1602_H__
#define __LCD1602_H__
#include <reg51.h>
//IO接口声明
#define LCD1602_DB P0
sbit LCD1602_RS = P2^5;
sbit LCD1602_RW = P2^6;
sbit LCD1602_EN = P2^7;
//LCD1602指令
//显示模式设置指令
//DB7 DB6 DB5 DB4 DB3 DB2 DB1 DB0
// 0 0 1 DL N F * *
// DL = 1,8位数据接口; DL = 0,4位数据接口。
// N = 1,两行显示; N = 0,单行显示。
// F = 1,5*10点阵字符;F = 0,5*8点阵字符
#define LCD_MODE_PIN8 0x38 //8位数据接口,两行,5*8点阵
#define LCD_MODE_PIN4 0x28 //4位数据接口,两行,5*8点阵
#define LCD_SCREEN_CLR 0x01 //清屏
#define LCD_CURSOR_RST 0x02 //光标复位
//显示开关控制指令
#define LCD_DIS_CUR_BLK_ON 0x0F//显示开,光标开,光标闪烁
#define LCD_DIS_CUR_ON 0x0E//显示开,光标开,光标不闪烁
#define LCD_DIS_ON 0x0C//显示开,光标关,光标不闪烁
#define LCD_DIS_OFF 0x08//显示关,光标关,光标不闪烁
//显示模式控制
#define LCD_CURSOR_RIGHT 0x06//光标右移,显示不移动
#define LCD_CURSOR_LEFT 0x04//光标左移,显示不移动
#define LCD_DIS_MODE_LEFT 0x07//操作后,AC自增,画面平移。
#define LCD_DIS_MODE_RIGHT 0x05//操作后,AC自减,画面平移。
//光标、显示移动指令
#define LCD_CUR_MOVE_LEFT 0x10//光标左移
#define LCD_CUR_MOVE_RIGHT 0x14//光标右移
#define LCD_DIS_MOVE_LEFT 0x18//显示左移
#define LCD_DIS_MOVE_RIGHT 0x1C//显示右移
//函数声明
void LCDReadBF();
void LCDWriteCmd(unsigned char cmd);
void LCDWriteData(unsigned char dat);
void LCDInit();
void LCDShowStr(unsigned char x,unsigned y,unsigned char *str);
void LCDWriteCGRAM();
unsigned char code str[];
// CGRAM 地址设置,character generator RAM (0~3FH)
// DB7 DB6 DB5 DB4 DB3 DB2 DB1 DB0
// 0 1 A5 A4 A3 A2 A1 A0
// DDRAM 地址设置
// DB7 DB6 DB5 DB4 DB3 DB2 DB1 DB0
// 1 A6 A5 A4 A3 A2 A1 A0
//
// N = 0,单行显示 —— A6 ~ A0 = 00H ~ 4FH
// N = 1,首行 —— A6 ~ A0 = 00H ~ 27H
// 次行 —— A6 ~ A0 = 40H ~ 67H
// 读BF、AC值(BF:Busy Flag)
// RS R/W DB7 DB6 DB5 DB4 DB3 DB2 DB1 DBO
// 0 1 BF A6 A5 A4 A3 A2 A1 A0
// 写数据
// RS R/W DB7 DB6 DB5 DB4 DB3 DB2 DB1 DBO
// 1 0 BF A6 A5 A4 A3 A2 A1 A0
// 写命令
// RS R/W DB7 DB6 DB5 DB4 DB3 DB2 DB1 DBO
// 0 0 BF A6 A5 A4 A3 A2 A1 A0
#endif
LCD1602.c
#include "LCD1602.h"
unsigned char code str[] = {0x00,0x00,0x00,0x00,0x1F,0x00,0x00,0x00};//汉字“一”的点亮代码
//忙检测(读忙信号)
void LCDReadBF()
{
unsigned char state;
unsigned char i;
LCD1602_DB = 0xFF;//IO口置1,做输入
LCD1602_RS = 0;
LCD1602_RW = 1;
do
{
LCD1602_EN = 1;
state = LCD1602_DB;
LCD1602_EN = 0;
//忙信号一直为1,认为显示屏错误,直接退出
i++;
if(i>50)
break;
}
while(state & 0x80);//最高位为1等待
}
//写命令
void LCDWriteCmd(unsigned char cmd)
{
LCDReadBF();//等待忙检测,不忙再进制写操作
LCD1602_RS = 0;
LCD1602_RW = 0;
LCD1602_DB = cmd;
LCD1602_EN = 1;
LCD1602_EN = 0;
}
//写数据
void LCDWriteData(unsigned char dat)
{
LCDReadBF();//等待忙检测,不忙再进制写操作
LCD1602_RS = 1;
LCD1602_RW = 0;
LCD1602_DB = dat;
LCD1602_EN = 1;
LCD1602_EN = 0;
}
//LCD的初始化
void LCDInit()
{
LCDWriteCmd(LCD_MODE_PIN8); //显示模式设置。8位数据,两行,5*8点阵
LCDWriteCmd(LCD_DIS_ON); //显示开,光标关,光标不闪烁
LCDWriteCmd(LCD_CURSOR_RIGHT); //光标右移,显示不移动
LCDWriteCmd(LCD_SCREEN_CLR); //清屏
}
//显示字符串
void LCDShowStr(unsigned char x,unsigned y,unsigned char *str)
{
//设置DDRAM,确定字符位置
if(y == 0)
{
LCDWriteCmd(0x80 | x); //DDRAM最高位为1
}
else
{
LCDWriteCmd(0x80 | (0x40 + x)); //次行地址从0x40开始
}
while(*str != '\0')
{
LCDWriteData(*str++);
}
}
//自定义字符
void LCDWriteCGRAM()
{
unsigned char i;
//1、构造自定义字符 CGRAM
LCDWriteCmd(0x40);
for(i=0;i<8;i++)
{
LCDWriteData(str[i]);
}
//2、显示这个字符
LCDWriteCmd(0x80 | 0x05); //定义显示的位置 ‘|0x05’代表显示在第5位上
LCDWriteData(0x00);//查的CGROM表
}
main.c
#include <reg51.h>
#include "delay.h"
#include "LCD1602.h"
void main()
{
unsigned char str1[] = "Hello LCD 1602";
unsigned char str2[] = "0123456789ABCDEF";
LCDInit();
DelayXms(10);
LCDWriteCGRAM();
LCDShowStr(0,1,str2);
while(1); //保持循环执行
}
3.仿真结果
4.显示自定义字符串 仿真(显示汉语 “一” )方法二
1.代码
LCD1602.h
#ifndef __LCD1602_H__
#define __LCD1602_H__
#include <reg51.h>
//IO接口声明
#define LCD1602_DB P0
sbit LCD1602_RS = P2^5;
sbit LCD1602_RW = P2^6;
sbit LCD1602_EN = P2^7;
#define LCD_MODE_PIN8 0x38 //8位数据接口,两行,5*8点阵
#define LCD_MODE_PIN4 0x28 //4位数据接口,两行,5*8点阵
#define LCD_SCREEN_CLR 0x01 //清屏
#define LCD_CURSOR_RST 0x02 //光标复位
//显示开关控制指令
#define LCD_DIS_CUR_BLK_ON 0x0F//显示开,光标开,光标闪烁
#define LCD_DIS_CUR_ON 0x0E//显示开,光标开,光标不闪烁
#define LCD_DIS_ON 0x0C//显示开,光标关,光标不闪烁
#define LCD_DIS_OFF 0x08//显示关,光标关,光标不闪烁
//显示模式控制
#define LCD_CURSOR_RIGHT 0x06//光标右移,显示不移动
#define LCD_CURSOR_LEFT 0x04//光标左移,显示不移动
#define LCD_DIS_MODE_LEFT 0x07//操作后,AC自增,画面平移。
#define LCD_DIS_MODE_RIGHT 0x05//操作后,AC自减,画面平移。
//光标、显示移动指令
#define LCD_CUR_MOVE_LEFT 0x10//光标左移
#define LCD_CUR_MOVE_RIGHT 0x14//光标右移
#define LCD_DIS_MOVE_LEFT 0x18//显示左移
#define LCD_DIS_MOVE_RIGHT 0x1C//显示右移
//函数声明
void LCDReadBF();
void LCDWriteCmd(unsigned char cmd);
void LCDWriteData(unsigned char dat);
void LCDInit();
void LCDShowStr(unsigned char x,unsigned char y,unsigned char *str);
void LCDWriteCGRAM();
void LCDSetPosition(unsigned char x,unsigned y);
void LCDSetChar(unsigned char x,unsigned char y,unsigned char pos,unsigned char *str);
#endif
LCD1602.c
#include "LCD1602.h"
//忙检测(读忙信号)
void LCDReadBF()
{
unsigned char state;
unsigned char i;
LCD1602_DB = 0xFF;//IO口置1,做输入
LCD1602_RS = 0;
LCD1602_RW = 1;
do
{
LCD1602_EN = 1;
state = LCD1602_DB;
LCD1602_EN = 0;
//忙信号一直为1,认为显示屏错误,直接退出
i++;
if(i>50)
break;
}
while(state & 0x80);//最高位为1等待
}
//写命令
void LCDWriteCmd(unsigned char cmd)
{
LCDReadBF(); //等待忙检测,不忙再进制写操作
LCD1602_RS = 0;
LCD1602_RW = 0;
LCD1602_DB = cmd;
LCD1602_EN = 1;
LCD1602_EN = 0;
}
//写数据
void LCDWriteData(unsigned char dat)
{
LCDReadBF(); //等待忙检测,不忙再进制写操作
LCD1602_RS = 1;
LCD1602_RW = 0;
LCD1602_DB = dat;
LCD1602_EN = 1;
LCD1602_EN = 0;
}
//LCD的初始化
void LCDInit()
{
LCDWriteCmd(LCD_MODE_PIN8); //显示模式设置。8位数据,两行,5*8点阵
LCDWriteCmd(LCD_DIS_ON); //显示开,光标关,光标不闪烁
LCDWriteCmd(LCD_CURSOR_RIGHT); //光标右移,显示不移动
LCDWriteCmd(LCD_SCREEN_CLR); //清屏
}
//设置DDRAM,确定字符位置
void LCDSetPosition(unsigned char x,unsigned y)
{
if(y == 0)
{
LCDWriteCmd(0x80 | x); //DDRAM最高位为1
}
else
{
LCDWriteCmd(0x80 | (0x40 + x)); //次行地址从0x40开始
}
}
//显示字符串
void LCDShowStr(unsigned char x,unsigned char y,unsigned char *str)
{
//设置DDRAM,确定字符位置
LCDSetPosition(x,y);
while(*str != '\0')
{
LCDWriteData(*str++);
}
}
自定义字符 方法一
//void LCDWriteCGRAM()
//{
// unsigned char i;
// LCDWriteCmd(0x40);//1、构造自定义字符 CGRAM
// for(i=0;i<8;i++)
// {
// LCDWriteData(str[i]);
// }
// //2、显示这个字符
// LCDWriteCmd(0x80 | 0x05);
// LCDWriteData(0x00);//查的CGROM表
//}
//自定义字符 方法二
//自定义字符在表中的位置x,y,显示在LCD的位置pos,要显示的字符str
void LCDSetChar(unsigned char x,unsigned char y,unsigned char pos,unsigned char *str)
{
unsigned char i;
//1、构造自定义字符 确定位置 CGRAM
for(i=0;i<8;i++)
{
LCDWriteCmd(0x40 + pos*8 +i);
LCDWriteData(*(str+i));
}
//2、显示这个字符
LCDSetPosition(x,y);//设置DDRAM,确定字符位置
LCDWriteData(0x00+pos);
main.c
#include <reg51.h>
#include "delay.h"
#include "LCD1602.h"
void main()
{
unsigned char str1[] = "Hello LCD 1602";
unsigned char str2[] = "0123456789ABCDEF";
unsigned char code str3[] = {0x00,0x00,0x00,0x00,0x1F,0x00,0x00,0x00};
LCDInit();
DelayXms(10);
//LCDWriteCGRAM();
LCDSetChar(2,0,0,str3);
LCDShowStr(0,1,str2);
while(1); //保持循环执行
}
2.仿真结果
总结
以上就是本文要讲的内容。