草庐IT

51单片机详解

No Iverson 2023-04-28 原文

前言

这是我对这几个月学习51单片机的一些汇总,谈谈对51的一些看法,51大致就是对定时器,计算器,中断,串口的一些操作,前面我浅谈一下51的基本操作,后面我会对Sg-90舵机,超声波,LCD1602,DHT11温湿度传感器,IIC-OLED,HC-05蓝牙模块,esp8266-01sWI-FI模块,4G模块,4驱小车进行一个详细的汇总,希望能对大家有所帮助。

文章目录

  • 前言
  • 一、单片机入门
  • 1.什么是单片机
  • 2.单片机工作的基本时序
  • 3.数字电路基础
  • 4.二进制逻辑运算
  • 6.89C52的引脚图
  • 二、单片机的一些项目
    • 1.点亮一个LED&按键点亮
    • 2.LED循环点亮
    • 3.震动传感器介绍
    • 4.震动传感器点亮Led&震动传感器触发继电器点亮Led
    • 5.433接发模块&Relay做一个简易的报警器
    • 6.定时器详解
    • 7.中断详解
    • 8.利用舵机&超声波通过中断&定时器做一个自动垃圾桶
    • 9.UART串口详解
    • 10.利用串口发一个字符到上位机
    • 11.通过发送发送字符串到上位机
    • 12.通过串口用HC-05蓝牙模块,手机连接蓝牙点亮一个LED
    • 13.通过esp8266WI-FI模块通过上位机点亮一个LED
    • 14.通过4GI模块通 过上位机点亮一个LED
    • 15.Lcd1602详解
    • 16.DTH11温湿度详解
    • 17.IIC_OLED详解
    • 18.四驱小车
  • 总结

一、单片机入门

1.什么是单片机

单片机是一种集成电路芯片,采用超大规模集成技术把具有处理数据能力的中央处理器CPU、随机存储器ROM、只读存储器RAM、定时器/计算器、中断系统和IO口等功能集成到一块硅片上构成的一个小型计算机,从当时的4为、8位发展到现在的300M的高速单片机。

2.单片机工作的基本时序

我们都知道在学校是通过铃声来控制所有班级的上下课时间,我们都知道单片机执行指令的过程就是从ROM取出一条指令执行来完成它在各个地方的作用,那它什么时候取指令这个是顺序呢?这里引入一个时序的周期,每访问一次ROM的时间,就是一个机器周期的时间

1个机器周期 = 6个状态周期 = 12个时钟(振荡)周期  

时钟周期:即单片机的基本时间单位,若晶体的频率=12MHZ,那时钟周期 = 1/12MHZ,一个时钟周期 = 1/12MHZ = 1/12000 000每秒

机器周期:即12x1/12 000 000 =0.000001s = 1us,访问一次ROM取指令的时间就是1us

3.数字电路基础 

电平特性

数字电路中只有两种电平:高电平和低电平

高电平:5v或者3.3v,取决与单片机电源

低电平:0V

RS232电平:计算机串口的电平

高电平:-12v

低电平:+12v

我们跟电脑通信的时候,要通过元器件将单片机的电平转换成电脑能识别的电平,才能跟电脑进行通信。

4.二进制逻辑运算

"与"运算

有0得0,全1才1

1&1 = 1,1&0 = 0; 0&0 = 0;

"或"运算

有1得1,全0才0;

1|1 = 1;1|0 = 1;0|0 = 0;

"非"运算

~1 = 0;~0 = 1;

6.89C52的引脚

 二、单片机的一些项目

1.点亮一个LED&按键点亮

通过sbit访问单片机P2^0口,给它一个低电平,使它点亮

#include <REGX52.h>

sbit led = P2^0;//sbit作用是定义特殊功能寄存器的位变量 此时变量led就保存了p2^0的地址

void main()
{

		led = 0;//通过看原理图,这个io口的led是低电平触发


}

通过单片机自带的按键使它点亮

#include <REGX52.h>

sbit led = P2^0;

sbit key_open  = P3^1;
sbit key_close = P3^2;

void main()
{
	
	led = 1;//先默认让led关

		while(1)
		{
				if(key_open == 0)//当按下key_open它会的到一个低电平
				{
					led = 0;//按下按键我打开led
				}
				
				if(key_close == 0)//按下key_close,就关闭led
				{
					led = 1;
				}
		}




}

 这里是通过按键开关来控制点亮了led的

2.LED循环点亮

#include <REGX52.h>
#include <intrins.h>

void main()
{
	
	P2 = 0xfe;//因为led是低电平点亮 0xfe = 1111 1110,我们先让第0位点亮
	
	while(1)
	{
			
			
			Delay1000ms();
	  	P2 = P2 << 1;//然后依次左移一位就= 1111 1101,让第1位点亮,后面依次操作
									//循环到第7位 = 是0111 1111 但是之前每循环左移都给了低电平
		if(P2 == 0x00)//所以当 = 0111 1111 的时候重新给它赋值位0xfe
		{
				
	    P2 = 0xfe;
		}

	}


}

void Delay1000ms()		//延迟函数
{
	unsigned char i, j, k;

	_nop_();
	i = 8;
	j = 154;
	k = 122;
	do
	{
		do
		{
			while (--k);
		} while (--j);
	} while (--i);
}

3.震动传感器介绍

 通过震动点亮led

#include <REGX52.h>
#include <intrins.h>

sbit shake = P0^0;
sbit led   = P2^0;

void main()
{
	led = 1;
	
	while(1)
	{
		if(shake == 0)//当发生震动 就点亮led
		{
			led = 0;
		}
		
	}
	


}

