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();
		}
	
	}
}
相关推荐
yutian06063 小时前
Keil MDK下载程序后MCU自动重启设置
单片机·嵌入式硬件·keil
析木不会编程6 小时前
【小白51单片机专用教程】protues仿真独立按键控制LED
单片机·嵌入式硬件·51单片机
枯无穷肉10 小时前
stm32制作CAN适配器4--WinUsb的使用
stm32·单片机·嵌入式硬件
不过四级不改名67710 小时前
基于HAL库的stm32的can收发实验
stm32·单片机·嵌入式硬件
嵌入式大圣10 小时前
单片机UDP数据透传
单片机·嵌入式硬件·udp
云山工作室11 小时前
基于单片机的视力保护及身姿矫正器设计(论文+源码)
stm32·单片机·嵌入式硬件·毕业设计·毕设
嵌入式-老费11 小时前
基于海思soc的智能产品开发(mcu读保护的设置)
单片机·嵌入式硬件
qq_3975623112 小时前
MPU6050 , 设置内部低通滤波器,对于输出数据的影响。(简单实验)
单片机
艺术家天选13 小时前
STM32点亮LED灯
stm32·单片机·嵌入式硬件
向阳逐梦13 小时前
基于STM32F4单片机实现ROS机器人主板
stm32·单片机·机器人