STC51单片机

STC51单片机

1.课程概述

(1)结合8个项目进行学习-》
01.电动车报警器:IO控制入门
02.感应开关盖垃圾桶:定时器,PWM开发,超声波
03.基于wifi的智能控制插座:串口开发,ESP8266模块AT控制指令学习,中断学习
04.基于蓝牙HC-05的智能控制插座:串口开发,蓝牙穿透
05.基于4G的智能控制插座:串口开发,蓝牙穿透
06.温湿度检测系统: DS18B20单线协议,如何看时序图,IIC协议液晶屏显示,SPI协议液晶显示
07.语音控制开关灯:语音模块二次开发
08.智能小车_远程控制/壁障/寻迹/数据采集等:综合性项目

2.开发软件Keil的安装

(1)双击c51v952应用程序,傻瓜式安装;破解方法:复制CID号到注册机中生成New License ID Code;

3.手把手创建一个Keil代码工程

(1)创建项目工程文件夹,可以当作模板Template-》创建文件,取名main.c-》
进入keil-》新建工程-》选择合适的芯片-》工程目录中添加代码文件-》编译(选择输出Hex文件)

4.Hex程序烧写到单片机

(1)usb转串口驱动安装(ch341ser.exe应用程序傻瓜式安装),STC-ISP傻瓜式安装;
(2)电脑连接单片机,打开stc-isp选择单片机型号,串口号,打开程序文件,点击下载/编程,单片机上电按钮拨动完成烧录;

5.单片机基本认知

(1)一种集成电路芯片,微型计算机系统(含cpu,存储,IO口,中断系统,定时器,计数器)
(2)STC89系列单片机手册运用:命名规则,封装(LQFP44)等

6.单片机那些事

(1)单片机编程来控制IO口,串口传输数据,cpu通过寻址的方式找到的IO口;

7.SFR及SBIT描述IO口和具体引脚

(1)sfr指令:用来直接描述硬件地址(一组IO口,例sfr P0=0x80;P0=0;);
sbit指令:对应可位寻址空间的一个位(一个IO口中一针脚,例sbit led1=P3^6;);

8.IO口输入输出

(1)89C52有5组输入输出口,其中P0,P1,P2,P3对应8个引脚,P4是RC系列才有,仅有7个引脚;
(2)输入:拿到数据;输出:给出数据;

9.点亮一个LED

(1)案例点亮一个LED(oneLight.c);

#include "reg52.h"
sbit led1=P3^7;
void main(){
	//灯亮,给一个低电平
	led1=0;
}

10.编程实现LED闪烁

(1)案例实现LED闪烁,isp烧录工具中生成延时函数并调用延时函数(ledshanshuo.c);

#include "reg52.h"
#include <INTRINS.H>
sbit led1=P3^7;
void Delay500ms()		//@11.0592MHz
{
	unsigned char i, j, k;

	_nop_();
	i = 4;
	j = 129;
	k = 119;
	do
	{
		do
		{
			while (--k);
		} while (--j);
	} while (--i);
}

void main(){
	while(1){
		//灯亮,给led1一个低电平
		led1=0;
		//灯亮延时500ms
		Delay500ms();
		//灯灭,给led1一个高电平
		led1=1;
		//灯灭延时500ms
		Delay500ms();
}
}

11.按下按键点亮灯

(1)案例按键点亮灯,按键按下输入低电平到单片机,单片机P2.1口检测到低电平,P3.7口输出低电平点亮灯(anjian_led.c);

#include "reg52.h"
sbit led1=P3^7;
sbit key1=P2^1;
void main(){
	while(1){
		//key1按下,表现低电平
		if(key1==0){
			//灯亮,给出低电平
			led1=0;
		}
	}
}

12.两个按键控制灯

(1)案例两个按键控制灯,一个控制灯亮(key1按下输入低电平,单片机输出低电平到led1亮),一个控制灯灭(key2按下输入低电平,单片机输出高电平led1灭)(two_button_led.c)

#include "reg52.h"
sbit led1=P3^7;
sbit key1=P2^1;
sbit key2=P2^0;
void main(){
	while(1){
		//按下key1按键,表现为低电平输入
		if(key1==0){
			//输出低电平,led1亮
			led1=0;
		}
		//按下key2按键,表现为低电平输入
		if(key2==0){
			//输出高电平,led1灭
			led1=1;
		}
	}
}

13.按键的软件消抖

(1)案例两个按键分别控制灯亮灭,按键1设定了延时消抖(button_control_led_no_shake.c)

#include "reg52.h"
sbit led1=P3^7;
sbit key1=P2^1;
sbit key2=P2^0;
void Delay50ms()		//@11.0592MHz
{
	unsigned char i, j;

	i = 90;
	j = 163;
	do
	{
		while (--j);
	} while (--i);
}

void main(){
	while(1){
		//按下key1按键,表现为低电平输入
		if(key1==0){
			//延时50ms,属软件消抖,再次判断是否有低电平输入
			Delay50ms();
			if(key1==0){
			//输出低电平,led1亮
			led1=0;
			}
		}
		//按下key2按键,表现为低电平输入
		if(key2==0){
			//输出高电平,led1灭
			led1=1;
		}
	}
}

14.IO口状态翻转

(1)案例IO口状态翻转,一直给led1一个高电平,按键按下发生电平翻转led1=!led1(button_control_led_io_flip.c);

#include "reg52.h"
sbit led1=P3^7;
sbit key1=P2^1;
sbit key2=P2^0;
void Delay50ms()		//@11.0592MHz
{
	unsigned char i, j;

	i = 90;
	j = 163;
	do
	{
		while (--j);
	} while (--i);
}

void main(){
	led1=1;
	while(1){
		//按下key1按键,表现为低电平输入
		if(key1==0){
			//延时50ms,属软件消抖,再次判断是否有低电平输入
			Delay50ms();
			if(key1==0){
			//输出低电平,led1亮
			led1=!led1;
			}
		}
		//按下key2按键,表现为低电平输入
		if(key2==0){
			//输出高电平,led1灭
			led1=1;
		}
	}
}

15.记录状态位来控制LED

(1)定义一个状态位变量,记录2开关按键按下的状态,再根据这个状态位变量了判定灯的亮灭(button_control_led_record_status.c);

#include "reg52.h"
#define ON_STATUS 1
#define OFF_STATUS 0
sbit led1=P3^7;
sbit key1=P2^1;
sbit key2=P2^0;
void Delay50ms()		//@11.0592MHz
{
	unsigned char i, j;

	i = 90;
	j = 163;
	do
	{
		while (--j);
	} while (--i);
}

void main(){
	int ledMark = OFF_STATUS;
	led1=1;
	while(1){
		//按下key1按键,表现为低电平输入
		if(key1==0){
			//延时50ms,属软件消抖,再次判断是否有低电平输入
			Delay50ms();
			if(key1==0){
			//输出低电平,led1亮
				ledMark = ON_STATUS;
			}
		}
		//按下key2按键,表现为低电平输入
		if(key2==0){
			//输出高电平,led1灭
			ledMark = OFF_STATUS;
		}
		if(ledMark == OFF_STATUS){
			led1 = 1;
		}else{
			led1 = 0;
		}
	}
}

16.简易电动车防盗小项目概述

(1)震动传感器模块,继电器模块,433M无线模块的应用

17.震动传感器介绍及实战

(1)震动传感器震动时DO口输出低电平,不震动时输出高电平;可作为单片机IO口输入信号,来控制led亮灭(vibrate_control_led.c);

#include "reg52.h"
#include "intrins.h"
sbit led1=P3^7;
sbit vibrate=P3^3;
void Delay2000ms()		//@11.0592MHz
{
	unsigned char i, j, k;

	_nop_();
	i = 15;
	j = 2;
	k = 235;
	do
	{
		do
		{
			while (--k);
		} while (--j);
	} while (--i);
}


void main(){
	while(1){
		if(vibrate==0){
			led1=0;
			Delay2000ms();
			led1=1;
		}else{
			led1=1;
		}
	}
}

18.继电器介绍及实战

(1)单片机输出低电平至继电器输入口,继电器闭合。(vibrate_control_relay.c)

#include "reg52.h"
#include "intrins.h"
sbit vibrate=P3^3;
sbit switcher=P1^1;
void Delay2000ms()		//@11.0592MHz
{
	unsigned char i, j, k;

	_nop_();
	i = 15;
	j = 2;
	k = 235;
	do
	{
		do
		{
			while (--k);
		} while (--j);
	} while (--i);
}


void main(){
	while(1){
		if(vibrate==0){
			switcher=0;
			Delay2000ms();
			switcher=1;
		}else{
			switcher=1;
		}
	}
}

19.震动控制喇叭

(1)继电器输出口的COM口跟NO口串联到喇叭中,继电器接收到低电平,继电器闭合,喇叭响;(vibrate_control_relay.c)

#include "reg52.h"
#include "intrins.h"
sbit vibrate=P3^3;
sbit switcher=P1^1;
void Delay2000ms()		//@11.0592MHz
{
	unsigned char i, j, k;

	_nop_();
	i = 15;
	j = 2;
	k = 235;
	do
	{
		do
		{
			while (--k);
		} while (--j);
	} while (--i);
}


void main(){
	while(1){
		if(vibrate==0){
			switcher=0;
			Delay2000ms();
			switcher=1;
		}else{
			switcher=1;
		}
	}
}

20.433M无线射频信号控制喇叭

(1)遥控器A按下,对应433M无线射频模块的D0口输出高电平;
遥控器B按下,对应433M无线射频模块的D1口输出高电平;
将D0口跟D1口分别接入单片机IO口作为判断的信号,来控制继电器开合;(433M_control_relay_switcher.c)

#include "reg52.h"
sbit D0_ON=P1^2;
sbit D1_OFF=P1^3;
sbit switcher=P1^1;
void main(){
	while(1){
		//遥控器A按下,D0表现高电平
		if(D0_ON==1){
			//A按下,要使继电器导通,需给继电器一个低电平;
			switcher=0;
		}
		//遥控器B按下,D1表现高电平
		if(D1_OFF==1){
			//B按下,要使继电器断开,需给继电器一个高电平;
			switcher=1;
		}
	}
}

21.电动车简易防盗器设计

(1)将433M无线射频模块,震动传感器,继电器,喇叭跟单片机连接,利用IO控制编程即可实现;

22.电动车简易防盗器编码实现

(1)编程思路(electric_vehicle_simple_alarm.c):
1.1 按A按键,设定为开启警报模式
1.1.1 长响,表示进入警报模式,标记进入警报模式
1.2 按B按键,设定为关闭警报模式
1.2.1 短响,表示解除警报模式,标记解除警报模式
1.3 开启警报模式
1.3.1 发生震动,喇叭响
1.3.2 没发生震动,喇叭不响

#include "reg52.h"
#include "intrins.h"
#define J_ON  1
#define J_OFF 0
sbit switcher = P1^0;
sbit D0_ON 		= P1^1;
sbit D1_OFF 	= P1^3;
sbit vibrate 	= P1^2;
void Delay2000ms()		//@11.0592MHz
{
	unsigned char i, j, k;

	_nop_();
	i = 15;
	j = 2;
	k = 235;
	do
	{
		do
		{
			while (--k);
		} while (--j);
	} while (--i);
}
void Delay500ms()		//@11.0592MHz
{
	unsigned char i, j, k;

	_nop_();
	i = 4;
	j = 129;
	k = 119;
	do
	{
		do
		{
			while (--k);
		} while (--j);
	} while (--i);
}

void main(){
	int mark = J_OFF;
	while(1){
		//1.1 按A按键,设定为开启警报模式
		if(D0_ON == 1){
			//1.1.1 长响,表示进入警报模式,标记进入警报模式		
			switcher = 0;
			Delay2000ms();
			switcher = 1;
			mark = J_ON;			
		}

	 //1.2 按B按键,设定为关闭警报模式
		if(D1_OFF == 1){
			//1.2.1 短响,表示解除警报模式,标记解除警报模式
			switcher = 0;
			Delay500ms();
			switcher = 1;
			mark = J_OFF;
		}			
	 //1.3 开启警报模式
		if(mark == J_ON){
			//1.3.1 发生震动,喇叭响
			if(vibrate == 0){
				switcher = 0;
				Delay2000ms();
				Delay2000ms();
			//1.3.2 没发生震动,喇叭不响
			}else{
				switcher = 1;
			}
		}
	}

}

23.感应开关盖垃圾桶需求概述

(1)垃圾桶感应到人靠近后,通过舵机顶开垃圾桶(需应用定时器,PWM信号,超声波测距);

24.定时计数器的概念引入

(1)软件延时,首先占有cpu资源用来数数;其次还会造成当cpu忙于数数时,外部信号输入都没法察觉,故需引入定时计数器;
(2)定时器跟计数器硬件电路一样,通过配置寄存器可当定时器或计数器使用;
(3)定时器靠内部震荡电路数数,每经过1个机器周期,计数存储器值加1;
(4)计数器靠读取外部信号,外部每来一个负跳变信号(高电平到低电平跳变),计数存储器加1;

25.晶振时钟周期机器周期

(1)晶振:晶体震荡器,数字电路的心脏;
(2)时钟周期:又称振荡周期,时钟频率的倒数,计算机最小的时间单元;
(3)机器周期: 又称CPU周期,若干个时钟周期组成;

26.定时器加一

(1)晶振频率是11.0592MHz的时候,等于11059.2KHz = 11059200Hz;
(2)机器周期 = 12 x 时钟周期 =12 x (1/时钟频率) 秒 = 12 / 时钟频率 秒
= 12 / 11059200 秒 = 12 000 000 / 11059200 微秒 = 1.085 微秒

27.什么是寄存器

(1)例如单片机中的特殊功能寄存器,都有相应的内存地址,可通过编程对寄存器操作,实现想要的功能;

28.定时器如何定时10毫秒

(1)16位寄存器可数65536下,10ms/1.085us=10000us/1.085us=9216下,65536-9216=56320,转16进制为0xDC00,设定TL0=0x00,TH0=0xDC

29.定时器编程前寄存器配置计划

(1)TCON(定时器/计算器中断控制寄存器):爆表时候bit5(TF0)会置1;bit4(TR0)置1表示开始计数;
(2)TMOD(定时器模式寄存器):工作方式1即16位计数器,设定bit1(M1)bit0(M0)设定为01;

30.定时器控制LED每隔一秒亮灭

(1)TCON(定时器/计算器中断控制寄存器)的操作:TF0 = 0;TR0 = 1;//3.开始计时
(2)TMOD(定时器模式寄存器)的操作:TMOD = 0x01; //1.配置定时器0工作模式16位计数
(3)TL0,TH0寄存器的操作:TL0 = 0x00;TH0 = 0xDC; //2.给定初值,10ms
(4)TF0==1作为爆表判定,爆表了TF0置0,并对TL0,TH0重新赋初值,爆表100次即为1s,led改变状态,统计爆表次数置0后再次统计;(timer01.c)

#include "reg52.h"
sbit led = P3^6;
/*
void Timer0Init(void)		//10毫秒@11.0592MHz
{
	AUXR &= 0x7F;		//定时器时钟12T模式
	TMOD &= 0xF0;		//设置定时器模式
	TMOD |= 0x01;		//设置定时器模式
	TL0 = 0x00;		//设置定时初值
	TH0 = 0xDC;		//设置定时初值
	TF0 = 0;		//清除TF0标志
	TR0 = 1;		//定时器0开始计时
}    

*/
void main(){
	int cnt = 0;
	led = 1;
	//1.配置定时器0工作模式16位计数
	TMOD = 0x01;
	//2.给定初值,10ms
	TL0 = 0x00;
	TH0 = 0xDC;
	//3.开始计时
	TF0 = 0;
	TR0 = 1;
	while(1){
		//爆表时,bit5(TF0)会置为1
		if(TF0 == 1){
			//TF0清零
			TF0 = 0;	 
			//统计爆表次数
			cnt ++;		 
			TL0 = 0x00;
			TH0 = 0xDC;
			//爆表100次,即为1s
			if(cnt == 100){
				//重新将统计次数清0
				cnt =0;
				//经过1s钟,翻转led状态
				led = !led;
			}
			
		}
	
	}
	
	
}

定时模块封装函数:

#include "reg52.h"
sbit led = P3^6;
/*
void Timer0Init(void)		//10毫秒@11.0592MHz
{
	AUXR &= 0x7F;		//定时器时钟12T模式
	TMOD &= 0xF0;		//设置定时器模式
	TMOD |= 0x01;		//设置定时器模式
	TL0 = 0x00;		//设置定时初值
	TH0 = 0xDC;		//设置定时初值
	TF0 = 0;		//清除TF0标志
	TR0 = 1;		//定时器0开始计时
}    
*/
void delay10ms(){
	//1.配置定时器0工作模式16位计数
	TMOD = 0x01;
	//2.给定初值,10ms
	TL0 = 0x00;
	TH0 = 0xDC;
	//3.开始计时
	TF0 = 0;
	TR0 = 1;
}
void main(){
	int cnt = 0;
	led = 1;
	delay10ms();
	while(1){
		//爆表时,bit5(TF0)会置为1
		if(TF0 == 1){
			//TF0清零
			TF0 = 0;	 
			//统计爆表次数
			cnt ++;		 
			TL0 = 0x00;
			TH0 = 0xDC;
			//爆表100次,即为1s
			if(cnt == 100){
				//重新将统计次数清0
				cnt = 0;
				//经过1s钟,翻转led状态
				led = !led;
			}
			
		}
	
	}
	
	
}

31.按位操作