4.震动传感器点亮Led&震动传感器触发继电器点亮Led

通过震动让继电器把接在继电器上的灯点亮

#include <REGX52.h>
#include <intrins.h>

sbit shake = P0^0;
sbit relay = P0^1;

void main()
{
	
	while(1)
	{
		if(shake == 0)//当发生震动打开继电器
		{
			relay = 0;
			
		}
		
	}
	
}

5.433接发模块&Relay做一个简易的报警器

通过433收发模块按下打开继电器让报警器响,在按一下让它停止响,当然你也可以在配一个震动传感器,如果发生震动就响,在按一下就停止响。

#include <REGX52.h>
#include <intrins.h>

sbit d0 = P0^0;
sbit d1 = P0^1;
sbit relay = P0^7;

void main()
{
	
	while(1)
	{
		if(d0 == 1)
		{
			relay = 0;
		}
		
		if(d1 == 1)
		{
			relay = 1;
		}
	}
	

}

 6.定时器详解

51单片机有两组定时器,一组是T0,一组是T1,因为几个定时/计数,所以称为定时器。定时器的本质就是每过一个机器周期加1

定时器一共有4个模式

GATE =1,要用软件使TR0/TR1置1,才能开始工作,同时外部中断INT0/1为高电平,才能启动定时器 

C/T = 0是定时模式,=1是计数模式

#include <REGX52.h>
#include <intrins.h>

sbit led = P2^0;


void Delay1000ms()		//延迟函数
{
	unsigned char i, j, k;

	_nop_();
	i = 8;
	j = 154;
	k = 122;
	do
	{
		do
		{
			while (--k);
		} while (--j);
	} while (--i);
}


void Time_Init()
{
	TMOD = 0x01;
	TL0 = 0x20;		//设置定时初值1ms,定时器初值=(2的16次方(因为我选的模式1)—x)x12/你单片机晶振的频率 = 你要设的初值(us)求出的x转换成16 进制就行了
	TH0 = 0xD1;
	TR0  = 1;
	TF0  = 0; 


}


void main()
{
	int num = 0;
	Time_Init();

	
	led = 1;//默认led为关的状态
	
	while(1)
	{
	   if(TF0 == 1)//定时好了溢出
  	{
       
	  	  ++num;//每溢出一次加1
		
				TL0 = 0x20;		//设置定时初值
				TH0 = 0xD1; 		
	 }
	 
	 if(num == 1000)//加到1000 也就是1s 打开led
			{
				led = !led;
				
			}
	 

}
	
	

}

7.中断详解

比如:你正在喝奶茶,此时发生了一个紧急事情,你就会先暂停喝奶茶,转而取处理这个紧急事情,等紧急事情处理完了,你就可以继续和奶茶了。我们把这种紧急事情叫做中断。但是如果你喝奶茶期间发生了许多事情,那就要按照紧急事情的优先级来处理。

#include <REGX51.H>

sbit led = P2^0;

int cnt = 0;

void Time_Init()
{
	TMOD = 0x10;
	TL1 = 0x18;		//设置定时初值
	TH1 = 0xFC;
	TR1 = 1;
	TF1 = 0;//现在还没有溢出,先让他=0
	ET1 = 1;//用的T1就必须用T1中断通道
	EA = 1;
	


}



void main()
{
	Time_Init();
	led = 1;
	
	while(1);
	
}

void zd() interrupt 3
{
	TL1 = 0x18;		
	TH1 = 0xFC;
	
	cnt++;
	
	if(cnt == 500)
	{
		cnt = 0;
		led =!led;
	}
}

8.利用舵机&超声波通过中断&定时器做一个自动垃圾桶

超声波测距

#include <regx52.h>

sbit trig = P0^1;//发送超声波,要发送超声波,必须给trig一个10us以上的高电平
sbit echo = P0^0;//判断超声波什么时候发送和什么时候接收
sbit led  = P2^0; 

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

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


void trig10us()
{
	trig = 0;
	trig = 1;
	Delay10us();
	trig = 0;
	
}

void Time1_Init()
{
    TMOD = 0x10;//choose 16bit 计数
    TL1 = 0;
    TH1 = 0;
}


void main()
{
    double time;
    double dis;
    Time1_Init();

    while(1)
    {

        trig10us();
        while(echo == 0);//判断超声波说明时候发送
        TR1 = 1;//开始定时
        while(echo == 1);//判断超声波什么时候返回
        TR1 = 0;//停止计时
        time = (TH1 *256 + TL1)*1;//TL1和TH1是所计的数,1是机器周期1us,time就是超声波去的时间和返回的时间
        dis = (time/2)*0.034;//因为time存放的是去和返回的时间,所以要除以2,超声波测距是340m/s换算成 34000cm/s = 34cm/ms = 0.034cm/us
        if(dis < 10)
        {
            led = 0;//open led
        }        
        else
        {
            led = 1;
        }

        TH1 = 0;
        TL1 = 0;
    }    

}

舵机 

/*
    控制舵机是通过占空比来控制的,也就是在单位时间内,你控制让它得到多少高电平
    sg90舵机最高频率是50Mhz 周期就是0.02s = 20ms,我们字舵机20ms这个周期内给它高电平控制舵机角度

     0° = 0.5ms
    45° = 1ms
    90° = 1.5ms
    135°= 2ms
    180°= 2.5ms 	

*/
#include <regx52.h>
#include <intrins.h>

sbit sg90 = P0^7;

int jd;//用于控制舵机角度
int cnt;//用于计数来判断定时器中断溢出

