自学内容网 自学内容网

51单片机

STC89C52

一.定时器

 1.介绍

C51中的定时器和计数器是同一个硬件电路支持的,通过寄存器配置不同,就可以将他当做定时器或者计数器使用。

定时器和计数器的区别在于其+1信号的不同
定时器:当配置为定时器使用时,每经过1个机器周期,计数存储器的值就加1
计数器:当配置为计数器时,每来一个负跳变信号(信号从P3.4 或者P3.5引脚输入),就加1

标准C51有2个定时器/计数器:T0和T1。他们的使用方法一致。C52相比C51多了一个T2

 2.计时

定时器每经过一个机器周期寄存器+1

晶振:晶振(晶体震荡器),又称数字电路的“心脏”,是各种电子产品里面必不可少的频率元器件。
 数字电路的所有工作都离不开时钟,晶振的好坏、晶振电路设计的好坏,会影响到整个系统的稳定性。

时钟周期:时钟周期也称为振荡周期,定义为时钟频率的倒数(晶振频率的倒数)。
 时钟周期是计算机中最基本的、最小的时间单位。在一个时钟周期内,CPU仅完成一个最基本的动作。
 时钟周期是一个时间的量。更小的时钟周期就意味着更高的工作频率

机器周期:机器周期也称为CPU周期。
 在计算机中,为了便于管理,常把一条指令的执行过程划分为若干个阶段(如取指、译码、执行等),每一阶段完成一个基本操作。
 完成一个基本操作所需要的时间称为机器周期。一般情况下,一个机器周期由若干个时钟周期组成

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

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

在这里插入图片描述

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

在这里插入图片描述

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

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

  2.3 定时器如何定时10毫秒

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

  2.4 定时器寄存器配置

    2.4.1 TCON

在这里插入图片描述

定时器有两个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 = 921665536 - 9216 = 56320 次
所以初值为56320 - 16进制数表示为 DC00;
TL0 = 00
TH0 = DC

    2.4.2 TMOD

在这里插入图片描述

四种工作模式:具体如上
M1 M0
0  0
0  1
1  0
1  1
选择工作方式1, TMOD的1(M1)配置为0, TOMD的0(M0)配置为1
    2.4.3 实现
#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 按位操作
void Timer0Init(void)
{
TMOD &= 0xF0;//保存定时器1的模式,清零定时器0
TMOD |= 0x01;//设置定时器0工作模式1
TL0 = 0x00;//设置定时初值
TH0 = 0xDC;//设置定时初值
TF0 = 0;//清除TF0标志
TR0 = 1;//定时器0开始计时
}

 3.定时器中断

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

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

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

  3.1 定时器中断相关寄存器

    3.1.1 IE 中断允许寄存器

在这里插入图片描述

ETO中断允许置1ET = 0
EA总中断置1EA = 1
中断号interrupt 1
首先对定时器进行相关配置: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

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

通过占空比编码模拟信号

占空比:一个周期内,高电平占据时长的百分比

在这里插入图片描述

  4.1 控制舵机

在这里插入图片描述

在这里插入图片描述

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

  4.2 IO输出模拟PWM

在这里插入图片描述

#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;
}
}

在这里插入图片描述

#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 舵机控制

#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 超声波测距

在这里插入图片描述
在这里插入图片描述

#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.智能垃圾桶

1. 舵机和超声波代码整合
舵机用定时器0
超声波用定时器1
实现物体靠近后,自动开盖,2秒后关盖
2. 查询的方式添加按键控制
3. 查询的方式添加震动控制
4. 使用外部中断0配合震动控制
5. 加入蜂鸣器
#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.串口编程要素

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

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

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

  2.串行通信

在这里插入图片描述

  2.1 电源控制寄存器PCON

在这里插入图片描述

SMOD = 0; 波特率不加倍
SMOD = 1; 波特率加倍

在这里插入图片描述

  2.2 串行控制寄存器SCON

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

其中可变,体现在可装初值TH1
不可变,则为可不可装初值TH1

  2.3 波特率计算

例工作模式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 初始化代码

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 发送字符串

#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 点灯

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;//启动定时器

}

怎么知道接收完毕?

在这里插入图片描述

char data_msg;
在发送中,我们知道
SBUF = data_msg; //将数据存放在缓冲区中,数据发送完成
那么在读取中
data_msg = SBUF; //将缓冲区的数据存放在,data_msg中即为读取数据
#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.串口中断

