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.电机驱动
-
- [ 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();
}
}
}