void Time0_Init()
{
    TMOD = 0x01;
  	TL0 = 0x33;	//定时器第八位初始化
	  TH0 = 0xFE;//定时器高八位初始化 设置定时0,5ms
    TR0 = 1;
    TF0 = 0;
    ET0 = 1;
    EA  = 1;
    
}

void Delay1000ms()		//延迟函数
{
	unsigned char i, j, k;

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


void main()
{
    Time0_Init();
    jd = 1;
		cnt = 0;
    
    while(1)
    {
        jd = 5;
				cnt = 0;
        Delay1000ms();
        jd = 1;
				cnt = 0;
        Delay1000ms();

    }

}

void zd() interrupt 1
{
	  TL0 = 0x33;	
	  TH0 = 0xFE;
    cnt++;

    if(cnt <= jd)
    {
        sg90 = 1;
    }
    
    else
    {
        sg90 = 0;
    }

    if(cnt == 40)
    {
        cnt = 0;
    }
}

垃圾桶

#include <regx52.h>
#include <intrins.h>

sbit led = P2^0;
sbit trig = P0^1; //超声波发送 给trig至少10us高电平
sbit echo = P0^0;//怎么知道他开始发和接受的返回波
sbit dj = P0^7;
sbit key = P3^1;
int cnt = 0;
int jd;

void Time0_Init()
{
	TMOD &= 0xF0;                                    
  TMOD |= 0x01;                                            
	TL0 = 0x33;	//定时器第八位初始化
	TH0 = 0xFE;//定时器高八位初始化 设置定时0,5ms
	TR0 = 1;//开始定时
	TF0 = 0;//定时器溢出标志位
	EA = 1;//中断允许总开关
	ET0 = 1;//T0中断允许开关



}

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

	_nop_();
	i = 18;
	j = 131;
	k = 103;
	do
	{
		do
		{
			while (--k);
		} while (--j);
	} while (--i);
}


void Delay10us()		//给trig一个10us延时
{
	unsigned char i;

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

void Time1_Init()//初始化定时器
{
	TMOD = 0x10;
	TH1 = 0;//从0开始定时
	TL1 = 0;

}

void trig10()//给trig一个10us高电平
{
	trig = 0;
	trig = 1;//向外发送
	Delay10us();
	trig = 0;//恢复
	
}

double Read_Csb()
{
	double dis;
	double time;
	
		TH1 = 0;//从0开始定时
	  TL1 = 0;
		
		trig10();//发送一个信号
		while(echo == 0);//通过echo从高电平跳转到低电平知道已经开始发送信号了
		TR1 = 1;//信号发送开始定时
		while(echo == 1);//通过从低电平跳转到高电平知道波回来了
		TR1 = 0;//波回来了结束定时
		time=(TH1 * 256 + TL1)*1;//us为单位  计算中间进过的时间 记得数TH0和TL0相加 就是TH0左移8位 移1位 = 2;8位 = 256 ;*1.085一个机器周期
		dis = time / 2* 0.034;//距离 = 340m/s = 34000cm/s = 34cm/ms = 0.034cm/us 时间来回两次 / 2
		
	
	return dis;

}

void main()
{
	double dis;
	Time1_Init();//初始化定时器
  Time0_Init();
	jd  = 1;
	cnt = 0;
	led = 1;
	
	while(1)
	{
		dis = Read_Csb();
		
		if(dis < 10 || key == 0)
		{
			led = 0;
			jd = 5;
			Delay2500ms();
			
			
			
		}
		else
		{
			led = 1;
			jd = 1;
		}

	
	
	}
	
}	

void zd() interrupt 1 //控制脉冲
{
	cnt++;
	
	TL0 = 0x33;		
	TH0 = 0xFE;
	
	if(cnt <= jd)
	{
		dj = 1;
	}
	else
	{
		dj = 0;
	}
	if(cnt == 40)
	{
		cnt = 0;
		
	}
	


}

9.UART串口详解

串口通信

1.单工   只有一根线,只能单向传输
2.半双工 有两根线Tx、Rx,交叉连接,但是任何时候只有一个方向传输和接收
3.全双工 有两根线可以在两个方向上传输

同步和异步通信

1.同步通信 发送数据和,等待接收方回应了,才能继续发送下个数据
同步通信 必须保持双方在同一个时钟
2.异步通信 发送数据后,不等待接收方的回应,继续发送下个数据
异步通信 因为发送效率低,所以每发送一个字符,要加开始位和停止位,还要配置波特率

10.发送一个字符到上位机

#include <REGX51.H>
#include <intrins.h>

void UART_Init()
{
    PCON = 0x00;//波特率不翻倍
	SCON = 0x50;//sm0 sm1 sm2 ren rb8 tb8 ti ri 每发送一个数据ti必须复位 每接收一个数据ri必须复位 ren = 1允许接收
	TMOD = 0x20;//波特率配置 方式1 9600 =(2smod/32)*t1的溢出率
	TH1  = 0XFD;//t1溢出率 = (频率/32)*(256 -TH1)
	TL1  = 0xFD;
	TR1 = 1;//打开定时器1
	
}

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

	_nop_();
	i = 71;
	j = 10;
	k = 171;
	do
	{
		do
		{
			while (--k);
		} while (--j);
	} while (--i);
}


void main()
{

	UART_Init();
	
	while(1)
	{
		Delay10000ms();
		SBUF = 'a';//每个一秒发送一个字符
	}
}

11.发送一个字符串到上位机

#include <REGX51.H>
#include <intrins.h>