(1)寄存器配置推荐按位操作:清零的位与上0,不清零的位与上1;置1的位或上1,不置1的位或上0;
(例:设定定时器为01模式,TMOD &= 0xF0;//设置定时器模式
TMOD |= 0x01;//设置定时器模式)

32.AUXR特殊功能寄存器时钟的电磁辐射

(1)AURX寄存器设定以降低对外界的电磁辐射:sfr AURX = 0x8e;AURX = 0x01;

33.单片机中断

(1)cpu正处理某件事的时候外界发生了紧急事件请求,cpu暂停工作转而处理紧急事件,
处理完后回到原先中断的地方继续原来的工作,这个过程叫中断;

34.定时器中断相关寄存器

(1)配置IE(中断允许寄存器):EA=1(总中断置1),ET0=1(ET0中断置1)-此2位为配置定时器0中断

35.定时器中断方式控制LED

(1)定时器中断配置完,发生中断就会执行中断函数(timer03_interrupt.c);

#include "reg52.h"
#include "intrins.h"
sbit led = P3^6;
sbit led1 = P3^7;
int cnt = 0;
/*
void Timer0Init(void)		//10毫秒@11.0592MHz
{
	AUXR &= 0x7F;		//定时器时钟12T模式
	TMOD &= 0xF0;		//设置定时器模式
	TMOD |= 0x01;		//设置定时器模式
	TL0 = 0x00;		//设置定时初值
	TH0 = 0xDC;		//设置定时初值
	TF0 = 0;		//清除TF0标志
	TR0 = 1;		//定时器0开始计时
}    
*/

void Delay300ms()		//@11.0592MHz
{
	unsigned char i, j, k;

	_nop_();
	i = 3;
	j = 26;
	k = 223;
	do
	{
		do
		{
			while (--k);
		} while (--j);
	} while (--i);
}

void TimerInit(){
	//1.配置定时器0工作模式16位计数
	TMOD = 0x01;
	//2.给定初值,10ms
	TL0 = 0x00;
	TH0 = 0xDC;
	//3.开始计时
	TF0 = 0;
	TR0 = 1;
	//打开中断定时器0中断
	ET0 = 1;
	//打开总中断EA
	EA = 1;
}
void main(){
	led = 1;
	TimerInit();
	while(1){
		led1 = 0;
		Delay300ms();
		led1 = 1;
		Delay300ms();
	}
}

void Timer0Hander() interrupt 1{
		//爆表时,bit5(TF0)会置为1
			//TF0清零
		  //	TF0 = 0; 中断的话,硬件清零不需要软件清零 
			//统计爆表次数
			cnt ++;		 
			TL0 = 0x00;
			TH0 = 0xDC;
			//爆表100次,即为1s
			if(cnt == 100){
				//重新将统计次数清0
				cnt = 0;
				//经过1s钟,翻转led状态
				led = !led;
			}
}

(2)89C52单片机提供了8个中断请求源(外部中断0,定时器0中断,外部中断1,定时器1中断,串口中断,定时器2中断,外部中断2,外部中断3)
(3)中断查询次号就是中断号;例如定时器0中断,void Timer0Hander() interrupt 1

36.认识PWM

(1)PWM(pluse width modulation)脉冲宽度调制,通过调节占空比来模拟信号;
(2)占空比:一个信号周期内高电平占据时长的百分比;
(3)PWM信号如何产生:a.芯片内部模块输出(部分芯片含PWM口)
b.IO口软件模拟(未集成PWM口芯片),精度略差

37.sg90舵机基本认知

(1)例如机械臂,摄像头等可以转动方向都用到了舵机,向舵机的黄色信号线中灌入PWM信号即可控制舵机;
(2)PWM波频率为50HZ时,周期=1/50=0.02s=20ms;0.5ms处0度角,1ms处在45度角,1.5ms处在90度角,2ms处在135度角,2.5ms处在180度角;

38.舵机编程实战

(1)实现舵机每隔2s从0度切换到135度,设定定时器每隔0.5ms爆表一次,此舵机一个周期需要爆表20ms/0.5ms=40次;
0度需要一个周期内的爆表次数在1次内高电平,超过1次为低电平;
135度需要一个周期内的爆表次数在4次内高电平,超过4次为低电平;
每隔2s中进行一次0度到135都的切换,也就是爆表次数从1次到4次的切换过程;(steering_engine_control.c)

#include "reg52.h"
#include "intrins.h"
int jd;
int cnt = 0;
sbit sg90_con = P1^1;

void Delay2000ms()		//@11.0592MHz
{
	unsigned char i, j, k;

	_nop_();
	i = 15;
	j = 2;
	k = 235;
	do
	{
		do
		{
			while (--k);
		} while (--j);
	} while (--i);
}


void Delay300ms()		//@11.0592MHz
{
	unsigned char i, j, k;

	_nop_();
	i = 3;
	j = 26;
	k = 223;
	do
	{
		do
		{
			while (--k);
		} while (--j);
	} while (--i);
}


void Time0Init(){

	//选择定时器0工作模式16位计数
	TMOD &= 0xF0;
	TMOD |= 0x01; 
	TL0 = 0x33;		//设置定时初值
	TH0 = 0xFE;		//设置定时初值	
	//开始计数
	TR0 = 1;
	TF0 = 0;
	//打开定时器0中断
	EA = 1;
	ET0 = 1;
}


void main(){
	Delay300ms();
	//定时器0初始化;
	Time0Init();
	//初始角度为0度,05ms
	jd = 1;
	cnt = 0;
	//初始电平为高电平
	sg90_con = 1;
	
	while(1){
		//90度,1.5ms的高电平
		jd = 3;
		cnt = 0;
		Delay2000ms();
		//0度
		jd = 1;
		cnt = 0;
		Delay2000ms();
	}
	

}
void Time0Handler() interrupt 1{
	//统计爆表次数
	cnt ++;
	//重新定时器赋初值
	TL0 = 0x33;
	TH0 = 0xFE;	
	//控制PWM波
	if(cnt < jd){
		sg90_con = 1;
	}else{
		sg90_con = 0;
	}
	//爆表40次,经过20ms
	if(cnt == 40){
		//经过20ms后重新累计,并且输出高电平`
		cnt = 0;
		sg90_con = 1;
	}

}

39.超声波测距传感器认知

(1)超声波测距模块就是通过发送和接收超声波来测量模块跟前方障碍物的距离(利用了时间差和声音传播速度计算);
(2)HC-SR04超声波测距模块,由2电源引脚,TRIG引脚,ECHO引脚组成;
(3)如何触发:给Trig端口至少10us高电平;
什么时候开始的:Echo端由低电平跳转到高电平,开始发送波;
什么时候结束:Echo端由高电平跳转到低电平,波回来;
如何计算时间:通过计算Echo端口高电平持续时间,波开始发送启动定时器,波回来停止定时器,计算中间的时间;
如何计算距离:距离 = 速度(340m/s)*时间/2

40.从零编程实现超声波测距

(1)定时器模式配置;超声波测距模块触发;低电平跳转高电平触发定时器,高电平跳转低电平停止定时器,距离计算;通过led亮灭判断距离大小;定时器清零;

#include "reg52.h"
#include "intrins.h"
sbit Trig = P1^5;
sbit Echo = P1^6;
sbit led1 = P3^6;
sbit led2 = P3^7;
void Delay10us()		//@11.0592MHz
{
	unsigned char i;

	i = 2;
	while (--i);
}
void Delay200ms()		//@11.0592MHz
{
	unsigned char i, j, k;

	_nop_();
	i = 2;
	j = 103;
	k = 147;
	do
	{
		do
		{
			while (--k);
		} while (--j);
	} while (--i);
}

void Timer0Init(){
	//选择定时器0,设定16位计数
	TMOD &= 0xF0;
	TMOD |= 0x01;
	//设定初始值为0
	TH0 = 0;
	TL0 = 0;
}
void TrigInit(){
	//让硬件稳定一下
	Delay200ms();
	Trig = 0;
	//高电平维持10us
	Trig = 1;
	Delay10us();
	Trig = 0;	
}

void main(){
	double time;
	double dis;
	//定时器初始化
	Timer0Init();
	while(1){
		//超声波模块启动
	  TrigInit();
	  //等待发送超声波
	  while(Echo == 0);
	  //开启定时器
	  TR0 = 1;
	  while(Echo == 1);
	  TR0 = 0;
		//计算时间,TH0的8位要左移2的8次方才可以与TL0相加求和
		time = (TH0*256+TL0)*1.085;
		//计算距离,距离=速度(340m/s)*时间/2=340*100cm/1000000us*时间/2=0.017cm/us*时间
		dis = time*0.017;
		if(dis < 10){
			led1 = 1;
			led2 = 0;
		}else{
			led1 = 0;
			led2 = 1;
		}
		//定时器数据清0,再次测距
		TH0 = 0;
		TL0 = 0;
	}
	
	
	

}

41.感应开关盖垃圾桶需求设计

(1)功能描述:靠近,震动,按键都可使垃圾桶自动开关并伴有蜂鸣声,2s后关盖
(2)硬件模块说明:SG90舵机,超声波模块,震动传感器,蜂鸣器
(3)接线:舵机控制口接P1.1;超声波Trig接P1.5;Echo接P1.6;蜂鸣器接P2.0;震动传感器接P3.2

42.垃圾桶01_修改超声波为定时器控制

(1)修改超声波代码用定时器1(更改成定时器1所需寄存器配置);

43.垃圾桶02_封装超声波测距代码

(1)将超声波测距那部分代码封装函数,返回距离;

44.垃圾桶03_实现距离感应开发盖

(1)超声波代码中整合舵机代码,进行相应封装

45.垃圾桶04_添加按键开盖功能

(1)按键与测距距离小于10ms进行或逻辑开盖

46.垃圾桶05_添加震动开盖功能_使用外部中断优化

(1)测距是一直存在,且一直处在距离大于10,处在等待期间;而震动电平微弱未及时捕获,故采取外部中断捕获震动信号;

47.垃圾桶06_添加开盖滴滴声

(1)开盖蜂鸣器响,低电平触发并延时即可

48.垃圾桶成品

49.垃圾桶的抽抽BUG解决

(1)距离一直小于10cm会不断调用开盖函数cnt=0导致抽抽,可保留先前角度进行判定,角度不变,则不改变cnt(dustbin.c);

#include "reg52.h"
#include "intrins.h"
sbit Trig = P1^5;
sbit Echo = P1^6;
sbit led1 = P3^6;
sbit led2 = P3^7;
sbit vibrate  = P3^2;
sbit sg90_con = P1^1;
sbit sw1 = P2^1;
sbit beep = P2^0;
int jd;
int cnt = 0;
int mark_vibrate = 0;
int jd_bak;

void Delay10us()		//@11.0592MHz
{
	unsigned char i;

	i = 2;
	while (--i);
}
void Delay200ms()		//@11.0592MHz
{
	unsigned char i, j, k;

	_nop_();
	i = 2;
	j = 103;
	k = 147;
	do
	{
		do
		{
			while (--k);
		} while (--j);
	} while (--i);
}
void Delay2000ms()		//@11.0592MHz
{
	unsigned char i, j, k;

	_nop_();
	i = 15;
	j = 2;
	k = 235;
	do
	{
		do
		{
			while (--k);
		} while (--j);
	} while (--i);
}

void Delay150ms()		//@11.0592MHz
{
	unsigned char i, j, k;

	_nop_();
	i = 2;
	j = 13;
	k = 237;
	do
	{
		do
		{
			while (--k);
		} while (--j);
	} while (--i);
}


void Timer1Init(){
	//选择定时器1,设定16位计数
	TMOD &= 0x0F;
	TMOD |= 0x10;
	//设定初始值为0
	TH1 = 0;
	TL1 = 0;
}

void Timer0Init(){

	//选择定时器0工作模式16位计数
	TMOD &= 0xF0;
	TMOD |= 0x01; 
	TL0 = 0x33;		//设置定时初值
	TH0 = 0xFE;		//设置定时初值	
	//开始计数
	TR0 = 1;
	TF0 = 0;
	//打开定时器0中断
	EA = 1;
	ET0 = 1;
}
void TrigInit(){
	//让硬件稳定一下
	Delay200ms();
	Trig = 0;
	//高电平维持10us
	Trig = 1;
	Delay10us();
	Trig = 0;	
}
double get_distance(){
	double time;
	//定时器数据清0,再次测距
	TH1 = 0;
	TL1 = 0;
	//超声波模块启动
	TrigInit();
	//等待发送超声波
	while(Echo == 0);
	//开启定时器
	TR1 = 1;
	while(Echo == 1);
	TR1 = 0;
	//计算时间,TH0的8位要左移2的8次方才可以与TL0相加求和
	time = (TH1*256+TL1)*1.085;
	//计算距离,距离=速度(340m/s)*时间/2=340*100cm/1000000us*时间/2=0.017cm/us*时间
	return (time*0.017);
}
void openStatusLight(){
	led1 = 0;
	led2 = 1;
}
void closeStatusLight(){
	led1 = 1;
	led2 = 0;
}
void initSG90(){
	jd = 1;
	cnt = 0;
	//初始电平为高电平
	sg90_con = 1;
}
void openDusbin(){
	int n;
	//90度,1.5ms的高电平
	jd = 3;
	if(jd_bak != jd){
		cnt = 0;
		//低电平触发
		beep = 0;
		for(n=0;n<2;n++)
		Delay150ms();
		beep = 1;
		Delay2000ms();
	}
	jd_bak = jd;
}
void closeDusbin(){
	//0度
	jd = 1;
	jd_bak = jd;
	cnt = 0;
	Delay150ms();
}
void EX0_Init(){
	//打开外部中断0
	EX0 = 1;
	//低电平触发
	IT0 = 0;
}
void main(){
	
	double dis;
	//定时器初始化
	Timer0Init();
	Timer1Init();
	initSG90();
	EX0_Init();
	while(1){
		dis = get_distance();
		if(dis < 10 || sw1 == 0 || mark_vibrate == 1){
			openStatusLight();
			openDusbin();
			mark_vibrate = 0;
		}else{
			closeStatusLight();
			closeDusbin();
		}

	}
}

void Time0Handler() interrupt 1{
	//统计爆表次数
	cnt ++;
	//重新定时器赋初值
	TL0 = 0x33;
	TH0 = 0xFE;	
	//控制PWM波
	if(cnt < jd){
		sg90_con = 1;
	}else{
		sg90_con = 0;
	}
	//爆表40次,经过20ms
	if(cnt == 40){
		//经过20ms后重新累计,并且输出高电平`
		cnt = 0;
		sg90_con = 1;
	}
}

void EX0_Handler() interrupt 0{
	mark_vibrate = 1;
}


50.初始串口

(1)串行通信接口简称串口,数据一位一位的按顺序发送,可实现双向通信,全双工,但传播速度相对较慢;
(2)RS232串口:一对一收发,最高速率20kb/s,最大传输距离15m;
RS422串口:一对多(从设备可接10个),一对多的双向通信,最高速率10Mb/s,最大传输距离1219m;
RS585串口:一对多(从设备可接32个);
(3)串口电平分为TTL电平的串口,RS232电平串口;
RS232电平:负电平-3到-15v为逻辑1,正电平3到15v为逻辑0
TTL电平:+5v为逻辑1,0v为逻辑0;

51.概念思维整理

52.串口编程的关键要素提点

(1)串口接线方式:TXD数据发送引脚,RXD数据输入引脚,两模块相连采用交叉接线法(模块1的TXD对模块2的RXD,模块1的RXD对模块2的TXD)
(2)输入/输出数据缓冲器:又叫SBUF,输入输出地址码都是99H,但是两个独立的8位寄存器;
接收数据(char data=SBUF),发送数据(SBUF=data);
(3)波特率:由于通信双方使用时钟,及硬件模块不同,为实现串行通信需要约定通信速率;

53.编程实现每秒发送数据给电脑

(1)单片机通过串口给PC发数据,首先要有波特率设置,可通过波特率计算器自动生成;
(2)发送数据:SBUF = data;(serial_communication01.c)

#include "reg52.h"
#include "intrins.h"
sfr AUXR = 0x8e;
void Delay1000ms()		//@11.0592MHz
{
	unsigned char i, j, k;

	_nop_();
	i = 8;
	j = 1;
	k = 243;
	do
	{
		do
		{
			while (--k);
		} while (--j);
	} while (--i);
}
void UartInit(void)		//9600bps@11.0592MHz
{
	PCON &= 0x7F;		//波特率不倍速
	SCON = 0x50;		//8位数据,可变波特率
	AUXR &= 0xBF;		//定时器1时钟为Fosc/12,即12T
	AUXR &= 0xFE;		//串口1选择定时器1为波特率发生器
	TMOD &= 0x0F;		//清除定时器1模式位
	TMOD |= 0x20;		//设定定时器1为8位自动重装方式
	TL1 = 0xFD;		//设定定时初值
	TH1 = 0xFD;		//设定定时器重装值
	ET1 = 0;		//禁止定时器1中断
	TR1 = 1;		//启动定时器1
}

void main(){
	char data_msg ='A';
	//配置串口通信方式
	UartInit();
	while(1){
		Delay1000ms();
		//发送数据到缓冲器中
		SBUF = data_msg;
	}
	
}

54.串口编程寄存器分析

(1)PCON:电源控制寄存器(B7位为SMOD波特率选择位,当SMOD=0时,波特率不加倍)
(2)SCON:串行控制寄存器(B7,B6为SM0,SM1串行口工作方式选择位,01为方式1,8位UART,波特率可变;
B4为REN允许/禁止串行接收控制位,REN=1为运行串行接收)
(3)TMOD:定时器工作方式寄存器(B5,B4为M1,M0定时器工作模式选择位,设10则为定时器1为8位自动重装载定时器)
55.串口编程01_自己实现串口初始化
(1)串口波特率配置:串行工作方式1->(2SMOD/32)*定时器1的溢出率=9600=(2SMOD/32)*SYSclk/12/(256-TH1)
=(1/32)*11059200/12/(256-TH1)
->3=256-TH1
->TH1=253=0xFD

56.串口编程02_发送字符串

(1)串口中\r\n组合才是换行;TI发送中断请求标志位,数据发送完,停止位开始发送时内部硬件置TI=1,需软件复位;利用指针发送字符串;(serial_communication02.c)

#include "reg52.h"
#include "intrins.h"

sfr AUXR = 0x8E;

void UartInit(void)		//9600bps@11.0592MHz
{
	AUXR = 0x01;
	SCON = 0x40; //配置串口工作方式1,REN不使能接收
	TMOD &= 0x0F;
	TMOD |= 0x20;//定时器1工作方式位8位自动重装
	
	TH1 = 0xFD;
	TL1 = 0xFD;//9600波特率的初值
	TR1 = 1;//启动定时器
}

void Delay1000ms()		//@11.0592MHz
{
	unsigned char i, j, k;

	_nop_();
	i = 8;
	j = 1;
	k = 243;
	do
	{
		do
		{
			while (--k);
		} while (--j);
	} while (--i);
}

void sendByte(char data_msg){
	//发送数据到缓冲器
	SBUF = data_msg;
	while(!TI);
	TI = 0;	
}
void sendString(char* str){
	while(*str !='\0'){
		sendByte(*str);
		str++;
	}
}
void main(){
	//配置串口通信方式
	UartInit();
	while(1){
		Delay1000ms();
		sendString("belive youserlf\r\n");
	}

}

57.串口通信编程03_PC发送指令控制LED

(1)SCON 串行控制寄存器中B4位(REN允许/禁止串行接收位)设定为1允许,并通过B0位(RI接收中断请求标志位)判断是否收到数据,收到数据后硬件置1;(serial_communication03.c)

#include "reg52.h"
#include "intrins.h"

sfr AUXR = 0x8E;
sbit D5 = P3^7;

void UartInit(void)		//9600bps@11.0592MHz
{
	AUXR = 0x01;
	SCON = 0x50; //配置串口工作方式1,REN使能接收
	TMOD &= 0x0F;
	TMOD |= 0x20;//定时器1工作方式位8位自动重装
	
	TH1 = 0xFD;
	TL1 = 0xFD;//9600波特率的初值
	TR1 = 1;//启动定时器
}

void Delay1000ms()		//@11.0592MHz
{
	unsigned char i, j, k;

	_nop_();
	i = 8;
	j = 1;
	k = 243;
	do
	{
		do
		{
			while (--k);
		} while (--j);
	} while (--i);
}

void sendByte(char data_msg){
	//发送数据到缓冲器
	SBUF = data_msg;
	while(!TI);
	TI = 0;	
}
void sendString(char* str){
	while(*str !='\0'){
		sendByte(*str);
		str++;
	}
}
void main(){
	char cmd;
	D5 = 1;
	//配置串口通信方式
	UartInit();
	while(1){
		Delay1000ms();
		sendString("belive youserlf\r\n");
		//判断是否收到数据,收到数据硬件会将RI置1
		if(RI == 1){
			RI = 0;
			cmd = SBUF;
			if(cmd == 'o'){
				D5 = 0;//点亮灯
			}
			if(cmd == 'c'){
				D5 = 1;//熄灭灯
			}
		}
	}
	

}

58.串口通信编程04_串口中断实时控制LED

(1)IE中断寄存器中配置B7位(EA总中断允许控制位),B4位(串行口1中断允许控制位);串口中断函数定义void Uart_Handler() interrupt 4;(serial_communication04.c)

#include "reg52.h"
#include "intrins.h"

sfr AUXR = 0x8E;
sbit D5 = P3^7;
char cmd;

void UartInit(void)		//9600bps@11.0592MHz
{
	AUXR = 0x01;
	SCON = 0x50; //配置串口工作方式1,REN使能接收
	TMOD &= 0x0F;
	TMOD |= 0x20;//定时器1工作方式位8位自动重装
	
	TH1 = 0xFD;
	TL1 = 0xFD;//9600波特率的初值
	TR1 = 1;//启动定时器
	EA = 1;//开启总中断
	ES = 1;//开启串口中断
}

void Delay1000ms()		//@11.0592MHz
{
	unsigned char i, j, k;

	_nop_();
	i = 8;
	j = 1;
	k = 243;
	do
	{
		do
		{
			while (--k);
		} while (--j);
	} while (--i);
}

void sendByte(char data_msg){
	//发送数据到缓冲器
	SBUF = data_msg;
	while(!TI);
	TI = 0;	
}
void sendString(char* str){
	while(*str !='\0'){
		sendByte(*str);
		str++;
	}
}
void main(){
	D5 = 1;
	//配置串口通信方式
	UartInit();
	while(1){
		Delay1000ms();
		sendString("belive youserlf\r\n");
	}
}
void Uart_Handler() interrupt 4 {
		//判断是否收到数据,收到数据硬件会将RI置1
		if(RI == 1){
			RI = 0;
			cmd = SBUF;
			if(cmd == 'o'){
				D5 = 0;//点亮灯
			}
			if(cmd == 'c'){
				D5 = 1;//熄灭灯
			}
		}
		//发送数据引起的中断不做任何处理
		if(TI);
}

59.小白玩串口控制的ASSII避坑

(1)串口中文本模式跟hex模式要注意,若选择“文本模式”发送数字1,为ASCII码,在c代码中应该为数字49;

60.串口支持单词型指令控制

(1)定义数组不断存接收到的字母,接受完后通过strstr函数判断,判断完后数组重新初始化’\0’;(serial_communication05.c)

#include "reg52.h"
#include "intrins.h"
#include "string.h"
#define SIZE 12
sfr AUXR = 0x8E;
sbit D5 = P3^7;
char cmd[SIZE];

void UartInit(void)		//9600bps@11.0592MHz
{
	AUXR = 0x01;
	SCON = 0x50; //配置串口工作方式1,REN使能接收
	TMOD &= 0x0F;
	TMOD |= 0x20;//定时器1工作方式位8位自动重装
	
	TH1 = 0xFD;
	TL1 = 0xFD;//9600波特率的初值
	TR1 = 1;//启动定时器
	EA = 1;//开启总中断
	ES = 1;//开启串口中断
}

void Delay1000ms()		//@11.0592MHz
{
	unsigned char i, j, k;

	_nop_();
	i = 8;
	j = 1;
	k = 243;
	do
	{
		do
		{
			while (--k);
		} while (--j);
	} while (--i);
}

void sendByte(char data_msg){
	//发送数据到缓冲器
	SBUF = data_msg;
	while(!TI);
	TI = 0;	
}
void sendString(char* str){
	while(*str !='\0'){
		sendByte(*str);
		str++;
	}
}
void main(){
	D5 = 1;
	//配置串口通信方式
	UartInit();
	while(1){
		Delay1000ms();
		sendString("belive youserlf\r\n");
	}
}
void Uart_Handler() interrupt 4 {
		static int i = 0; //静态变量,被初始化一次
		//判断是否收到数据,收到数据硬件会将RI置1
		if(RI == 1){
			RI = 0;
			cmd[i] = SBUF;
			i++;
			if(i == SIZE){
				i = 0;
			}
			if(strstr(cmd,"en")){
				D5 = 0;//点亮灯
				i = 0;
				memset(cmd,'\0',SIZE);
			}
			if(strstr(cmd,"se")){
				D5 = 1;//熄灭灯
				i = 0;
				memset(cmd,'\0',SIZE);
			}
		}
		//发送数据引起的中断不做任何处理
		if(TI);
}

61.串口原理协议概念收尾

(1)串口工作模式1为例:8位UART,波特率可变;一帧信息为10位,1位起始位,8位数据位(低位在先)和1位停止位;常用串口工作模式1跟3可调整波特率;

62.通过蓝牙控制LED

(1)蓝牙模块(HC-08)与单片机模块连接,下载HC蓝牙助手,连接蓝牙模块后可在蓝牙助手中收到单片机发送过来的数据,并可通过蓝牙助手发送指令控制单片机;

63.蓝牙概述

(1)蓝牙串口模块,通过无线传输的方式将数据原封不动的发送给接收者,称为串口透传;

64.AT指令修改蓝牙名字

(1)CH340模块,将TTL转USB;
(2)通过AT指令进行蓝牙设置,例修改蓝牙名称AT+NAME=XXX;(蓝牙模块连接CH340模块再连接电脑)

65.wifi模块课程目标概述

(1)AT指令集是从终端设备或数据终端设备向终端适配器或数据电路中断设备发送的。

66.wifi模块的AT指令联网数据交互

(1)打开串口调试助手
->配置波特率(AT+UART=9600,8,1,0,0)
->设置工作模式(AT+CWMODE=3 //1. 是station(设备)模式 2.是AP(路由)模式 3.是双模)
->设备连接家里路由器(AT+CWJAP=“wifi名”,“wifi密码” )
->查询IP地址(AT+CIFSR)
->打开网络调试助手,建立TCP服务器
—>串口调试助手中连接服务器(AT+CIPSTART=“TCP”,“电脑ip地址”,8880)
->串口调试助手中发送数据:a.AT指令设定长度发送(AT+CIPSEND=4 // 设置即将发送数据的长度,这里是4个字节),
b.透传(AT+CIPMODE=1 //开启透传模式,AT+CIPSEND //带回车,+++退出透传模式,不勾发送新行)-(电脑连接ESP8266模块测试)

67.单片机发送AT指令实现联网1

(1)某51单片机内部有三个存储器:内部RAM(IRAM),外部RAM(XRAM),以及程序存储器(IROM)。
内部RAM由256个字节组成,具有较高的存取性能(支持直接寻址和间接寻址)。
相比之下,外部RAM有1KB大小,但需要更长的存取周期。
程序存储器是一个16KB的FLASH存储器,最快的存取速度可达8MHz。
—》"code关键字"可将数据存在程序存储器Flash中,以节省单片机的RAM的使用量。
(2)单片机通过串口发送数据给wifi模块:将连接wifi,连接服务器,开启透传模式,开始传输数据都定义成用字符串发送;(wifi_module01.c)

#include "reg52.h"
#include "intrins.h"
#include <string.h>
#define SIZE 12
sfr AUXR = 0x8E;
sbit D5 = P3^7;
char cmd[SIZE];

code char LJWL[] = "AT+CWJAP=\"wifi账号\",\"wifi密码\"\r\n";
code char LJFWQ[] = "AT+CIPSTART=\"TCP\",\"192.168.2.161\",8880\r\n";
char TCMS[] = "AT+CIPMODE=1\r\n";
char SJCS[]= "AT+CIPSEND\r\n";

void UartInit(void)		//9600bps@11.0592MHz
{
	AUXR = 0x01;
	SCON = 0x50; //配置串口工作方式1,REN使能接收
	TMOD &= 0x0F;
	TMOD |= 0x20;//定时器1工作方式位8位自动重装
	
	TH1 = 0xFD;
	TL1 = 0xFD;//9600波特率的初值
	TR1 = 1;//启动定时器
	EA = 1;//开启总中断
	ES = 1;//开启串口中断
}

void Delay1000ms()		//@11.0592MHz
{
	unsigned char i, j, k;

	_nop_();
	i = 8;
	j = 1;
	k = 243;
	do
	{
		do
		{
			while (--k);
		} while (--j);
	} while (--i);
}

void sendByte(char data_msg){
	//发送数据到缓冲器
	SBUF = data_msg;
	while(!TI);
	TI = 0;	
}
void sendString(char* str){
	while(*str !='\0'){
		sendByte(*str); 
		str++;
	}
}
void main(){
	D5 = 1;
	//配置串口通信方式
	UartInit();
	while(1){
		//Delay1000ms();
		//sendString("belive youserlf\r\n");
		sendString(LJWL);
		Delay1000ms();
		Delay1000ms();
		Delay1000ms();
		Delay1000ms();
		Delay1000ms();
		sendString(LJFWQ);
		Delay1000ms();
		Delay1000ms();
		Delay1000ms();
		Delay1000ms();
		Delay1000ms();
		sendString(TCMS);
		Delay1000ms();
		Delay1000ms();
		Delay1000ms();
		Delay1000ms();
		Delay1000ms();
		sendString(SJCS);
		Delay1000ms();
		Delay1000ms();
		Delay1000ms();
		Delay1000ms();
		Delay1000ms();
	}
}
void Uart_Handler() interrupt 4 {
		static int i = 0;//静态变量,被初始化一次
		//判断是否收到数据,收到数据硬件会将RI置1
		if(RI == 1){
			RI = 0;
			cmd[i] = SBUF;
			i++;
			if(i == SIZE){
				i = 0;
			}
			if(strstr(cmd,"en")){
				D5 = 0;//点亮灯
				i = 0;
				memset(cmd,'\0',SIZE);
			}
			if(strstr(cmd,"se")){
				D5 = 1;//熄灭灯
				i = 0;
				memset(cmd,'\0',SIZE);
			}
		}
		//发送数据引起的中断不做任何处理
		if(TI);
}

68.通过网络TCP通信控制LED

(1)主函数单片机发送指令连接网络,服务器,开启透传等;中断函数中单片机接收到服务器中指令来判断开关led灯;(wifi_module02.c)

#include "reg52.h"
#include "intrins.h"
#include <string.h>
#define SIZE 12
sfr AUXR = 0x8E;
sbit D5 = P3^7;
char cmd[SIZE];

code char LJWL[] = "AT+CWJAP=\"wifi账号\",\"wifi密码\"\r\n";
code char LJFWQ[] = "AT+CIPSTART=\"TCP\",\"192.168.2.161\",8880\r\n";
char TCMS[] = "AT+CIPMODE=1\r\n";
char SJCS[]= "AT+CIPSEND\r\n";

void UartInit(void)		//9600bps@11.0592MHz
{
	AUXR = 0x01;
	SCON = 0x50; //配置串口工作方式1,REN使能接收
	TMOD &= 0x0F;
	TMOD |= 0x20;//定时器1工作方式位8位自动重装
	
	TH1 = 0xFD;
	TL1 = 0xFD;//9600波特率的初值
	TR1 = 1;//启动定时器
	EA = 1;//开启总中断
	ES = 1;//开启串口中断
}

void Delay1000ms()		//@11.0592MHz
{
	unsigned char i, j, k;

	_nop_();
	i = 8;
	j = 1;
	k = 243;
	do
	{
		do
		{
			while (--k);
		} while (--j);
	} while (--i);
}

void sendByte(char data_msg){
	//发送数据到缓冲器
	SBUF = data_msg;
	while(!TI);
	TI = 0;	
}
void sendString(char* str){
	while(*str !='\0'){
		sendByte(*str);
		str++;
	}
}
void main(){
	int mark = 0;
	D5 = 1;
	//配置串口通信方式
	UartInit();
	while(1){
		//Delay1000ms();
		//sendString("belive youserlf\r\n");
		if(mark == 0){
			sendString(LJWL);
			Delay1000ms();
			Delay1000ms();
			Delay1000ms();
			Delay1000ms();
			Delay1000ms();
			sendString(LJFWQ);
			Delay1000ms();
			Delay1000ms();
			Delay1000ms();
			Delay1000ms();
			Delay1000ms();
			sendString(TCMS);
			Delay1000ms();
			Delay1000ms();
			Delay1000ms();
			Delay1000ms();
			Delay1000ms();
			sendString(SJCS);
			Delay1000ms();
			Delay1000ms();
			Delay1000ms();
			Delay1000ms();
			Delay1000ms();
			mark = 1;
		}else{
			sendString("belive youserlf\r\n");
			Delay1000ms();
		}		
	}
}
void Uart_Handler() interrupt 4 {
		//判断是否收到数据,收到数据硬件会将RI置1
		if(RI){
			RI = 0;//清楚接收中断标志位
			cmd[0] = SBUF;
			if(cmd[0] == '1'){
				D5 = 0;//点亮D5
			}
			if(cmd[0] == '0'){
				D5 = 1;//熄灭D5
			}
		}
		//发送数据引起的中断不做任何处理
		if(TI);
}

69.白盒方式看到连接不上原因,调试手段

(1)单片机跟wifi模块ESP8266连接,单片机TX接ESP8266的RX,ESP8266的TX接电脑(通过usb转ttl模块连接);
打开串口助手跟网络调试助手测试;(模块连线后测试)

70.优化8266使用,检测AT执行结果

(1)根据连接网络成功会返回WIFI GOT IP及OK字样;
连接服务器,开启透传,数据开始传输成功会返回OK字样作为判断是否连接成功;
(2)标志位定义后,主函数根据标志位作为等待条件;网络连接成功有D5灯亮,
服务器连接成功并开启透传进行数据传输有D6灯亮;
(3)中断函数中定义数组多次存储返回单词,根据不同的返回值进行给标志为置位;
同时服务器可发送L-1,L-0进行灯光亮灭控制;(wifi_module03.c)

#include "reg52.h"
#include "intrins.h"
#include <string.h>
#define SIZE 12
sfr AUXR = 0x8E;
sbit D5 = P3^7;
sbit D6 = P3^6;

char buffer[SIZE];
code char LJWL[] = "AT+CWJAP=\"wifi账号\",\"wifi密码\"\r\n";				//连接网络
code char LJFWQ[] = "AT+CIPSTART=\"TCP\",\"192.168.2.161\",8880\r\n";//连接服务器
char TCMS[] = "AT+CIPMODE=1\r\n";//开启透传
char SJCS[] = "AT+CIPSEND\r\n";			//数据传输开始
char RESET[] = "AT+REST\r\n";		//重启模块
char AT_OK_Flag = 0;						//OK返回值的标志位
char AT_Connect_Net_Flag = 0;		//WIFI GOT IP返回值的标志位

void UartInit(void)		//9600bps@11.0592MHz
{
	AUXR = 0x01;
	SCON = 0x50; //配置串口工作方式1,REN使能接收        
	TMOD &= 0x0F;
	TMOD |= 0x20;//定时器1工作方式位8位自动重装
	
	TH1 = 0xFD;
	TL1 = 0xFD;//9600波特率的初值
	TR1 = 1;//启动定时器
	EA = 1;//开启总中断
	ES = 1;//开启串口中断
}

void Delay1000ms()		//@11.0592MHz
{
	unsigned char i, j, k;

	_nop_();
	i = 8;
	j = 1;
	k = 243;
	do
	{
		do
		{
			while (--k);
		} while (--j);
	} while (--i);
}

void sendByte(char data_msg){
	//发送数据到缓冲器
	SBUF = data_msg;
	while(!TI);
	TI = 0;	
}
void sendString(char* str){
	while(*str !='\0'){
		sendByte(*str);
		str++;
	}
}
void main(){
	D5 = D6 = 1;				//灭灯
	UartInit();					//配置串口通信方式
	Delay1000ms();			//espwifi模块上电等待时间
	sendString(LJWL);		//发送联网AT指令
	while(!AT_OK_Flag);	//等待返回OK
	AT_OK_Flag = 0;
	sendString(LJFWQ);	//发送连接服务器指令
	while(!AT_OK_Flag);	//等待返回OK
	AT_OK_Flag = 0;
	sendString(TCMS);		//发送透传模式指令
	while(!AT_OK_Flag);	//等待返回OK
	AT_OK_Flag = 0;
	sendString(SJCS);		//发送数据传输指令
	while(!AT_OK_Flag);	//等待返回OK
	if(AT_Connect_Net_Flag){
		D5 = 0;					 	//网络连接成功,点亮D5
	}
	if(AT_OK_Flag){ 
		D6 = 0;						//连接好服务器并打开透传模式开始发送数据,点亮D6
	}	
	while(1){
		Delay1000ms();
		sendString("belive youserlf\r\n");//发送心跳包
	}		
}
void Uart_Handler() interrupt 4 {
	static int i = 0;//静态变量,背初始化一次
	char tmp;
	//判断是否收到数据,收到数据硬件会将RI置1
	if(RI){
		RI = 0;//清楚接收中断标志位
		tmp = SBUF;
		if(tmp == 'W' || tmp =='O' || tmp =='L' || tmp =='F'){
			i = 0;
		}
		buffer[i++] = tmp;
		//入网成功判断依据WIFI GOT IP
		if(buffer[0]=='W' && buffer[5] == 'G'){
			AT_Connect_Net_Flag = 1;
			memset(buffer,'\0',SIZE);
		}
		//返回OK判断
		if(buffer[0] == 'O' && buffer[1] =='K'){
			AT_OK_Flag = 1;
			memset(buffer,'\0',SIZE);
		}
		//联网失败FAIL字样捕获
		if(buffer[0] == 'F' && buffer[1] =='A'){
			for(i=0;i<5;i++){
				D5 = 0;
				Delay1000ms();
				D5 = 1;
				Delay1000ms();
			}
			sendString(RESET);
			memset(buffer,'\0',SIZE);
		}
		//开灯指令L-1
		if(buffer[0] == 'L' && buffer[2] == '1'){
			D5 = 0;//点亮D5
			memset(buffer,'\0',SIZE);
		}
		//关灯指令L-0
		if(buffer[0] == 'L' && buffer[2] =='0'){
			D5 = 1;//熄灭D5
			memset(buffer,'\0',SIZE);
		}
		if(i == 12) i=0;
	}
}

71.优化8266,捕获联网失败状态

(1)中断函数中捕获FAIL字样,失败了D5灯闪5次,后发送重启wifi模块指令;(wifi_module03.c)

#include "reg52.h"
#include "intrins.h"
#include <string.h>
#define SIZE 12
sfr AUXR = 0x8E;
sbit D5 = P3^7;
sbit D6 = P3^6;

char buffer[SIZE];
code char LJWL[] = "AT+CWJAP=\"wifi账号\",\"wifi密码\"\r\n";				//连接网络
code char LJFWQ[] = "AT+CIPSTART=\"TCP\",\"192.168.2.161\",8880\r\n";//连接服务器
char TCMS[] = "AT+CIPMODE=1\r\n";//开启透传
char SJCS[] = "AT+CIPSEND\r\n";			//数据传输开始
char RESET[] = "AT+REST\r\n";		//重启模块
char AT_OK_Flag = 0;						//OK返回值的标志位
char AT_Connect_Net_Flag = 0;		//WIFI GOT IP返回值的标志位

void UartInit(void)		//9600bps@11.0592MHz
{
	AUXR = 0x01;
	SCON = 0x50; //配置串口工作方式1,REN使能接收        
	TMOD &= 0x0F;
	TMOD |= 0x20;//定时器1工作方式位8位自动重装
	
	TH1 = 0xFD;
	TL1 = 0xFD;//9600波特率的初值
	TR1 = 1;//启动定时器
	EA = 1;//开启总中断
	ES = 1;//开启串口中断
}

void Delay1000ms()		//@11.0592MHz
{
	unsigned char i, j, k;

	_nop_();
	i = 8;
	j = 1;
	k = 243;
	do
	{
		do
		{
			while (--k);
		} while (--j);
	} while (--i);
}

void sendByte(char data_msg){
	//发送数据到缓冲器
	SBUF = data_msg;
	while(!TI);
	TI = 0;	
}
void sendString(char* str){
	while(*str !='\0'){
		sendByte(*str);
		str++;
	}
}
void main(){
	D5 = D6 = 1;				//灭灯
	UartInit();					//配置串口通信方式
	Delay1000ms();			//espwifi模块上电等待时间
	sendString(LJWL);		//发送联网AT指令
	while(!AT_OK_Flag);	//等待返回OK
	AT_OK_Flag = 0;
	sendString(LJFWQ);	//发送连接服务器指令
	while(!AT_OK_Flag);	//等待返回OK
	AT_OK_Flag = 0;
	sendString(TCMS);		//发送透传模式指令
	while(!AT_OK_Flag);	//等待返回OK
	AT_OK_Flag = 0;
	sendString(SJCS);		//发送数据传输指令
	while(!AT_OK_Flag);	//等待返回OK
	if(AT_Connect_Net_Flag){
		D5 = 0;					 	//网络连接成功,点亮D5
	}
	if(AT_OK_Flag){ 
		D6 = 0;						//连接好服务器并打开透传模式开始发送数据,点亮D6
	}	
	while(1){
		Delay1000ms();
		sendString("belive youserlf\r\n");//发送心跳包
	}		
}
void Uart_Handler() interrupt 4 {
	static int i = 0;//静态变量,背初始化一次
	char tmp;
	//判断是否收到数据,收到数据硬件会将RI置1
	if(RI){
		RI = 0;//清楚接收中断标志位
		tmp = SBUF;
		if(tmp == 'W' || tmp =='O' || tmp =='L' || tmp =='F'){
			i = 0;
		}
		buffer[i++] = tmp;
		//入网成功判断依据WIFI GOT IP
		if(buffer[0]=='W' && buffer[5] == 'G'){
			AT_Connect_Net_Flag = 1;
			memset(buffer,'\0',SIZE);
		}
		//返回OK判断
		if(buffer[0] == 'O' && buffer[1] =='K'){
			AT_OK_Flag = 1;
			memset(buffer,'\0',SIZE);
		}
		//联网失败FAIL字样捕获
		if(buffer[0] == 'F' && buffer[1] =='A'){
			for(i=0;i<5;i++){
				D5 = 0;
				Delay1000ms();
				D5 = 1;
				Delay1000ms();
			}
			sendString(RESET);
			memset(buffer,'\0',SIZE);
		}
		//开灯指令L-1
		if(buffer[0] == 'L' && buffer[2] == '1'){
			D5 = 0;//点亮D5
			memset(buffer,'\0',SIZE);
		}
		//关灯指令L-0
		if(buffer[0] == 'L' && buffer[2] =='0'){
			D5 = 1;//熄灭D5
			memset(buffer,'\0',SIZE);
		}
		if(i == 12) i=0;
	}
}

72.ESP工作为AP路由模式并当成服务器

(1)ESP8266连接电脑(通过usb转ttl模块插入)
(2)打开串口调试助手,打开网络调试助手(选择客户端模式)
-》发送AT指令{AT+CWMODE=2(配置成路由)
AT+CIPMUX=1(使能多链接)
AT+CIPSERVER=1(建立TCPServer)
AT+CIPSEND=0,4(发送数据,发送4个字节在连接0通道上)
AT+CIPCLOSE=0(断开连接)
} (连接电脑后测试,电脑连接远程主机端口号333)

73.上官一号当路由和服务器模式控制

(1)中断函数中通过接收到的单词来改变标签位,
主函数根据标签位来发送路由模式指令,多连接指令,建立TCPServer指令;
再进行发送数据(采用AT指令选择通道并设定长度发送)(wifi_module04.c)

#include "reg52.h"
#include "intrins.h"
#include <string.h>
#define SIZE 12
sfr AUXR = 0x8E;
sbit D5 = P3^7;
sbit D6 = P3^6;

char buffer[SIZE];
//1 配置成路由
char LYMO[] = "AT+CWMODE=2\r\n";
//2 使能多链接
char DLJ[] = "AT+CIPMUX=1\r\n";
//3 建立TCPServer
char JLFW[] = "AT+CIPSERVER=1\r\n";
//4 发送数据
char FSSJ[] = "AT+CIPSEND=0,5\r\n";
char AT_OK_Flag = 0;						//OK返回值的标志位
char AT_Connect_Net_Flag = 0;		//WIFI GOT IP返回值的标志位
char Client_Connect_Flag = 0;
void UartInit(void)		//9600bps@11.0592MHz
{
	AUXR = 0x01;
	SCON = 0x50; //配置串口工作方式1,REN使能接收
	TMOD &= 0x0F;
	TMOD |= 0x20;//定时器1工作方式位8位自动重装
	
	TH1 = 0xFD;
	TL1 = 0xFD;//9600波特率的初值
	TR1 = 1;//启动定时器
	EA = 1;//开启总中断
	ES = 1;//开启串口中断
}

void Delay1000ms()		//@11.0592MHz
{
	unsigned char i, j, k;

	_nop_();
	i = 8;
	j = 1;
	k = 243;
	do
	{
		do
		{
			while (--k);
		} while (--j);
	} while (--i);
}

void sendByte(char data_msg){
	//发送数据到缓冲器
	SBUF = data_msg;
	while(!TI);
	TI = 0;	
}
void sendString(char* str){
	while(*str !='\0'){
		sendByte(*str);
		str++;
	}
}
void main(){
	D5 = D6 = 1;				//灭灯
	UartInit();					//配置串口通信方式
	Delay1000ms();			//espwifi模块上电等待时间
	sendString(LYMO);		//发送选择路由模式指令
	while(!AT_OK_Flag);	//等待返回OK
	AT_OK_Flag = 0;
	sendString(DLJ);		//发送建立多连接指令
	while(!AT_OK_Flag);	//等待返回OK
	AT_OK_Flag = 0;
	sendString(JLFW);		//发送建立TCPServer指令
	while(!Client_Connect_Flag);	//等待返回OK
	AT_OK_Flag = 0;
	if(Client_Connect_Flag){
		D5 = 0;
		D6 = 0;
	}
	
	while(1){
		sendString(FSSJ);//发送数据
		Delay1000ms();
		Delay1000ms();
		sendString("Hello");
		Delay1000ms();
		Delay1000ms();
	}		
}
void Uart_Handler() interrupt 4 {
	static int i = 0;//静态变量,背初始化一次
	char tmp;
	//判断是否收到数据,收到数据硬件会将RI置1
	if(RI){
		RI = 0;//清楚接收中断标志位
		tmp = SBUF;
		if(tmp == 'W' || tmp =='O' || tmp =='L' || tmp =='0' || tmp==':'){
			i = 0;
		}
		buffer[i++] = tmp;
		//入网成功判断依据WIFI GOT IP
		if(buffer[0]=='W' && buffer[5] == 'G'){
			AT_Connect_Net_Flag = 1;
			memset(buffer,'\0',SIZE);
		}
		//返回OK判断
		if(buffer[0] == 'O' && buffer[1] =='K'){
			AT_OK_Flag = 1;
			memset(buffer,'\0',SIZE);
		}
		//客户端连接成功判断
		if(buffer[0] == '0' && buffer[2] == 'C'){
			Client_Connect_Flag = 1;
			memset(buffer,'\0',SIZE);
		}
		//开灯指令:open
		if(buffer[0] == ':' && buffer[1] == 'o' && buffer[2] == 'p'){
			D5 = 0;//点亮D5
			memset(buffer,'\0',SIZE);
		}
		//关灯指令:close
		if(buffer[0] == ':' && buffer[1] =='c' && buffer[2] == 'l'){
			D5 = 1;//熄灭D5
			memset(buffer,'\0',SIZE);
		}
		if(i == 12){
			i = 0;	
		}
	//发送数据引起的中断不做任何处理
		if(TI);
	}
}

74.4G模块认识

(1)EC03-DNC4G模块连接电脑(通过ttl转usb模块连),用户手册下载及熟悉

75.4G模块AT指令操作

(1)上电后模块默认工作在透传模式,并且波特率为115200,在串口助手中输入+++,
3秒内输入任意AT指令即可进入AT指令模式(AT+CSQ 查询信号强度),
设置波特率9600(AT+UART=9600,NONE),重启模块(AT+REBT)(4G模块连接电脑测试)

76.内网穿透建立一个外网可访问的服务器

(1)内网穿透:为局域网设备提供一个外网可访问的地址和端口号
(2)4g模块无法连接局域网的解决方案:通过花生壳软件将局域网的ip地址和端口号映射成一个外网能访问的ip地址和端口号

77.配置4G模块连接服务器并进行数据交互

(1)将电源、天线、SIM 卡、串口线等硬件连接好后,将4G模块连接到电脑,串口助手内发送+++(不要勾选发送新行),必须在发送+++指令3s内发送其他任何AT指令进入AT模式;
(2)串口出产默认波特率是115200,更改为9600;(AT+UART=9600,NONE)
(3)观察SIM卡灯是否亮起,AT+ICCID获得SIM卡信息,确认SIM卡安装完好返回数据例如:+OK=89860087141431250710;
检查信号是否正常,通过AT+CSQ指令检查信号值,建议插入信号天线,返回数据例如:+OK=31
(4)连接socket服务器AT+SOCK=TCPC,115.236.153.174,11590,通过花生壳软件获取公网IP地址(115.236.153.174),端口号(11590);
(5)AT+LINKSTA查看连接状态,如果第四步没有问题,此时串口返回+OK=Connect
注:AT+REBT重启后,模块自动进入透传模式,直接可以和服务器进行通信

78.新版4g模块讲解

79.上官一号通过4g模块控制LED

(1)4g模块连接单片机,单片机连接上电脑;
(2)4g模块配置完,已连接公网,后采用串口模块控制灯亮灭相同代码,网络调试助手中输入:open即可开灯,:close即可关灯;(4g_control_led.c)

#include "reg52.h"
#include "intrins.h"
#include <string.h>
#define SIZE 12
sfr AUXR = 0x8E;
sbit D5 = P3^7;
char cmd[SIZE];

void UartInit(void)		//9600bps@11.0592MHz
{
	AUXR = 0x01;
	SCON = 0x50; //配置串口工作方式1,REN不使能接收
	TMOD &= 0x0F;
	TMOD |= 0x20;//定时器1工作方式位8位自动重装
	
	TH1 = 0xFD;
	TL1 = 0xFD;//9600波特率的初值
	TR1 = 1;//启动定时器
	EA = 1;//开启总中断
	ES = 1;//开启串口中断
}

void Delay1000ms()		//@11.0592MHz
{
	unsigned char i, j, k;

	_nop_();
	i = 8;
	j = 1;
	k = 243;
	do
	{
		do
		{
			while (--k);
		} while (--j);
	} while (--i);
}

void sendByte(char data_msg){
	//发送数据到缓冲器
	SBUF = data_msg;
	while(!TI);
	TI = 0;	
}
void sendString(char* str){
	while(*str !='\0'){
		sendByte(*str);
		str++;
	}
}
void main(){
	D5 = 1;
	//配置串口通信方式
	UartInit();
	while(1){
		Delay1000ms();
		//sendString("belive youserlf\r\n");
	}
}
void Uart_Handler() interrupt 4 {
		static int i = 0; //静态变量,只被初始化一次
		char tmp;
		//判断是否收到数据,收到数据硬件会将RI置1
		if(RI == 1){
			RI = 0;
			tmp = SBUF;
			if(tmp == ':'){
				i = 0;
			}
			cmd[i] = tmp;
			i++;
			if(i == SIZE){
				i = 0;
			}
			if(cmd[0] == ':' && cmd[1] == 'o' && cmd[2] == 'p'){
				D5 = 0;//点亮灯
				i = 0;
				memset(cmd,'\0',SIZE);
			}
			if(cmd[0] == ':' && cmd[1] =='c' && cmd[2] == 'l'){
				D5 = 1;//熄灭灯
				i = 0;
				memset(cmd,'\0',SIZE);
			}
		}
		//发送数据引起的中断不做任何处理
		if(TI);
}

80.初识LCD1602

(1)LCD(Liquid Crystal Display)是一种工业字符液晶,显示2行16列共32个字符;
(2)LCD1602引脚说明,共16个引脚;

81.LCD在一个位置显示一个字母逻辑

(1)地址:例如第二行第一位40H,但显示地址要求最高位D7为高电平,故实际写入的地址为01000000B(40H) +10000000B(80H)=11000000B(C0H);
(2)数据:例如小写字母a(01100001);

82.LCD1602时序分析

(1)RS为低电平写指令/地址,RS为高电平写内容;
(2)写操作R/W的话,起始结束的话,高低电平都可以,中间要为低电平;
(3)写操作E的话,需要低电平,高电平,再低电平;
(4)写操作D7-D0,根据数据二进制进行高低电平变化;

83-84.实现LCD1602显示字符B

(1)根据写操作时序图分别编写写指令函数,写数据函数;
(2)根据说明书要求编写lcd1602初始化函数;
(3)主函数中定义位置,及数据,再调用函数写入即可;
(4)根据读操作时序编写读忙信号函数;(lcd1602_01.c)

#include "reg52.h"
#include "intrins.h"

#define databuffer P0 //定义8位数据线,P0端口组
/*
RS  -- P1.0
RW  -- P1.1 
E   -- P1.4 */
sbit RS = P1^0;
sbit RW = P1^1;
sbit EN = P1^4;
//读忙信号函数
void chek_busy(){
	char tmp = 0x80;
	databuffer = 0x80;
	while(tmp & 0x80){//BF:为忙标志位,高电平表示忙,此时模块不能接收命令或者数据,如果为低电平表示不忙。
	RS = 0;
	RW = 1;
	EN = 0;
	_nop_();
	EN = 1;
	_nop_();
	_nop_();
	tmp = databuffer;
	EN = 0;
	_nop_();
	}
}
//写指令函数
void Write_Cmd_Func(char cmd){
	chek_busy();
	RS = 0;
	RW = 0;
	EN = 0;
	_nop_();
	databuffer = cmd;
	_nop_();
	EN = 1;
	_nop_();
	_nop_();
	EN = 0;
	_nop_();
}
//写数据函数
void Write_Data_Func(char dataShow){
	chek_busy();
	RS = 1;
	RW = 0;
	EN = 0;
	_nop_();
	databuffer = dataShow;
	_nop_();
	EN = 1;
	_nop_();
	_nop_();
	EN = 0;
	_nop_();
}
void Delay15ms()		//@11.0592MHz
{
	unsigned char i, j;

	i = 27;
	j = 226;
	do
	{
		while (--j);
	} while (--i);
}
void Delay5ms()		//@11.0592MHz
{
	unsigned char i, j;

	i = 9;
	j = 244;
	do
	{
		while (--j);
	} while (--i);
}

void LCD1602_INIT(){
//(1)延时 15ms 
	Delay15ms();
//(2)写指令 38H(不检测忙信号) 
	Write_Cmd_Func(0x38);
//(3)延时 5ms 
	Delay5ms();
//(4)以后每次写指令,读/写数据操作均需要检测忙信号 
//(5)写指令 38H:显示模式设置 
	Write_Cmd_Func(0x38);
//(6)写指令 08H:显示关闭 
	Write_Cmd_Func(0x08);
//(7)写指令 01H:显示清屏 
	Write_Cmd_Func(0x01);
//(8)写指令 06H:显示光标移动设置 
	Write_Cmd_Func(0x06);
//(9)写指令 0CH:显示开及光标设置 
	Write_Cmd_Func(0x0c);
}
void main(){
	char position = 0x80 + 0x08;
	char dataShow = 'B';
	LCD1602_INIT();
	Write_Cmd_Func(position);//选择显示的地址
	Write_Data_Func(dataShow);//写入显示的字符
}

85.编程实现LCD1602显示一行

(1)显示一个字符字符的基础上新增显示行的函数(包含行,列,字符串形参;主函数通过switch判断显示哪一行)(lcd1602_02.c)

#include "reg52.h"
#include "intrins.h"

#define databuffer P0 //定义8位数据线,P0端口组
/*
RS  -- P1.0
RW  -- P1.1 
E   -- P1.4 */
sbit RS = P1^0;
sbit RW = P1^1;
sbit EN = P1^4;
//读忙信号函数
void chek_busy(){
	char tmp = 0x80;
	databuffer = 0x80;
	while(tmp & 0x80){//BF:为忙标志位,高电平表示忙,此时模块不能接收命令或者数据,如果为低电平表示不忙。
	RS = 0;
	RW = 1;
	EN = 0;
	_nop_();
	EN = 1;
	_nop_();
	_nop_();
	tmp = databuffer;
	EN = 0;
	_nop_();
	}
}
//写指令函数
void Write_Cmd_Func(char cmd){
	chek_busy();
	RS = 0;
	RW = 0;
	EN = 0;
	_nop_();
	databuffer = cmd;
	_nop_();
	EN = 1;
	_nop_();
	_nop_();
	EN = 0;
	_nop_();
}
//写数据函数
void Write_Data_Func(char dataShow){
	chek_busy();
	RS = 1;
	RW = 0;
	EN = 0;
	_nop_();
	databuffer = dataShow;
	_nop_();
	EN = 1;
	_nop_();
	_nop_();
	EN = 0;
	_nop_();
}
void Delay15ms()		//@11.0592MHz
{
	unsigned char i, j;

	i = 27;
	j = 226;
	do
	{
		while (--j);
	} while (--i);
}
void Delay5ms()		//@11.0592MHz
{
	unsigned char i, j;

	i = 9;
	j = 244;
	do
	{
		while (--j);
	} while (--i);
}

void LCD1602_INIT(){
//(1)延时 15ms 
	Delay15ms();
//(2)写指令 38H(不检测忙信号) 
	Write_Cmd_Func(0x38);
//(3)延时 5ms 
	Delay5ms();
//(4)以后每次写指令,读/写数据操作均需要检测忙信号 
//(5)写指令 38H:显示模式设置 
	Write_Cmd_Func(0x38);
//(6)写指令 08H:显示关闭 
	Write_Cmd_Func(0x08);
//(7)写指令 01H:显示清屏 
	Write_Cmd_Func(0x01);
//(8)写指令 06H:显示光标移动设置 
	Write_Cmd_Func(0x06);
//(9)写指令 0CH:显示开及光标设置 
	Write_Cmd_Func(0x0c);
}
void LCD1602_showLine(char row,char col,char *string){
	switch(row){
		case 1:
				Write_Cmd_Func(0x80 + col);
				while(*string){
					Write_Data_Func(*string);
					string++;
				}
				break;
		case 2:
				Write_Cmd_Func(0x80 + 0x40 + col);
				while(*string){
					Write_Data_Func(*string);
					string++;
				}
				break;
	}
}
void main(){
	char position = 0x80 + 0x08;
	char dataShow = 'B';
	LCD1602_INIT();
	LCD1602_showLine(1,0,"qiuz");
	LCD1602_showLine(2,0,"believe yourself");
}

86.DHT11温湿度传感器初始

(1)检测湿度和温度,信号传输距离20m;
(2)单片机发信号触发DH11模块,模块返回40bit,高位先出;
(3)数据格式:8bit湿度整数数据+8bit湿度小数数据+8bit温度整数数据+8bit温度小数数据+8bit校验和;

87.发送时序检测模块是否存在

(1)单片机向模块发送触发指令,通过数据线dat=1->dat=0,延时30ms->dat=1,延时60us,再次判定dht电平;(dht11_check.c)

#include "reg52.h"
#include "intrins.h"
sbit dat = P3^3; //模块data数据线接P3.3
sbit D5 = P3^7;
void Delay30ms()		//@11.0592MHz
{
	unsigned char i, j;

	i = 54;
	j = 199;
	do
	{
		while (--j);
	} while (--i);
}

void Delay60us()		//@11.0592MHz
{
	unsigned char i;

	i = 25;
	while (--i);
}
void Delay1000ms()		//@11.0592MHz
{
	unsigned char i, j, k;

	_nop_();
	i = 8;
	j = 1;
	k = 243;
	do
	{
		do
		{
			while (--k);
		} while (--j);
	} while (--i);
}

void checkModule(){
	//dat=1
	dat = 1;
	//->dat=0,延时30ms
	dat = 0;
	Delay30ms();
	//->dat=1,延时60us,再次判定dht电平
	dat = 1;
	Delay60us();
	if(dat == 0){
		//灯闪烁表示模块存在
		while(1){
			D5 = 0;
			Delay1000ms();
			D5 = 1;
			Delay1000ms();		
		}
	}
	

}

void main(){
	//传感器上电后,要等待 1 s 以越过不稳定状态
	Delay1000ms();
	Delay1000ms();
	checkModule();
}

88.读取DHT11数据的时序分析

(1)dht=1-》dht=0,延时30ms-》dht=1-》后续卡点判定,卡低位点while(dht)
-》卡高位点while(!dht)->卡低位点while(dht)->卡高位点while(!dht)
->延时50us对dht读数,为低电平则传输数据为0,为高电平则传输数据为1;

89.根据时序写的代码获取DHT11的数据

(1) 封装触发dht11函数;封装读取dht11函数(读5次,每次读8位数据,刚好8位char类型存储,采用位左移或逻辑存储);(dht11_read_data.c)

#include "reg52.h"
#include "intrins.h"
sbit dht = P3^3; //模块data数据线接P3.3
sbit D5 = P3^7;
char datas[5];
void Delay30ms()		//@11.0592MHz
{
	unsigned char i, j;

	i = 54;
	j = 199;
	do
	{
		while (--j);
	} while (--i);
}
void Delay1000ms()		//@11.0592MHz
{
	unsigned char i, j, k;

	_nop_();
	i = 8;
	j = 1;
	k = 243;
	do
	{
		do
		{
			while (--k);
		} while (--j);
	} while (--i);
}
void Delay40us()		//@11.0592MHz
{
	unsigned char i;

	_nop_();
	i = 15;
	while (--i);
}
void DHT11_Start(){
	//dht=1
	dht = 1;
	//->dht=0,延时30ms
	dht = 0;
	Delay30ms();
	//->dht=1,延时60us,再次判定dht电平
	dht = 1;   
	//-》后续卡点判定,卡低位点while(dht)
	while(dht);
	//-》卡高位点while(!dht)
	while(!dht);
	//->卡低位点while(dht)
	while(dht);
}
void Read_Data_From_DHT(){
	int i;
	int j;
	char tmp;
	char flag;
	//用户 MCU发送一次开始信号后, DHT1 1从低功耗模式转换到高速模式,等待主机开始信号结束后, DHT1 1发送响应信号,送出 40bi t的数据,并触发一次信号采集
	DHT11_Start();
	for(i=0;i < 5;i++){
		for(j=0;j<8;j++){
		//->卡高位点while(!dht)
			while(!dht);
		//->延时40us对dht读数,为低电平则传输数据为0,为高电平则传输数据为1;
			Delay40us();
			if(dht == 1){
				flag = 1;
				//卡低电平点,以便下回卡高电平
				while(dht);
			}else{
				flag = 0;
			}
			//tmp左移一位
			tmp <<= 1; 
			//左移一位后或逻辑相当于给空位补上
			tmp |= flag;
		}
		datas[i] = tmp;
	}
}

void main(){
	//传感器上电后,要等待 1 s 以越过不稳定状态
	Delay1000ms();
	Delay1000ms();
	//不断的读
	while(1){
		//间隔一秒读,以防读取过快
		Delay1000ms();
		Read_Data_From_DHT();
	}
}

90.温湿度通过串口传到PC显示

(1)添加串口初始化函数;发送字节函数;发送字符串函数;将datas[5]拆开分别发给串口显示(十进制数字转ascill,直接加0x30)(dht11_read_by_serial.c)

#include "reg52.h"
#include "intrins.h"
sbit dht = P3^3; //模块data数据线接P3.3
sbit D5 = P3^7;
char datas[5];
sfr AUXR = 0x8E;

void UartInit(void)		//9600bps@11.0592MHz
{
	AUXR = 0x01;
	SCON = 0x40; //配置串口工作方式1,REN不使能接收
	TMOD &= 0x0F;
	TMOD |= 0x20;//定时器1工作方式位8位自动重装
	
	TH1 = 0xFD;
	TL1 = 0xFD;//9600波特率的初值
	TR1 = 1;//启动定时器
}

void sendByte(char data_msg){
	//发送数据到缓冲器
	SBUF = data_msg;
	while(!TI);
	TI = 0;	
}
void sendString(char* str){
	while(*str !='\0'){
		sendByte(*str);
		str++;
	}
}
void Delay30ms()		//@11.0592MHz
{
	unsigned char i, j;

	i = 54;
	j = 199;
	do
	{
		while (--j);
	} while (--i);
}
void Delay1000ms()		//@11.0592MHz
{
	unsigned char i, j, k;

	_nop_();
	i = 8;
	j = 1;
	k = 243;
	do
	{
		do
		{
			while (--k);
		} while (--j);
	} while (--i);
}
void Delay40us()		//@11.0592MHz
{
	unsigned char i;

	_nop_();
	i = 15;
	while (--i);
}
void DHT11_Start(){
	//dht=1
	dht = 1;
	//->dht=0,延时30ms
	dht = 0;
	Delay30ms();
	//->dht=1,延时60us,再次判定dht电平
	dht = 1;   
	//-》后续卡点判定,卡低位点while(dht)
	while(dht);
	//-》卡高位点while(!dht)
	while(!dht);
	//->卡低位点while(dht)
	while(dht);
}
void Read_Data_From_DHT(){
	int i;
	int j;
	char tmp;
	char flag;
	//用户 MCU发送一次开始信号后, DHT1 1从低功耗模式转换到高速模式,等待主机开始信号结束后, DHT1 1发送响应信号,送出 40bi t的数据,并触发一次信号采集
	DHT11_Start();
	for(i=0;i < 5;i++){
		for(j=0;j<8;j++){
		//->卡高位点while(!dht)
			while(!dht);
		//->延时40us对dht读数,为低电平则传输数据为0,为高电平则传输数据为1;
			Delay40us();
			if(dht == 1){
				flag = 1;
				//卡低电平点,以便下回卡高电平
				while(dht);
			}else{
				flag = 0;
			}
			//tmp左移一位
			tmp <<= 1; 
			//左移一位后或逻辑相当于给空位补上
			tmp |= flag;
		}
		datas[i] = tmp;
	}
}

void main(){
	UartInit();
	//传感器上电后,要等待 1 s 以越过不稳定状态
	Delay1000ms();
	Delay1000ms();
	//不断的读
	while(1){
		//间隔一秒读,以防读取过快
		Delay1000ms();
		Read_Data_From_DHT();
		sendString("H:");
		//数字转ascill,直接加0x30
		sendByte(datas[0]/10+0x30);
		sendByte(datas[0]%10+0x30);
		sendByte('.');
		sendByte(datas[1]/10+0x30);
		sendByte(datas[1]%10+0x30);
		sendString("\r\n");
		sendString("T:");
		sendByte(datas[2]/10+0x30);
		sendByte(datas[2]%10+0x30);
		sendByte('.');
		sendByte(datas[3]/10+0x30);
		sendByte(datas[3]%10+0x30);
		sendString("\r\n");		
	}
}

91.温度检测小系统

(1)添加了lcd1602显示代码,及继电器驱动电机代码;(dht11_lcd_display.c)

#include "reg52.h"
#include "intrins.h"
sbit dht = P3^3; //模块data数据线接P3.3
sbit D5 = P3^7;
char datas[5];
sfr AUXR = 0x8E;
sbit fengshan = P3^6;


#define databuffer P0 //定义8位数据线,P0端口组
/*
RS  -- P1.0
RW  -- P1.1 
E   -- P1.4 */
sbit RS = P1^0;
sbit RW = P1^1;
sbit EN = P1^4;
char temp[8];
char huma[8];

//读忙信号函数
void chek_busy(){
	char tmp = 0x80;
	databuffer = 0x80;
	while(tmp & 0x80){//BF:为忙标志位,高电平表示忙,此时模块不能接收命令或者数据,如果为低电平表示不忙。
	RS = 0;
	RW = 1;
	EN = 0;
	_nop_();
	EN = 1;
	_nop_();
	_nop_();
	tmp = databuffer;
	EN = 0;
	_nop_();
	}
}
//写指令函数
void Write_Cmd_Func(char cmd){
	chek_busy();
	RS = 0;
	RW = 0;
	EN = 0;
	_nop_();
	databuffer = cmd;
	_nop_();
	EN = 1;
	_nop_();
	_nop_();
	EN = 0;
	_nop_();
}
//写数据函数
void Write_Data_Func(char dataShow){
	chek_busy();
	RS = 1;
	RW = 0;
	EN = 0;
	_nop_();
	databuffer = dataShow;
	_nop_();
	EN = 1;
	_nop_();
	_nop_();
	EN = 0;
	_nop_();
}
void Delay15ms()		//@11.0592MHz
{
	unsigned char i, j;

	i = 27;
	j = 226;
	do
	{
		while (--j);
	} while (--i);
}
void Delay5ms()		//@11.0592MHz
{
	unsigned char i, j;

	i = 9;
	j = 244;
	do
	{
		while (--j);
	} while (--i);
}

void LCD1602_INIT(){
//(1)延时 15ms 
	Delay15ms();
//(2)写指令 38H(不检测忙信号) 
	Write_Cmd_Func(0x38);
//(3)延时 5ms 
	Delay5ms();
//(4)以后每次写指令,读/写数据操作均需要检测忙信号 
//(5)写指令 38H:显示模式设置 
	Write_Cmd_Func(0x38);
//(6)写指令 08H:显示关闭 
	Write_Cmd_Func(0x08);
//(7)写指令 01H:显示清屏 
	Write_Cmd_Func(0x01);
//(8)写指令 06H:显示光标移动设置 
	Write_Cmd_Func(0x06);
//(9)写指令 0CH:显示开及光标设置 
	Write_Cmd_Func(0x0c);
}
void LCD1602_showLine(char row,char col,char *string){
	switch(row){
		case 1:
				Write_Cmd_Func(0x80 + col);
				while(*string){
					Write_Data_Func(*string);
					string++;
				}
				break;
		case 2:
				Write_Cmd_Func(0x80 + 0x40 + col);
				while(*string){
					Write_Data_Func(*string);
					string++;
				}
				break;
	}
}


void UartInit(void)		//9600bps@11.0592MHz
{
	AUXR = 0x01;
	SCON = 0x40; //配置串口工作方式1,REN不使能接收
	TMOD &= 0x0F;
	TMOD |= 0x20;//定时器1工作方式位8位自动重装
	
	TH1 = 0xFD;
	TL1 = 0xFD;//9600波特率的初值
	TR1 = 1;//启动定时器
}

void sendByte(char data_msg){
	//发送数据到缓冲器
	SBUF = data_msg;
	while(!TI);
	TI = 0;	
}
void sendString(char* str){
	while(*str !='\0'){
		sendByte(*str);
		str++;
	}
}
void Delay30ms()		//@11.0592MHz
{
	unsigned char i, j;

	i = 54;
	j = 199;
	do
	{
		while (--j);
	} while (--i);
}
void Delay1000ms()		//@11.0592MHz
{
	unsigned char i, j, k;

	_nop_();
	i = 8;
	j = 1;
	k = 243;
	do
	{
		do
		{
			while (--k);
		} while (--j);
	} while (--i);
}
void Delay40us()		//@11.0592MHz
{
	unsigned char i;

	_nop_();
	i = 15;
	while (--i);
}
void DHT11_Start(){
	//dht=1
	dht = 1;
	//->dht=0,延时30ms
	dht = 0;
	Delay30ms();
	//->dht=1,延时60us,再次判定dht电平
	dht = 1;   
	//-》后续卡点判定,卡低位点while(dht)
	while(dht);
	//-》卡高位点while(!dht)
	while(!dht);
	//->卡低位点while(dht)
	while(dht);
}
void Read_Data_From_DHT(){
	int i;
	int j;
	char tmp;
	char flag;
	//用户 MCU发送一次开始信号后, DHT1 1从低功耗模式转换到高速模式,等待主机开始信号结束后, DHT1 1发送响应信号,送出 40bi t的数据,并触发一次信号采集
	DHT11_Start();
	for(i=0;i < 5;i++){
		for(j=0;j<8;j++){
		//->卡高位点while(!dht)
			while(!dht);
		//->延时40us对dht读数,为低电平则传输数据为0,为高电平则传输数据为1;
			Delay40us();
			if(dht == 1){
				flag = 1;
				//卡低电平点,以便下回卡高电平
				while(dht);
			}else{
				flag = 0;
			}
			//tmp左移一位
			tmp <<= 1; 
			//左移一位后或逻辑相当于给空位补上
			tmp |= flag;
		}
		datas[i] = tmp;
	}
}

void Build_Datas(){
	huma[0] = 'H';
	huma[1] = datas[0]/10 + 0x30;
	huma[2] = datas[0]%10 + 0x30;
	huma[3] = '.';
	huma[4] = datas[1]/10 + 0x30;
	huma[5] = datas[1]%10 + 0x30;
	huma[6] = '%';
	huma[7] = '\0';
	temp[0] = 'T';
	temp[1] = datas[2]/10 + 0x30;
	temp[2] = datas[2]%10 + 0x30;
	temp[3] = '.';
	temp[4] = datas[3]/10 + 0x30;
	temp[5] = datas[3]%10 + 0x30;
	temp[6] = 'C';
	temp[7] = '\0';
}
void main(){
	Delay1000ms();
	UartInit();
	//传感器上电后,要等待 1 s 以越过不稳定状态
	LCD1602_INIT();
	Delay1000ms();
	Delay1000ms();
	//不断的读
	while(1){
		//间隔一秒读,以防读取过快
		Delay1000ms();
		Read_Data_From_DHT();
		if( datas[2]>24 ){
			fengshan = 0;
		}		
		Build_Datas();
		//进行串口发送显示
		sendString(huma);
		sendString("\r\n");
		sendString(temp);
		sendString("\r\n");
		//进行LCD显示
		LCD1602_showLine(1,2,huma);
		LCD1602_showLine(2,2,temp);
	}
}

92.分文件实现代码优化

(1)将主函数中不同模块代码拆分成不同的.c文件;->然后main函数中一个个添加调用的函数并编译;并一步步添加.h头文件;
(2).h头文件中仅需申明main函数中要调用的函数,不需要定义变量,端口等;.h文件不需要对其对应.c文件的内部函数不需要声明;
(3)可通过定义外部变量来调用外部函数的变量,例extern char datas[5];
(4).c代码:main.c,dht11.c,lcd1602.c,delay.c,uart.c
.h代码:dht11.h,lcd1602.h,delay.h,uart.h
代码main.c:

#include "reg52.h"
#include "intrins.h"
#include "delay.h"
#include "uart.h"
#include "lcd1602.h"
#include "dht11.h"

sbit D5 = P3^7;
sbit fengshan = P3^6;
char temp[8];
char huma[8];
extern char datas[5];

void Build_Datas(){
	huma[0] = 'H';
	huma[1] = datas[0]/10 + 0x30;
	huma[2] = datas[0]%10 + 0x30;
	huma[3] = '.';
	huma[4] = datas[1]/10 + 0x30;
	huma[5] = datas[1]%10 + 0x30;
	huma[6] = '%';
	huma[7] = '\0';
	temp[0] = 'T';
	temp[1] = datas[2]/10 + 0x30;
	temp[2] = datas[2]%10 + 0x30;
	temp[3] = '.';
	temp[4] = datas[3]/10 + 0x30;
	temp[5] = datas[3]%10 + 0x30;
	temp[6] = 'C';
	temp[7] = '\0';
}

void main(){ 
		Delay1000ms();
		UartInit();
		//传感器上电后,要等待 1 s 以越过不稳定状态
		LCD1602_INIT();
		Delay1000ms();
		Delay1000ms();
		//不断的读
	while(1){
		//间隔一秒读,以防读取过快
		Delay1000ms();
		Read_Data_From_DHT();
		if( datas[2]>24 ){
			fengshan = 0;
		}		
		Build_Datas();
		//进行串口发送显示
		sendString(huma);
		sendString("\r\n");
		sendString(temp);
		sendString("\r\n");
		//进行LCD显示
		LCD1602_showLine(1,2,huma);
		LCD1602_showLine(2,2,temp);
	}

}

代码dht11.c:

#include "reg52.h"
#include "delay.h"
sbit dht = P3^3; //模块data数据线接P3.3
char datas[5];

void DHT11_Start(){
	//dht=1
	dht = 1;
	//->dht=0,延时30ms
	dht = 0;
	Delay30ms();
	//->dht=1,延时60us,再次判定dht电平
	dht = 1;   
	//-》后续卡点判定,卡低位点while(dht)
	while(dht);
	//-》卡高位点while(!dht)
	while(!dht);
	//->卡低位点while(dht)
	while(dht);
}
void Read_Data_From_DHT(){
	int i;
	int j;
	char tmp;
	char flag;
	//用户 MCU发送一次开始信号后, DHT1 1从低功耗模式转换到高速模式,等待主机开始信号结束后, DHT1 1发送响应信号,送出 40bi t的数据,并触发一次信号采集
	DHT11_Start();
	for(i=0;i < 5;i++){
		for(j=0;j<8;j++){
		//->卡高位点while(!dht)
			while(!dht);
		//->延时40us对dht读数,为低电平则传输数据为0,为高电平则传输数据为1;
			Delay40us();
			if(dht == 1){
				flag = 1;
				//卡低电平点,以便下回卡高电平
				while(dht);
			}else{
				flag = 0;
			}
			//tmp左移一位
			tmp <<= 1; 
			//左移一位后或逻辑相当于给空位补上
			tmp |= flag;
		}
		datas[i] = tmp;
	}
}

代码lcd1602.c:

#include "reg52.h"
#include "intrins.h"
#include "delay.h"
#define databuffer P0 //定义8位数据线,P0端口组
/*
RS  -- P1.0
RW  -- P1.1 
E   -- P1.4 */
sbit RS = P1^0;
sbit RW = P1^1;
sbit EN = P1^4;
//读忙信号函数
void chek_busy(){
	char tmp = 0x80;
	databuffer = 0x80;
	while(tmp & 0x80){//BF:为忙标志位,高电平表示忙,此时模块不能接收命令或者数据,如果为低电平表示不忙。
	RS = 0;
	RW = 1;
	EN = 0;
	_nop_();
	EN = 1;
	_nop_();
	_nop_();
	tmp = databuffer;
	EN = 0;
	_nop_();
	}
}
//写指令函数
void Write_Cmd_Func(char cmd){
	chek_busy();
	RS = 0;
	RW = 0;
	EN = 0;
	_nop_();
	databuffer = cmd;
	_nop_();
	EN = 1;
	_nop_();
	_nop_();
	EN = 0;
	_nop_();
}
//写数据函数
void Write_Data_Func(char dataShow){
	chek_busy();
	RS = 1;
	RW = 0;
	EN = 0;
	_nop_();
	databuffer = dataShow;
	_nop_();
	EN = 1;
	_nop_();
	_nop_();
	EN = 0;
	_nop_();
}


void LCD1602_INIT(){
//(1)延时 15ms 
 	Delay15ms();
//(2)写指令 38H(不检测忙信号) 
	Write_Cmd_Func(0x38);
//(3)延时 5ms 
	Delay5ms();
//(4)以后每次写指令,读/写数据操作均需要检测忙信号 
//(5)写指令 38H:显示模式设置 
	Write_Cmd_Func(0x38);
//(6)写指令 08H:显示关闭 
	Write_Cmd_Func(0x08);
//(7)写指令 01H:显示清屏 
	Write_Cmd_Func(0x01);
//(8)写指令 06H:显示光标移动设置 
	Write_Cmd_Func(0x06);
//(9)写指令 0CH:显示开及光标设置 
	Write_Cmd_Func(0x0c);
}
void LCD1602_showLine(char row,char col,char *string){
	switch(row){
		case 1:
				Write_Cmd_Func(0x80 + col);
				while(*string){
					Write_Data_Func(*string);
					string++;
				}
				break;
		case 2:
				Write_Cmd_Func(0x80 + 0x40 + col);
				while(*string){
					Write_Data_Func(*string);
					string++;
				}
				break;
	}
}


代码delay.c:

#include "intrins.h"
void Delay15ms()		//@11.0592MHz
{
	unsigned char i, j;

	i = 27;
	j = 226;
	do
	{
		while (--j);
	} while (--i);
}
void Delay5ms()		//@11.0592MHz
{
	unsigned char i, j;

	i = 9;
	j = 244;
	do
	{
		while (--j);
	} while (--i);
}
void Delay30ms()		//@11.0592MHz
{
	unsigned char i, j;

	i = 54;
	j = 199;
	do
	{
		while (--j);
	} while (--i);
}
void Delay1000ms()		//@11.0592MHz
{
	unsigned char i, j, k;

	_nop_();
	i = 8;
	j = 1;
	k = 243;
	do
	{
		do
		{
			while (--k);
		} while (--j);
	} while (--i);
}
void Delay40us()		//@11.0592MHz
{
	unsigned char i;

	_nop_();
	i = 15;
	while (--i);
}

代码uart.c:

#include "reg52.h"
sfr AUXR = 0x8E;
void UartInit(void)		//9600bps@11.0592MHz
{
	AUXR = 0x01;
	SCON = 0x40; //配置串口工作方式1,REN不使能接收
	TMOD &= 0x0F;
	TMOD |= 0x20;//定时器1工作方式位8位自动重装
	
	TH1 = 0xFD;
	TL1 = 0xFD;//9600波特率的初值
	TR1 = 1;//启动定时器
}

void sendByte(char data_msg){
	//发送数据到缓冲器
	SBUF = data_msg;
	while(!TI);
	TI = 0;	
}
void sendString(char* str){
	while(*str !='\0'){
		sendByte(*str);
		str++;
	}
}

代码dht11.h:

#include "reg52.h"
#include "delay.h"
void Read_Data_From_DHT();

代码lcd1602.h:

#include "reg52.h"
#include "intrins.h"
#include "delay.h"
#include "dht11.h"
void LCD1602_INIT();
void LCD1602_showLine(char row,char col,char *string);

代码delay.h:

#include "intrins.h"
void Delay15ms();
void Delay5ms();	
void Delay30ms();	
void Delay1000ms();	
void Delay40us();

代码uart.h:

#include "reg52.h"
void UartInit(void);
void sendByte(char data_msg);
void sendString(char* str);

93.IIC协议

(1)IIC全称Inter-Integrated Circuit(集成电路总线),只有一个数据线,同一时间要么发送数据,要么接收数据,属半双工;
(2)最大传输速率10kb/s,支持多个组件,各组件都可以成为主控设备,各组件并联在SDA数据线跟SCL数据线上;

94.IIC协议起始和终止信号

(1)11.0592MHZ对应的一个_nop_()大概5us;
(2)起始信号:其中SCL等于高电平,SDA先等于高电平,延时>4.7us,变为低电平后延时>4us;
(3)终止信号:其中SCL等于高电平,SDA先等于低电平,延时>4us,变为高电平后延时>4.7us;(IIC_basic_function.c)

#include "reg52.h"
#include "intrins.h"
sbit scl = P0^1;
sbit sda = P0^3;
void IIC_Start(){
	sda = 1;
	scl = 1;
	_nop_();
	sda = 0;
	_nop_();
}
void IIC_Stop(){
	sda = 0;
	scl = 1;
	_nop_();
	sda = 1;
	_nop_();
}
char IIC_ACK(){
	char flag;
	sda = 1;//释放数据线
	_nop_();
	scl = 1;
	_nop_();
	flag = sda;
	_nop_();
	scl = 0;
	_nop_();
	return flag;
}
void IIC_Send_Byte(char dataSend){
	int i;
	for(i = 0;i<8;i++){
		scl = 0;//scl拉低
		sda =dataSend & 0x80;//获取最高位
		_nop_();//发送数据建立时间
		scl = 1;//scl拉高开始发送
		_nop_();//数据发送时间
		scl = 0;//发送完毕拉低
		_nop_();
		dataSend = dataSend << 1;
	}
}
void main(){
	int a = 10;
	IIC_Start();
}

95.IIC协议ACK函数封装

(1)应答信号:SDA高电平,延时5us;SCL高电平,延时5us;获取SDA电平,延时5us;SCL低电平,延时5us;(IIC_basic_function.c)

#include "reg52.h"
#include "intrins.h"
sbit scl = P0^1;
sbit sda = P0^3;
void IIC_Start(){
	sda = 1;
	scl = 1;
	_nop_();
	sda = 0;
	_nop_();
}
void IIC_Stop(){
	sda = 0;
	scl = 1;
	_nop_();
	sda = 1;
	_nop_();
}
char IIC_ACK(){
	char flag;
	sda = 1;//释放数据线
	_nop_();
	scl = 1;
	_nop_();
	flag = sda;
	_nop_();
	scl = 0;
	_nop_();
	return flag;
}
void IIC_Send_Byte(char dataSend){
	int i;
	for(i = 0;i<8;i++){
		scl = 0;//scl拉低
		sda =dataSend & 0x80;//获取最高位
		_nop_();//发送数据建立时间
		scl = 1;//scl拉高开始发送
		_nop_();//数据发送时间
		scl = 0;//发送完毕拉低
		_nop_();
		dataSend = dataSend << 1;
	}
}
void main(){
	int a = 10;
	IIC_Start();
}

96.IIC协议发送一个字节的函数封装

(1)SCL为低电平时才发生数据翻转变化;SCL为低电平,发送数据SDA,延时5us;
SCL为高电平,延时5us;SCL为低电平,延时5us;(IIC_basic_function.c)

#include "reg52.h"
#include "intrins.h"
sbit scl = P0^1;
sbit sda = P0^3;
void IIC_Start(){
	sda = 1;
	scl = 1;
	_nop_();
	sda = 0;
	_nop_();
}
void IIC_Stop(){
	sda = 0;
	scl = 1;
	_nop_();
	sda = 1;
	_nop_();
}
char IIC_ACK(){
	char flag;
	sda = 1;//释放数据线
	_nop_();
	scl = 1;
	_nop_();
	flag = sda;
	_nop_();
	scl = 0;
	_nop_();
	return flag;
}
void IIC_Send_Byte(char dataSend){
	int i;
	for(i = 0;i<8;i++){
		scl = 0;//scl拉低
		sda =dataSend & 0x80;//获取最高位
		_nop_();//发送数据建立时间
		scl = 1;//scl拉高开始发送
		_nop_();//数据发送时间
		scl = 0;//发送完毕拉低
		_nop_();
		dataSend = dataSend << 1;
	}
}
void main(){
	int a = 10;
	IIC_Start();
}

97.OLED写入指令和数据

(1)1. start()—》2. 写入 b0111 1000 0x78—》3. ACK
—》4. cotrol byte: (0)(0)000000 写入命令 (0)(1)000000写入数据
—》5. ACK—》6. 写入指令/数据—》7. ACK—》8. STOP (IIC_write_data_or_command.c)

#include "reg52.h"
#include "intrins.h"
sbit scl = P0^1;
sbit sda = P0^3;
void IIC_Start(){
	sda = 1;
	scl = 1;
	_nop_();
	sda = 0;
	_nop_();
}
void IIC_Stop(){
	sda = 0;
	scl = 1;
	_nop_();
	sda = 1;
	_nop_();
}
char IIC_ACK(){
	char flag;
	sda = 1;//释放数据线
	_nop_();
	scl = 1;
	_nop_();
	flag = sda;
	_nop_();
	scl = 0;
	_nop_();
	return flag;
}
void IIC_Send_Byte(char dataSend){
	int i;
	for(i = 0;i<8;i++){
		scl = 0;//scl拉低
		sda =dataSend & 0x80;//获取最高位
		_nop_();//发送数据建立时间
		scl = 1;//scl拉高开始发送
		_nop_();//数据发送时间
		scl = 0;//发送完毕拉低
		_nop_();
		dataSend = dataSend << 1;
	}
}
//写命令函数
void Oled_Write_Cmd(dataCmd){
	//1. start()
	IIC_start();
  //2. 写入 b0111 1000 0x78
	IIC_Send_Byte(0x78);
  //3. ACK
	IIC_ACK();
  //4. cotrol byte: (0)(0)000000 写入命令   (0)(1)000000写入数据
  IIC_Send_Byte(0x00);
  //5. ACK
	IIC_ACK();
  //6. 写入指令/数据
  IIC_Send_Byte(dataCmd);
  //7. ACK
	IIC_ACK();
  //8. STOP
	IIC_Stop();
}
//写命令函数
void Oled_Write_Data(dataData){
	//1. start()
	IIC_start();
  //2. 写入 b0111 1000 0x78
	IIC_Send_Byte(0x78);
  //3. ACK
	IIC_ACK();
  //4. cotrol byte: (0)(0)000000 写入命令   (0)(1)000000写入数据
  IIC_Send_Byte(0x40);
  //5. ACK
	IIC_ACK();
  //6. 写入指令/数据
  IIC_Send_Byte(dataData);
  //7. ACK
	IIC_ACK();
  //8. STOP
	IIC_Stop();
}
void main(){
	int a = 10;
	IIC_Start();
}

98.OLED显示一个点的思路

(1)寻址模式:页地址模式,水平地址模式,垂直地址模式;

99.OLED显示一个点的代码实现

(1)OLED初始化->选择一个位置->确认页寻址模式->选择PAGE0->显示一个点(IIC_display_one_point.c)

#include "reg52.h"
#include "intrins.h"
sbit scl = P0^1;
sbit sda = P0^3;
void IIC_Start(){
	scl = 0;//防雪花
	sda = 1;
	scl = 1;
	_nop_();
	sda = 0;
	_nop_();
}
void IIC_Stop(){
	scl = 0;//防雪花
	sda = 0;
	scl = 1;
	_nop_();
	sda = 1;
	_nop_();
}
char IIC_ACK(){
	char flag;
	sda = 1;//释放数据线
	_nop_();
	scl = 1;
	_nop_();
	flag = sda;
	_nop_();
	scl = 0;
	_nop_();
	return flag;
}
void IIC_Send_Byte(char dataSend){
	int i;
	for(i = 0;i<8;i++){
		scl = 0;//scl拉低
		sda =dataSend & 0x80;//获取最高位
		_nop_();//发送数据建立时间
		scl = 1;//scl拉高开始发送
		_nop_();//数据发送时间
		scl = 0;//发送完毕拉低
		_nop_();
		dataSend = dataSend << 1;
	}
}
//写命令函数
void Oled_Write_Cmd(dataCmd){
	//1. start()
	IIC_Start();
  //2. 写入 b0111 1000 0x78
	IIC_Send_Byte(0x78);
  //3. ACK
	IIC_ACK();
  //4. cotrol byte: (0)(0)000000 写入命令   (0)(1)000000写入数据
  IIC_Send_Byte(0x00);
  //5. ACK
	IIC_ACK();
  //6. 写入指令/数据
  IIC_Send_Byte(dataCmd);
  //7. ACK
	IIC_ACK();
  //8. STOP
	IIC_Stop();
}
//写命令函数
void Oled_Write_Data(dataData){
	//1. start()
	IIC_Start();
  //2. 写入 b0111 1000 0x78
	IIC_Send_Byte(0x78);
  //3. ACK
	IIC_ACK();
  //4. cotrol byte: (0)(0)000000 写入命令   (0)(1)000000写入数据
  IIC_Send_Byte(0x40);
  //5. ACK
	IIC_ACK();
  //6. 写入指令/数据
  IIC_Send_Byte(dataData);
  //7. ACK
	IIC_ACK();
  //8. STOP
	IIC_Stop();
}
//Oled初始化
void Oled_Init(void){
	Oled_Write_Cmd(0xAE);//--display off
	Oled_Write_Cmd(0x00);//---set low column address
	Oled_Write_Cmd(0x10);//---set high column address
	Oled_Write_Cmd(0x40);//--set start line address  
	Oled_Write_Cmd(0xB0);//--set page address
	Oled_Write_Cmd(0x81); // contract control
	Oled_Write_Cmd(0xFF);//--128   
	Oled_Write_Cmd(0xA1);//set segment remap 
	Oled_Write_Cmd(0xA6);//--normal / reverse
	Oled_Write_Cmd(0xA8);//--set multiplex ratio(1 to 64)
	Oled_Write_Cmd(0x3F);//--1/32 duty
	Oled_Write_Cmd(0xC8);//Com scan direction
	Oled_Write_Cmd(0xD3);//-set display offset
	Oled_Write_Cmd(0x00);//
	
	Oled_Write_Cmd(0xD5);//set osc division
	Oled_Write_Cmd(0x80);//
	
	Oled_Write_Cmd(0xD8);//set area color mode off
	Oled_Write_Cmd(0x05);//
	
	Oled_Write_Cmd(0xD9);//Set Pre-Charge Period
	Oled_Write_Cmd(0xF1);//
	
	Oled_Write_Cmd(0xDA);//set com pin configuartion
	Oled_Write_Cmd(0x12);//
	
	Oled_Write_Cmd(0xDB);//set Vcomh
	Oled_Write_Cmd(0x30);//
	
	Oled_Write_Cmd(0x8D);//set charge pump enable
	Oled_Write_Cmd(0x14);//
	
	Oled_Write_Cmd(0xAF);//--turn on oled panel		
}


void main(){
		//1. OLED初始化
		Oled_Init();
		//2. 选择一个位置
		//2.1 确认页寻址模式
		Oled_Write_Cmd(0x20);
		Oled_Write_Cmd(0x02);
		//2.2 选择PAGE0   1011 0000
		Oled_Write_Cmd(0xB0);
	  //3. 显示一个点
		Oled_Write_Data(0x08);
		while(1);
}

100.OLED列地址和雪花BUG解决

(1)写命令例如0列,Oled_Write_Cmd(0x00);Oled_Write_Cmd(0x10);(IIC_column_address_display.c)

#include "reg52.h"
#include "intrins.h"
sbit scl = P0^1;
sbit sda = P0^3;
void IIC_Start(){
	scl = 0;//防雪花
	sda = 1;
	scl = 1;
	_nop_();
	sda = 0;
	_nop_();
}
void IIC_Stop(){
	scl = 0;//防雪花
	sda = 0;
	scl = 1;
	_nop_();
	sda = 1;
	_nop_();
}
char IIC_ACK(){
	char flag;
	sda = 1;//释放数据线
	_nop_();
	scl = 1;
	_nop_();
	flag = sda;
	_nop_();
	scl = 0;
	_nop_();
	return flag;
}
void IIC_Send_Byte(char dataSend){
	int i;
	for(i = 0;i<8;i++){
		scl = 0;//scl拉低
		sda =dataSend & 0x80;//获取最高位
		_nop_();//发送数据建立时间
		scl = 1;//scl拉高开始发送
		_nop_();//数据发送时间
		scl = 0;//发送完毕拉低
		_nop_();
		dataSend = dataSend << 1;
	}
}
//写命令函数
void Oled_Write_Cmd(dataCmd){
	//1. start()
	IIC_Start();
  //2. 写入 b0111 1000 0x78
	IIC_Send_Byte(0x78);
  //3. ACK
	IIC_ACK();
  //4. cotrol byte: (0)(0)000000 写入命令   (0)(1)000000写入数据
  IIC_Send_Byte(0x00);
  //5. ACK
	IIC_ACK();
  //6. 写入指令/数据
  IIC_Send_Byte(dataCmd);
  //7. ACK
	IIC_ACK();
  //8. STOP
	IIC_Stop();
}
//写数据函数
void Oled_Write_Data(dataData){
	//1. start()
	IIC_Start();
  //2. 写入 b0111 1000 0x78
	IIC_Send_Byte(0x78);
  //3. ACK
	IIC_ACK();
  //4. cotrol byte: (0)(0)000000 写入命令   (0)(1)000000写入数据
  IIC_Send_Byte(0x40);
  //5. ACK
	IIC_ACK();
  //6. 写入指令/数据
  IIC_Send_Byte(dataData);
  //7. ACK
	IIC_ACK();
  //8. STOP
	IIC_Stop();
}
//Oled初始化
void Oled_Init(void){
	Oled_Write_Cmd(0xAE);//--display off
	Oled_Write_Cmd(0x00);//---set low column address
	Oled_Write_Cmd(0x10);//---set high column address
	Oled_Write_Cmd(0x40);//--set start line address  
	Oled_Write_Cmd(0xB0);//--set page address
	Oled_Write_Cmd(0x81); // contract control
	Oled_Write_Cmd(0xFF);//--128   
	Oled_Write_Cmd(0xA1);//set segment remap 
	Oled_Write_Cmd(0xA6);//--normal / reverse
	Oled_Write_Cmd(0xA8);//--set multiplex ratio(1 to 64)
	Oled_Write_Cmd(0x3F);//--1/32 duty
	Oled_Write_Cmd(0xC8);//Com scan direction
	Oled_Write_Cmd(0xD3);//-set display offset
	Oled_Write_Cmd(0x00);//
	
	Oled_Write_Cmd(0xD5);//set osc division
	Oled_Write_Cmd(0x80);//
	
	Oled_Write_Cmd(0xD8);//set area color mode off
	Oled_Write_Cmd(0x05);//
	
	Oled_Write_Cmd(0xD9);//Set Pre-Charge Period
	Oled_Write_Cmd(0xF1);//
	
	Oled_Write_Cmd(0xDA);//set com pin configuartion
	Oled_Write_Cmd(0x12);//
	
	Oled_Write_Cmd(0xDB);//set Vcomh
	Oled_Write_Cmd(0x30);//
	
	Oled_Write_Cmd(0x8D);//set charge pump enable
	Oled_Write_Cmd(0x14);//
	
	Oled_Write_Cmd(0xAF);//--turn on oled panel		
}


void main(){
		//1. OLED初始化
		Oled_Init();
		//2. 选择一个位置
		//2.1 确认页寻址模式
		Oled_Write_Cmd(0x20);
		Oled_Write_Cmd(0x02);
		//2.2 选择PAGE0   1011 0000
		Oled_Write_Cmd(0xB0);
	  //列地址选择
		Oled_Write_Cmd(0x00);
		Oled_Write_Cmd(0x10);
		//3. 显示一个点
		Oled_Write_Data(0x08);
		Oled_Write_Data(0x08);
		Oled_Write_Data(0x08);
		Oled_Write_Data(0x08);

		//2.2 选择PAGE0   1011 0000
		Oled_Write_Cmd(0xB2);
	  //列地址选择
		Oled_Write_Cmd(0x00);
		Oled_Write_Cmd(0x10);
		//3. 显示一个点
		Oled_Write_Data(0x08);
		Oled_Write_Data(0x08);
		Oled_Write_Data(0x08);
		Oled_Write_Data(0x08);
		
		//2.2 选择PAGE0   1011 0000
		Oled_Write_Cmd(0xB4);
	  //列地址选择
		Oled_Write_Cmd(0x00);
		Oled_Write_Cmd(0x10);
		//3. 显示一个点
		Oled_Write_Data(0x08);
		Oled_Write_Data(0x08);
		Oled_Write_Data(0x08);
		Oled_Write_Data(0x08);
		while(1);
}

(2)雪花问题在起始跟结束信号函数中都加入scl=0;

101.OLED清屏添加清屏函数

(1)从page0-page7的0-127列进行清屏(IIC_clear_screen.c)

#include "reg52.h"
#include "intrins.h"
sbit scl = P0^1;
sbit sda = P0^3;
void IIC_Start(){
	scl = 0;//防雪花
	sda = 1;
	scl = 1;
	_nop_();
	sda = 0;
	_nop_();
}
void IIC_Stop(){
	scl = 0;//防雪花
	sda = 0;
	scl = 1;
	_nop_();
	sda = 1;
	_nop_();
}
char IIC_ACK(){
	char flag;
	sda = 1;//释放数据线
	_nop_();
	scl = 1;
	_nop_();
	flag = sda;
	_nop_();
	scl = 0;
	_nop_();
	return flag;
}
void IIC_Send_Byte(char dataSend){
	int i;
	for(i = 0;i<8;i++){
		scl = 0;//scl拉低
		sda =dataSend & 0x80;//获取最高位
		_nop_();//发送数据建立时间
		scl = 1;//scl拉高开始发送
		_nop_();//数据发送时间
		scl = 0;//发送完毕拉低
		_nop_();
		dataSend = dataSend << 1;
	}
}
//写命令函数
void Oled_Write_Cmd(dataCmd){
	//1. start()
	IIC_Start();
  //2. 写入 b0111 1000 0x78
	IIC_Send_Byte(0x78);
  //3. ACK
	IIC_ACK();
  //4. cotrol byte: (0)(0)000000 写入命令   (0)(1)000000写入数据
  IIC_Send_Byte(0x00);
  //5. ACK
	IIC_ACK();
  //6. 写入指令/数据
  IIC_Send_Byte(dataCmd);
  //7. ACK
	IIC_ACK();
  //8. STOP
	IIC_Stop();
}
//写数据函数
void Oled_Write_Data(dataData){
	//1. start()
	IIC_Start();
  //2. 写入 b0111 1000 0x78
	IIC_Send_Byte(0x78);
  //3. ACK
	IIC_ACK();
  //4. cotrol byte: (0)(0)000000 写入命令   (0)(1)000000写入数据
  IIC_Send_Byte(0x40);
  //5. ACK
	IIC_ACK();
  //6. 写入指令/数据
  IIC_Send_Byte(dataData);
  //7. ACK
	IIC_ACK();
  //8. STOP
	IIC_Stop();
}
//Oled初始化
void Oled_Init(void){
	Oled_Write_Cmd(0xAE);//--display off
	Oled_Write_Cmd(0x00);//---set low column address
	Oled_Write_Cmd(0x10);//---set high column address
	Oled_Write_Cmd(0x40);//--set start line address  
	Oled_Write_Cmd(0xB0);//--set page address
	Oled_Write_Cmd(0x81); // contract control
	Oled_Write_Cmd(0xFF);//--128   
	Oled_Write_Cmd(0xA1);//set segment remap 
	Oled_Write_Cmd(0xA6);//--normal / reverse
	Oled_Write_Cmd(0xA8);//--set multiplex ratio(1 to 64)
	Oled_Write_Cmd(0x3F);//--1/32 duty
	Oled_Write_Cmd(0xC8);//Com scan direction
	Oled_Write_Cmd(0xD3);//-set display offset
	Oled_Write_Cmd(0x00);//
	
	Oled_Write_Cmd(0xD5);//set osc division
	Oled_Write_Cmd(0x80);//
	
	Oled_Write_Cmd(0xD8);//set area color mode off
	Oled_Write_Cmd(0x05);//
	
	Oled_Write_Cmd(0xD9);//Set Pre-Charge Period
	Oled_Write_Cmd(0xF1);//
	
	Oled_Write_Cmd(0xDA);//set com pin configuartion
	Oled_Write_Cmd(0x12);//
	
	Oled_Write_Cmd(0xDB);//set Vcomh
	Oled_Write_Cmd(0x30);//
	
	Oled_Write_Cmd(0x8D);//set charge pump enable
	Oled_Write_Cmd(0x14);//
	
	Oled_Write_Cmd(0xAF);//--turn on oled panel		
}

void Oled_Clear(){
	int i,j;
	for(i=0;i<8;i++){
		//从page0-page7
		Oled_Write_Cmd(0xB0+i);
		//每隔page从0列开始
		Oled_Write_Cmd(0x00);
		Oled_Write_Cmd(0x10);
		for(j=0;j<128;j++){
			Oled_Write_Data(0);
		}
	}

}

void main(){
		//1. OLED初始化
		Oled_Init();
		//2. 选择一个位置
		//2.1 确认页寻址模式
		Oled_Write_Cmd(0x20);
		Oled_Write_Cmd(0x02);
		//清屏操作
		Oled_Clear();
		//2.2 选择PAGE0   1011 0000
		Oled_Write_Cmd(0xB0);
	  //列地址选择
		Oled_Write_Cmd(0x00);
		Oled_Write_Cmd(0x10);
		//3. 显示一个点
		Oled_Write_Data(0x08);
		Oled_Write_Data(0x08);
		Oled_Write_Data(0x08);
		Oled_Write_Data(0x08);

		//2.2 选择PAGE0   1011 0000
		Oled_Write_Cmd(0xB2);
	  //列地址选择
		Oled_Write_Cmd(0x00);
		Oled_Write_Cmd(0x10);
		//3. 显示一个点
		Oled_Write_Data(0x08);
		Oled_Write_Data(0x08);
		Oled_Write_Data(0x08);
		Oled_Write_Data(0x08);
		
		//2.2 选择PAGE0   1011 0000
		Oled_Write_Cmd(0xB4);
	  //列地址选择
		Oled_Write_Cmd(0x00);
		Oled_Write_Cmd(0x10);
		//3. 显示一个点
		Oled_Write_Data(0x08);
		Oled_Write_Data(0x08);
		Oled_Write_Data(0x08);
		Oled_Write_Data(0x08);
		while(1);
}

102.OLED显示字母A

(1)字模软件取模,参数配置,A为12号字体占2个page,建立2个数组存写入的数据;(IIC_A_display.c)

#include "reg52.h"
#include "intrins.h"
sbit scl = P0^1;
sbit sda = P0^3;
void IIC_Start(){
	scl = 0;//防雪花
	sda = 1;
	scl = 1;
	_nop_();
	sda = 0;
	_nop_();
}
void IIC_Stop(){
	scl = 0;//防雪花
	sda = 0;
	scl = 1;
	_nop_();
	sda = 1;
	_nop_();
}
char IIC_ACK(){
	char flag;
	sda = 1;//释放数据线
	_nop_();
	scl = 1;
	_nop_();
	flag = sda;
	_nop_();
	scl = 0;
	_nop_();
	return flag;
}
void IIC_Send_Byte(char dataSend){
	int i;
	for(i = 0;i<8;i++){
		scl = 0;//scl拉低
		sda =dataSend & 0x80;//获取最高位
		_nop_();//发送数据建立时间
		scl = 1;//scl拉高开始发送
		_nop_();//数据发送时间
		scl = 0;//发送完毕拉低
		_nop_();
		dataSend = dataSend << 1;
	}
}
//写命令函数
void Oled_Write_Cmd(dataCmd){
	//1. start()
	IIC_Start();
  //2. 写入 b0111 1000 0x78
	IIC_Send_Byte(0x78);
  //3. ACK
	IIC_ACK();
  //4. cotrol byte: (0)(0)000000 写入命令   (0)(1)000000写入数据
  IIC_Send_Byte(0x00);
  //5. ACK
	IIC_ACK();
  //6. 写入指令/数据
  IIC_Send_Byte(dataCmd);
  //7. ACK
	IIC_ACK();
  //8. STOP
	IIC_Stop();
}
//写数据函数
void Oled_Write_Data(dataData){
	//1. start()
	IIC_Start();
  //2. 写入 b0111 1000 0x78
	IIC_Send_Byte(0x78);
  //3. ACK
	IIC_ACK();
  //4. cotrol byte: (0)(0)000000 写入命令   (0)(1)000000写入数据
  IIC_Send_Byte(0x40);
  //5. ACK
	IIC_ACK();
  //6. 写入指令/数据
  IIC_Send_Byte(dataData);
  //7. ACK
	IIC_ACK();
  //8. STOP
	IIC_Stop();
}
//Oled初始化
void Oled_Init(void){
	Oled_Write_Cmd(0xAE);//--display off
	Oled_Write_Cmd(0x00);//---set low column address
	Oled_Write_Cmd(0x10);//---set high column address
	Oled_Write_Cmd(0x40);//--set start line address  
	Oled_Write_Cmd(0xB0);//--set page address
	Oled_Write_Cmd(0x81); // contract control
	Oled_Write_Cmd(0xFF);//--128   
	Oled_Write_Cmd(0xA1);//set segment remap 
	Oled_Write_Cmd(0xA6);//--normal / reverse
	Oled_Write_Cmd(0xA8);//--set multiplex ratio(1 to 64)
	Oled_Write_Cmd(0x3F);//--1/32 duty
	Oled_Write_Cmd(0xC8);//Com scan direction
	Oled_Write_Cmd(0xD3);//-set display offset
	Oled_Write_Cmd(0x00);//
	
	Oled_Write_Cmd(0xD5);//set osc division
	Oled_Write_Cmd(0x80);//
	
	Oled_Write_Cmd(0xD8);//set area color mode off
	Oled_Write_Cmd(0x05);//
	
	Oled_Write_Cmd(0xD9);//Set Pre-Charge Period
	Oled_Write_Cmd(0xF1);//
	
	Oled_Write_Cmd(0xDA);//set com pin configuartion
	Oled_Write_Cmd(0x12);//
	
	Oled_Write_Cmd(0xDB);//set Vcomh
	Oled_Write_Cmd(0x30);//
	
	Oled_Write_Cmd(0x8D);//set charge pump enable
	Oled_Write_Cmd(0x14);//
	
	Oled_Write_Cmd(0xAF);//--turn on oled panel		
}

void Oled_Clear(){
	int i,j;
	for(i=0;i<8;i++){
		//从page0-page7
		Oled_Write_Cmd(0xB0+i);
		//每隔page从0列开始
		Oled_Write_Cmd(0x00);
		Oled_Write_Cmd(0x10);
		for(j=0;j<128;j++){
			Oled_Write_Data(0);
		}
	}

}

/*--  文字:  A  --*/
/*--  宋体12;  此字体下对应的点阵为:宽x高=8x16   --*/
char A1[8]={0x00,0x00,0xC0,0x38,0xE0,0x00,0x00,0x00};
char A2[8]={0x20,0x3C,0x23,0x02,0x02,0x27,0x38,0x20};
void main(){
		int i;
		//1. OLED初始化
		Oled_Init();
		//2. 选择一个位置
		//2.1 确认页寻址模式
		Oled_Write_Cmd(0x20);
		Oled_Write_Cmd(0x02);
		//清屏操作
		Oled_Clear();
		//2.2 选择PAGE0   1011 0000
		Oled_Write_Cmd(0xB0);
	  //列地址选择
		Oled_Write_Cmd(0x00);
		Oled_Write_Cmd(0x10);
		//3. 显示page0内容
		for(i=0;i<8;i++){
			Oled_Write_Data(A1[i]);
		}
		
		//2.2 选择PAGE1   1011 0001
		Oled_Write_Cmd(0xB1);
	  //列地址选择
		Oled_Write_Cmd(0x00);
		Oled_Write_Cmd(0x10);
		//3. 显示page1内容
		for(i=0;i<8;i++){
			Oled_Write_Data(A2[i]);
		}		
		while(1);
}

103.OLED显示嵌入式软件开发

(1)字模软件取模,参数配置,12号字体,嵌入式软件开发16*16,占2个page;分别存放文字的上下部分;(IIC_words_display.c)

#include "reg52.h"
#include "intrins.h"
sbit scl = P0^1;
sbit sda = P0^3;
void IIC_Start(){
	scl = 0;//防雪花
	sda = 1;
	scl = 1;
	_nop_();
	sda = 0;
	_nop_();
}
void IIC_Stop(){
	scl = 0;//防雪花
	sda = 0;
	scl = 1;
	_nop_();
	sda = 1;
	_nop_();
}
char IIC_ACK(){
	char flag;
	sda = 1;//释放数据线
	_nop_();
	scl = 1;
	_nop_();
	flag = sda;
	_nop_();
	scl = 0;
	_nop_();
	return flag;
}
void IIC_Send_Byte(char dataSend){
	int i;
	for(i = 0;i<8;i++){
		scl = 0;//scl拉低
		sda =dataSend & 0x80;//获取最高位
		_nop_();//发送数据建立时间
		scl = 1;//scl拉高开始发送
		_nop_();//数据发送时间
		scl = 0;//发送完毕拉低
		_nop_();
		dataSend = dataSend << 1;
	}
}
//写命令函数
void Oled_Write_Cmd(dataCmd){
	//1. start()
	IIC_Start();
  //2. 写入 b0111 1000 0x78
	IIC_Send_Byte(0x78);
  //3. ACK
	IIC_ACK();
  //4. cotrol byte: (0)(0)000000 写入命令   (0)(1)000000写入数据
  IIC_Send_Byte(0x00);
  //5. ACK
	IIC_ACK();
  //6. 写入指令/数据
  IIC_Send_Byte(dataCmd);
  //7. ACK
	IIC_ACK();
  //8. STOP
	IIC_Stop();
}
//写数据函数
void Oled_Write_Data(dataData){
	//1. start()
	IIC_Start();
  //2. 写入 b0111 1000 0x78
	IIC_Send_Byte(0x78);
  //3. ACK
	IIC_ACK();
  //4. cotrol byte: (0)(0)000000 写入命令   (0)(1)000000写入数据
  IIC_Send_Byte(0x40);
  //5. ACK
	IIC_ACK();
  //6. 写入指令/数据
  IIC_Send_Byte(dataData);
  //7. ACK
	IIC_ACK();
  //8. STOP
	IIC_Stop();
}
//Oled初始化
void Oled_Init(void){
	Oled_Write_Cmd(0xAE);//--display off
	Oled_Write_Cmd(0x00);//---set low column address
	Oled_Write_Cmd(0x10);//---set high column address
	Oled_Write_Cmd(0x40);//--set start line address  
	Oled_Write_Cmd(0xB0);//--set page address
	Oled_Write_Cmd(0x81); // contract control
	Oled_Write_Cmd(0xFF);//--128   
	Oled_Write_Cmd(0xA1);//set segment remap 
	Oled_Write_Cmd(0xA6);//--normal / reverse
	Oled_Write_Cmd(0xA8);//--set multiplex ratio(1 to 64)
	Oled_Write_Cmd(0x3F);//--1/32 duty
	Oled_Write_Cmd(0xC8);//Com scan direction
	Oled_Write_Cmd(0xD3);//-set display offset
	Oled_Write_Cmd(0x00);//
	
	Oled_Write_Cmd(0xD5);//set osc division
	Oled_Write_Cmd(0x80);//
	
	Oled_Write_Cmd(0xD8);//set area color mode off
	Oled_Write_Cmd(0x05);//
	
	Oled_Write_Cmd(0xD9);//Set Pre-Charge Period
	Oled_Write_Cmd(0xF1);//
	
	Oled_Write_Cmd(0xDA);//set com pin configuartion
	Oled_Write_Cmd(0x12);//
	
	Oled_Write_Cmd(0xDB);//set Vcomh
	Oled_Write_Cmd(0x30);//
	
	Oled_Write_Cmd(0x8D);//set charge pump enable
	Oled_Write_Cmd(0x14);//
	
	Oled_Write_Cmd(0xAF);//--turn on oled panel		
}

void Oled_Clear(){
	int i,j;
	for(i=0;i<8;i++){
		//从page0-page7
		Oled_Write_Cmd(0xB0+i);
		//每隔page从0列开始
		Oled_Write_Cmd(0x00);
		Oled_Write_Cmd(0x10);
		for(j=0;j<128;j++){
			Oled_Write_Data(0);
		}
	}

}

/*--  文字:  嵌  --*/
/*--  宋体12;  此字体下对应的点阵为:宽x高=16x16   --*/
code char q1[16]={0x80,0x80,0xEE,0x88,0x88,0x88,0xE8,0x8F,0x08,0x88,0x78,0x48,0x4E,0x40,0xC0,0x00};
code char q2[16]={0x00,0x00,0x7F,0x24,0x24,0x24,0x7F,0x00,0x81,0x40,0x30,0x0F,0x30,0x41,0x80,0x00};
/*--  文字:  入  --*/
/*--  宋体12;  此字体下对应的点阵为:宽x高=16x16   --*/
code char r1[16]={0x00,0x00,0x00,0x00,0x00,0x01,0xE2,0x1C,0xE0,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
code char r2[16]={0x80,0x40,0x20,0x10,0x0C,0x03,0x00,0x00,0x00,0x03,0x0C,0x30,0x40,0x80,0x80,0x00};
/*--  文字:  式  --*/
/*--  宋体12;  此字体下对应的点阵为:宽x高=16x16   --*/
code char s1[16]={0x10,0x10,0x90,0x90,0x90,0x90,0x90,0x10,0x10,0xFF,0x10,0x10,0x11,0x16,0x10,0x00};
code char s2[16]={0x00,0x20,0x60,0x20,0x3F,0x10,0x10,0x10,0x00,0x03,0x0C,0x10,0x20,0x40,0xF8,0x00};
/*--  文字:  软  --*/
/*--  宋体12;  此字体下对应的点阵为:宽x高=16x16   --*/
code char u1[16]={0x08,0xC8,0xB8,0x8F,0xE8,0x88,0x88,0x40,0x30,0x0F,0xC8,0x08,0x28,0x18,0x00,0x00};
code char u2[16]={0x08,0x18,0x08,0x08,0xFF,0x04,0x84,0x40,0x30,0x0E,0x01,0x0E,0x30,0x40,0x80,0x00};
/*--  文字:  件  --*/
/*--  宋体12;  此字体下对应的点阵为:宽x高=16x16   --*/
code char j1[16]={0x00,0x80,0x60,0xF8,0x07,0x80,0x60,0x1C,0x10,0x10,0xFF,0x10,0x10,0x10,0x00,0x00};
code char j2[16]={0x01,0x00,0x00,0xFF,0x00,0x02,0x02,0x02,0x02,0x02,0xFF,0x02,0x02,0x02,0x02,0x00};
/*--  文字:  开  --*/
/*--  宋体12;  此字体下对应的点阵为:宽x高=16x16   --*/
code char k1[16]={0x80,0x82,0x82,0x82,0xFE,0x82,0x82,0x82,0x82,0x82,0xFE,0x82,0x82,0x82,0x80,0x00};
code char k2[16]={0x00,0x80,0x40,0x30,0x0F,0x00,0x00,0x00,0x00,0x00,0xFF,0x00,0x00,0x00,0x00,0x00};
/*--  文字:  发  --*/
/*--  宋体12;  此字体下对应的点阵为:宽x高=16x16   --*/
code char f1[16]={0x00,0x00,0x18,0x16,0x10,0xD0,0xB8,0x97,0x90,0x90,0x90,0x92,0x94,0x10,0x00,0x00};
code char f2[16]={0x00,0x20,0x10,0x8C,0x83,0x80,0x41,0x46,0x28,0x10,0x28,0x44,0x43,0x80,0x80,0x00};
void main(){
		int i;
		//1. OLED初始化
		Oled_Init();
		//2. 选择一个位置
		//2.1 确认页寻址模式
		Oled_Write_Cmd(0x20);
		Oled_Write_Cmd(0x02);
		//清屏操作
		Oled_Clear();
		//2.2 选择PAGE0   1011 0000
		Oled_Write_Cmd(0xB0);
	  //列地址选择
		Oled_Write_Cmd(0x00);
		Oled_Write_Cmd(0x10);
		//3. 显示page0内容
		for(i=0;i<16;i++){
			Oled_Write_Data(q1[i]);
		}
		for(i=0;i<16;i++){
			Oled_Write_Data(r1[i]);
		}
		for(i=0;i<16;i++){
			Oled_Write_Data(s1[i]);
		}
		for(i=0;i<16;i++){
			Oled_Write_Data(u1[i]);
		}
		for(i=0;i<16;i++){
			Oled_Write_Data(j1[i]);
		}
		for(i=0;i<16;i++){
			Oled_Write_Data(k1[i]);
		}
		for(i=0;i<16;i++){
			Oled_Write_Data(f1[i]);
		}
		
		//2.2 选择PAGE1   1011 0001
		Oled_Write_Cmd(0xB1);
	  //列地址选择
		Oled_Write_Cmd(0x00);
		Oled_Write_Cmd(0x10);
		//3. 显示page1内容
		for(i=0;i<16;i++){
			Oled_Write_Data(q2[i]);
		}
		for(i=0;i<16;i++){
			Oled_Write_Data(r2[i]);
		}
		for(i=0;i<16;i++){
			Oled_Write_Data(s2[i]);
		}
		for(i=0;i<16;i++){
			Oled_Write_Data(u2[i]);
		}
		for(i=0;i<16;i++){
			Oled_Write_Data(j2[i]);
		}
		for(i=0;i<16;i++){
			Oled_Write_Data(k2[i]);
		}
		for(i=0;i<16;i++){
			Oled_Write_Data(f2[i]);
		}
		while(1);
}

104.OLED显示图片

(1)像素64*64,保存生成bmp格式图片;字模软件打开图片,生成点阵;8个page页,64列进行扫描;(IIC_picture_display.c)
(2)变量长度不够用unsigned,存储空间由RAM改为ROM用code定义变量;

#include "reg52.h"
#include "intrins.h"
sbit scl = P0^1;
sbit sda = P0^3;
void IIC_Start(){
	scl = 0;//防雪花
	sda = 1;
	scl = 1;
	_nop_();
	sda = 0;
	_nop_();
}
void IIC_Stop(){
	scl = 0;//防雪花
	sda = 0;
	scl = 1;
	_nop_();
	sda = 1;
	_nop_();
}
char IIC_ACK(){
	char flag;
	sda = 1;//释放数据线
	_nop_();
	scl = 1;
	_nop_();
	flag = sda;
	_nop_();
	scl = 0;
	_nop_();
	return flag;
}
void IIC_Send_Byte(char dataSend){
	int i;
	for(i = 0;i<8;i++){
		scl = 0;//scl拉低
		sda =dataSend & 0x80;//获取最高位
		_nop_();//发送数据建立时间
		scl = 1;//scl拉高开始发送
		_nop_();//数据发送时间
		scl = 0;//发送完毕拉低
		_nop_();
		dataSend = dataSend << 1;
	}
}
//写命令函数
void Oled_Write_Cmd(dataCmd){
	//1. start()
	IIC_Start();
  //2. 写入 b0111 1000 0x78
	IIC_Send_Byte(0x78);
  //3. ACK
	IIC_ACK();
  //4. cotrol byte: (0)(0)000000 写入命令   (0)(1)000000写入数据
  IIC_Send_Byte(0x00);
  //5. ACK
	IIC_ACK();
  //6. 写入指令/数据
  IIC_Send_Byte(dataCmd);
  //7. ACK
	IIC_ACK();
  //8. STOP
	IIC_Stop();
}
//写数据函数
void Oled_Write_Data(dataData){
	//1. start()
	IIC_Start();
  //2. 写入 b0111 1000 0x78
	IIC_Send_Byte(0x78);
  //3. ACK
	IIC_ACK();
  //4. cotrol byte: (0)(0)000000 写入命令   (0)(1)000000写入数据
  IIC_Send_Byte(0x40);
  //5. ACK
	IIC_ACK();
  //6. 写入指令/数据
  IIC_Send_Byte(dataData);
  //7. ACK
	IIC_ACK();
  //8. STOP
	IIC_Stop();
}
//Oled初始化
void Oled_Init(void){
	Oled_Write_Cmd(0xAE);//--display off
	Oled_Write_Cmd(0x00);//---set low column address
	Oled_Write_Cmd(0x10);//---set high column address
	Oled_Write_Cmd(0x40);//--set start line address  
	Oled_Write_Cmd(0xB0);//--set page address
	Oled_Write_Cmd(0x81); // contract control
	Oled_Write_Cmd(0xFF);//--128   
	Oled_Write_Cmd(0xA1);//set segment remap 
	Oled_Write_Cmd(0xA6);//--normal / reverse
	Oled_Write_Cmd(0xA8);//--set multiplex ratio(1 to 64)
	Oled_Write_Cmd(0x3F);//--1/32 duty
	Oled_Write_Cmd(0xC8);//Com scan direction
	Oled_Write_Cmd(0xD3);//-set display offset
	Oled_Write_Cmd(0x00);//
	
	Oled_Write_Cmd(0xD5);//set osc division
	Oled_Write_Cmd(0x80);//
	
	Oled_Write_Cmd(0xD8);//set area color mode off
	Oled_Write_Cmd(0x05);//
	
	Oled_Write_Cmd(0xD9);//Set Pre-Charge Period
	Oled_Write_Cmd(0xF1);//
	
	Oled_Write_Cmd(0xDA);//set com pin configuartion
	Oled_Write_Cmd(0x12);//
	
	Oled_Write_Cmd(0xDB);//set Vcomh
	Oled_Write_Cmd(0x30);//
	
	Oled_Write_Cmd(0x8D);//set charge pump enable
	Oled_Write_Cmd(0x14);//
	
	Oled_Write_Cmd(0xAF);//--turn on oled panel		
}

void Oled_Clear(){
	int i,j;
	for(i=0;i<8;i++){
		//从page0-page7
		Oled_Write_Cmd(0xB0+i);
		//每隔page从0列开始
		Oled_Write_Cmd(0x00);
		Oled_Write_Cmd(0x10);
		for(j=0;j<128;j++){
			Oled_Write_Data(0);
		}
	}

}

/*--  调入了一幅图像:花朵  --*/
/*--  宽度x高度=64x64  --*/
code unsigned char bmpImager[]={
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0xC0,0x40,0x80,0x00,0x80,0xC0,0x40,0xC0,0x80,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x80,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,
0x80,0xF0,0x80,0x80,0x40,0x40,0xE0,0x40,0x60,0x20,0x20,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0xC0,0x40,0x40,
0x40,0x40,0x40,0x5F,0x70,0xC0,0x83,0x7F,0x78,0x20,0x20,0x23,0x3E,0x70,0x48,0xC6,
0x63,0x71,0x51,0x59,0x4D,0x43,0x40,0xC0,0x80,0x00,0x00,0x00,0x00,0x00,0x81,0x81,
0xC0,0x30,0x1F,0x0C,0x00,0x80,0xFB,0xC0,0x40,0x40,0x30,0x10,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x03,0x04,0x04,
0x84,0xCC,0x68,0x28,0x38,0x1F,0x10,0x20,0x20,0xC0,0xC0,0x40,0x40,0x20,0x20,0xF0,
0x8F,0x06,0x06,0x07,0x85,0x85,0x8D,0xD9,0x70,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x3F,0x00,0x00,0x00,0x01,0x01,0x3F,0x20,0x20,0x20,0x30,0x10,0x18,0x08,0x0E,0x00,
0x00,0x00,0x80,0x80,0x80,0x80,0xC0,0x40,0x60,0x30,0x10,0x10,0x20,0x60,0x80,0x00,
0x03,0x06,0x0C,0x08,0x08,0x0C,0x04,0x06,0x03,0xFF,0xFF,0x0E,0x18,0x10,0x1C,0x07,
0x01,0x01,0x01,0x01,0x81,0x80,0xC0,0x40,0x60,0x20,0x20,0x20,0x20,0x20,0x20,0x20,
0x60,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x01,0x02,0x02,0x04,0x08,0x08,0x10,0x30,0x60,0xC0,0x80,0x81,0x03,
0x06,0x0C,0x30,0x60,0xC0,0x00,0x00,0x00,0x00,0xFF,0xFF,0x00,0x00,0x80,0x40,0x30,
0x18,0x0C,0x06,0x03,0x01,0x00,0x00,0x00,0x00,0x80,0x80,0xC0,0x40,0x20,0x30,0x10,
0x18,0x0D,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x03,
0x06,0x0C,0x18,0x30,0x61,0x43,0xC6,0x98,0xF0,0xFF,0xFF,0x7C,0x26,0x31,0x18,0x08,
0x08,0x0C,0x04,0x04,0x06,0x02,0x03,0x01,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
};

void Oled_Show_Image(unsigned char *image){
	unsigned char i;
	unsigned int j;
	for(i=0;i<8;i++){
		//从page0-page7
		Oled_Write_Cmd(0xB0+i);
		//每隔page从0列开始
		Oled_Write_Cmd(0x00);
		Oled_Write_Cmd(0x10);
		for(j=64*i;j<(64*(i+1));j++){
			Oled_Write_Data(image[j]);
		}
		
	}
	
}
void main(){
		//1. OLED初始化
		Oled_Init();
		//2. 选择一个位置
		//2.1 确认页寻址模式
		Oled_Write_Cmd(0x20);
		Oled_Write_Cmd(0x02);
		//清屏操作
		Oled_Clear();
		Oled_Show_Image(bmpImager);
		while(1);
}
  • 28
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值