单片机 | 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
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 8
    评论
基于51单片机开发的小程序,学习资料,用于学习51单片机的嵌入式开发,应用案例 基于51单片机开发的小程序,学习资料,用于学习51单片机的嵌入式开发,应用案例 基于51单片机开发的小程序,学习资料,用于学习51单片机的嵌入式开发,应用案例 基于51单片机开发的小程序,学习资料,用于学习51单片机的嵌入式开发,应用案例 基于51单片机开发的小程序,学习资料,用于学习51单片机的嵌入式开发,应用案例基于51单片机开发的小程序,学习资料,用于学习51单片机的嵌入式开发,应用案例基于51单片机开发的小程序,学习资料,用于学习51单片机的嵌入式开发,应用案例基于51单片机开发的小程序,学习资料,用于学习51单片机的嵌入式开发,应用案例基于51单片机开发的小程序,学习资料,用于学习51单片机的嵌入式开发,应用案例基于51单片机开发的小程序,学习资料,用于学习51单片机的嵌入式开发,应用案例基于51单片机开发的小程序,学习资料,用于学习51单片机的嵌入式开发,应用案例基于51单片机开发的小程序,学习资料,用于学习51单片机的嵌入式开发,应用案例基于51单片机开发的小程序,学习资料,用于学习51单片机的嵌入式开发,应用案例基于51单片机开发的小程序,学习资料,用于学习51单片机的嵌入式开发,应用案例基于51单片机开发的小程序,学习资料,用于学习51单片机的嵌入式开发,应用案例基于51单片机开发的小程序,学习资料,用于学习51单片机的嵌入式开发,应用案例基于51单片机开发的小程序,学习资料,用于学习51单片机的嵌入式开发,应用案例基于51单片机开发的小程序,学习资料,用于学习51单片机的嵌入式开发,应用案例基于51单片机开发的小程序,学习资料,用于学习51单片机的嵌入式开发,应用案例基于51单片机开发的小程序,学习资料,用于学习51单片机的嵌入式开发,应用案例基于51单片机开发的小程序,学习资料,用于学习51单片机的嵌入式开发,应用案例基于51单片机开发的小程序,学习资料,用于学习51单片机的嵌入式开发,应用案例基于51单片机开发的小程序,学习资料,用于学习51单片机的嵌入式开发,应用案例基于51单片机开发的小程序,学习资料,用于学习51单片机的嵌入式开发,应用案例基于51单片机开发的小程序,学习资料,用于学习51单片机的嵌入式开发,应用案例
这是DS小龙哥编写的(STC)51单片机开发手册,适合51单片机入门初学者,大学生等当做工具书使用。实验的平台采用的是普中科技单片机平台,文章中的知识点详细,代码清晰,注释清楚,可以复制粘贴,配套代码详细。目前写了4个大章节,后续会持续更新,更新后重新下载即可。 每个例子的完整的工程源码在PDF里有百度云盘的下载链接。失效了可以私信发送。 目前PDF里包含的目录如下: 51单片机开发手册 1 一、 单片机开发入门知识介绍 1 1.1 51单片机介绍 1 1.2 市场上的主流单片机种类 1 1.3 FPGA与单片机区别 2 1.4 DSP和单片机区别 3 二、搭建开发环境 4 2.1 STC单片机命名介绍 4 2.2 安装keil软件 4 2.3 新建工程 5 2.4 下载程序 8 2.5 使用辅助工具计算延时时间 12 2.6 STC90C51系列单片机引脚图 13 三、 基础入门学习 14 3.1 LED灯模块 14 3.2 蜂鸣器模块 17 3.5 独立按键 18 3.6 矩阵键盘 22 3.7 独立数码管(静态数码管) 26 3.8 动态数码管 29 3.9 LED 16*16点阵 33 3.10 采用38译码器驱动8位数码管 48 四、单片机提高 53 4.1 定时器 53 4.2 中断 60 4.3 串口通信 71 4.4 NEC红外线遥控器解码 80 4.5 DS18B20温度传感器 87 4.6 EEPROM存储芯片(AT24C02) 96 4.7 DS1302 实时时钟芯片 107 4.8 PCF8591(ADC/DAC)转换芯片 119 4.9 (HC-SR04)超声波测距模块 130 4.10 使用计数器测量NE555脉冲频率 134 4.11 LCD1602显示屏 139 四、 单片机项目 146 5.1 16x16点阵滚动显示 146
Modbus协议是一种用于通信和控制的开放式通信协议。在单片机中使用Modbus协议可以实现与其他设备的数据交互和控制功能。 要在单片机中实现Modbus通信,首先需要编写相应的代码。在开始编写代码之前,需要了解Modbus协议的基本概念和通信流程。 代码编写过程中,首先需要确定使用的通信接口,比如串口、以太网等。然后根据接口特性配置相关的寄存器、波特率等参数。 接下来,需要编写初始化函数,用于配置通信接口的参数以及初始化相关的寄存器。然后编写发送和接收函数,用于发送和接收Modbus命令。 在编写发送函数时,需要构造Modbus命令帧,包括设备地址、功能码、数据等。然后通过通信接口将命令帧发送给目标设备。 在编写接收函数时,需要不断检测接收寄存器,并判断接收到的数据是否为Modbus响应帧。如果是响应帧,则根据功能码解析数据并进行相应的处理。 最后,需要编写主程序,用于控制整个Modbus通信过程的流程和逻辑。主程序中可以包括周期性发送数据、接收数据、状态机控制等功能。 编写完成后,将代码下载到单片机中,即可实现Modbus通信。可以使用调试工具对通信过程进行测试和调整,确保通信正常。 需要注意的是,每个单片机厂商对于Modbus通信实现的细节可能有所不同,因此在编写代码之前,最好查阅相关的单片机文档和资料,确保编写的代码能够正确运行。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值