void UART_Init()
{
	SCON = 0x50;
	TMOD = 0x20;
	TH1  = 0XFD;
	TL1  = 0xFD;
	TR1= 1;

	
}


void Send_Bit(char Data)
{
	SBUF = Data;
	while(!TI);
	TI = 0;

}

void Send_String(char * p)
{
	
	while(*p != '\0')
	{
		
		Send_Bit(*p);
		p++;
	}

}

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 main()
{
	UART_Init();

	while(1)
	{
		Delay1000ms();
		Send_String("iverson");
	}

	
}

12.通过串口用HC-05蓝牙模块,手机连接蓝牙点亮一个LED 

#include <REGX51.H>
#include <intrins.h>

sbit led =P2^0;

void UART_Init()
{
	SCON = 0x50;
	TMOD = 0x20;
	TH1  = 0XFD;
	TL1  = 0xFD;
	TR1  = 1;
	ES   = 1;
  EA	 = 1;
	
}

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

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


void main()
{


	UART_Init();
	
	while(1)
	{
		Delay1000ms();
		SBUF = 'a';
	}
}


void zd() interrupt 4//串口中断
{

	
	if(RI == 1)
	{
		RI = 0;
		
		if(SBUF == 'o')
		{
			led = 0;
		}
		if(SBUF == 'c')
		{
			led = 1;
		}
	}
		

}

 13.通过esp8266WI-FI模块通过上位机点亮一个LED

/*
				就是在同一个局域网 服务器先连接WiFi 获得WiFiIP esp826601s在通过WIFIIP连接服务器  
				单片机通过串口发送数据给esp826601s esp826601s接受该AT数据连接 WIFI 和 服务器等
				就开始实现数据传输 

				AT+CWMODE=3 //1. 是station(设备)模式 2.是AP(路由)模式 3.是双模
				OK

				AT+CWJAP="TP-LINK_3E30","18650711783" //指令
				WIFI CONNECTED //结果
				WIFI GOT IP //结果
				
				AT+CIFSR //指令
				+CIFSR:APIP,"192.168.4.1"         当esp826601s做路由的IP
				+CIFSR:APMAC,"4e:75:25:0d:ae:2f"
				+CIFSR:STAIP,"192.168.0.148"
				+CIFSR:STAMAC,"4c:75:25:0d:ae:2f"
				OK
				
				AT+CIPSTART="TCP","192.168.0.113",8888 //指令,注意双引号逗号都要半角(英文)输入
				CONNECT //结果:成功
				OK //结果:成功

				AT+CIPSEND=4 // 设置即将发送数据的长度 (这里是4个字节)
				>CLCA // 看到大于号后,输入消息,CLCA,不要带回车
				Response :SEND OK //结果:成功
				//注意,这种情况下,每次发送前都要先发送AT+CIPSEND=长度 的指令,再发数据!

				AT+CIPMODE=1 //开启透传模式
				Response :OK
				+++退出透传模式

				AT+CIPSEND //带回车
				Response: > //这个时候随意发送接收数据咯


*/

#include <regx52.h>
#include <intrins.h>
#include <string.h>
#define SIZE 12

sbit Led = P2^0;
sbit Led1= P2^7; 

code char LJWL []="AT+CWJAP=\"iPhone\",\"00000000\"\r\n";
code char LJFWQ[]="AT+CIPSTART=\"TCP\",\"169.254.165.190\",8880\r\n";
char TC  []="AT+CIPMODE=1\r\n";
char SJFS[]="AT+CIPSEND\r\n";

char buffer[SIZE];

void Delay7000ms()		
{
	unsigned char i, j, k;

	_nop_();
	i = 50;
	j = 7;
	k = 195;
	do
	{
		do
		{
			while (--k);
		} while (--j);
	} while (--i);
}

