51单片机

STC89C52

  • 一.定时器
    •  1.介绍
    •  2.计时
    •  2.定时器寄存器
      • [  2.1 定时器控制寄存器TCON](#  2.1 定时器控制寄存器TCON)
      • [  2.2 定时器模式寄存器TMOD](#  2.2 定时器模式寄存器TMOD)
      • [  2.3 定时器如何定时10毫秒](#  2.3 定时器如何定时10毫秒)
      • [  2.4 定时器寄存器配置](#  2.4 定时器寄存器配置)
        • [    2.4.1 TCON](#    2.4.1 TCON)
        • [    2.4.2 TMOD](#    2.4.2 TMOD)
        • [    2.4.3 实现](#    2.4.3 实现)
        • [    2.4.5 按位操作](#    2.4.5 按位操作)
    •  3.定时器中断
      • [  3.1 定时器中断相关寄存器](#  3.1 定时器中断相关寄存器)
        • [    3.1.1 IE 中断允许寄存器](#    3.1.1 IE 中断允许寄存器)
    •  4.PWM开发SG90
      • [  4.1 控制舵机](#  4.1 控制舵机)
      • [  4.2 IO输出模拟PWM](#  4.2 IO输出模拟PWM)
      • [  4.3 舵机控制](#  4.3 舵机控制)
      • [  4.4 超声波测距](#  4.4 超声波测距)
    •  5.智能垃圾桶
  • 二.串口
    •  1.串口编程要素
    • [  2.串行通信](#  2.串行通信)
      • [  2.1 电源控制寄存器PCON](#  2.1 电源控制寄存器PCON)
      • [  2.2 串行控制寄存器SCON](#  2.2 串行控制寄存器SCON)
      • [  2.3 波特率计算](#  2.3 波特率计算)
      • [  2.4 初始化代码](#  2.4 初始化代码)
      • [  2.5 发送字符串](#  2.5 发送字符串)
      • [  2.6 接收数据](#  2.6 接收数据)
      • [  2.7 点灯](#  2.7 点灯)
    • [  3.串口中断](#  3.串口中断)
      • [  3.1 串口支持单词型指令](#  3.1 串口支持单词型指令)
      • [  3.2 发送序列](#  3.2 发送序列)
    • [  4.EPS8266](#  4.EPS8266)
      • [  4.1 单片机发送AT指令入网](#  4.1 单片机发送AT指令入网)
      • [  4.2 ESP8266当服务器](#  4.2 ESP8266当服务器)
    • [  5. EC03-DNC4G通信模块](#  5. EC03-DNC4G通信模块)
      • [  5.1 4G模块配置连接服务器](#  5.1 4G模块配置连接服务器)
      • [  5.2 4G点灯](#  5.2 4G点灯)
    • [  6. LCD1602](#  6. LCD1602)
      • [  6.1 写操作](#  6.1 写操作)
      • [  6.2 读操作](#  6.2 读操作)
      • [  6.3 显示一个字符](#  6.3 显示一个字符)
      • [  6.4 显示一行字符](#  6.4 显示一行字符)
    • [  7. DHT11温湿度传感器](#  7. DHT11温湿度传感器)
      • [  7.1 检测模块是否存在](#  7.1 检测模块是否存在)
      • [  7.2 读取数据](#  7.2 读取数据)
      • [  7.3 串口](#  7.3 串口)
      • [  7.4 温湿度数据LCD1602显示温度大于24度风扇转](#  7.4 温湿度数据LCD1602显示温度大于24度风扇转)
      • [  7.5 分文件操作](#  7.5 分文件操作)
  • 三.IIC协议
    •  1.起始信号
    •  2.应答信号
    •  3.数据发送
    •  4.OLED
      • [  4.1 OLED写入指令和数据](#  4.1 OLED写入指令和数据)
      • [  4.2 OLED页寻址模式](#  4.2 OLED页寻址模式)
      • [  4.3 清屏](#  4.3 清屏)
      • [  4.4 显示字符A](#  4.4 显示字符A)
      • [  4.4 显示多个字](#  4.4 显示多个字)
  • 四.智能小车
    •  1.电机驱动
      • [  1.1 L9110S控制小车](#  1.1 L9110S控制小车)
      • [  1.2 串口控制小车](#  1.2 串口控制小车)
      • [  1.3 蓝牙控制小车点动](#  1.3 蓝牙控制小车点动)
      • [  1.4 小车PWM调速](#  1.4 小车PWM调速)
      • [  1.5 小车PWM调速转弯](#  1.5 小车PWM调速转弯)
    •  2.循迹小车
    •  3.跟踪小车==循迹小车
    •  4.摇头避障小车
      • [  4.1 摇头(舵机)](#  4.1 摇头(舵机))
      • [  4.2 测距(超声波测距)](#  4.2 测距(超声波测距))
      • [  4.3 移动(电机驱动)](#  4.3 移动(电机驱动))
      • [  4.3 摇头测距避障(舵机+超声波测距+电机驱动)](#  4.3 摇头测距避障(舵机+超声波测距+电机驱动))
    •  5.测速小车+远程控制
      • [  5.1 测速并上传上位机](#  5.1 测速并上传上位机)
      • [  5.2 蓝牙控制小车且OLED显示速度和蓝牙获取速度数据](#  5.2 蓝牙控制小车且OLED显示速度和蓝牙获取速度数据)
      • [  5.3 WIFI控制小车且OLED显示速度和WIFI获取速度数据](#  5.3 WIFI控制小车且OLED显示速度和WIFI获取速度数据)
      • [  5.4 4G控制小车且OLED显示速度和4G获取速度数据](#  5.4 4G控制小车且OLED显示速度和4G获取速度数据)
      • [  5.5 以上都一样只是模块的配置和模块发送控制信号的方式不同](#  5.5 以上都一样只是模块的配置和模块发送控制信号的方式不同)
    •  6.语音小车

一.定时器

1.介绍

c 复制代码
	C51中的定时器和计数器是同一个硬件电路支持的,通过寄存器配置不同,就可以将他当做定时器或者计数器使用。
	
	定时器和计数器的区别在于其+1信号的不同
	定时器:当配置为定时器使用时,每经过1个机器周期,计数存储器的值就加1
	计数器:当配置为计数器时,每来一个负跳变信号(信号从P3.4 或者P3.5引脚输入),就加1
	
	标准C51有2个定时器/计数器:T0和T1。他们的使用方法一致。C52相比C51多了一个T2

2.计时

c 复制代码
	定时器每经过一个机器周期寄存器+1
	
	晶振:晶振(晶体震荡器),又称数字电路的"心脏",是各种电子产品里面必不可少的频率元器件。
		 数字电路的所有工作都离不开时钟,晶振的好坏、晶振电路设计的好坏,会影响到整个系统的稳定性。
	
	时钟周期:时钟周期也称为振荡周期,定义为时钟频率的倒数(晶振频率的倒数)。
			 时钟周期是计算机中最基本的、最小的时间单位。在一个时钟周期内,CPU仅完成一个最基本的动作。
			 时钟周期是一个时间的量。更小的时钟周期就意味着更高的工作频率
	
	机器周期:机器周期也称为CPU周期。
			 在计算机中,为了便于管理,常把一条指令的执行过程划分为若干个阶段(如取指、译码、执行等),每一阶段完成一个基本操作。
			 完成一个基本操作所需要的时间称为机器周期。一般情况下,一个机器周期由若干个时钟周期组成


c 复制代码
	在12T时:
	当晶振频率是11.0592MHz的时候,等于11059.2KHz = 11059200Hz
	机器周期 = 12 x 时钟周期 =12 x (1/时钟频率) 秒 
			= 12 / 时钟频率 秒 
			= 12 / 11059200 秒 
			= 12 000 000 / 11059200 微秒 = 1.085 微秒

	以此:如果要定时20ms寄存器要加多少个1(机器周期)?
		 + 20000 / 1.085个机器周期

2.定时器寄存器

2.1 定时器控制寄存器TCON

c 复制代码
	TCON --TimerControl
	TF0 -- Timer 0 Flag    -- TO溢出标志位
	TF1 -- Timer 1 Flag1   -- T1溢出标志位
	TR0 -- Timer 0 Run
	TR1 -- Timer 1 Run

	TR开始计时, TF溢出计时完毕停止计时

2.2 定时器模式寄存器TMOD

c 复制代码
	以00B为例
	可组成: 00,01,10,11 四种相当于1234,可以数到4 计数4次
	计数一次 一个机器周期1.085 微秒
	四次则	4*1.085
	如果从 00000000B开始 到 11111111B结束
	则有2^8种 则可计数 2^8*1.086微秒
	也可设置从 0000 1000B 到 11111111B结束

	所以当只使用低8位时
	2^8 后标志位溢出
	当低8为+高8位时为16位
	2^16 后标志位溢出---最高计数时间71ms

2.3 定时器如何定时10毫秒

c 复制代码
	已知,采用16位定时器寄存器最大计数为65536
	65536 * 1.085 us = 71 ms
	10 ms = 10000 us
	10000 / 1.085 = 9216 次
	65536 - 9216 = 56320 次
	所以从初始值的56320次跳到65536次为10ms
	所以应当配置的初始值为
	TL0 = 00;
	TH0 = DC;
注:溢出后计时结束(计数结束),所以需要从65536终值去算初值 

2.4 定时器寄存器配置

2.4.1 TCON
c 复制代码
定时器有两个TO,T1
	采用T0计时器
	
	1.怎知道溢出?
	TCON的B5 TF0-溢出标志位溢出后会硬件置1,如果不用中断,代码清零
	
	2.怎么开始?
	TCON的B4 TR0-置位由软件置位
			 TR0 = 1 定时器T0开始计数
			 TR0 = 0 定时器T0停止计数
	
	3.怎么计算初值
	采用TL0+TH0最大计数值65536
	如计算时间10ms
	10ms = 10000us
	10000 / 1.085 = 9216 次
	65536 - 9216 = 56320 次
	所以初值为56320 - 16进制数表示为 DC00;
	TL0 = 00
	TH0 = DC
	
2.4.2 TMOD
c 复制代码
	四种工作模式:具体如上
	M1 M0
	0  0
	0  1
	1  0
	1  1
	选择工作方式1, TMOD的1(M1)配置为0, TOMD的0(M0)配置为1
2.4.3 实现
c 复制代码
#include "reg52.h"
#include <intrins.h>
sbit led1 = P3^7;
sbit led2 = P3^6;

void delay10ms()
{
	//1.配置定时器0工作模式1--16位计时 M1 = 0, M0=0
	TMOD &= 0xF0;
	TMOD |= 0x01;
	//2.设置初值,定10ms
	TL0 = 0x00;
	TH0 = 0xDC;
	//3.开始计时
	TR0 = 1;
	TF0 = 0;
}
void main()
{
	int cnt;
	led1 = 1;

	delay10ms();
	//4.溢出了,操作led,累计到1s,再操作led
			//如何累计到1s? 定义变量+1, +100次为1s,每隔1s转换led状态
	while(1){
		if(TF0 == 1){	//溢出了,硬件置1,如果不用中断则代码清零
			TF0 = 0;	//不用中断必须软件清零
			//重新设置初值
			TL0 = 0x00;
			TH0 = 0xDC;
			cnt++;
			if(cnt == 100){//经过1s翻转LED的状态
				cnt = 0;
				led2 = !led2;
			}
		}
	}
}
2.4.5 按位操作
c 复制代码
void Timer0Init(void)
{
	TMOD &= 0xF0;		//保存定时器1的模式,清零定时器0
	TMOD |= 0x01;		//设置定时器0工作模式1
	TL0 = 0x00;		//设置定时初值
	TH0 = 0xDC;		//设置定时初值
	TF0 = 0;		//清除TF0标志
	TR0 = 1;		//定时器0开始计时
}

3.定时器中断

c 复制代码
	在上文中采用的是查询的方式处理定时器
	由于定时器采用中断后,TF溢出后自动置0
	因此采用定时器中断更方便处理


c 复制代码
	通过上图可以看到我们只要用Timer0中断
	需要配置ET0,EA
	

3.1 定时器中断相关寄存器

3.1.1 IE 中断允许寄存器
c 复制代码
	ETO中断允许置1	ET = 0
	EA总中断置1		EA = 1
	中断号			interrupt 1
c 复制代码
	首先对定时器进行相关配置:void Timer0Init()
	当定时器TF标志位溢出后会自动跳入中断函数:void Timer0Handler() interrupt 1,并自动将TF清零,进行下一次计时
	相关处理在中断函数中进行


#include "reg52.h"
#include <intrins.h>
sbit led1 = P3^7;
sbit led2 = P3^6;

int cnt = 0;
void Timer0Init()
{
	//1.配置定时器0工作模式1--16位计时 M1 = 0, M0=0
	TMOD &= 0xF0;
	TMOD |= 0x01;
	//2.设置初值,定10ms
	TL0 = 0x00;
	TH0 = 0xDC;
	//3.开始计时
	TR0 = 1;
	TF0 = 0;
	//4.打开定时器0中断
	ET0 = 1;
	//5.打开总中断
	EA = 1;
}
void main()
{
	led1 = 1;
	Timer0Init();
	while(1);
}

void Timer0Handler() interrupt 1
{
	cnt++;
	//重新赋初值进行下一次计数		
	TL0 = 0x00;
	TH0 = 0xDC;

	if(cnt == 100){//经过1s翻转LED的状态
		cnt = 0;
		led2 = !led2;
	}
}

4.PWM开发SG90

c 复制代码
	PWM:英文名Pulse Width Modulation,是脉冲宽度调制缩写,它是通过对一系列脉冲的宽度进行调制,等效出所需要的波形(包含形状以及幅值),对模拟信号电平进行数字编码,也就是说通过调节占空比的变化来调节信号、能量等的变化,占空比就是指在一个周期内,信号处于高电平的时间占据整个信号周期的百分比,例如方波的占空比就是50%.
	
	通过占空比编码模拟信号
	
	占空比:一个周期内,高电平占据时长的百分比

4.1 控制舵机

c 复制代码
	即20ms的一个周期内,持续不同时间的高电平舵机的转向不同
	一个周期: 20ms
	最小时间单位: 0.5ms
	因此定时器的定时时间应该是0.5ms

4.2 IO输出模拟PWM

c 复制代码
#include "reg52.h"
#include <intrins.h>

sbit sg90_con = P1^1;

int cnt = 0;
void Timer0Init()
{
	//1.配置定时器0工作模式1--16位计时 M1 = 0, M0=0
	TMOD &= 0xF0;
	TMOD |= 0x01;
	//2.设置初值,定0.5ms
	TL0 = 0x33;
	TH0 = 0xFE;
	//3.开始计时
	TR0 = 1;
	TF0 = 0;
	//4.打开定时器0中断
	ET0 = 1;
	//5.打开总中断
	EA = 1;
}
void main()
{
	Timer0Init();
	sg90_con = 1;
	while(1){

	}
}

void Timer0Handler() interrupt 1
{
	cnt++;	//cnt = 1溢出了一次
	//重新赋初值
	TL0 = 0x33;
	TH0 = 0xFE;
	if(cnt < 1){
		sg90_con = 1;
	}else{
		sg90_con = 0;
	}
	
	if(cnt == 40){//经过20ms
		cnt = 0;
		sg90_con = 1;
	}
}
c 复制代码
#include "reg52.h"
#include <intrins.h>

sbit sg90_con = P1^1;

int cnt = 0;
void Timer0Init()
{
	//1.配置定时器0工作模式1--16位计时 M1 = 0, M0=0
	TMOD &= 0xF0;
	TMOD |= 0x01;
	//2.设置初值,定0.5ms
	TL0 = 0x33;
	TH0 = 0xFE;
	//3.开始计时
	TR0 = 1;
	TF0 = 0;
	//4.打开定时器0中断
	ET0 = 1;
	//5.打开总中断
	EA = 1;
}
void main()
{
	Timer0Init();
	sg90_con = 1;
	while(1){

	}
}

void Timer0Handler() interrupt 1
{
	cnt++;	//cnt = 1溢出了一次
	//重新赋初值
	TL0 = 0x33;
	TH0 = 0xFE;
	if(cnt < 4){
		sg90_con = 1;
	}else{
		sg90_con = 0;
	}
	
	if(cnt == 40){//经过20ms
		cnt = 0;
		sg90_con = 1;
	}
}

4.3 舵机控制

c 复制代码
#include "reg52.h"
#include <intrins.h>

sbit sg90_con = P1^1;

int jd;
int cnt = 0;

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 Timer0Init()
{
	//1.ÅäÖö¨Ê±Æ÷0¹¤×÷ģʽ1--16Î>>¼ÆÊ± M1 = 0, M0=0
	TMOD &= 0xF0;
	TMOD |= 0x01;
	//2.ÉèÖóõÖµ£¬¶¨0.5ms
	TL0 = 0x33;
	TH0 = 0xFE;
	//3.¿ªÊ¼¼ÆÊ±
	TR0 = 1;
	TF0 = 0;
	//4.´ò¿ª¶¨Ê±Æ÷0ÖжÏ
	ET0 = 1;
	//5.´ò¿ª×ÜÖжÏ
	EA = 1;
}
void main()
{
	Delay500ms();
	Timer0Init();
	jd = 1;
	cnt = 0;

	sg90_con = 1;
	while(1){
		jd = 4;
		cnt = 0;	//½Ç¶È±äÁËcnt´ÓÍ·¿ªÊ¼
		Delay500ms();
		jd = 1;
		cnt = 0;
		Delay500ms();
	}
}

void Timer0Handler() interrupt 1
{
	cnt++;	//cnt = 1Òç³öÁËÒ>>´Î
	//ÖØÐ¸³³õÖµ
	TL0 = 0x33;
	TH0 = 0xFE;
	if(cnt < jd){
		sg90_con = 1;
	}else{
		sg90_con = 0;
	}
	
	if(cnt == 40){//¾­¹ý20ms
		cnt = 0;
		sg90_con = 1;
	}
}

4.4 超声波测距


c 复制代码
#include "reg52.h"
#include <intrins.h>


//距离小于10cm D5亮,D6灭,反之相反
sbit D5 = P3^7;
sbit D6 = P3^6;
sbit Trig = P1^5;
sbit Echo = P1^6;

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 Delay10us()		//@11.0592MHz
{
	unsigned char i;

	i = 3;
	while (--i);
}
void Timer0Init()
{
	TMOD &= 0xF0;		
	TMOD |= 0x01;	
	TH0 = 0;
	TL0 = 0;
	//设置定时器0工作模式1,初始值设定0开始数数,不急启动定时器
}
void startHc()
{
	Trig = 0;
	Trig = 1;
	Delay10us();	//软件延时
	Trig = 0;
}
double get_distance()
{
		double time;
	//定时器数据清0
		TH0 = 0;
		TL0 = 0;

	//1.给一个10us秒的脉冲
		startHc();
		//2.由低电平跳转到高电平,表示开始测距
			
		while(Echo == 0);
			
		//2.1 波发送后启动定时器
		TR0 = 1;
		
		//3.由高电平转回低电平,表示测距完毕
		while(Echo == 1);
			//3.1 波回来,停止定时器
		TR0 = 0;
		//4.计算时间
			//将两个8位寄存器合起来  TH0 + TL0  ==  TH0左移8位 TH0*256 + TL0
		time = (TH0 * 256 + TL0)*1.085; //us为单位
		//5.计算距离	= 速度(340m/s) * 时间/2
		return time * 0.017;
}

void openStatusLight()
{
	D5 = 0;
	D6 = 1;
	
}
void closeStatusLight()
{
	D5 = 1;
	D6 = 0;
}
void main()
{

	double dis;
	Delay500ms();
	Timer0Init();
	
	while(1){
		dis = get_distance();

		if(dis < 10){
			openStatusLight();
		}else{
			closeStatusLight();
		}
	}
}
//十进制左移一位 * 10
//二进制左移一位 * 2,左移8位,乘以2^8=256

5.智能垃圾桶

c 复制代码
	1. 舵机和超声波代码整合
	舵机用定时器0
	超声波用定时器1
	实现物体靠近后,自动开盖,2秒后关盖
	2. 查询的方式添加按键控制
	3. 查询的方式添加震动控制
	4. 使用外部中断0配合震动控制
	5. 加入蜂鸣器
c 复制代码
#include "reg52.h"
#include <intrins.h>

//距离小于10cm D5亮,D6灭,反之相反
sbit D5  			= P3^7;
sbit D6   		= P3^6;
sbit Trig 		= P1^5;
sbit Echo 		= P1^6;
sbit sg90_con = P1^1;
sbit sw1  		= P2^1;
sbit vibrate 	= P3^2;
sbit beef 		= P2^0; 
int jd;
int cnt = 0;
int vibrateMark = 0;
char jd_bak;
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 Delay500ms()		//@11.0592MHz
{
	unsigned char i, j, k;

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


void Timer0Init()
{
	//1.配置定时器0工作模式1--16位计时 M1 = 0, M0=0
	TMOD &= 0xF0;
	TMOD |= 0x01;
	//2.设置初值,定0.5ms
	TL0 = 0x33;
	TH0 = 0xFE;
	//3.开始计时
	TR0 = 1;
	TF0 = 0;
	//4.打开定时器0中断
	ET0 = 1;
	//5.打开总中断
	EA = 1;
}

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

	i = 3;
	while (--i);
}
void Timer1Init()
{
	TMOD &= 0x0F;		
	TMOD |= 0x10;	
	TH1 = 0;
	TL1 = 0;
	//设置定时器0工作模式1,初始值设定0开始数数,不急启动定时器
}

void startHc()
{
	Trig = 0;
	Trig = 1;
	Delay13us();	//软件延时
	Trig = 0;
}

double get_distance()
{

	
	double time;
	//定时器数据清0
	TH1 = 0;
	TL1 = 0;
	//1.给一个10us秒的脉冲
	startHc();
	//2.由低电平跳转到高电平,表示开始测距
			
	while(Echo == 0);
			
	//2.1 波发送后启动定时器
	TR1 = 1;
		
	//3.由高电平转回低电平,表示测距完毕
	while(Echo == 1);
		//3.1 波回来,停止定时器
	TR1 = 0;
	//4.计算时间
		//将两个8位寄存器合起来  TH0 + TL0  ==  TH0左移8位 TH0*256 + TL0
	time = (TH1 * 256 + TL1)*1.085; //us为单位
	//5.计算距离	= 速度(340m/s) * 时间/2
	return time * 0.017;
}

void openStatusLight()
{
	D5 = 0;
	D6 = 1;
}
void closeStatusLight()
{
	D5 = 1;
	D6 = 0;
}

void initSG90_0()
{
	jd = 1;
	cnt = 0;
	sg90_con = 1;
}
void openDusbin()
{
	jd = 4;
	if(jd_bak != jd){
		cnt = 0;	//角度变了cnt从头开始
		beef = 0; //蜂鸣器响
		Delay150ms();
		beef = 1;
		Delay500ms();
	}
	jd_bak = jd;


}
void closeDusbin()
{
		jd = 1;
		jd_bak = jd;
		cnt = 0;
		Delay150ms();
}
void EX0Init()
{
	//打开外部中断
	EX0 = 1;
	IT0 = 0; //低电平触发, =1 下降沿触发
}
void main()
{

	double dis;
	
	Delay500ms();
	Timer0Init();
	Timer1Init();
	initSG90_0();
	EX0Init();
	

	while(1){
		//超声波测距
		dis = get_distance();
		if(dis < 10 || sw1 == 0 || vibrateMark == 1){
			openStatusLight();
			//开盖子
			openDusbin();
			vibrateMark = 0;
		}else{
			closeStatusLight();
			//关盖子
			closeDusbin();
		}
	}
}

void Timer0Handler() interrupt 1
{
	cnt++;	//cnt = 1溢出了一次
	//重新赋初值
	TL0 = 0x33;
	TH0 = 0xFE;
	if(cnt < jd){
		sg90_con = 1;
	}else{
		sg90_con = 0;
	}
	
	if(cnt == 40){//经过20ms
		cnt = 0;
		sg90_con = 1;
	}
}
void Ex0_Handler() interrupt 0
{
	vibrateMark = 1;
}
//十进制左移一位 * 10
//二进制左移一位 * 2,左移8位,乘以2^8=256

二.串口

1.串口编程要素

c 复制代码
输入/输出数据缓冲器都叫做SBUF, 都用99H地址码,但是是两个独立的8位寄存器

代码体现为: 想要接收数据 char data = SBUF 想要发送数据-即将数据写入到数据缓冲区即完成数据的发送SBUF = data

UART是异步串行接口,通信双方使用时钟不同,因为双方硬件配置不同,但是需要约定通信
速度,叫做波特率	-- 即你和别人吵架了,你讲话语速快了别听不懂,你讲话语速慢了,你吵不过别人,你和别人讲话的语速一样,你们才能吵得有来有回 -- 吵架的语速就是波特率,骂的脏话就是数据

2.串行通信

2.1 电源控制寄存器PCON

c 复制代码
	SMOD = 0; 波特率不加倍
	SMOD = 1; 波特率加倍

2.2 串行控制寄存器SCON




c 复制代码
	其中可变,体现在可装初值TH1
	不可变,则为可不可装初值TH1

2.3 波特率计算

c 复制代码
	例工作模式1,SMOD = 0 不加倍
	配置9600
	9600 = (2^0 / 32) * SYSclk(11059200) / 12 / (256 - TH1);
	算出TH1的初值 
	TH1 = 253 
	转成16进制
	TH1 = 0xFD;

	又因为自动重载
	所以
	TH1 = 0xFD;
	TL1 = 0xFD;

2.4 初始化代码

c 复制代码
void UartInit(void)
{
	SCON = 0x40;	//配置串口方式1,REN不使能接收
	//波特率由定时器1产生,配置定时器1,8位自动重载工作模式1
	TMOD &= 0x0F;
	TMOD |= 0x20; 	//配置定时器1,8位自动重载工作模式1
	TH1 = 0xFD;
	TL1 = 0xFD;	//9600定时器初值
	TR1 = 1;	//启动定时器
	
}

2.5 发送字符串

c 复制代码
#include "reg52.h"
#include "intrins.h"
sfr AUXR = 0x8E;
void UartInit(void)
{
	SCON = 0x40;	//配置串口方式1,REN不使能接收
	//波特率由定时器1产生,配置定时器1,8位自动重载工作模式1
	TMOD &= 0x0F;
	TMOD |= 0x20; 	//配置定时器1,8位自动重载工作模式1
	TH1 = 0xFD;
	TL1 = 0xFD;	//9600定时器初值
	TR1 = 1;	//启动定时器
	
}

void sendByte(char data_msg)
{
	SBUF = data_msg;
	while(!TI);	//发送完毕置1请求中断
	TI = 0;
}
void sendString(char *str)
{
	while(*str != '\0'){
		sendByte(*str);
		str++;
	}
}



void main()
{
	UartInit();
	while(1){
		Delay1000ms();
		//往发送缓冲区写入数据,就完成了数据的发送
		sendString("hello\r\n");
	}
}

2.6 接收数据

2.7 点灯

c 复制代码
void UartInit(void)
{
	SCON = 0x50;	//配置串口方式1,REN使能接收
	//波特率由定时器1产生,配置定时器1,8位自动重载工作模式1
	TMOD &= 0x0F;
	TMOD |= 0x20; 	//配置定时器1,8位自动重载工作模式1
	TH1 = 0xFD;
	TL1 = 0xFD;	//9600定时器初值
	TR1 = 1;	//启动定时器
	
}

	怎么知道接收完毕?
c 复制代码
	char data_msg;
	在发送中,我们知道
	SBUF = data_msg; //将数据存放在缓冲区中,数据发送完成
	那么在读取中
	data_msg = SBUF; //将缓冲区的数据存放在,data_msg中即为读取数据
c 复制代码
#include "reg52.h"
#include "intrins.h"

sbit D5 = P3^7;
void UartInit(void)
{
	SCON = 0x50;	//配置串口方式1,REN不使能接收
	//波特率由定时器1产生,配置定时器1,8位自动重载工作模式1
	TMOD &= 0x0F;
	TMOD |= 0x20; 	//配置定时器1,8位自动重载工作模式1
	TH1 = 0xFD;
	TL1 = 0xFD;	//9600定时器初值
	TR1 = 1;	//启动定时器
	
}

void sendByte(char data_msg)
{
	SBUF = data_msg;
	while(!TI);	//发送完毕置1请求中断
	TI = 0;
}
void sendString(char *str)
{
	while(*str != '\0'){
		sendByte(*str);
		str++;
	}
}



void main()
{
	char cmd;
	D5 = 1;
	UartInit();
	while(1){
		//往发送缓冲区写入数据,就完成了数据的发送
		sendString("hello\r\n");
		if(RI == 1){
			RI = 0; //软件复位
			cmd = SBUF;
			if(cmd == 'o'){
				D5 = 0;
			}
			if(cmd == 'c'){
				D5 = 1;
			}
		}
	}
}

3.串口中断


c 复制代码
	启用中断需要
	ES = 1;	//开启串口中断
	EA = 1;	//开启总中断
c 复制代码
#include "reg52.h"
#include "intrins.h"
char cmd;
sbit D5 = P3^7;
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)
{
	SCON = 0x50;	//配置串口方式1,REN不使能接收
	//波特率由定时器1产生,配置定时器1,8位自动重载工作模式1
	TMOD &= 0x0F;
	TMOD |= 0x20; 	//配置定时器1,8位自动重载工作模式1
	TH1 = 0xFD;
	TL1 = 0xFD;	//9600定时器初值
	TR1 = 1;	//启动定时器
		
	ES = 1;	//开启串口中断
	EA = 1;	//开启总中断
	
}

void sendByte(char data_msg)
{
	SBUF = data_msg;
	while(!TI);	//发送完毕置1请求中断
	TI = 0;
}
void sendString(char *str)
{
	while(*str != '\0'){
		sendByte(*str);
		str++;
	}
}



void main()
{

	D5 = 1;
	UartInit();
	while(1){
		Delay1000ms();
		//往发送缓冲区写入数据,就完成了数据的发送
		sendString("hello\r\n");
	}
}
//无论是接收引起的中断还是发送引起的中断都会进入到该函数中
void Uart_Handler() interrupt 4
{
	if(RI){	//接收引起的中断
		RI = 0; //软件复位
		cmd = SBUF;
		if(cmd == 'o'){
			D5 = 0;
		}
		if(cmd == 'c'){
			D5 = 1;
		}
	}
	
	if(TI);
}

3.1 串口支持单词型指令

c 复制代码
void Uart_Handler() interrupt 4
{
	static int i = 0;	//静态局部不会单独执行,只执行一次
	if(RI){	//接收引起的中断
		RI = 0; //软件复位
		cmd[i++] = SBUF;
		if(i == 12){
			i = 0;
		}
		if(strstr(cmd,"en")){	//找子串
			D5 = 0;
			i = 0;
			memset(cmd,'\0',12);
		}
		if(strstr(cmd,"cl")){
			D5 = 1;
			i = 0;
			memset(cmd,'\0',12);
		}
	}
	
	if(TI);
}

3.2 发送序列

c 复制代码
从上图可以看到
	发送时:
	START BIT = 0;
	STOP BIT = 1;
	TXD 从低8位开始发送
	例:a ASCII 97 二进制: 0110 0001 
	则在TXD发送时序列为
原:	0110 0001 
	0	1000 0110   1
 起始位            停止位


 	RXD同理 从低8位开始偏移发送

4.EPS8266

4.1 单片机发送AT指令入网

bash 复制代码
	AT
	AT+UART=9600,8,1,0,0	//设置波特率9600
	AT+CWMODE=3 //1. 是station(设备)模式 2.是AP(路由)模式 3.是双模
	AT+CWJAP="TP-LINK_3E30","18650711783" //指令
	AT+CIFSR //查询IP
	AT+CIPSTART="TCP","192.168.0.113",8888 //指令,注意双引号逗号都要半角(英文)输入,连接服务器
	AT+CIPMODE=1 //开启透传模式
	AT+CIPSEND //带回车
	//在透传发送数据过程中,若识别到单独的⼀包数据 "+++",则退出透传发送
c 复制代码
#include "reg52.h"
#include "intrins.h"
#include <string.h>

char buffer[12];
sbit D5 = P3^7;
sbit D6 = P3^6;
code char LJWL[] = "AT+CWJAP=\"qtcreator\",\"yfq4738619\"\r\n";
code char LJFWQ[] = "AT+CIPSTART=\"TCP\",\"192.168.0.100\",8888\r\n";
char TCMS[] = "AT+CIPMODE=1\r\n";
char RESET[] = "AT+RST\r\n";	 //重启
char SJCS[] = "AT+CIPSEND\r\n";
char AT_Connect_Net_Flag = 0;
char AT_OK_Flag = 0;
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)
{
	SCON = 0x50;	//配置串口方式1,REN不使能接收
	//波特率由定时器1产生,配置定时器1,8位自动重载工作模式1
	TMOD &= 0x0F;
	TMOD |= 0x20; 	//配置定时器1,8位自动重载工作模式1
	TH1 = 0xFD;
	TL1 = 0xFD;	//9600定时器初值
	TR1 = 1;	//启动定时器
		
	ES = 1;	//开启串口中断
	EA = 1;	//开启总中断
	
}

void sendByte(char data_msg)
{
	SBUF = data_msg;
	while(!TI);	//发送完毕置1请求中断
	TI = 0;
}
void sendString(char *str)
{
	while(*str != '\0'){
		sendByte(*str);
		str++;
	}
}

void main()
{
	int mark = 0;
	D5 = D6 = 1;
	UartInit();
	Delay1000ms();	//给esp8266上电时间
	sendString(LJWL);	//连接网络
	//while(!AT_Connect_Net_Flag);
	while(!AT_OK_Flag);
	AT_OK_Flag = 0;
	sendString(LJFWQ);	//连接服务器
	while(!AT_OK_Flag);
	AT_OK_Flag = 0;
	sendString(TCMS);	//开启透传模式
	while(!AT_OK_Flag);
	AT_OK_Flag = 0;
	sendString(SJCS);	//数据传输
	while(!AT_OK_Flag);
	
	if(AT_Connect_Net_Flag){
		D5 = 0; //入网成功亮灯
	}
	if(AT_OK_Flag){
		D6 = 0; //入网成功亮灯
	}
	while(1){
		Delay1000ms();
		//往发送缓冲区写入数据,就完成了数据的发送
		sendString("hello\r\n");
	}
}
//无论是接收引起的中断还是发送引起的中断都会进入到该函数中
void Uart_Handler() interrupt 4
{
	static int i = 0;	//静态局部不会单独执行,只执行一次
	char tmp;
	if(RI){	//接收引起的中断
		RI = 0; //软件复位
		tmp = SBUF;	//读取数据
		if(tmp == 'W' || tmp == 'O' || tmp == 'L' || tmp == 'F'){	//字符预设强制位置
			i = 0;
		}
		buffer[i++] = tmp;
		
		if(buffer[0] == 'W' && buffer[5] == 'G'){	//入网成功
			AT_Connect_Net_Flag = 1;
			memset(buffer,'\0',12);
		}
		
		if(buffer[0] == 'O' && buffer[1] == 'K'){	
			AT_OK_Flag = 1;
			memset(buffer,'\0',12);
		}
		//联网失败
		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);
		}
		if(buffer[0] == 'L' && buffer[2] == '1'){	//找子串
			D5 = 0;
			memset(buffer,'\0',12);
		}
		if(buffer[0] == 'L' && buffer[2] == '0'){
			D5 = 1;
			memset(buffer,'\0',12);
		}
		if(i == 12) i=0;
	}
	
	if(TI);
}

4.2 ESP8266当服务器

c 复制代码
//1 配置成双模
AT+CWMODE=2
Response :OK
//2 使能多链接
AT+CIPMUX=1
Response :OK
//3 建立TCPServer
AT+CIPSERVER=1 // default port = 333
Response :OK
//4 发送数据
AT+CIPSEND=0,4 // 发送4个字节在连接0通道上
>abcd //输入数据,不带回车
Response :SEND OK
//• 接收数据
+IPD, 0, n: xxxxxxxxxx //+IPD是固定字符串 0是通道,n是数据长度,xxx是数据
//断开连接
AT+CIPCLOSE=0
Response :0, CLOSED OK
c 复制代码
#include "reg52.h"
#include "intrins.h"
#include <string.h>

char buffer[12];
sbit D5 = P3^7;
sbit D6 = P3^6;
char AT_OK_Flag = 0;
char AT_Connect_Flag = 0;
char Client_Connect_Flag = 0;
//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"; // default port = 333
//4 发送数据
char FSSJ[] = "AT+CIPSEND=0,5\r\n"; // 发送4个字节在连接0通道上


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)
{
	SCON = 0x50;	//配置串口方式1,REN不使能接收
	//波特率由定时器1产生,配置定时器1,8位自动重载工作模式1
	TMOD &= 0x0F;
	TMOD |= 0x20; 	//配置定时器1,8位自动重载工作模式1
	TH1 = 0xFD;
	TL1 = 0xFD;	//9600定时器初值
	TR1 = 1;	//启动定时器
		
	ES = 1;	//开启串口中断
	EA = 1;	//开启总中断
	
}

void sendByte(char data_msg)
{
	SBUF = data_msg;
	while(!TI);	//发送完毕置1请求中断
	TI = 0;
}
void sendString(char *str)
{
	while(*str != '\0'){
		sendByte(*str);
		str++;
	}
}

void main()
{
	int mark = 0;
	D5 = D6 = 1;
	UartInit();
	Delay1000ms();	//给esp8266上电时间
	
	sendString(LYMO);
	while(!AT_OK_Flag);
	AT_OK_Flag = 0;
	sendString(DLJ);
	while(!AT_OK_Flag);
	AT_OK_Flag = 0;
	sendString(JLFW);

	while(!Client_Connect_Flag);
	AT_OK_Flag = 0;
	if(Client_Connect_Flag){
		D5 = 0;	//有客户端接入
		D6 = 0;
	}

	
	while(1){
		//发送数据
		sendString(FSSJ);
		Delay1000ms();
		sendString("Hello");
	}
}
//无论是接收引起的中断还是发送引起的中断都会进入到该函数中
void Uart_Handler() interrupt 4
{
	static int i = 0;	//静态局部不会单独执行,只执行一次
	char tmp;
	if(RI){	//接收引起的中断
		RI = 0; //软件复位
		tmp = SBUF;	//读取数据
		if(tmp == 'W' || tmp == 'O' || tmp == 'L' || tmp == 'F' || tmp == '0' || tmp == ':'){	//字符预设强制位置
			i = 0;
		}
		buffer[i++] = tmp;
		
		if(buffer[0] == 'W' && buffer[5] == 'G'){	//入网成功
			AT_Connect_Flag = 1;
			memset(buffer,'\0',12);
		}
		
		if(buffer[0] == 'O' && buffer[1] == 'K'){	
			AT_OK_Flag = 1;
			memset(buffer,'\0',12);
		}
		if(buffer[0] == '0' && buffer[2] == 'C'){	
			Client_Connect_Flag = 1;
			memset(buffer,'\0',12);
		}
		if(buffer[0] == ':' && buffer[1] == '0' && buffer[2] == 'p' ){	//找子串
			D5 = 0;
			memset(buffer,'\0',12);
		}
		if(buffer[0] == ':' && buffer[1] == 'c' && buffer[2] == '1'){
			D5 = 1;
			memset(buffer,'\0',12);
		}
		if(i == 12) i=0;
	}
	
	if(TI);
}

5. EC03-DNC4G通信模块

5.1 4G模块配置连接服务器

c 复制代码
	4G无法识别局域网ip,通过花生壳,内网ip穿透,为局域网的设备提供一个外网可访问的地址和端口
c 复制代码
	1. 打开串口连接4G模块,串口出产默认波特率是115200,可以自行根据用户手册修改
	2. 进入AT指令模式,在串口助手内发送+++(不要勾选发送新行),必须在发送+++指令 3s 内发送其
	他任意 AT 指令,比如AT+CPIN
	3. 观察SIM卡灯是否亮起,AT+ICCID获得SIM卡信息,确认SIM卡安装完好 返回数据:
	+OK=89860116838013413419
	检查信号是否正常,通过AT+CSQ指令检查信号值,建议插入信号天线,返回数据:+OK=31
	4. AT+SOCK=TCPC,103.46.128.21,52541 连接socket服务器,
	103.46.128.21是公网IP地址,通过花生壳获得,26532是端口号,参数之间逗号隔开
	5. AT+LINKSTA查看连接状态,如果第四步没有问题,此时串口返回+OK=Connect

5.2 4G点灯

c 复制代码
#include "reg52.h"
#include "intrins.h"
#include <string.h>
#define SIZE 12
char cmd[SIZE];
sbit D5 = P3^7;
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)
{
	SCON = 0x50;	//配置串口方式1,REN不使能接收
	//波特率由定时器1产生,配置定时器1,8位自动重载工作模式1
	TMOD &= 0x0F;
	TMOD |= 0x20; 	//配置定时器1,8位自动重载工作模式1
	TH1 = 0xFD;
	TL1 = 0xFD;	//9600定时器初值
	TR1 = 1;	//启动定时器
		
	ES = 1;	//开启串口中断
	EA = 1;	//开启总中断
	
}

void sendByte(char data_msg)
{
	SBUF = data_msg;
	while(!TI);	//发送完毕置1请求中断
	TI = 0;
}
void sendString(char *str)
{
	while(*str != '\0'){
		sendByte(*str);
		str++;
	}
}



void main()
{

	D5 = 1;
	UartInit();
	while(1){
		Delay1000ms();
		//往发送缓冲区写入数据,就完成了数据的发送
		//sendString("hello\r\n");
	}
}
//无论是接收引起的中断还是发送引起的中断都会进入到该函数中
void Uart_Handler() interrupt 4
{
	char tmp;
	static int i = 0;	//静态局部不会单独执行,只执行一次
	if(RI){	//接收引起的中断
		RI = 0;//清除接收中断标志位
		tmp = SBUF;
		if(tmp == ':'){
			i = 0;
		}
		cmd[i++] = tmp;
		if(cmd[0]== ':' && cmd[1] == 'o' && cmd[2]=='p'){
			D5 = 0;//点亮D5
			i = 0;
			memset(cmd,'\0',SIZE);
		}
		if(cmd[0]== ':' && cmd[1] == 'c' && cmd[2]=='l'){
			D5 = 1;//熄灭D5
			i = 0;
			memset(cmd,'\0',SIZE);
		}
		if(i == 12) i = 0;
	}
	
	if(TI);
}

6. LCD1602



c 复制代码
	a = 01100001 --- 0x61   ------  97     ASCII

6.1 写操作


c 复制代码
	1.RS(寄存器选择):
		高电平:数据寄存器(写内容)
		低电平:指令寄存器(写指令/地址)
	2.R/W(读写信号线):
		高电平:进行读操作
		低电平:进行写操作。
		
		当 RS 和 R/W 共同为低电平:可以写入指令或者显示地址
		当 RS 为低电平 R/W 为高电平:读忙信号,
		当 RS 为高电平 R/W 为低电平:写入数据。

	3.E: 
		开始低电平 				
			EN = 0;
		延时至少一个TR(最大25ns)--可以延时一个时钟周期_nop_(); //1us
			_nop_();
		在E拉高前读DB0-DB7--数据线组P0
			databuffer = dataShow;
		拉高E
			EN = 1;
		延时一个tpw(最小400ns) + tf(最大25ns)后拉低
			_nop_(); //1us
			_nop_(); //1us
		拉低
			EN = 0;
		拉低后延时一
			_nop_(); //1us
				
c 复制代码
void Write_Cmd_Func(char cmd)
{
	check_busy();
	//当 RS 和 R/W 共同为低电平:可以写入指令或者显示地址
	RS = 0;
	RW = 0;
	
	EN = 0;
	_nop_();
	databuffer = cmd;
	_nop_(); //1us
	EN = 1;
	_nop_(); //1us
	_nop_(); //1us
	EN = 0;
	_nop_(); //1us
	
}
void Write_Data_Func(char dataShow)
{
	//(4)以后每次写指令,读/写数据操作均需要检测忙信号
	check_busy();
	//RS高电平,RW低电平:写入数据
	RS = 1;
	RW = 0;
	
	EN = 0;
	_nop_();
	//在EN拉高前开始读取数据
	databuffer = dataShow;
	_nop_(); //1us
	EN = 1;
	_nop_(); //1us
	_nop_(); //1us
	EN = 0;
	_nop_(); //1us
}

6.2 读操作

c 复制代码
//读操作
void check_busy()
{
	char tmp = 0x80;
	databuffer = 0x80;
	
	
	while(tmp & 0x80){	//1000 0000 忙
		//当 RS 为低电平 R/W 为高电平:读忙信号,
		RS = 0;
		RW = 1;
		
		
		EN = 0;
		_nop_();
		EN = 1;
		_nop_();
		_nop_();
		//拉高延时后读取数据
		tmp = databuffer;
		EN = 0;
		_nop_();
	}
}

6.3 显示一个字符

c 复制代码
#include "reg52.h"
#include <intrins.h>

#define databuffer P0 //定义8位数据线
sbit RS = P1^0;
sbit RW = P1^1;
sbit EN = P1^4;

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 check_busy()
{
	char tmp = 0x80;
	databuffer = 0x80;
	
	
	while(tmp & 0x80){	//1000 0000 忙
		//当 RS 为低电平 R/W 为高电平:读忙信号,
		RS = 0;
		RW = 1;
		
		
		EN = 0;
		_nop_();
		EN = 1;
		_nop_();
		_nop_();
		//拉高延时后读取数据
		tmp = databuffer;
		EN = 0;
		_nop_();
	}
}
void Write_Cmd_Func(char cmd)
{
	check_busy();
	//当 RS 和 R/W 共同为低电平:可以写入指令或者显示地址
	RS = 0;
	RW = 0;
	
	EN = 0;
	_nop_();
	databuffer = cmd;
	_nop_(); //1us
	EN = 1;
	_nop_(); //1us
	_nop_(); //1us
	EN = 0;
	_nop_(); //1us
	
}
void Write_Data_Func(char dataShow)
{
	//(4)以后每次写指令,读/写数据操作均需要检测忙信号
	check_busy();
	//RS高电平,RW低电平:写入数据
	RS = 1;
	RW = 0;
	
	EN = 0;
	_nop_();
	//在EN拉高前开始读取数据
	databuffer = dataShow;
	_nop_(); //1us
	EN = 1;
	_nop_(); //1us
	_nop_(); //1us
	EN = 0;
	_nop_(); //1us
}
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 + 0x05;
	char dataShow = 'C';
	LCD1602_INIT();
	Write_Cmd_Func(position); //选择要显示的地址
	Write_Data_Func(dataShow); //发送要显示字符
}

6.4 显示一行字符

c 复制代码
#include "reg52.h"
#include <intrins.h>

#define databuffer P0 //定义8位数据线
sbit RS = P1^0;
sbit RW = P1^1;
sbit EN = P1^4;

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 check_busy()
{
	char tmp = 0x80;
	databuffer = 0x80;
	
	
	while(tmp & 0x80){	//1000 0000 忙
		//当 RS 为低电平 R/W 为高电平:读忙信号,
		RS = 0;
		RW = 1;
		
		
		EN = 0;
		_nop_();
		EN = 1;
		_nop_();
		_nop_();
		//拉高延时后读取数据
		tmp = databuffer;
		EN = 0;
		_nop_();
	}
}
void Write_Cmd_Func(char cmd)
{
	check_busy();
	//当 RS 和 R/W 共同为低电平:可以写入指令或者显示地址
	RS = 0;
	RW = 0;
	
	EN = 0;
	_nop_();
	databuffer = cmd;
	_nop_(); //1us
	EN = 1;
	_nop_(); //1us
	_nop_(); //1us
	EN = 0;
	_nop_(); //1us
	
}
void Write_Data_Func(char dataShow)
{
	//(4)以后每次写指令,读/写数据操作均需要检测忙信号
	check_busy();
	//RS高电平,RW低电平:写入数据
	RS = 1;
	RW = 0;
	
	EN = 0;
	_nop_();
	//在EN拉高前开始读取数据
	databuffer = dataShow;
	_nop_(); //1us
	EN = 1;
	_nop_(); //1us
	_nop_(); //1us
	EN = 0;
	_nop_(); //1us
}
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 + 0x05;
	char dataShow = 'C';
	LCD1602_INIT();
	LCD1602_showLine(1,5,"NO.1");
	LCD1602_showLine(2,0, "qzh handsome");
}

7. DHT11温湿度传感器

c 复制代码
	数据格式:
	8bit湿度整数数据+8bit湿度小数数据+8bi温度整数数据+8bit温度小数数据+8bit校验和

7.1 检测模块是否存在

c 复制代码
	主机经过abcd三段操作后,DHT会拉低80us响应,
	当读取到低电平则模块存在
c 复制代码
#include "reg52.h"
#include <intrins.h>
sbit led1 = P3^7;
sbit dht = 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 Delay60us()		//@11.0592MHz
{
	unsigned char i;

	i = 25;
	while (--i);
}

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

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

void check_DTH()
{
	//a : dht = 1;
	dht = 1;
	//b : dht = 0;
	dht = 0;
	//延时30ms
	Delay30ms();
	//c : dth = 1;
	dht = 1;
	//20~40us后dht11拉低电平持续80us
	//在60us后检查端口是否是低电平,低则存在
	Delay60us();
	if(dht == 0){
		led1 = 0;
	}
}
void main()
{
	
	led1 = 1;
	//稳定模块
	Delay2000ms();
	check_DTH();
	while(1);
}

7.2 读取数据


c 复制代码
	高电平 70us 表示1
	高电平 26-28us表示低电平
	因此检测50us时电平高低
	高位1,低为0
c 复制代码
void DHT11_Start()
{
	dht = 1;
	dht = 0;
	Delay30ms();
	dht = 1;
	Delay60us();
	//卡cd段
	while(dht);
	//卡de段
	while(!dht);
	//卡ef段
	while(dht);
}

void Read_Data_From_DHT()
{
	int i;
	int j;
	char tmp;
	char flag;
	DHT11_Start();

	for(i=0;i<5;i++){
		for(j=0;j<8;j++){
			//卡fg段
			while(!dht);	//开始读取数据,60us后高电平1 低电平0
			Delay60us();
			if(dht == 1){
				flag = 1;
				while(dht);	//等1变零
			}else{
				flag = 0;
			}
			tmp <<= 1;	//左移8次得到tmp数组,一个组数据8bit
			tmp |= flag;
		}
		datas[i] = tmp;
	}
}

7.3 串口

c 复制代码
#include "reg52.h"
#include <intrins.h>
sbit led1 = P3^7;
sbit dht = P3^3;
char datas[5];
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 Delay60us()		//@11.0592MHz
{
	unsigned char i;

	i = 25;
	while (--i);
}

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

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

void DHT11_Start()
{
	dht = 1;
	dht = 0;
	Delay30ms();
	dht = 1;
	Delay60us();
	//卡cd段
	while(dht);
	//卡de段
	while(!dht);
	//卡ef段
	while(dht);
}

void Read_Data_From_DHT()
{
	int i;
	int j;
	char tmp;
	char flag;
	DHT11_Start();

	for(i=0;i<5;i++){
		for(j=0;j<8;j++){
			//卡fg段
			while(!dht);	//开始读取数据,40s后高电平1 低电平0
			Delay40us();
			if(dht == 1){
				flag = 1;
				while(dht);	//等1变零
			}else{
				flag = 0;
			}
			tmp = tmp << 1;	//左移8次得到tmp数组,一个组数据8bit
			tmp |= flag;
		}
		datas[i] = tmp;
	}
}
void UartInit(void)
{
	SCON = 0x40;	//配置串口方式1,REN不使能接收
	//波特率由定时器1产生,配置定时器1,8位自动重载工作模式1
	TMOD &= 0x0F;
	TMOD |= 0x20; 	//配置定时器1,8位自动重载工作模式1
	TH1 = 0xFD;
	TL1 = 0xFD;	//9600定时器初值
	TR1 = 1;	//启动定时器
	
}

void sendByte(char data_msg)
{
	SBUF = data_msg;
	while(!TI);	//发送完毕置1请求中断
	TI = 0;
}
void sendString(char *str)
{
	while(*str != '\0'){
		sendByte(*str);
		str++;
	}
}
void Build_Data_DHT(char *temp, char *huma)
{
	huma[0] = 'H';
	//湿度整数位
	huma[1] = datas[0]/10 + 0x30;	//获得十位数并变成字符	+ 0x30
	huma[2] = datas[0]%10 + 0x30;	//获得个位数并变成字符
	//湿度小数位
	huma[3] = '.';
	huma[4] = datas[1]/10 + 0x30;	//获得十位数并变成字符	+ 0x30
	huma[5] = datas[1]%10 + 0x30;	//获得个位数并变成字符
	huma[6] = '\0';	//结束标志,字符必须得有
	
	temp[0] = 'T';
	//温度整数位
	temp[1] = datas[2]/10 + 0x30;	//获得十位数并变成字符	+ 0x30
	temp[2] = datas[2]%10 + 0x30;	//获得个位数并变成字符
	//温度小数位
	temp[3] = '.';
	temp[4] = datas[3]/10 + 0x30;	//获得十位数并变成字符	+ 0x30
	temp[5] = datas[3]%10 + 0x30;	//获得个位数并变成字符
	temp[6] = '\0';
}
void main()
{
	char temp[7];
	char huma[7];
	led1 = 1;
	//稳定模块
	UartInit();
	Delay1000ms();
	Delay1000ms();
	while(1){
		Delay1000ms();
		Read_Data_From_DHT();
		Build_Data_DHT(temp,huma);
		sendString(temp);
		sendString("\r\n");
		sendString(huma);
		sendString("\r\n");
	}
}

7.4 温湿度数据LCD1602显示温度大于24度风扇转

c 复制代码
#include "reg52.h"
#include <intrins.h>
sbit led1 = P3^7;
sbit dht = P3^3;
char datas[5];
#define databuffer P0 //定义8位数据线
sbit RS = P1^0;
sbit RW = P1^1;
sbit EN = P1^4;
sbit fengshan = P1^6;
char temp[8];
char huma[8];
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 Delay60us()		//@11.0592MHz
{
	unsigned char i;

	i = 25;
	while (--i);
}

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

	i = 54;
	j = 199;
	do
	{
		while (--j);
	} while (--i);
}
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 check_busy()
{
	char tmp = 0x80;
	databuffer = 0x80;
	
	
	while(tmp & 0x80){	//1000 0000 忙
		//当 RS 为低电平 R/W 为高电平:读忙信号,
		RS = 0;
		RW = 1;
		
		
		EN = 0;
		_nop_();
		EN = 1;
		_nop_();
		_nop_();
		//拉高延时后读取数据
		tmp = databuffer;
		EN = 0;
		_nop_();
	}
}
void Write_Cmd_Func(char cmd)
{
	check_busy();
	//当 RS 和 R/W 共同为低电平:可以写入指令或者显示地址
	RS = 0;
	RW = 0;
	
	EN = 0;
	_nop_();
	databuffer = cmd;
	_nop_(); //1us
	EN = 1;
	_nop_(); //1us
	_nop_(); //1us
	EN = 0;
	_nop_(); //1us
	
}
void Write_Data_Func(char dataShow)
{
	//(4)以后每次写指令,读/写数据操作均需要检测忙信号
	check_busy();
	//RS高电平,RW低电平:写入数据
	RS = 1;
	RW = 0;
	
	EN = 0;
	_nop_();
	//在EN拉高前开始读取数据
	databuffer = dataShow;
	_nop_(); //1us
	EN = 1;
	_nop_(); //1us
	_nop_(); //1us
	EN = 0;
	_nop_(); //1us
}
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 DHT11_Start()
{
	dht = 1;
	dht = 0;
	Delay30ms();
	dht = 1;
	Delay60us();
	//卡cd段
	while(dht);
	//卡de段
	while(!dht);
	//卡ef段
	while(dht);
}

void Read_Data_From_DHT()
{
	int i;
	int j;
	char tmp;
	char flag;
	DHT11_Start();

	for(i=0;i<5;i++){
		for(j=0;j<8;j++){
			//卡fg段
			while(!dht);	//开始读取数据,40s后高电平1 低电平0
			Delay40us();
			if(dht == 1){
				flag = 1;
				while(dht);	//等1变零
			}else{
				flag = 0;
			}
			tmp = tmp << 1;	//左移8次得到tmp数组,一个组数据8bit
			tmp |= flag;
		}
		datas[i] = tmp;
	}
}
void UartInit(void)
{
	SCON = 0x40;	//配置串口方式1,REN不使能接收
	//波特率由定时器1产生,配置定时器1,8位自动重载工作模式1
	TMOD &= 0x0F;
	TMOD |= 0x20; 	//配置定时器1,8位自动重载工作模式1
	TH1 = 0xFD;
	TL1 = 0xFD;	//9600定时器初值
	TR1 = 1;	//启动定时器
	
}

void sendByte(char data_msg)
{
	SBUF = data_msg;
	while(!TI);	//发送完毕置1请求中断
	TI = 0;
}
void sendString(char *str)
{
	while(*str != '\0'){
		sendByte(*str);
		str++;
	}
}
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()
{
	
	led1 = 1;
	//稳定模块
	UartInit();
	LCD1602_INIT();
	Delay1000ms();
	Delay1000ms();
	while(1){
		Delay1000ms();
		Read_Data_From_DHT();
		if(datas[2] > 24){	//温度大于24 风扇开启
			fengshan = 0;
		}
		Build_Datas();
		sendString(huma);
		sendString("\r\n");
		sendString(temp);
		sendString("\r\n");
		LCD1602_showLine(1,2,huma);
		LCD1602_showLine(2,2,temp);
	}
}

7.5 分文件操作

复制代码
.........

三.IIC协议

c 复制代码
	IIC总线在传输数据的过程中一共有三种类型信号,
	分别为:开始信号、结束信号和应答信号。

1.起始信号

c 复制代码
void IIC_Start()
{
	scl = 0;	//防止雪花
	scl = 1;
	sda = 1;
	_nop_();
	sda = 0;
	_nop_();
}
void IIC_Stop()
{
	scl = 0;
	sda = 0;
	scl = 1;
	_nop_();	//5us
	sda = 1;
	_nop_();	//5us
	
}

2.应答信号

c 复制代码
	发送器每发送一个字节(8个bit),就在时钟脉冲9期间释放数据线,由接收器反馈一个应答信号。

	应答信号为低电平时,规定为有效应答位(ACK,简称应答位),表示接收器已经成功地接收了该字
节;
	应答信号为高电平时,规定为非应答位(NACK),一般表示接收器接收该字节没有成功

3.数据发送

c 复制代码
void IIC_Send_Byte(char dataSend)
{
	int i;
	for(i=0;i<8;i++){
		scl = 0;//scl拉低,让sda做好数据准备
		sda = dataSend & 0x80; //sda = dataSend的高位,8个bit
		_nop_();//发送数据建立时间
		scl = 1;//拉高开始传输
		_nop_();//数据发送时间
		scl = 0;//发送完毕
		_nop_();
		dataSend = dataSend << 1;
	}
}

4.OLED

4.1 OLED写入指令和数据

c 复制代码
/*
	1. start()
	2. 写入 b0111 1000 0x78
	3. ACK
	4. cotrol byte: (0)(0)000000 写入命令 (0)(1)000000写入数据
	5. ACK
	6. 写入指令/数据
	7. ACK
	8. STOP
*/
void Oled_Write_Cmd(char dataCmd)
{
	IIC_Start();
	
	IIC_Send_Byte(0x78);
	
	IIC_ACK();
	
	IIC_Send_Byte(0x00);
	
	IIC_ACK();
	
	IIC_Send_Byte(dataCmd);
	IIC_Stop();	
	
}

void Oled_Write_Data(char dataDatas)
{
	IIC_Start();
	
	IIC_Send_Byte(0x78);
	
	IIC_ACK();
	
	IIC_Send_Byte(0x40);
	
	IIC_ACK();
	
	IIC_Send_Byte(dataDatas);
	IIC_Stop();	
	
}

4.2 OLED页寻址模式

c 复制代码
	发送cmd : 0x20;
	发送cmd : 0x02; 默认页模式


c 复制代码
	//2.1确认寻址模式
	Oled_Write_Cmd(0x20);
	Oled_Write_Cmd(0x02);
	//2.2选择PAGEO 1011 0000
	Oled_Write_Cmd(0xB0);

4.3 清屏

c 复制代码
void Oled_Clear()
{
	//page0--page7
	//每个page从0列到127列,依次写入0,没写入数据,列地址自动偏移
	int i;
	int j;
	for(i=0;i<8;i++){
		Oled_Write_Cmd(0xB0 + i); //page0 -- page7
		//每个page从0列到127列
		Oled_Write_Cmd(0x00);
		Oled_Write_Cmd(0x10);
		//依次写入0
		for(j = 0 ;j<128; j++){
			Oled_Write_Data(0);
		}
	}
}

4.4 显示字符A

c 复制代码
/*--  文字:  A  --*/
/*--  宋体12;  此字体下对应的点阵为:宽x高=8x16   --*/
0x00,0x00,0xC0,0x38,0xE0,0x00,0x00,0x00,0x20,0x3C,0x23,0x02,0x02,0x27,0x38,0x20,
c 复制代码
#include "reg52.h"
#include <intrins.h>
sbit led1 = P3^7;
sbit led2 = P3^6;
sbit scl = P0^0;
sbit sda = P0^3;
void IIC_Start()
{
	scl = 0;	//防止雪花
	scl = 1;
	sda = 1;
	_nop_();
	sda = 0;
	_nop_();
}
void IIC_Stop()
{
	scl = 0;
	sda = 0;
	scl = 1;
	_nop_();	//5us
	sda = 1;
	_nop_();	//5us
	
}
char IIC_ACK()
{
	char flag;
	sda = 1;	//发送8为后在脉冲9释放数据线
	_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做好数据准备
		sda = dataSend & 0x80; //sda = dataSend的高位,8个bit
		_nop_();//发送数据建立时间
		scl = 1;//拉高开始传输
		_nop_();//数据发送时间
		scl = 0;//发送完毕
		_nop_();
		dataSend = dataSend << 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
*/
void Oled_Write_Cmd(char dataCmd)
{
	IIC_Start();
	
	IIC_Send_Byte(0x78);
	
	IIC_ACK();
	
	IIC_Send_Byte(0x00);
	
	IIC_ACK();
	
	IIC_Send_Byte(dataCmd);
	IIC_Stop();	
	
}

void Oled_Write_Data(char dataDatas)
{
	IIC_Start();
	
	IIC_Send_Byte(0x78);
	
	IIC_ACK();
	
	IIC_Send_Byte(0x40);
	
	IIC_ACK();
	
	IIC_Send_Byte(dataDatas);
	IIC_Stop();	
	
}
void Oled_Init()
{
	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()
{
	//page0--page7
	//每个page从0列到127列,依次写入0,没写入数据,列地址自动偏移
	int i;
	int j;
	for(i=0;i<8;i++){
		Oled_Write_Cmd(0xB0 + i); //page0 -- page7
		//每个page从0列到127列
		Oled_Write_Cmd(0x00);
		Oled_Write_Cmd(0x10);
		//依次写入0
		for(j = 0 ;j<128; j++){
			Oled_Write_Data(0);
		}
	}
}
/*--  文字:  A  --*/
/*--  宋体12;  此字体下对应的点阵为:宽x高=8x16   --*/
//一个page是8位,所以用两个page
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选择PAGEO 1011 0000
	Oled_Write_Cmd(0xB0);
	//从第0列开始
	Oled_Write_Cmd(0x00);
	Oled_Write_Cmd(0x10);
	for(i=0;i<8;i++){
		Oled_Write_Data(A1[i]);
	}
	Oled_Write_Cmd(0xB1);
	Oled_Write_Cmd(0x00);	
	Oled_Write_Cmd(0x10);
	for(i=0;i<8;i++){
		Oled_Write_Data(A2[i]);
	}
	while(1);
	
}

4.4 显示多个字

c 复制代码
#include "reg52.h"
#include <intrins.h>
sbit led1 = P3^7;
sbit led2 = P3^6;
sbit scl = P0^0;
sbit sda = P0^3;
void IIC_Start()
{
	scl = 0;	//防止雪花
	scl = 1;
	sda = 1;
	_nop_();
	sda = 0;
	_nop_();
}
void IIC_Stop()
{
	scl = 0;
	sda = 0;
	scl = 1;
	_nop_();	//5us
	sda = 1;
	_nop_();	//5us
	
}
char IIC_ACK()
{
	char flag;
	sda = 1;	//发送8为后在脉冲9释放数据线
	_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做好数据准备
		sda = dataSend & 0x80; //sda = dataSend的高位,8个bit
		_nop_();//发送数据建立时间
		scl = 1;//拉高开始传输
		_nop_();//数据发送时间
		scl = 0;//发送完毕
		_nop_();
		dataSend = dataSend << 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
*/
void Oled_Write_Cmd(char dataCmd)
{
	IIC_Start();
	
	IIC_Send_Byte(0x78);
	
	IIC_ACK();
	
	IIC_Send_Byte(0x00);
	
	IIC_ACK();
	
	IIC_Send_Byte(dataCmd);
	IIC_Stop();	
	
}

void Oled_Write_Data(char dataDatas)
{
	IIC_Start();
	
	IIC_Send_Byte(0x78);
	
	IIC_ACK();
	
	IIC_Send_Byte(0x40);
	
	IIC_ACK();
	
	IIC_Send_Byte(dataDatas);
	IIC_Stop();	
	
}
void Oled_Init()
{
	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()
{
	//page0--page7
	//每个page从0列到127列,依次写入0,没写入数据,列地址自动偏移
	int i;
	int j;
	for(i=0;i<8;i++){
		Oled_Write_Cmd(0xB0 + i); //page0 -- page7
		//每个page从0列到127列
		Oled_Write_Cmd(0x00);
		Oled_Write_Cmd(0x10);
		//依次写入0
		for(j = 0 ;j<128; j++){
			Oled_Write_Data(0);
		}
	}
}


/*--  文字:  Q  --*/
/*--  宋体12;  此字体下对应的点阵为:宽x高=8x16   --*/
code char Q1[8] = {0xE0,0x10,0x08,0x08,0x08,0x10,0xE0,0x00};

code char Q2[8] = {0x0F,0x10,0x28,0x28,0x30,0x50,0x4F,0x00};

/*--  文字:  Z  --*/
/*--  宋体12;  此字体下对应的点阵为:宽x高=8x16   --*/
code char Z1[8] = {0x10,0x08,0x08,0x08,0xC8,0x38,0x08,0x00};

code char Z2[8] = {0x20,0x38,0x26,0x21,0x20,0x20,0x18,0x00};

/*--  文字:  H  --*/
/*--  宋体12;  此字体下对应的点阵为:宽x高=8x16   --*/
code char H1[8] = {0x08,0xF8,0x08,0x00,0x00,0x08,0xF8,0x08};
code char H2[8] = {0x20,0x3F,0x21,0x01,0x01,0x21,0x3F,0x20};

/*--  文字:     --*/
/*--  宋体12;  此字体下对应的点阵为:宽x高=8x16   --*/
code char K1[8] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
code char K2[8] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};


/*--  文字:  a  --*/
/*--  宋体12;  此字体下对应的点阵为:宽x高=8x16   --*/
code char a1[8] = {0x00,0x00,0x80,0x80,0x80,0x00,0x00,0x00};
code char a2[8] = {0x00,0x19,0x24,0x24,0x12,0x3F,0x20,0x00};

/*--  文字:  n  --*/
/*--  宋体12;  此字体下对应的点阵为:宽x高=8x16   --*/
code char n1[8] = {0x80,0x80,0x00,0x80,0x80,0x80,0x00,0x00};
code char n2[8] = {0x20,0x3F,0x21,0x00,0x00,0x20,0x3F,0x20};

/*--  文字:  d  --*/
/*--  宋体12;  此字体下对应的点阵为:宽x高=8x16   --*/
code char d1[8] = {0x00,0x00,0x80,0x80,0x80,0x90,0xF0,0x00};
code char d2[8] = {0x00,0x1F,0x20,0x20,0x20,0x10,0x3F,0x20};

/*--  文字:  s  --*/
/*--  宋体12;  此字体下对应的点阵为:宽x高=8x16   --*/
code char s1[8] = {0x00,0x00,0x80,0x80,0x80,0x80,0x80,0x00};
code char s2[8] = {0x00,0x33,0x24,0x24,0x24,0x24,0x19,0x00};

/*--  文字:  o  --*/
/*--  宋体12;  此字体下对应的点阵为:宽x高=8x16   --*/
code char o1[8] = {0x00,0x00,0x80,0x80,0x80,0x80,0x00,0x00};
code char o2[8] = {0x00,0x1F,0x20,0x20,0x20,0x20,0x1F,0x00};

/*--  文字:  m  --*/
/*--  宋体12;  此字体下对应的点阵为:宽x高=8x16   --*/
code char m1[8] = {0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x00};
code char m2[8] = {0x20,0x3F,0x20,0x00,0x3F,0x20,0x00,0x3F};

/*--  文字:  e  --*/
/*--  宋体12;  此字体下对应的点阵为:宽x高=8x16   --*/
code char e1[8] = {0x00,0x00,0x80,0x80,0x80,0x80,0x00,0x00};
code char e2[8] = {0x00,0x1F,0x24,0x24,0x24,0x24,0x17,0x00};
void SB1()
{
	int i;
	Oled_Write_Cmd(0xB0);
	//从第0列开始
	Oled_Write_Cmd(0x00);
	Oled_Write_Cmd(0x10);
	for(i=0;i<8;i++){
		Oled_Write_Data(Q1[i]);
	}
	for(i=0;i<8;i++){
		Oled_Write_Data(Z1[i]);
	}
	for(i=0;i<8;i++){
		Oled_Write_Data(H1[i]);
	}
	for(i=0;i<8;i++){
		Oled_Write_Data(K1[i]);
	}
	for(i=0;i<8;i++){
		Oled_Write_Data(H1[i]);
	}
	for(i=0;i<8;i++){
		Oled_Write_Data(a1[i]);
	}
	for(i=0;i<8;i++){
		Oled_Write_Data(n1[i]);
	}
	for(i=0;i<8;i++){
		Oled_Write_Data(d1[i]);
	}
	for(i=0;i<8;i++){
		Oled_Write_Data(s1[i]);
	}
	for(i=0;i<8;i++){
		Oled_Write_Data(o1[i]);
	}
	for(i=0;i<8;i++){
		Oled_Write_Data(m1[i]);
	}
	for(i=0;i<8;i++){
		Oled_Write_Data(e1[i]);
	}
}
void SB2()
{
	int i;
	Oled_Write_Cmd(0xB1);
	//从第0列开始
	Oled_Write_Cmd(0x00);
	Oled_Write_Cmd(0x10);
	for(i=0;i<8;i++){
		Oled_Write_Data(Q2[i]);
	}
	for(i=0;i<8;i++){
		Oled_Write_Data(Z2[i]);
	}
	for(i=0;i<8;i++){
		Oled_Write_Data(H2[i]);
	}
	for(i=0;i<8;i++){
		Oled_Write_Data(K2[i]);
	}
	for(i=0;i<8;i++){
		Oled_Write_Data(H2[i]);
	}
	for(i=0;i<8;i++){
		Oled_Write_Data(a2[i]);
	}
	for(i=0;i<8;i++){
		Oled_Write_Data(n2[i]);
	}
	for(i=0;i<8;i++){
		Oled_Write_Data(d2[i]);
	}
	for(i=0;i<8;i++){
		Oled_Write_Data(s2[i]);
	}
	for(i=0;i<8;i++){
		Oled_Write_Data(o2[i]);
	}
	for(i=0;i<8;i++){
		Oled_Write_Data(m2[i]);
	}
	for(i=0;i<8;i++){
		Oled_Write_Data(e2[i]);
	}
}
void main()
{
	//1.OLED初始化
	Oled_Init();
	//2.选择一个位置
	//2.1确认寻址模式
	Oled_Write_Cmd(0x20);
	Oled_Write_Cmd(0x02);
	Oled_Clear();
	//2.2选择PAGEO 1011 0000
	SB1();
	SB2();

	while(1);
}

四.智能小车

1.电机驱动

1.1 L9110S控制小车

c 复制代码
//motor.h

#include "reg52.h"

sbit RightCon1A = P3^2;
sbit RightCon1B = P3^3;
sbit LeftCon1A = P3^4;
sbit LeftCon1B = P3^5;
void goForward();
void goLeft();
void goRight();
void goBack();
c 复制代码
//motor.c

#include "motor.h"
void goForward()
{
	LeftCon1A = 0;
	LeftCon1B = 1;
	
	RightCon1A = 0;
	RightCon1B = 1;
}
void goLeft()
{
	LeftCon1A = 0;
	LeftCon1B = 0;
	
	RightCon1A = 0;
	RightCon1B = 1;
}
void goRight()
{
	LeftCon1A = 0;
	LeftCon1B = 1;
	
	RightCon1A = 0;
	RightCon1B = 0;
}
void goBack()
{
	LeftCon1A = 1;
	LeftCon1B = 0;
	
	RightCon1A = 1;
	RightCon1B = 0;
}

1.2 串口控制小车

c 复制代码
#include "motor.h"
#include "string.h"
#include "reg52.h"
char buffer[12];
void UartInit(void)
{
	SCON = 0x50;	//配置串口方式1,REN不使能接收
	//波特率由定时器1产生,配置定时器1,8位自动重载工作模式1
	TMOD &= 0x0F;
	TMOD |= 0x20; 	//配置定时器1,8位自动重载工作模式1
	TH1 = 0xFD;
	TL1 = 0xFD;	//9600定时器初值
	TR1 = 1;	//启动定时器
		
	ES = 1;	//开启串口中断
	EA = 1;	//开启总中断
	
}
//无论是接收引起的中断还是发送引起的中断都会进入到该函数中
void Uart_Handler() interrupt 4
{
	static int i = 0;	//静态局部不会单独执行,只执行一次
	char tmp;
	if(RI){	//接收引起的中断
		RI = 0; //软件复位
		tmp = SBUF;	//读取数据
		
		if(tmp == 'M'){	//找子串
			i = 0;
		}
		buffer[i++] = tmp;
		
		if(buffer[0] == 'M'){
			switch(buffer[1]){
				case '1':
					D5 = 0;
					goForward();
					break;
				case '2':
					goBack();
					D6 = 0;
					break;
				case '3':
					D5 = 1;
					goLeft();
					break;
				case '4':
					D6 = 1;
					goRight();
					break;
				case '5':
					D6 = 0;
					D5 = 0;
					stop();
					break;						
			}
			
		}
		if(i == 12){
			memset(buffer, '\0',12);
			i=0;
		}
	}
	if(TI);
}

1.3 蓝牙控制小车点动

c 复制代码
//在主函数中设置停止
//串口中断处理处添加延时,只要数据发送的足够快就能一直跑,即可实现点动

void main()
{
	D5 = 1;
	D6 = 1;
	UartInit();
	while(1){
		stop();
		D5 = 1;
		D6 = 1;
	}
}
//中断处
void Uart_Handler() interrupt 4
{
	static int i = 0;	//静态局部不会单独执行,只执行一次
	char tmp;
	if(RI){	//接收引起的中断
		RI = 0; //软件复位
		tmp = SBUF;	//读取数据
		
		if(tmp == 'M'){	//找子串
			i = 0;
		}
		buffer[i++] = tmp;
		
		if(buffer[0] == 'M'){
			switch(buffer[1]){
				case '1':
					D5 = 0;
					goForward();
					Delay10ms();
					break;
				case '2':
					goBack();
					D6 = 0;
					Delay10ms();
					break;
				case '3':
					D5 = 1;
					goLeft();
					Delay10ms();
					break;
				case '4':
					D6 = 1;
					goRight();
					Delay10ms();
					break;
				case '5':
					D6 = 0;
					D5 = 0;
					stop();
					Delay10ms();
					break;						
			}
			
		}
		if(i == 12){
			memset(buffer, '\0',12);
			i=0;
		}
	}
	if(TI);
}

1.4 小车PWM调速

c 复制代码
	假设在20ms周期内
	小车15ms全速前进 5ms停止
与  小车5ms全速前进  15ms停止的功率是不一样的
	在不同的功率下其惯性的大小也不同
	因此通过该原理实现小车调速
c 复制代码
	while(1){
		speed = 10; //10份单位时间内全速运行,30份停止,所以慢,20ms的40份的500us
		Delay1000ms();
		Delay1000ms();
		speed = 20;
		Delay1000ms();
		Delay1000ms();
		speed = 40;
		Delay1000ms();
		Delay1000ms();
	}



#include "reg52.h"
#include "motor.h"

char speed;
char cnt = 0;
void Timer0Init()
{
	//1.配置定时器0工作模式1--16位计时 M1 = 0, M0=0
	TMOD &= 0xF0;
	TMOD |= 0x01;
	//2.设置初值,定0.5ms
	TL0 = 0x33;
	TH0 = 0xFE;
	//3.开始计时
	TR0 = 1;
	TF0 = 0;
	//4.打开定时器0中断
	ET0 = 1;
	//5.打开总中断
	EA = 1;
}

void Timer0Handler() interrupt 1
{
	cnt++;	//cnt = 1溢出了一次
	//重新赋初值
	TL0 = 0x33;
	TH0 = 0xFE;
	if(cnt < speed){
		//前进
		goForward();
	}else{
		//停止
		stop();
	}
	
	if(cnt == 40){//经过20ms
		cnt = 0;
	}
}

1.5 小车PWM调速转弯

c 复制代码
	设置两个定时器,分别控制两个轮子
c 复制代码
//motor.c
#include "reg52.h"

void goForwardLeft()
{
	LeftCon1A = 0;
	LeftCon1B = 1;
}
void stopLeft()
{
	LeftCon1A = 0;
	LeftCon1B = 0;
}
void goForwardRight()
{
	
	RightCon1A = 0;
	RightCon1B = 1;
}
void stopRight()
{
	
	RightCon1A = 0;
	RightCon1B = 0;
}

void stop()
{
{
	LeftCon1A = 0;
	LeftCon1B = 0;
	
	RightCon1A = 0;
	RightCon1B = 0;
}
}
void goForward()
{
	LeftCon1A = 0;
	LeftCon1B = 1;
	
	RightCon1A = 0;
	RightCon1B = 1;
}
void goLeft()
{
	LeftCon1A = 0;
	LeftCon1B = 0;
	
	RightCon1A = 0;
	RightCon1B = 1;
}
void goRight()
{
	LeftCon1A = 0;
	LeftCon1B = 1;
	
	RightCon1A = 0;
	RightCon1B = 0;
}
void goBack()
{
	LeftCon1A = 1;
	LeftCon1B = 0;
	
	RightCon1A = 1;
	RightCon1B = 0;
}
c 复制代码
//timer.c
#include "reg52.h"
#include "motor.h"

char speedLeft;
char speedRight;
char cnt = 0;
void Timer1Init()
{
	//1.配置定时器1工作模式1--16位计时 M1 = 0, M0=0
	TMOD &= 0x0F;
	TMOD |= 0x10;
	//2.设置初值,定0.5ms
	TL1 = 0x33;
	TH1 = 0xFE;
	//3.开始计时
	TR1 = 1;
	TF1 = 0;
	//4.打开定时器1中断
	ET1 = 1;
	//5.打开总中断
	EA = 1;
}


void Timer0Init()
{
	//1.配置定时器0工作模式1--16位计时 M1 = 0, M0=0
	TMOD &= 0xF0;
	TMOD |= 0x01;
	//2.设置初值,定0.5ms
	TL0 = 0x33;
	TH0 = 0xFE;
	//3.开始计时
	TR0 = 1;
	TF0 = 0;
	//4.打开定时器0中断
	ET0 = 1;
	//5.打开总中断
	EA = 1;
}

void Timer0Handler() interrupt 1
{
	cnt++;	//cnt = 1溢出了一次
	//重新赋初值
	TL0 = 0x33;
	TH0 = 0xFE;
	if(cnt < speedLeft){
		//前进
		D5 = 1;
		goForwardLeft();
	}else{
		//停止
		D5 = 0;
		stopLeft();
	}
	
	if(cnt == 40){//经过20ms
		cnt = 0;
	}
}
void Timer1Handler() interrupt 3
{
	cnt++;	//cnt = 1溢出了一次
	//重新赋初值
	TL0 = 0x33;
	TH0 = 0xFE;
	if(cnt < speedRight){
		//前进
		D5 = 1;
		goForwardRight();
	}else{
		//停止
		D5 = 0;
		stopRight();
	}
	
	if(cnt == 40){//经过20ms
		cnt = 0;
	}
}
c 复制代码
#include "delay.h"
#include "motor.h"
#include "uart.h"
#include "reg52.h"
#include "timer.h"

extern char speedLeft;
extern char speedRight;

void main()
{
	D5 = 1;
	D6 = 1;
	Timer0Init();
	Timer1Init();
	UartInit();

	while(1){
		speedLeft = 10; //10份单位时间内全速运行,30份停止,所以慢,20ms的40份的500us
		speedRight = 40;
		Delay1000ms();
		Delay1000ms();
		speedLeft = 20;
		speedRight = 20;
		Delay1000ms();
		Delay1000ms();
		speedLeft = 40;
		speedRight = 20;
		Delay1000ms();
		Delay1000ms();
	}

}

2.循迹小车

c 复制代码
	TCRT5000传感器的红外发射二极管不断发射红外线
	当发射出的红外线没有被反射回来或被反射回来但强度不够大时,红外接收管一直处于关断状态,此时模块的输出端为高电平,指示二极管一直处于熄灭状态被检测物体出现在检测范围内时,
	红外线被反射回来且强度足够大,红外接收管饱和,此时模块的输出端为低电平,指示二极管被点亮
	总结就是一句话,没反射回来,D0输出高电平,灭灯
c 复制代码
#include "delay.h"
#include "motor.h"
#include "uart.h"
#include "reg52.h"
#include "timer.h"
sbit leftSensor = P2^7;
sbit rightSensor = P2^6;
extern char speedLeft;
extern char speedRight;

void main()
{
	D5 = 1;
	D6 = 1;
	Timer0Init();
	Timer1Init();
	UartInit();

	while(1){
	//下方小车两个模块都能反射回来红外,输出低电平,灯亮,直走
	//上方小车左模块遇到黑线,红外被吸收,左模块输出高电平,右模块输出低电平,左转,反之右转
		if(leftSensor == 0 && rightSensor == 0){
			//根据实际调速
			speedLeft = 32;
			speedRight = 40;
		}
		if(leftSensor == 1 && rightSensor == 0){
			speedLeft = 12;
			speedRight = 40;
		}
		if(leftSensor == 0 && rightSensor == 1){
			speedLeft = 32;
			speedRight = 20;
		}
		if(leftSensor == 1 && rightSensor == 1){
			speedLeft = 0;
			speedRight = 0;
		}
	}
}

3.跟踪小车==循迹小车

c 复制代码
#include "delay.h"
#include "motor.h"
#include "uart.h"
#include "reg52.h"


sbit leftSensor = P2^5;
sbit rightSensor = P2^4;
void main()
{
	//左边跟随模块能返回红外,输出低电平,右边不能返回,输出高电平,说明物体在左边,需要左转
	//右边跟随模块能返回红外,输出低电平,左边不能返回,输出高电平,说明物体在右边,需要右转
	UartInit();

	while(1){
		if(leftSensor == 0 && rightSensor == 0){
			goForward();
		}
		if(leftSensor == 1 && rightSensor == 0){
			goLeft();
		}
		if(leftSensor == 0 && rightSensor == 1){
			goRight();
		}
		if(leftSensor == 1 && rightSensor == 1){
			stop();
		}
	}

}

4.摇头避障小车

4.1 摇头(舵机)

c 复制代码
//sg90.c
#include "reg52.h"
#include "delay.h"

sbit sg90_con = P1^1;

int jd;
int cnt = 0;

void Time0Init()
{
	//1. 配置定时器0工作模式位16位计时
	TMOD &= 0xF0;		//设置定时器模式
	TMOD |= 0x01;
	//2. 给初值,定一个0.5出来
	TL0=0x33;
	TH0=0xFE;
	//3. 开始计时
	TR0 = 1;
	TF0 = 0;
	//4. 打开定时器0中断
	ET0 = 1;
	//5. 打开总中断EA
	EA = 1;
}

void sgMiddle()
{
	//中间位置
	jd = 3; //90度 1.5ms高电平
	cnt = 0;
}

void sgLeft()
{
	//左边位置
	jd = 5; //135度 1.5ms高电平
	cnt = 0;
}

void sgRight()
{
	//右边位置
	jd = 1; //0度
	cnt = 0;
}


void Time0Handler() interrupt 1
{
	cnt++;  //统计爆表的次数. cnt=1的时候,报表了1
	//重新给初值
	TL0=0x33;
	TH0=0xFE;
	
	//控制PWM波
	if(cnt < jd){
		sg90_con = 1;
	}else{
		sg90_con = 0;
	}
	
	if(cnt == 40){//爆表40次,经过了20ms
		cnt = 0;  //当100次表示1s,重新让cnt从0开始,计算下一次的1s
		sg90_con = 1;
	}
		
}

4.2 测距(超声波测距)

c 复制代码
//hc04.c
#include "reg52.h"
#include "delay.h"

sbit Trig     = P2^3;
sbit Echo     = P2^2;

void Time1Init()
{	
	TMOD &= 0x0F;		//设置定时器模式
	TMOD |= 0x10;
	TH1 = 0;
	TL1 = 0;
	//设置定时器0工作模式1,初始值设定0开始数数,不着急启动定时器
}

void startHC()
{
	Trig = 0;
	Trig = 1;
	Delay10us();
	Trig = 0;
}

double get_distance()
{
		double time;
		//定时器数据清零,以便下一次测距
		TH1 = 0;
		TL1 = 0;
	//1. Trig ,给Trig端口至少10us的高电平
		startHC();
		//2. echo由低电平跳转到高电平,表示开始发送波
		while(Echo == 0);
		//波发出去的那一下,开始启动定时器
		TR1 = 1;
		//3. 由高电平跳转回低电平,表示波回来了
		while(Echo == 1);
		//波回来的那一下,我们开始停止定时器
		TR1 = 0;
		//4. 计算出中间经过多少时间
		time = (TH1 * 256 + TL1)*1.085;//us为单位
		//5. 距离 = 速度 (340m/s)* 时间/2
		return  (time * 0.017);
}

4.3 移动(电机驱动)

c 复制代码
//moto.c
#include "reg52.h"

sbit RightCon1A = P3^2;
sbit RightCon1B = P3^3;

sbit LeftCon1A = P3^4;
sbit LeftCon1B = P3^5;

void goForward()
{
	LeftCon1A = 0;
	LeftCon1B = 1;
	
	RightCon1A = 0;
	RightCon1B = 1;
}

void goRight()
{
	LeftCon1A = 0;
	LeftCon1B = 1;
	
	RightCon1A = 0;
	RightCon1B = 0;
}


void goLeft()
{
	LeftCon1A = 0;
	LeftCon1B = 0;
	
	RightCon1A = 0;
	RightCon1B = 1;
}

void goBack()
{
	LeftCon1A = 1;
	LeftCon1B = 0;
	
	RightCon1A = 1;
	RightCon1B = 0;
}

void stop()
{
	LeftCon1A = 0;
	LeftCon1B = 0;
	
	RightCon1A = 0;
	RightCon1B = 0;
}

4.3 摇头测距避障(舵机+超声波测距+电机驱动)

c 复制代码
#include "reg52.h"
#include "hc04.h"
#include "delay.h"
#include "sg90.h"
#include "motor.h"

#define MIDDLE 0
#define LEFT 1
#define RIGHT 2

void main()
{
	char dir;	//方向
	
	double disMiddle;
	double disLeft;
	double disRight;
	
	Time0Init();	//舵机控制
	Time1Init();	//超声波测距
	//舵机的初始位置

	sgMiddle();
	Delay300ms();
	Delay300ms();
	dir = MIDDLE;	//设置初始方向
	
	while(1){
		
		if(dir != MIDDLE){	//复位初始方向
			sgMiddle();
			dir = MIDDLE;
			Delay300ms();
		}
		disMiddle = get_distance();
		
		if(disMiddle > 35){
			//前进
			goForward();
		}else if(disMiddle < 10){
			goBack();
			
		}else
		{
			//停止
			stop();
			//测左边距离
			sgLeft();
			Delay300ms();
			disLeft = get_distance();
			
			sgMiddle();
			Delay300ms();
			
			sgRight();
			dir = RIGHT;
			Delay300ms();
			disRight = get_distance();
			
			if(disLeft < disRight){
				goRight();
				Delay150ms();
				stop();
			}
			if(disRight < disLeft){
				goLeft();
				Delay150ms();
				stop();
			}
		}
		
	}
}

5.测速小车+远程控制

5.1 测速并上传上位机

c 复制代码
	有遮挡,输出高电平;无遮挡,输出低电平
	轮子走一圈,经过一个周长,C = 2x3.14x半径= 3.14 x 直径(6.5cm)
	对应的码盘也转了一圈,码盘有20个格子,每经过一个格子,会遮挡(高电平)和不遮挡(低电平),
	那么一个脉冲就是走了 3.14 * 6.5 cm /20 = 1.0205CM
	定时器可以设计成一秒,统计脉冲数,一个脉冲就是1cm
	假设一秒有80脉冲,那么就是80cm/s
	
	又因为无遮挡时,其脉冲信号由高电平变低电平,因此采用外部中断下降沿触发
c 复制代码
//timer.c
#include "motor.h"
#include "reg52.h"

extern unsigned int speedCnt;
unsigned int speed;
char signal = 0;
unsigned int cnt = 0;

void Time0Init()
{
	//1. 配置定时器0工作模式位16位计时
	TMOD = 0x01;
	//2. 给初值,定一个0.5出来
	TL0=0x33;
	TH0=0xFE;
	//3. 开始计时
	TR0 = 1;
	TF0 = 0;
	//4. 打开定时器0中断
	ET0 = 1;
	//5. 打开总中断EA
	EA = 1;
}

void Time0Handler() interrupt 1
{
	cnt++;  //统计爆表的次数. cnt=1的时候,报表了1
	//重新给初值
	TL0=0x33;
	TH0=0xFE;
	
	if(cnt == 2000){//爆表2000次,经过了1s
		signal = 1;
		cnt = 0;  //当100次表示1s,重新让cnt从0开始,计算下一次的1s
		//计算小车的速度,也就是拿到speedCnt的值
		speed = speedCnt;
		speedCnt = 0;//1秒后拿到speedCnt个格子,就能算出这1s的速度,格子清零
	}
		
c 复制代码
//uart.c
#include "reg52.h"
#include "motor.h"
#include "string.h"
sbit D5 = P3^7;
#define SIZE 12

sfr AUXR = 0x8E;
char buffer[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 SendByte(char mydata)
{
	SBUF = mydata;
	while(!TI);
	TI = 0;
}

void SendString(char *str)
{
	while(*str != '\0'){
		SendByte(*str);
		str++;
	}
}


//M1qian  M2 hou M3 zuo  M4 you
void Uart_Handler() interrupt 4
{
	static int i = 0;//静态变量,被初始化一次
	char tmp;

	if(RI)//中断处理函数中,对于接收中断的响应
	{
			RI = 0;//清除接收中断标志位
			tmp = SBUF;
			if(tmp == 'M'){
				i = 0;
			}
			buffer[i++] = tmp;
		
			//灯控指令
			if(buffer[0] == 'M'){
				switch(buffer[1]){
					case '1':
						goForward();
						break;
					case '2':
						goBack();
						break;
					case '3':
						goLeft();
						break;
					case '4':
						goRight();
						break;
					default:
						stop();
						break;
				}
			}
		
			if(i == 12) {
				memset(buffer, '\0', SIZE);
				i = 0;
			}
	}
		
}
c 复制代码
#include "motor.h"
#include "delay.h"
#include "uart.h"
#include "reg52.h"
#include "time.h"
#include "stdio.h"

sbit speedIO = P3^2;//外部中断0
unsigned int speedCnt = 0; //统计格子,脉冲次数
extern unsigned int speed;//速度
extern char signal; //主程序发速度数据的通知
char speedMes[24];  //主程序发送速度数据的字符串缓冲区

void Ex0Init()
{
	EX0 = 1;//允许中断
	//EA = 1;在串口初始化函数中已经打开了总中断
	IT0 = 1;//外部中断的下降沿触发
}

void main()
{
	Time0Init();//定时器0初始化
	UartInit();//串口相关初始化
	//外部中断初始化
	Ex0Init();
	
	while(1){
		if(signal){//定时器1s到点,把signal置一,主程序发送速度
			sprintf(speedMes,"speed:%d cm/s",speed);//串口数据的字符串拼装,speed是格子,每个格子1cm
			SendString(speedMes);//速度发出去
			signal = 0;//清0speed,下次由定时器1s后的中断处理中再置一
		}
	}
}

void speedHandler() interrupt 0 //外部中断处理函数
{
	speedCnt++;//码盘转动了一个格子
}

5.2 蓝牙控制小车且OLED显示速度和蓝牙获取速度数据

c 复制代码
//oled.c
#include "reg52.h"
#include "intrins.h"
#include "Oledfont.h"


sbit scl = P1^2;
sbit sda = P1^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;//就在时钟脉冲9期间释放数据线
	_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做好数据准备
		sda = dataSend & 0x80;//1000 0000获得dataSend的最高位,给sda
		_nop_();//发送数据建立时间
		scl = 1;//scl拉高开始发送
		_nop_();//数据发送时间
		scl = 0;//发送完毕拉低
		_nop_();//
		dataSend = dataSend << 1;
	}
}

void Oled_Write_Cmd(char 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(char 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 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()
{
	unsigned char i,j; //-128 --- 127
	
	for(i=0;i<8;i++){
		Oled_Write_Cmd(0xB0 + i);//page0--page7
		//每个page从0列
		Oled_Write_Cmd(0x00);
		Oled_Write_Cmd(0x10);
		//0到127列,依次写入0,每写入数据,列地址自动偏移
		for(j = 0;j<128;j++){
			Oled_Write_Data(0);
		}
	}
}

void Oled_Show_Char(char row,char col,char oledChar){ //row*2-2
	unsigned int  i;
	Oled_Write_Cmd(0xb0+(row*2-2));                           //page 0
	Oled_Write_Cmd(0x00+(col&0x0f));                          //low
	Oled_Write_Cmd(0x10+(col>>4));                            //high	
	for(i=((oledChar-32)*16);i<((oledChar-32)*16+8);i++){
		Oled_Write_Data(F8X16[i]);                            //写数据oledTable1
	}

	Oled_Write_Cmd(0xb0+(row*2-1));                           //page 1
	Oled_Write_Cmd(0x00+(col&0x0f));                          //low
	Oled_Write_Cmd(0x10+(col>>4));                            //high
	for(i=((oledChar-32)*16+8);i<((oledChar-32)*16+8+8);i++){
		Oled_Write_Data(F8X16[i]);                            //写数据oledTable1
	}		
}


/******************************************************************************/
// 函数名称:Oled_Show_Char 
// 输入参数:oledChar 
// 输出参数:无 
// 函数功能:OLED显示单个字符
/******************************************************************************/
void Oled_Show_Str(char row,char col,char *str){
	while(*str!=0){
		Oled_Show_Char(row,col,*str);
		str++;
		col += 8;	
	}		
}
c 复制代码
void Ex0Init()
{
	EX0 = 1;//允许中断
	//EA = 1;在串口初始化函数中已经打开了总中断
	IT0 = 1;//外部中断的下降沿触发
}

void main()
{
	Time0Init();//定时器0初始化
	UartInit();//串口相关初始化
	//外部中断初始化
	Ex0Init();
	Oled_Init();
	Oled_Clear();
	while(1){
		if(signal){//定时器1s到点,把signal置一,主程序发送速度
			sprintf(speedMes,"speed:%d cm/s",speed);//串口数据的字符串拼装,speed是格子,每个格子1cm
			SendString(speedMes);//速度发出去
		
			signal = 0;//清0speed,下次由定时器1s后的中断处理中再置一
		}
		Oled_Show_Str(2,2,speedMes);
	}
}

void speedHandler() interrupt 0 //外部中断处理函数
{
	speedCnt++;//码盘转动了一个格子
}

5.3 WIFI控制小车且OLED显示速度和WIFI获取速度数据

c 复制代码
//esp8266.c
#include "uart.h"

//1 工作在路由模式
code char LYMO[] = "AT+CWMODE=2\r\n";
//2 使能多链接
code char DLJ[] = "AT+CIPMUX=1\r\n"; 
//3 建立TCPServer
code char JLFW[] = "AT+CIPSERVER=1\r\n"; // default port = 333 



char AT_OK_Flag = 0;				//OK返回值的标志位
char Client_Connect_Flag = 0;

void initWifi_AP()
{
	SendString(LYMO);
	while(!AT_OK_Flag);
	AT_OK_Flag = 0;
	SendString(DLJ);
	while(!AT_OK_Flag);
	AT_OK_Flag = 0;
}

void waitConnect()
{
	SendString(JLFW);
	while(!Client_Connect_Flag);
	AT_OK_Flag = 0;	
}
c 复制代码
//uart.c
#include "reg52.h"
#include "motor.h"
#include "string.h"
sbit D5 = P3^7;
#define SIZE 12

sfr AUXR = 0x8E;
char buffer[SIZE];

extern char AT_OK_Flag;				//OK返回值的标志位
extern char Client_Connect_Flag;

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 SendByte(char mydata)
{
	SBUF = mydata;
	while(!TI);
	TI = 0;
}

void SendString(char *str)
{
	while(*str != '\0'){
		SendByte(*str);
		str++;
	}
}


//M1qian  M2 hou M3 zuo  M4 you
void Uart_Handler() interrupt 4
{
	static int i = 0;//静态变量,被初始化一次
	char tmp;

	if(RI)//中断处理函数中,对于接收中断的响应
	{
			RI = 0;//清除接收中断标志位
			tmp = SBUF;
			if(tmp == 'M' || tmp == 'O' || tmp == '0'){
				i = 0;
			}
			buffer[i++] = tmp;
		
			//连接服务器等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);
			}
			//灯控指令
			if(buffer[0] == 'M'){
				switch(buffer[1]){
					case '1':
						goForward();
						break;
					case '2':
						goBack();
						break;
					case '3':
						goLeft();
						break;
					case '4':
						goRight();
						break;
					default:
						stop();
						break;
				}
			}
		
			if(i == 12) {
				memset(buffer, '\0', SIZE);
				i = 0;
			}
	}
		
}
c 复制代码
sbit speedIO = P3^2;//外部中断0
unsigned int speedCnt = 0; //统计格子,脉冲次数
extern unsigned int speed;//速度
extern char signal; //主程序发速度数据的通知
char speedMes[24];  //主程序发送速度数据的字符串缓冲区
//发送数据
char FSSJ[] = "AT+CIPSEND=0,5\r\n";

void Ex0Init()
{
	EX0 = 1;//允许中断
	//EA = 1;在串口初始化函数中已经打开了总中断
	IT0 = 1;//外部中断的下降沿触发
}

void main()
{
	Time0Init();//定时器0初始化
	UartInit();//串口相关初始化
	Delay1000ms();//给espwifi模块上电时间
	
	initWifi_AP(); //初始化wifi工作在ap模式
	waitConnect(); //等待客户端的连接
	
	//外部中断初始化
	Ex0Init();
	Oled_Init();
	Oled_Clear();
	while(1){
		if(signal){//定时器1s到点,把signal置一,主程序发送速度
			SendString(FSSJ);
			Delay1000ms();
			sprintf(speedMes,"%dcms",speed);//串口数据的字符串拼装,speed是格子,每个格子1cm
			SendString(speedMes);//速度发出去
		
			signal = 0;//清0speed,下次由定时器1s后的中断处理中再置一
		}
		Oled_Show_Str(2,2,speedMes);
	}
}

void speedHandler() interrupt 0 //外部中断处理函数
{
	speedCnt++;//码盘转动了一个格子
}

5.4 4G控制小车且OLED显示速度和4G获取速度数据

c 复制代码
	按EC03-DNC4G通信模块章节设置,终端不变

5.5 以上都一样只是模块的配置和模块发送控制信号的方式不同

6.语音小车

c 复制代码
	SU-03T LD3320----通过智能公元配置模块自动生成sdk(作用:配置对应引脚电平高低)
	将其烧录到模块中
	将模块接入到单片机上,说出相应的指令,模块的引脚会自动输出相应的高低电平
	通过对电平的检索匹配相应的模式
c 复制代码
#include "reg52.h"
#include "hc04.h"
#include "delay.h"
#include "sg90.h"
#include "Oled.h"
#include "motor.h"

#define MIDDLE 0
#define LEFT 1
#define RIGHT 2

#define BZ 1
#define XJ 2
#define GS 3


sbit A25 = P1^5;
sbit A26 = P1^6;
sbit A27 = P1^7;

sbit leftSensorX = P2^7;
sbit rightSensorX = P2^6;

sbit leftSensorG = P2^5;
sbit rightSensorG = P2^4;

char dir;
	
double disMiddle;
double disLeft;
double disRight;

void xunjiMode()
{
		if(leftSensorX == 0 && rightSensorX == 0){
			goForward();
		}
		if(leftSensorX == 1 && rightSensorX == 0){
			goLeft();
		}
		
		if(leftSensorX == 0 && rightSensorX == 1){
			goRight();
		}
		
		if(leftSensorX == 1 && rightSensorX == 1){
			//停
			stop();
		}
}

void gensuiMode()
{
		if(leftSensorG == 0 && rightSensorG == 0){
			goForward();
		}
		if(leftSensorG == 1 && rightSensorG == 0){
			goRight();
		}
		
		if(leftSensorG == 0 && rightSensorG == 1){
			
			goLeft();
		}
		
		if(leftSensorG == 1 && rightSensorG == 1){
			//停
			stop();
		}
}

void bizhangMode()
{
	if(dir != MIDDLE){
			sgMiddle();
			dir = MIDDLE;
			Delay300ms();
		}
		disMiddle = get_distance();
		
		if(disMiddle > 35){
			//前进
			goForward();
		}else if(disMiddle < 10){
			goBack();
			
		}else
		{
			//停止
			stop();
			//测左边距离
			sgLeft();
			Delay300ms();
			disLeft = get_distance();
			
			sgMiddle();
			Delay300ms();
			
			sgRight();
			dir = RIGHT;
			Delay300ms();
			disRight = get_distance();
			
			if(disLeft < disRight){
				goRight();
				Delay150ms();
				stop();
			}
			if(disRight < disLeft){
				goLeft();
				Delay150ms();
				stop();
			}
		}

}

void main()
{

	int mark = 0;
	
	Time0Init();
	Time1Init();
	//舵机的初始位置

	sgMiddle();
	Delay300ms();
	Delay300ms();
	dir = MIDDLE;
	
	Oled_Init();
	Oled_Clear();
	Oled_Show_Str(2,2,"-----Ready----");
	
	while(1){
		//满足寻迹模式的条件
		if(A25 == 0 && A26 == 1 && A27 == 1){
			if(mark != XJ){
				Oled_Clear();
				Oled_Show_Str(2,2,"-----XunJi----");
			}
			mark = XJ;
			xunjiMode();
		}
		//满足跟随模式的条件
		if(A25 == 1 && A26 == 0 && A27 == 1){
			if(mark != GS){
				Oled_Clear();
				Oled_Show_Str(2,2,"-----GenSui----");
			}
			mark = GS;
			gensuiMode();
		}
		//满足避障模式的条件
		if(A25 == 1 && A26 == 1 && A27 == 0){
			if(mark != BZ){
				Oled_Clear();
				Oled_Show_Str(2,2,"-----BiZhang----");
			}
			mark = BZ;
			bizhangMode();
		}
	
	}
}
相关推荐
智商偏低5 小时前
单片机之helloworld
单片机·嵌入式硬件
青牛科技-Allen6 小时前
GC3910S:一款高性能双通道直流电机驱动芯片
stm32·单片机·嵌入式硬件·机器人·医疗器械·水泵、
森焱森8 小时前
无人机三轴稳定控制(2)____根据目标俯仰角,实现俯仰稳定化控制,计算出升降舵输出
c语言·单片机·算法·架构·无人机
白鱼不小白8 小时前
stm32 USART串口协议与外设(程序)——江协教程踩坑经验分享
stm32·单片机·嵌入式硬件
S,D9 小时前
MCU引脚的漏电流、灌电流、拉电流区别是什么
驱动开发·stm32·单片机·嵌入式硬件·mcu·物联网·硬件工程
芯岭技术12 小时前
PY32F002A单片机 低成本控制器解决方案,提供多种封装
单片机·嵌入式硬件
youmdt12 小时前
Arduino IDE ESP8266连接0.96寸SSD1306 IIC单色屏显示北京时间
单片机·嵌入式硬件
嘿·嘘13 小时前
第七章 STM32内部FLASH读写
stm32·单片机·嵌入式硬件
Meraki.Zhang13 小时前
【STM32实践篇】:I2C驱动编写
stm32·单片机·iic·驱动·i2c
几个几个n15 小时前
STM32-第二节-GPIO输入(按键,传感器)
单片机·嵌入式硬件