单片机 | 51单片机实践【基础篇】

【金善愚】 单片机应用实践——基础篇 笔记整理
课程视频 :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=HDO~D7 = 状态字
写指令RS=L,RW=L,DO~D7=指令码,E=高脉冲
读数据RS=H,RW=H,E=HDO~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地址的关系式是

序号CGRAMCGROM
10x40 - 0x47显示代码 0x00
20x48 - 0x4F显示代码 0x01
30x50 - 0x57显示代码 0x02
40x58 - 0x5F显示代码 0x03
50x60 - 0x67显示代码 0x04
60x68 - 0x6F显示代码 0x05
70x70 - 0x77显示代码 0x06
80x78 - 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.仿真结果

在这里插入图片描述


总结

以上就是本文要讲的内容。

  • 3
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 8
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值