void Delay1000ms()		
{
	unsigned char i, j, k;

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


void Uart()
{
	PCON = 0x00;
	SCON = 0x50;
	TMOD = 0x20;
	TH1  = 0xFD;
	TL1  = 0xFD;
	TR1  = 1;
	ES   = 1;
	EA   = 1;
}

void SendByt(char Js)
{
	SBUF = Js;
	while(! TI );
	TI = 0;
}

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

void main()
{
	Led = 1;
	
	Uart();
	Delay1000ms();
	
	SendString(LJWL);
	Delay7000ms();
	
	SendString(LJFWQ);
	Delay7000ms();
	
	SendString(TC);
	Delay1000ms();

	SendString(SJFS);
	Delay1000ms();	
	
	Led = 0;
	
	while(1)
	{
		SendString("666\r\n");
	}
	
}

void Zd() interrupt 4
{
	unsigned char cmd; 
	static int i = 0;
	if(RI == 1)
	{
		RI = 0;
		cmd = SBUF;
		
		if(cmd == 'o' ||cmd == 'c')
		{
			i = 0;
		}
		buffer[i++] = cmd;
		if(buffer[0] == 'o'&&buffer[3]=='n')
		{
			Led1 = 0;
			memset(buffer, '\0', SIZE);

		}
		
		if(buffer[0] == 'c'&& buffer[4]=='e')
		{
			Led1 = 1;
			memset(buffer, '\0', SIZE);

		}
		
		if(i == 12)i = 0;
	}
}

14.通过4GI模块通 过上位机点亮一个LED

/*
	
	4G是公网通信不认识:‘局域网’;
	我们可以将内网IP穿透 就是利用花生壳 把内网ip弄成外网可以访问的一个地址端口
	
	我建立个服务器 通过花生壳把IP打造成外网可以访问的IP  通过AT指令先配置好(4G模块是先配置 重启才能使用)
	单片机接受4G发来的数据 如果检索到跟我设置一样的数据就可以进行点灯操作等。。。
	跟蓝牙一样 优点是突破了地域限制
	
	
	1. 打开串口连接4G模块,串口出产默认波特率是115200,可以自行根据用户手册修改
  	2. 进入AT指令模式,在串口助手内发送+++(不要勾选发送新行),必须在发送+++指令 3s 内发送其
     	他任意 AT 指令,比如AT+CPIN 1 检测到sim卡 0 没有检测到
  	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+REBT 重启模块。
	6.AT+UART=9600,NONE  none表示没有奇偶校验位 4g默认uart=115200;
	7.AT+LINKSTA 查询 TCP 链接是否已建立链接 返回Connect(TCP 连接)/ Disconnect(TCP 断开)

	
*/

#include <regx52.h>
#include <string.h>


sbit Led = P2^7;



void Uart()
{
	PCON = 0x00;
	SCON = 0x50;
	TMOD = 0x20;
	TH1  = 0xFD;
	TL1  = 0xFD;
	TR1  = 1;
	ES   = 1;
  EA   = 1;	
}

void main()
{
	Led = 1;
	
	Uart();
	while(1);
	
}

void Zd() interrupt 4
{
	 char cmd;

	
	if(RI == 1)
	{
		RI = 0;
		cmd = SBUF;
		
		if(cmd == 'o')
		{
			Led = 0;
		}
		if(cmd == 'c')
		{
			Led = 1;
		}
	}
}

 15.Lcd1602详解

RS 为寄存器选择,高电平时选择数据寄存器、低电平时选择指令寄存器
R/W 为读写信号线,高电平时进行读操作,低电平时进行写操作,当 RS 为低电平 R/W 为高电平时可以读忙信号。

#include <regx52.h>
#include <intrins.h>
#define buffer P0

sbit RS = P2^6;
sbit RW = P2^5;
sbit EN = P2^7;



void Read_Busy()
{
	char flag = 0x80;
	buffer = 0x80;
	
	while(flag & 0x80)
	{
		RS = 0;
		RW = 1;
		EN = 0;
		_nop_();
		
		EN = 1;
		_nop_();
		_nop_();
		
		flag = buffer;
		
		EN = 0;
		_nop_();
		
	}


}

void Write_Cmd(char cmd)
{
	Read_Busy();
	
	RS = 0;
	RW = 0;
	
	EN = 0;
	_nop_();
	
	buffer = cmd;
	_nop_();
	
	EN = 1;
	_nop_();
	_nop_();
	
	EN = 0;
	_nop_();
	


}

void Write_Data(char Data)
{
	Read_Busy();

	
	RW = 0;
	RS = 1;
	EN = 0;
	_nop_();
	
	buffer = Data;
	_nop_();
	
	EN = 1;
	_nop_();
	_nop_();
	
	EN = 0;
	_nop_();
	

}

void Delay5ms()		//@12.000MHz
{
	unsigned char i, j;

	i = 10;
	j = 183;
	do
	{
		while (--j);
	} while (--i);
}

void Delay15ms()		//@12.000MHz
{
	unsigned char i, j;

	i = 30;
	j = 43;
	do
	{
		while (--j);
	} while (--i);
}



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


}


void Data_Show(char hang,char lie,char *p)
{
	switch(hang)
	{
		case 1:
			
		  Write_Cmd(0x80 + lie);
			
		  while(*p != '\0')
			{
					Write_Data(*p);
				  p++;
			}
		case 2:
			
		 Write_Cmd(0x80 + 0x40 + lie);
			
			while(*p != '\0')
			{
			     Write_Data(*p);
						p++;
			}
	}

}

void main()
{
	LCD1602_Init();
	
	while(1)
	{
		
		Data_Show(1,6,"NO.1");
		Data_Show(2,4,"iverson!");
		
	}
	//Write_Cmd(0x80);
	//Write_Data('a');
	

}

 16.DTH11温湿度详解

 

#include <regx52.h>
#include <intrins.h>

sbit Led = P2^0;
sbit Dht = P0^0;
char Data[5];

void Uart()
{
	PCON = 0x00;
	SCON = 0x50;
	TMOD = 0x20;
	TH1  = 0xFD;
	TL1  = 0xFD;
	TR1  = 1;
}

void SendByte(char Js)
{
	SBUF = Js;
	while(!TI);
	TI  =  0;
}

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