在这里插入图片描述
在这里插入图片描述

启用中断需要
ES = 1;//开启串口中断
EA = 1;//开启总中断
#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 串口支持单词型指令

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 发送序列

在这里插入图片描述

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


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

  4.EPS8266

  4.1 单片机发送AT指令入网

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 //带回车
//在透传发送数据过程中,若识别到单独的⼀包数据 “+++”,则退出透传发送
#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当服务器

//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
#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模块配置连接服务器

4G无法识别局域网ip,通过花生壳,内网ip穿透,为局域网的设备提供一个外网可访问的地址和端口
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点灯

#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

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

a = 01100001 --- 0x61   ------  97     ASCII

  6.1 写操作

在这里插入图片描述
在这里插入图片描述

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

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 读操作

在这里插入图片描述

//读操作
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 显示一个字符

#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 显示一行字符

#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温湿度传感器

数据格式:
8bit湿度整数数据+8bit湿度小数数据+8bi温度整数数据+8bit温度小数数据+8bit校验和

在这里插入图片描述

  7.1 检测模块是否存在

在这里插入图片描述

主机经过abcd三段操作后,DHT会拉低80us响应,
当读取到低电平则模块存在
#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 读取数据

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

高电平 70us 表示1
高电平 26-28us表示低电平
因此检测50us时电平高低
高位1,低为0
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 串口

#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度风扇转

#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协议

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

 1.起始信号

在这里插入图片描述

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.应答信号

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

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

在这里插入图片描述

 3.数据发送

在这里插入图片描述

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写入指令和数据

在这里插入图片描述


/*
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页寻址模式

发送cmd : 0x20;
发送cmd : 0x02; 默认页模式

在这里插入图片描述
在这里插入图片描述

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

  4.3 清屏

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

/*--  文字:  A  --*/
/*--  宋体12;  此字体下对应的点阵为:宽x高=8x16   --*/
0x00,0x00,0xC0,0x38,0xE0,0x00,0x00,0x00,0x20,0x3C,0x23,0x02,0x02,0x27,0x38,0x20,
#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 显示多个字

#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控制小车

//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();

//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 串口控制小车

#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 蓝牙控制小车点动

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

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调速

假设在20ms周期内
小车15ms全速前进 5ms停止
与  小车5ms全速前进  15ms停止的功率是不一样的
在不同的功率下其惯性的大小也不同
因此通过该原理实现小车调速
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调速转弯

设置两个定时器,分别控制两个轮子
//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;
}
//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;
}
}
#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.循迹小车

TCRT5000传感器的红外发射二极管不断发射红外线
当发射出的红外线没有被反射回来或被反射回来但强度不够大时,红外接收管一直处于关断状态,此时模块的输出端为高电平,指示二极管一直处于熄灭状态被检测物体出现在检测范围内时,
红外线被反射回来且强度足够大,红外接收管饱和,此时模块的输出端为低电平,指示二极管被点亮
总结就是一句话,没反射回来,D0输出高电平,灭灯
#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.跟踪小车==循迹小车

#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 摇头(舵机)

//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 测距(超声波测距)

//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 移动(电机驱动)

//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 摇头测距避障(舵机+超声波测距+电机驱动)

#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 = 2x3.14x半径= 3.14 x 直径(6.5cm)
对应的码盘也转了一圈,码盘有20个格子,每经过一个格子,会遮挡(高电平)和不遮挡(低电平),
那么一个脉冲就是走了 3.14 * 6.5 cm /20 = 1.0205CM
定时器可以设计成一秒,统计脉冲数,一个脉冲就是1cm
假设一秒有80脉冲,那么就是80cm/s

又因为无遮挡时,其脉冲信号由高电平变低电平,因此采用外部中断下降沿触发
//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的速度,格子清零
}

//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;
}
}

}
#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显示速度和蓝牙获取速度数据

//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;
}
}

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获取速度数据

//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;
}
//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;
}
}

}
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获取速度数据

按EC03-DNC4G通信模块章节设置,终端不变

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

 6.语音小车

在这里插入图片描述

SU-03T LD3320----通过智能公元配置模块自动生成sdk(作用:配置对应引脚电平高低)
将其烧录到模块中
将模块接入到单片机上,说出相应的指令,模块的引脚会自动输出相应的高低电平
通过对电平的检索匹配相应的模式
#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();
}

}
}

原文地址:https://blog.csdn.net/qq_46626969/article/details/137513234

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!