void Delay30us()		
{
	unsigned char i;

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

void Delay40us()		
{
	unsigned char i;

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


void Dht_Start()
{
	Dht = 1;
	Dht = 0;
	Delay30us();
	Dht = 1;
	
	while(Dht);
	while(!Dht);
	while(Dht);
	
	
}

void Read_Dht()
{
	int i;
	int j;
	char tmp;
	char flag;
	
	Dht_Start();
	
	for(i = 0;i < 5;++i)
	{
		for(j = 0;j < 8;++j)
		{
			
			
			while(!Dht);
			Delay40us();
			
			if(Dht == 1)
			{
				flag = 1;
				while(Dht);
			}
			else
			{
				flag = 0;
			}
			tmp = tmp << 1;
			tmp = flag | tmp;
			
		}
		Data[i] = tmp;
	}
}


void Delay500ms()		
{
	unsigned char i, j, k;

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


void main()
{
	
	Uart();
	
	while( 1 )
	{
		Delay500ms();
		Read_Dht();
		
		SendString("H:");
		
	  SendByte(Data[0] / 10 +0x30); //0x30 - 0x39 是0 - 9的ascll码 
		SendByte(Data[0] % 10 + 0x30);//这样就可以把对应的字符变成ascll输出
		SendByte('.');
		SendByte(Data[1] / 10 +0x30);
		SendByte(Data[1] % 10 + 0x30);
		SendString("\r\n");
		
		SendString("T:");
		
		SendByte(Data[2] / 10 +0x30);
		SendByte(Data[2] % 10 + 0x30);
		SendByte('.');
		SendByte(Data[3] / 10 +0x30);
		SendByte(Data[3] % 10 + 0x30);
		SendString("\r\n");
	
		
		
	}

	
	

	
}

让dht温湿度数据显示在Lcd1602上面

#include <regx52.h>
#include <intrins.h>
#define buffer P0


/*---------定义LCD1602 & DHT 引脚-----------*/

sbit dht = P0^0;
sbit RS = P2^6;
sbit RW = P2^5;
sbit EN = P2^7;

/*----------wd&sd用于存放温湿度数据---------*/
char Data[5];
char sd[9];
char wd[9];

/*----------读第7位是不是高电平-------------*/
void Read_Busy()
{
	char flag = 0x80;
	buffer = 0x80;
	
	while(flag & 0x80)
	{
		RS = 0;
		RW = 1;
		EN = 0;
		_nop_();
		
		EN = 1;
		_nop_();
		_nop_();
		
		flag = buffer;
		
		EN = 0;
		_nop_();
		
	}


}

void Write_Cmd(char cmd)
{
	Read_Busy();
	
	RS = 0;
	RW = 0;
	
	EN = 0;
	_nop_();
	
	buffer = cmd;
	_nop_();
	
	EN = 1;
	_nop_();
	_nop_();
	
	EN = 0;
	_nop_();
	


}

void Write_Data(char Data)
{
	Read_Busy();

	
	RW = 0;
	RS = 1;
	EN = 0;
	_nop_();
	
	buffer = Data;
	_nop_();
	
	EN = 1;
	_nop_();
	_nop_();
	
	EN = 0;
	_nop_();
	

}

void Delay5ms()		//@12.000MHz
{
	unsigned char i, j;

	i = 10;
	j = 183;
	do
	{
		while (--j);
	} while (--i);
}

void Delay15ms()		//@12.000MHz
{
	unsigned char i, j;

	i = 30;
	j = 43;
	do
	{
		while (--j);
	} while (--i);
}



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


}


void Data_Show(char hang,char lie,char *p)
{
	switch(hang)
	{
		case 1:
			
		  Write_Cmd(0x80 + lie);
			
		  while(*p != '\0')
			{
					Write_Data(*p);
				  p++;
			}
		case 2:
			
		 Write_Cmd(0x80 + 0x40 + lie);
			
			while(*p != '\0')
			{
			     Write_Data(*p);
						p++;
			}
	}

}

void Uart()
{
	PCON = 0x00;
	SCON = 0x50;
	TMOD = 0x20;
	TH1  = 0xFD;
	TL1  = 0xFD;
	TR1  = 1;
}

void SendByte(char Js)
{
	SBUF = Js;
	while(!TI);
	TI  =  0;
}

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


void Delay30us()		
{
	unsigned char i;

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

void Delay40us()		
{
	unsigned char i;

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


void Dht_Start()
{
	dht = 1;
	dht = 0;
	Delay30us();
	dht = 1;
	
	while(dht);
	while(!dht);
	while(dht);
	
	
}

void Read_Dht()
{
	int i;
	int j;
	char tmp;
	char flag;
	
	Dht_Start();
	
	for(i = 0;i < 5;++i)
	{
		for(j = 0;j < 8;++j)
		{
			
			
			while(!dht);
			Delay40us();
			
			if(dht == 1)
			{
				flag = 1;
				while(dht);
			}
			else
			{
				flag = 0;
			}
			tmp = tmp << 1;
			tmp = flag | tmp;
			
		}
		Data[i] = tmp;
	}
}


void Put_Data()
{
	sd[0] = 's';
	sd[1] = 'd';
	sd[2] = ':';
	sd[3] = Data[0] /10 + 0x30;
	sd[4] = Data[0] %10 + 0x30;
	sd[5] = '.';
	sd[6] = Data[1] /10 + 0x30;
	sd[7] = Data[1] %10 + 0x30;
	sd[8] = '\0';

  wd[0] = 'w';
	wd[1] = 'd';
	wd[2] = ':';
	wd[3] = Data[2] /10 + 0x30;
	wd[4] = Data[2] %10 + 0x30;
	wd[5] = '.';
	wd[6] = Data[3] /10 + 0x30;
	wd[7] = Data[3] %10 + 0x30;
	wd[8] = '\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 main()
{
	Uart();
	Delay1000ms();
	
	LCD1602_Init();
	
	

	while(1)
	{
		Delay1000ms();
		Read_Dht();
		Put_Data();
		
		Delay1000ms();
		Delay1000ms();

		SendString(wd);
		SendString("\r\n");
		SendString(sd);
		SendString("\r\n");
		
		Data_Show(1,0,wd);
		Data_Show(2,0,sd);

		
		
	}
}

 17.IIC_OLED详解

/*
    代码太多,需要的可以加我qq:2652408527
*/
#include <regx52.h>
#include <intrins.h>
#include "oled.h"
#include "oled_ziku.h"
#include "dht.h"

sbit SDA = P0^0;
sbit SCL = P0^1;
sbit dht = P0^2;

extern char Data[5];
extern char sd[9];
extern char wd[9];



void main()
{
	
	
	Oled_INIT();
	
	Oled_Write_Cmd(0x20);
	Oled_Write_Cmd(0x02);//选择页寻址
	
	Oled_Clear();//清屏
	
	while(1)
	{
	
		Oled_Show_Str(1,0,"Li zhen");
		Oled_Show_Str(2,0,"I LOVE YOU");
		Oled_Show_Str(2,80,"!");
		
		
	}
	
	
}
	

/*
	部分代码,需要的可以加我QQ:2652408527
*/

#include <regx52.h>
#include <intrins.h>
#include "oled.h"
#include "oled_ziku.h"
#include "dht.h"

sbit SDA = P0^0;
sbit SCL = P0^1;
sbit dht = P0^2;

extern char Data[5];
extern char sd[9];
extern char wd[9];
	char command[5];


void UART_Init()
{
	PCON = 0x00;
	SCON = 0x50;
	TMOD = 0x20;
	TH1  = 0xFD;
	TL1  = 0xFD;
	TR1  = 1;
	ES   = 1;
  EA   = 1;	
}


void main()
{
	UART_Init();
	 Delay1000ms();
	Oled_INIT();
	
	Oled_Write_Cmd(0x20);
	Oled_Write_Cmd(0x02);//选择页寻址
	
	Oled_Clear();//清屏
	

	while(1)
	{
		
		
		Read_Dht();
	
	  Put_Data();
		

		
		Oled_Show_Str(4,40,"Li jian hua");
		
		Delay1000ms();
		
	}
	
	
}
	

void zd() interrupt 4
{
	static i = 0;
	

	
	if(RI == 1)
	{
		RI = 0;
		command[i] = SBUF;
		i++;
		
		if(command[0]=='o'&&command[1]=='p'&&command[2]=='e'&&command[3]=='n')
		{
					Oled_Show_Str(1,0,wd);
		      Oled_Show_Str(2,0,sd); 
		}
		
	if(command[0]=='c'&&command[1]=='l'&&command[2]=='o'&&command[3]=='s')
	{
					Oled_Show_Str(1,0,"                                                                 ");
		      Oled_Show_Str(2,0,"                                                                  "); 
		}
		
	}

}

 18.四驱小车

由于没有循迹模块,我直接写了一个可以上下左右的小车代码

#include <REGX52.H>
#include "dianji.h"

void UART_Init()
{
	PCON = 0x00;
	SCON = 0x50;
	TMOD = 0x20;
	TH1  = 0xFD;
  TL1  = 0xFD;
  TR1  = 1;
  ES   = 1;
  EA   = 1; 	
}

void zd() interrupt 4
{
	if(RI == 1)
	{
			RI = 0;
			
		 if(SBUF == 'a')
		 {
			 		 Dianji_Left1Ward_Init();
					 Dianji_Left2Ward_Init();
					 Dianji_Right1Ward_Init();
					 Dianji_Right2Ward_Init();
			 
			}
			
			if(SBUF == 'b')
			{
					   Dianji_Left1Back_Init();
						 Dianji_Left2Back_Init();
						 Dianji_Right1Back_Init();
						 Dianji_Right2Back_Init();
			}
			
			if(SBUF == 'e')
			{
				7 Stop();
			}
	}


}

 

 总结

感谢各位的观看,需要其中代码的兄弟可以私聊我。

有关51单片机详解的更多相关文章

  1. C51单片机——实现用独立按键控制LED亮灭(调用函数篇) - 2

    说在前面这部分我本来是合为一篇来写的,因为目的是一样的,都是通过独立按键来控制LED闪灭本质上是起到开关的作用,即调用函数和中断函数。但是写一篇太累了,我还是决定分为两篇写,这篇是调用函数篇。在本篇中你主要看到这些东西!!!1.调用函数的方法(主要讲语法和格式)2.独立按键如何控制LED亮灭3.程序中的一些细节(软件消抖等)1.调用函数的方法思路还是比较清晰地,就是通过按下按键来控制LED闪灭,即每按下一次,LED取反一次。重要的是,把按键与LED联系在一起。我打算用K1来作为开关,看了一下开发板原理图,K1连接的是单片机的P31口,当按下K1时,P31是与GND相连的,也就是说,当我按下去时

  2. 物联网MQTT协议详解 - 2

    一、什么是MQTT协议MessageQueuingTelemetryTransport:消息队列遥测传输协议。是一种基于客户端-服务端的发布/订阅模式。与HTTP一样,基于TCP/IP协议之上的通讯协议,提供有序、无损、双向连接,由IBM(蓝色巨人)发布。原理:(1)MQTT协议身份和消息格式有三种身份:发布者(Publish)、代理(Broker)(服务器)、订阅者(Subscribe)。其中,消息的发布者和订阅者都是客户端,消息代理是服务器,消息发布者可以同时是订阅者。MQTT传输的消息分为:主题(Topic)和负载(payload)两部分Topic,可以理解为消息的类型,订阅者订阅(Su

  3. Tcl脚本入门笔记详解(一) - 2

    TCL脚本语言简介•TCL(ToolCommandLanguage)是一种解释执行的脚本语言(ScriptingLanguage),它提供了通用的编程能力:支持变量、过程和控制结构;同时TCL还拥有一个功能强大的固有的核心命令集。TCL经常被用于快速原型开发,脚本编程,GUI和测试等方面。•实际上包含了两个部分:一个语言和一个库。首先,Tcl是一种简单的脚本语言,主要使用于发布命令给一些互交程序如文本编辑器、调试器和shell。由于TCL的解释器是用C\C++语言的过程库实现的,因此在某种意义上我们又可以把TCL看作C库,这个库中有丰富的用于扩展TCL命令的C\C++过程和函数,所以,Tcl是

  4. 【详解】Docker安装Elasticsearch7.16.1集群 - 2

    开门见山|拉取镜像dockerpullelasticsearch:7.16.1|配置存放的目录#存放配置文件的文件夹mkdir-p/opt/docker/elasticsearch/node-1/config#存放数据的文件夹mkdir-p/opt/docker/elasticsearch/node-1/data#存放运行日志的文件夹mkdir-p/opt/docker/elasticsearch/node-1/log#存放IK分词插件的文件夹mkdir-p/opt/docker/elasticsearch/node-1/plugins若你使用了moba,直接右键新建即可如上图所示依次类推创建

  5. 【Elasticsearch基础】Elasticsearch索引、文档以及映射操作详解 - 2

    文章目录概念索引相关操作创建索引更新副本查看索引删除索引索引的打开与关闭收缩索引索引别名查询索引别名文档相关操作新建文档查询文档更新文档删除文档映射相关操作查询文档映射创建静态映射创建索引并添加映射概念es中有三个概念要清楚,分别为索引、映射和文档(不用死记硬背,大概有个印象就可以)索引可理解为MySQL数据库;映射可理解为MySQL的表结构;文档可理解为MySQL表中的每行数据静态映射和动态映射上面已经介绍了,映射可理解为MySQL的表结构,在MySQL中,向表中插入数据是需要先创建表结构的;但在es中不必这样,可以直接插入文档,es可以根据插入的文档(数据),动态的创建映射(表结构),这就

  6. 最强Http缓存策略之强缓存和协商缓存的详解与应用实例 - 2

    HTTP缓存是指浏览器或者代理服务器将已经请求过的资源保存到本地,以便下次请求时能够直接从缓存中获取资源,从而减少网络请求次数,提高网页的加载速度和用户体验。缓存分为强缓存和协商缓存两种模式。一.强缓存强缓存是指浏览器直接从本地缓存中获取资源,而不需要向web服务器发出网络请求。这是因为浏览器在第一次请求资源时,服务器会在响应头中添加相关缓存的响应头,以表明该资源的缓存策略。常见的强缓存响应头如下所述:Cache-ControlCache-Control响应头是用于控制强制缓存和协商缓存的缓存策略。该响应头中的指令如下:max-age:指定该资源在本地缓存的最长有效时间,以秒为单位。例如:Ca

  7. micropython复现经典单片机项目(二)可视化音频 频谱解析(基本搞定) - 2

    本人是音乐爱好者,从小就特别喜欢那个随着音乐跳动的方框效果,就是这个:arduino上一大把对,我忍你很久了,我就想用mpy做,全网没有,行我自己研究。果然兴趣是最好的老师,我之前有篇博客专门讲音频,有兴趣的可以回顾一下。提到可视化频谱,必然绕不开fft,大学学过这玩意,当时一心玩,老师讲的一个字都么听进去,网上教程简略扫了一下,大该就是把时域转频域的工具,我大mpy居然没有fft函数,奶奶的,先放着。音频信息如何收集?第一种傻瓜式的ADC,模拟转数字,原始粗暴,第二种,I2S库,我之前博客有讲过,数据是PCM编码。然后又去学PCM编码,一学豁然开朗,舒服,以代码为例:audio_in=I2S

  8. IDEA 2022 创建 Spring Boot 项目详解 - 2

    如何用IDEA2022创建并初始化一个SpringBoot项目?目录如何用IDEA2022创建并初始化一个SpringBoot项目?0. 环境说明1.  创建SpringBoot项目 2.编写初始化代码0. 环境说明IDEA2022.3.1JDK1.8SpringBoot1.  创建SpringBoot项目        打开IDEA,选择NewProject创建项目。        填写项目名称、项目构建方式、jdk版本,按需要修改项目文件路径等信息。        选择springboot版本以及需要的包,此处只选择了springweb。        此处需特别注意,若你使用的是jdk1

  9. 详解Unity中的粒子系统Particle System (二) - 2

    前言上一篇我们简要讲述了粒子系统是什么,如何添加,以及基本模块的介绍,以及对于曲线和颜色编辑器的讲解。从本篇开始,我们将按照模块结构讲解下去,本篇主要讲粒子系统的主模块,该模块主要是控制粒子的初始状态和全局属性的,以下是关于该模块的介绍,请大家指正。目录前言本系列提要一、粒子系统主模块1.阅读前注意事项2.参考图3.参数讲解DurationLoopingPrewarmStartDelayStartLifetimeStartSpeed3DStartSizeStartSize3DStartRotationStartRotationFlipRotationStartColorGravityModif

  10. VMware虚拟机与本地主机进行磁盘共享(详解) - 2

    VMware虚拟机与本地主机进行磁盘共享前提虚拟机版本为Windows10(专业版,不是可能有问题)本地主机为家庭版或学生版(此版本会有问题,但有替代方式)最好是专业版VMware操作1.关闭防火墙,全部关闭。2.打开电脑属性3.点击共享-》高级共享-》权限4.如果没有everyone,就添加权限选择完全控制,然后应用确定。5.打开cmd输入lusrmgr.msc(只有专业版可以打开)如果不是专业版,可以跳过这一步。点击用户-》administrator密码要复杂密码,否则不行。推荐admaiN@1234类型的密码。设置完密码,点击属性,将禁用解开。6.如果虚拟机的windows不是专业版,可

随机推荐