51单片机

一、简介

1、
当提到"51单片机"时,通常指的是Intel 8051系列单片机。这是一种非常流行的8位微控制器,最初由Intel公司在1980年代推出。它的名字"51"源于其型号的"80C51",其中"80"表示1980年代,而"C51"则表示是8051系列。

以下是一些关于51单片机的基本信息:

(1)、体系结构: 8051是一种基于哈佛结构的单片机,具有8位数据总线、16位地址总线和一组通用寄存器。它包含了CPU、RAM、ROM(或Flash Memory)、定时器/计数器、串行通信控制器(UART)等基本组件。

(2)、存储: 8051通常包括内部ROM(或Flash Memory)用于存储程序代码,以及RAM用于存储数据。外部存储器可以通过外部总线扩展。

(3)、时钟: 它工作在低至4MHz的时钟频率下,通常是晶振振荡器提供时钟信号。

(4)、输入/输出: 8051具有通用输入/输出引脚,可以通过编程配置为数字输入或输出。此外,它还有串行通信端口(UART)用于与其他设备通信。

(5)、中断: 8051支持中断,可以通过硬件或软件触发。中断是一种机制,允许程序在执行过程中响应外部事件。

(6)、定时器/计数器: 8051有一个或多个定时器/计数器,用于进行时间测量或生成定时脉冲。

(7)、应用领域: 8051系列单片机被广泛应用于嵌入式系统和各种控制应用,包括家电、汽车电子、工业自动化等。

(8)、编程语言: 8051可以使用汇编语言或高级语言(如C语言)进行编程。

需要注意的是,虽然Intel首先推出了8051系列,但由于其开放的架构,其他制造商也生产了兼容的版本,因此有许多不同厂商生产的51单片机

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

二、环境

1、软件:
Keil5 编写代码
STC_ISP 将代码下载到单片机上
在这里插入图片描述

2、安装下载驱动:
在这里插入图片描述

在这里插入图片描述
3、编写一个程序,并下载到单片机上
打开keil5,新建一个项目,新建文件夹

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

编写程序,编译程序(设置创建文件(用于下载到单片机))
在这里插入图片描述

在这里插入图片描述

下载型号,安装文件
在这里插入图片描述

三、各个模块的使用

1、LED灯

在这里插入图片描述

1、电路在这里插入图片描述

2、令一个灯亮

#include <REGX52.H>

void main(){
	P2=0xFE;	//1111 1110
	while(1){}	//为了防止程序不空转
}

3、闪烁

#include <REGX52.H>

void Delay1ms(unsigned int xms){
	unsigned char i, j;
	while(xms){
		i=2;
		j=239;
		do{
			while(--j);
		}while(--i);
		xms-=1;
	}
}

void main(){
	while(1){
		P2=0xFE;	//1111 1110
		Delay1ms(1000);
		P2=0xFF;	//1111 1111
		Delay1ms(1000);
	}	
}

4、流水灯

#include <REGX52.H>

void Delay1ms(unsigned int xms){
	unsigned char i, j;
	while(xms){
		i=2;
		j=239;
		do{
			while(--j);
		}while(--i);
		xms-=1;
	}
}

void main(){
	unsigned int flag=0;
	P2=0xFE;//1111 1110
	
	while(1){
		flag=(P2&0x80)>>7;
		Delay1ms(1000);
		P2=(P2<<=1)|flag;
	}
}

2、独立按钮

在这里插入图片描述
1、电路
在这里插入图片描述
2、按键抖动
在这里插入图片描述
3、按下后 改变灯的状态

#include <REGX52.H>

void main(){

	while(1){
		if(P3_5==0){
			P2_0=0;
		}else{
			P2_0=1;
		}
	}
}

4、松开按钮后 改变灯的状态

#include <REGX52.H>

void Delay1ms(unsigned int xms){
	unsigned char i, j;
	while(xms){
		i=2;
		j=239;
		do{
			while(--j);
		}while(--i);
		xms-=1;
	}
}

void main(){
	//unsigned int flag=0;
	while(1){
		if(P3_5==0){
			Delay1ms(20);//检测按下
			
			while(P3_5==0);
			Delay1ms(20);//确认松手
			
			P2_0=~P2_0;//状态取反
		}
	}
}

5、加法操作

#include <REGX52.H>

void Delay1ms(unsigned int xms){
	unsigned char i, j;
	while(xms){
		i=2;
		j=239;
		do{
			while(--j);
		}while(--i);
		xms-=1;
	}
}

void main(){
	unsigned char LEDNum=0;
	while(1){
		if(P1_5==0){
			Delay1ms(20);//检测按下
			
			while(P1_5==0);
			Delay1ms(20);//确认松手
			
			LEDNum++;//自加一操作
			P2=~LEDNum;
		}
	}
}

6、左移与右移

#include <REGX52.H>

void Delay1ms(unsigned int xms){
	unsigned char i, j;
	while(xms){
		i=2;
		j=239;
		do{
			while(--j);
		}while(--i);
		xms-=1;
	}
}

void main(){
	unsigned char LEDNum=0;
	P2=~0x01;
	
	while(1){
		if(P1_7==0){
			Delay1ms(20);//检测按下
			
			while(P1_7==0);
			Delay1ms(20);//确认松手
			
			LEDNum++;//加一
			if(LEDNum>=8)//越界检查
				LEDNum=0;
			P2=~(0x01<<LEDNum);
		}
		if(P1_5==0){
			Delay1ms(20);//检测按下
			
			while(P1_5==0);
			Delay1ms(20);//确认松手
			
			LEDNum--;//减一
			if(-1==LEDNum)//越界检查
				LEDNum=7;
			P2=~(0x01<<LEDNum);
		}
	}
}

3、数码管

电路图:
在这里插入图片描述
74HC1380译码器:
在这里插入图片描述

p01-p07
控制显示哪个数字

P22、P23、P24
控制74HC138译码器,来决定显示哪个数字

(p10-p13 控制显示哪个数字)

1、在指定的位置显示一个指定的数字

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

unsigned char number_array[10]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F};

void print_number(unsigned char index,unsigned char value){
	if(value>10||value<0)return;

	P2_2=0;
	P2_3=0;
	P2_4=0;//显示下个数字前先熄灯,消除残影

	switch (index){
		case 0:
			P2_2=1;
			P2_3=1;
			P2_4=1;
			break;
		case 1:
			P2_2=0;
			P2_3=1;
			P2_4=1;
			break;
		case 2:
			P2_2=1;
			P2_3=0;
			P2_4=1;
			break;
		case 3:
			P2_2=0;
			P2_3=0;
			P2_4=1;
			break;
		case 4:
			P2_2=1;
			P2_3=1;
			P2_4=0;
			break;
		case 5:
			P2_2=0;
			P2_3=1;
			P2_4=0;
			break;
		case 6:
			P2_2=1;
			P2_3=0;
			P2_4=0;
			break;
		case 7:
			P2_2=0;
			P2_3=0;
			P2_4=0;
			break;
		default:
			break;
	}
	P0=number_array[value];
	
	Delay(1);//显示一毫秒
}

void main(){
	print_number(0,1);
	
	while(1){}
}

2、在8个位置显示8个不同的数字

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

unsigned char number_array[10]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F};

void print_number(unsigned char index,unsigned char value){
	if(value>10||value<0)return;

	P2_2=0;
	P2_3=0;
	P2_4=0;//显示下个数字前先熄灯,消除残影

	switch (index){
		case 0:
			P2_2=1;
			P2_3=1;
			P2_4=1;
			break;
		case 1:
			P2_2=0;
			P2_3=1;
			P2_4=1;
			break;
		case 2:
			P2_2=1;
			P2_3=0;
			P2_4=1;
			break;
		case 3:
			P2_2=0;
			P2_3=0;
			P2_4=1;
			break;
		case 4:
			P2_2=1;
			P2_3=1;
			P2_4=0;
			break;
		case 5:
			P2_2=0;
			P2_3=1;
			P2_4=0;
			break;
		case 6:
			P2_2=1;
			P2_3=0;
			P2_4=0;
			break;
		case 7:
			P2_2=0;
			P2_3=0;
			P2_4=0;
			break;
		default:
			break;
	}
	P0=number_array[value];
	
	Delay(1);//显示一毫秒
}

void main(){
	while(1){
		print_number(0,1);
		print_number(1,2);
		print_number(2,3);
		print_number(3,4);
		print_number(4,5);
		print_number(5,6);
		print_number(6,7);
		print_number(7,8);
	}
}

4、LCD1602

在这里插入图片描述

在这里插入图片描述

LCD1602.c:

#include <REGX52.H>

//引脚配置:
sbit LCD_RS=P2^6;
sbit LCD_RW=P2^5;
sbit LCD_EN=P2^7;
#define LCD_DataPort P0

//函数定义:
/**
  * @brief  LCD1602延时函数,12MHz调用可延时1ms
  * @param  无
  * @retval 无
  */
void LCD_Delay()
{
	unsigned char i, j;

	i = 2;
	j = 239;
	do
	{
		while (--j);
	} while (--i);
}

/**
  * @brief  LCD1602写命令
  * @param  Command 要写入的命令
  * @retval 无
  */
void LCD_WriteCommand(unsigned char Command)
{
	LCD_RS=0;
	LCD_RW=0;
	LCD_DataPort=Command;
	LCD_EN=1;
	LCD_Delay();
	LCD_EN=0;
	LCD_Delay();
}

/**
  * @brief  LCD1602写数据
  * @param  Data 要写入的数据
  * @retval 无
  */
void LCD_WriteData(unsigned char Data)
{
	LCD_RS=1;
	LCD_RW=0;
	LCD_DataPort=Data;
	LCD_EN=1;
	LCD_Delay();
	LCD_EN=0;
	LCD_Delay();
}

/**
  * @brief  LCD1602设置光标位置
  * @param  Line 行位置,范围:1~2
  * @param  Column 列位置,范围:1~16
  * @retval 无
  */
void LCD_SetCursor(unsigned char Line,unsigned char Column)
{
	if(Line==1)
	{
		LCD_WriteCommand(0x80|(Column-1));
	}
	else if(Line==2)
	{
		LCD_WriteCommand(0x80|(Column-1+0x40));
	}
}

/**
  * @brief  LCD1602初始化函数
  * @param  无
  * @retval 无
  */
void LCD_Init()
{
	LCD_WriteCommand(0x38);//八位数据接口,两行显示,5*7点阵
	LCD_WriteCommand(0x0c);//显示开,光标关,闪烁关
	LCD_WriteCommand(0x06);//数据读写操作后,光标自动加一,画面不动
	LCD_WriteCommand(0x01);//光标复位,清屏
}

/**
  * @brief  在LCD1602指定位置上显示一个字符
  * @param  Line 行位置,范围:1~2
  * @param  Column 列位置,范围:1~16
  * @param  Char 要显示的字符
  * @retval 无
  */
void LCD_ShowChar(unsigned char Line,unsigned char Column,char Char)
{
	LCD_SetCursor(Line,Column);
	LCD_WriteData(Char);
}

/**
  * @brief  在LCD1602指定位置开始显示所给字符串
  * @param  Line 起始行位置,范围:1~2
  * @param  Column 起始列位置,范围:1~16
  * @param  String 要显示的字符串
  * @retval 无
  */
void LCD_ShowString(unsigned char Line,unsigned char Column,char *String)
{
	unsigned char i;
	LCD_SetCursor(Line,Column);
	for(i=0;String[i]!='\0';i++)
	{
		LCD_WriteData(String[i]);
	}
}

/**
  * @brief  返回值=X的Y次方
  */
int LCD_Pow(int X,int Y)
{
	unsigned char i;
	int Result=1;
	for(i=0;i<Y;i++)
	{
		Result*=X;
	}
	return Result;
}

/**
  * @brief  在LCD1602指定位置开始显示所给数字
  * @param  Line 起始行位置,范围:1~2
  * @param  Column 起始列位置,范围:1~16
  * @param  Number 要显示的数字,范围:0~65535
  * @param  Length 要显示数字的长度,范围:1~5
  * @retval 无
  */
void LCD_ShowNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
{
	unsigned char i;
	LCD_SetCursor(Line,Column);
	for(i=Length;i>0;i--)
	{
		LCD_WriteData(Number/LCD_Pow(10,i-1)%10+'0');
	}
}

/**
  * @brief  在LCD1602指定位置开始以有符号十进制显示所给数字
  * @param  Line 起始行位置,范围:1~2
  * @param  Column 起始列位置,范围:1~16
  * @param  Number 要显示的数字,范围:-32768~32767
  * @param  Length 要显示数字的长度,范围:1~5
  * @retval 无
  */
void LCD_ShowSignedNum(unsigned char Line,unsigned char Column,int Number,unsigned char Length)
{
	unsigned char i;
	unsigned int Number1;
	LCD_SetCursor(Line,Column);
	if(Number>=0)
	{
		LCD_WriteData('+');
		Number1=Number;
	}
	else
	{
		LCD_WriteData('-');
		Number1=-Number;
	}
	for(i=Length;i>0;i--)
	{
		LCD_WriteData(Number1/LCD_Pow(10,i-1)%10+'0');
	}
}

/**
  * @brief  在LCD1602指定位置开始以十六进制显示所给数字
  * @param  Line 起始行位置,范围:1~2
  * @param  Column 起始列位置,范围:1~16
  * @param  Number 要显示的数字,范围:0~0xFFFF
  * @param  Length 要显示数字的长度,范围:1~4
  * @retval 无
  */
void LCD_ShowHexNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
{
	unsigned char i,SingleNumber;
	LCD_SetCursor(Line,Column);
	for(i=Length;i>0;i--)
	{
		SingleNumber=Number/LCD_Pow(16,i-1)%16;
		if(SingleNumber<10)
		{
			LCD_WriteData(SingleNumber+'0');
		}
		else
		{
			LCD_WriteData(SingleNumber-10+'A');
		}
	}
}

/**
  * @brief  在LCD1602指定位置开始以二进制显示所给数字
  * @param  Line 起始行位置,范围:1~2
  * @param  Column 起始列位置,范围:1~16
  * @param  Number 要显示的数字,范围:0~1111 1111 1111 1111
  * @param  Length 要显示数字的长度,范围:1~16
  * @retval 无
  */
void LCD_ShowBinNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
{
	unsigned char i;
	LCD_SetCursor(Line,Column);
	for(i=Length;i>0;i--)
	{
		LCD_WriteData(Number/LCD_Pow(2,i-1)%2+'0');
	}
}

LCD1602.h:

#ifndef __LCD1602_H__
#define __LCD1602_H__

//用户调用函数:
void LCD_Init();
void LCD_ShowChar(unsigned char Line,unsigned char Column,char Char);
void LCD_ShowString(unsigned char Line,unsigned char Column,char *String);
void LCD_ShowNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);
void LCD_ShowSignedNum(unsigned char Line,unsigned char Column,int Number,unsigned char Length);
void LCD_ShowHexNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);
void LCD_ShowBinNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);

#endif

main.c:

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

void main(){
	//P2=0xFE;	//1111 1110
	LCD_Init();
	
	//LCD_ShowChar(2,2,'B');
	//LCD_ShowString(1,2,"youyou");
	//LCD_ShowNum(1,9,123,3);
	//LCD_ShowSignedNum(1,13,-66,2);
	//LCD_ShowHexNum(2,1,0xA8,2);
	LCD_ShowBinNum(2,4,0xDD,8);
		
	while(1){}
		
}

5、独立矩阵

在这里插入图片描述
简单表述下,按下一个按键就会联通两个接口。
举例:
按下S3, P17 和 P11连通
按下S10,P15 和 P12连通

类似cpu从内存中寻找数据。节省IO口

按下一个按键显示一个数字

按下按键1-16,然后在LCD1602上显示出来

matrixKey.c:

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

unsigned char matrixKey(){
	int checkNumber = 0;
	char whichColumn = 0;
	char whichLine = 0;

	
	//第一步检查被按下的按钮是哪行一的
	P1=0xF0;// 1111            0000
					//  p7 p6 p5 p4     p3 p2 p1 p0 
	
	if(0 == P1_4){
		//p4列(即第4行)
		Delay(20);//等待信号稳定
		whichColumn=4;
	}else if(0 == P1_5){
		//p4列(即第3行)
		Delay(20);//等待信号稳定
		whichColumn=3;	
	}else if(0 == P1_6){
		//p4列(即第2行)
		Delay(20);//等待信号稳定
		whichColumn=2;	
	}else if(0 == P1_7){
		//p4列(即第1行)
		Delay(20);//等待信号稳定
		whichColumn=1;
	}else{
		//没有被按下
		return 0;
	}
	
	//第二部检查被按下的按钮是哪一列的
	P1=0x0F;//0000             1111
			//  p7 p6 p5 p4     p3 p2 p1 p0 
	if(0 == P1_0){
		//p4列(即第4列)
		while(0 == P1_0);
		Delay(20);//等待其松手,后信号稳定
		whichLine=4;
	}else if(0 == P1_1){
		//p4列(即第3列)
		while(0 == P1_1);
		Delay(20);//等待其松手,后信号稳定
		whichLine=3;
	}else if(0 == P1_2){
		//p4列(即第2列)
		while(0 == P1_2);
		Delay(20);//等待其松手,后信号稳定
		whichLine=2;
	}else if(0 == P1_3){
		//p4列(即第1列)
		while(0 == P1_3);
		Delay(20);//等待其松手,后信号稳定
		whichLine=1;
	}else{//impossible
		//没有被按下
		return 0;
	}

	return (whichColumn-1)*4+whichLine;
}

main.c

#include <REGX52.H>
#include "LCD1602.h"
#include "matrixKey.h"

void main(){
	char which_key = 0;//零代表没有按键被按下
	LCD_Init();
		
	while(1){
		which_key = matrixKey();
		if(which_key){
			LCD_ShowNum(1,1,which_key,2);		
		}
	}
}

密码锁:
main.c:

#include <REGX52.H>
#include "LCD1602.h"
#include "matrixKey.h"

#define PASS_WORD 1212

void main(){
	char which_key = 0;//零代表没有按键被按下
	int input=0;
	
	LCD_Init();
	LCD_ShowString(1,1,"Password:");
	LCD_ShowString(2,1,"0000");

	while(1){
		which_key = matrixKey();
		if(which_key > 9){
			return;//重置
		}else if(which_key){
			LCD_ShowNum(2,1,which_key,1);//回显
			break;
		}
	}//等待输入
	input+=which_key*1000;
	
	while(1){
		which_key = matrixKey();
		if(which_key > 9){
			return;//重置
		}else if(which_key){
			LCD_ShowNum(2,2,which_key,1);//回显
			break;
		}	
	}//等待输入
	input+=which_key*100;		

	while(1){
		which_key = matrixKey();
		if(which_key > 9){
			return;//重置
		}else if(which_key){
			LCD_ShowNum(2,3,which_key,1);//回显
			break;
		}	
	}//等待输入
	input+=which_key*10;
					
	while(1){
		which_key = matrixKey();
		if(which_key > 9){
			return;//重置
		}else if(which_key){
			LCD_ShowNum(2,4,which_key,1);//回显
			break;
		}	
	}//等待输入	
	input+=which_key;
	
	if(PASS_WORD == input){
		LCD_Init();
		LCD_ShowString(2,1,"OK");
	}else{
		LCD_ShowString(2,1,"ERROR");		
	}
		
	while(1){}

}

6、计数器/计时器(晶振、中断)

1、电路(集成在芯片内部):
在这里插入图片描述
寄存器:
在这里插入图片描述

M1和M2决定使用4中工作模式的中的哪一个

计数输入位置:
在这里插入图片描述
晶振(频率是11.0592):
在这里插入图片描述

2、利用计时器使得小灯一秒一闪

#include <REGX52.H>

void timer0_init(){
	//这个寄存器的名字叫 TMOD,用来设置计时器的工作模式
	TMOD=TMOD&0xF0;
	TMOD=TMOD|0x01;
	TF0=0;				//打开通向中断的开关
	TR0=1;				//允许计数/计时
	TH0=64535/256;
	TL0=64535%256+1;
	/* 
	TH0 TL0 总共可计数65535
	65535 - 64535 =1000,即 1ms
	(经过12的分频器,12M变成1M)
	 */
	
//	TH0=12000/256;
//	TL0=12000%256+1;
	ET0=1;	//总断的分开关
	EA=1;	//中断的总开关
	PT0=0;	//设置中断的优先级别
}

unsigned int T0Count;
void Timer0_Routine() interrupt 1{
	TH0=64535/256;
	TL0=64535%256;
	
//	TH0=12000/256;
//	TL0=12000%256+1;
	
	T0Count++;
	if(T0Count>=1000){
		T0Count=0;
		P2_0=~P2_0;
	}
}

void main(){
	timer0_init();
	
	while(1){}
}

3、使用按键控制流水灯的方向:

#include <REGX52.H>

void Delay1ms(unsigned int xms){
	unsigned char i, j;
	while(xms){
		i=2;
		j=239;
		do{
			while(--j);
		}while(--i);
		xms-=1;
	}
}

void timer0_init(){
	TMOD=TMOD&0xF0;
	TMOD=TMOD|0x01;
	TF0=0;
	TR0=1;
	TH0=64535/256;
	TL0=64535%256+1;
	
//	TH0=12000/256;
//	TL0=12000%256+1;
	ET0=1;
	EA=1;
	PT0=0;
}

unsigned int T0Count;
unsigned char direction=0;//0 left 1 right
void Timer0_Routine() interrupt 1{
	unsigned int flag=0;

	TH0=64535/256;
	TL0=64535%256;
	
//	TH0=12000/256;
//	TL0=12000%256+1;
	
	T0Count++;
	if(T0Count>=1000){
		T0Count=0;
		
		switch(direction){
			case 0:
				flag=(P2&0x80)>>7;
				P2=(P2<<=1)|flag;
				break;
			case 1:
				flag=(P2&0x01)<<7;
				P2=(P2>>=1)|flag;
				break;
			default:
				break;
		}
	}
}


void main(){
	P2=0xFE;//1111 1110
	
	timer0_init();
	
	while(1){
		if(P1_7==0){
			Delay1ms(20);
			
			while(P1_7==0);
			Delay1ms(20);
			
			direction=0;
		}
		if(P1_5==0){
			Delay1ms(20);
			
			while(P1_5==0);
			Delay1ms(20);
			
			direction=1;
		}
	}
}

4、计时器

#include <REGX52.H>

unsigned char number_array[10]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F};
unsigned int T0Count;
unsigned char min=0;
unsigned char sec=0;

void Delay1ms(unsigned int xms){
	unsigned char i, j;
	while(xms){
		i=2;
		j=239;
		do{
			while(--j);
		}while(--i);
		xms-=1;
	}
}

void print_number(unsigned char index,unsigned char value){
	if(value>10||value<0)return;

	P1_0=0;
	P1_1=0;
	P1_2=0;
	P1_3=0;

	switch (index){
		case 0:
			P1_3=1;
			break;
		case 1:
			P1_2=1;
			break;
		case 2:
			P1_1=1;
			break;
		case 3:
			P1_0=1;
			break;
		default:
			break;
	}
	P0=number_array[value];
	
	Delay1ms(1);
	P0=0x00;
}

void timer0_init(){
	TMOD=TMOD&0xF0;
	TMOD=TMOD|0x01;
	TF0=0;
	TR0=1;
	TH0=64535/256;
	TL0=64535%256+1;
	
//	TH0=12000/256;
//	TL0=12000%256+1;
	
	ET0=1;
	EA=1;
	PT0=0;
}

void Timer0_Routine() interrupt 1{
	TH0=64535/256;
	TL0=64535%256;
	
//	TH0=12000/256;
//	TL0=12000%256+1;
	
	T0Count++;
	if(T0Count>=1000){
		T0Count=0;
		
		sec++;
		if(sec>=60){
			min++;
			sec=0;
		}
		if(min>=60){
			min=0;
			sec=0;
		}
	}
}


void main(){	
	timer0_init();
	
	while(1){
		print_number(0,min/10);
		print_number(1,min%10);
		print_number(2,sec/10);
		print_number(3,sec%10);
	}
}

使用软件配置代码:
在这里插入图片描述

7、串口(计时、中断、晶振、CH340C)

电路:
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

电脑用串口想单片机发送信息

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

void UART_Init(void){
	PCON|=0X80;		//是能波特率倍速位 SMOD
	SCON = 0X50;	//串口以一模式工作 8位数据,可变波特率	
	TMOD&=0X0F;		//
	TMOD |=0X20;	//设置计时器一 以8位重装的方式工作
	TL1=0xF3;			//设置定时初值
	TH1=0xF3;			//设置定时器重装值
	ET1=0;				//禁止定时器1 发起中断
	TR1=1;				//启动定时器1
}

void UART_SendByte(unsigned char Byte){
	SBUF=Byte;	//给传出的 SBUF 赋值
	while(0==TI);
	TI=0;	//重置,等待下次的中断发起
}

void main(){
	unsigned char count = 0;
	
	UART_Init();
	UART_SendByte(0x11);
	
	
	while(1){
		if(count>0xFF)count=0;
		UART_SendByte(count++);
		Delay(1000);
	}
}

串口工具的使用:在这里插入图片描述
关于4800HZ 的计算过程:
在这里插入图片描述

用于软件计算4800HZ 的TL1 TH1:
在这里插入图片描述
晶振(频率是11.0592):
在这里插入图片描述

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

void UART_Init(void){
	PCON|=0X80;		//是能波特率倍速位 SMOD
	SCON = 0X50;	//串口以一模式工作 8位数据,可变波特率	
	TMOD&=0X0F;		//
	TMOD |=0X20;	//设置计时器一 以8位重装的方式工作
//	TL1=0xF3;			//设置定时初值
//	TH1=0xF3;			//设置定时器重装值		//时钟频率为:12MHZ	时
	TL1 = 0xF4;		//设定定时初值
	TH1 = 0xF4;		//设定定时器重装值			//时钟频率为:11.0592MHZ 时
	
	ET1=0;				//禁止定时器1 发起中断
	TR1=1;				//启动定时器1	
	
	EA=1;
	ES=1;//打开串口中断开关
}

void UART_SendByte(unsigned char Byte){
	SBUF=Byte;	//给传出的 SBUF 赋值
	while(0==TI);
	TI=0;	//重置,等待下次的中断发起
}
										  
void UART_Rountine() interrupt 4
{
	if(1==RI){
		unsigned char x = SBUF;
		RI=0;
		P2=~x;
		UART_SendByte(x);
	}
}

void main(){	
	UART_Init();
	
	while(1){}
}

8、LED点阵屏(74HC595D)

电路图:
在这里插入图片描述
OE 是74HC595的使能控制。
上面有横线,代表低电位有效,所以接线帽需要接GND。
在这里插入图片描述

在这里插入图片描述

1、显示静态画面(栅栏)

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

sbit RCK=P3^5;	//RCLK
sbit SCK=P3^6;	//SRCLK
sbit SER=P3^4;	//SER

unsigned char map_array[8]={0x50,0xFF,0x50,0xFF,0x50,0xFF,0x50,0x50};

void _74H595_WriteByte(unsigned char Byte){
	unsigned char i;
	for(i=0;i<8;i++){
		SER=Byte&(0x80>>i);
		
		SCK=1;//跳一下将SER的值输入D0,D0->D1 , D1->D2 ... D6->D7
		SCK=0;
	}
	RCK=1;//跳一下,将值整体输入一次(到D9-D16)
	RCK=0;
}

void main(){
	unsigned char i = 0;
	//初始化74HC595
	SCK=0;
	RCK=0;

	while(1){
		for(i=0; i<8; ++i){
			_74H595_WriteByte(map_array[i]);
			P0=~(0x80>>i);
			Delay(1);
			P0=0xFF;//消除残影
		}
	}
}

2、播放动态文字(JK)

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

sbit RCK=P3^5;	//RCLK
sbit SCK=P3^6;	//SRCLK
sbit SER=P3^4;	//SER

//code 可以让数组中的值存入 flash ,节省内存(ROM)空间,但其中的值不可改变
unsigned char code map_array[24]={
														0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
														0x42,0x7E,0x40,0x00,0x7E,0x08,0x14,0x22,
														0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
													 };

void _74H595_WriteByte(unsigned char Byte){
	unsigned char i;
	
	for(i=0;i<8;i++){
		SER=Byte&(0x80>>i);
		
		SCK=1;//跳一下将SER的值输入D0,D0->D1 , D1->D2 ... D6->D7
		SCK=0;
	}
	RCK=1;//跳一下,将值整体输入一次(到D9-D16)
	RCK=0;
}

void main(){
	unsigned char i = 0;
	unsigned char j = 0;
	unsigned char unit_count = 0;
	//初始化74HC595
	SCK=0;
	RCK=0;
	
	while(1){
		for(i=0; i<8; ++i){
			_74H595_WriteByte(map_array[i+j]);
			P0=~(0x80>>i);
			Delay(1);
			P0=0xFF;//消除残影
		}
		
		if(++unit_count < 10){
			continue;
		}else if(j++ < 16){
			unit_count = 0;
		}else{
			unit_count = 0;
			j = 0;
		}
	}
}

9、时钟 (DS1302)

电路图:
在这里插入图片描述

在这里插入图片描述

DS1302的使用说明:
在这里插入图片描述

1、计时器

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

#define RTC_READ_SECONDS 0x81
#define RTC_READ_MINUTES 0x83
#define RTC_READ_HOUR 0x85
#define RTC_READ_DATE 0x87
#define RTC_READ_MONTH 0x89
#define RTC_READ_DAY 0x8B
#define RTC_READ_YEAR 0x8D

#define RTC_WRITE_SECOND 0x80
#define RTC_WRITE_MINUTES 0x82
#define RTC_WRITE_HOUR 0x84
#define RTC_WRITE_DATE 0x86
#define RTC_WRITE_MONTH 0x88
#define RTC_WRITE_DAY 0x8A
#define RTC_WRITE_YEAR 0x8C

//初始时间 秒......年
unsigned char set_time[8]={0x00,0x00,0x00,0x01,0x02,0x04,0x24};
//unsigned char set_time[8]={0x55,0x59,0x23,0x28,0x02,0x04,0x01};
//unsigned char set_time[8]={0x55,0x59,0x23,0x28,0x02,0x04,0x04};

//定义DS1302的三个线角
sbit DS1302_SCLK = P3^6;
sbit DS1302_IO = P3^4;
sbit DS1302_CE = P3^5;

//指令须在上升沿写入,所以需要先置0
void DS1302_Init(void){
	DS1302_CE = 0;
	DS1302_SCLK = 0;
}

void DS1302_WriteByte(unsigned char Command,Data){
	unsigned char i;
	DS1302_CE = 1;//打开使能,高电平开启
	
	//写入8位的指令
	for(i=0;i<8;++i){
		DS1302_IO = Command&(0x01<<i);
		DS1302_SCLK = 1;
		DS1302_SCLK = 0;//每次上升沿,写入为 DS1302_IO 中的数据
	}
	//写入8位的数据
	for(i=0;i<8;++i){
		DS1302_IO = Data&(0x01<<i);
		DS1302_SCLK = 1;
		DS1302_SCLK = 0;
	}
	DS1302_CE = 0;//关闭使能,高电平开启
}

unsigned char DS1302_ReadByte(unsigned char Command){
	unsigned char i,Data = 0x00;
	DS1302_CE = 1;
	
	//写入8位的指令
	for(i=0;i<8;++i){
		DS1302_IO = Command&(0x01<<i);
		DS1302_SCLK = 0;
		DS1302_SCLK = 1;
		/*
		第一个读出数据的下降沿与最后一个写入指令的上升沿是紧挨着的,
		所以需要保证在完成写入读指令后,
		SCLK保持高电平,等做好读取准备的时候在将其赋值为0
		*/
	}
	DS1302_IO = 1;//准备好,开始读取数据
	//读出八位的数据
	for(i=0;i<8;++i){		
		DS1302_SCLK = 1;
		DS1302_SCLK = 0;
		if(DS1302_IO){Data|=(0x01<<i);}
	}
	DS1302_CE = 0;
	DS1302_IO = 0;//这里不置0,下次读就会出问题。我不知道具体是为什么(据说是因为 P3_4口 没有上拉电阻)
	/*
		这里写
			DS1302_IO = 0;
			DS1302_IO = 1;
		或
			DS1302_IO = 0;
		都可以的,所以所需要强下拉下,后面才能正常使用
	*/
	
	//DS1302_IO = 1;
	return Data;
}

void main(){
	unsigned char  Second = 0;
	
	LCD_Init();
	DS1302_Init();
	LCD_ShowString(1,1,"20");
	LCD_ShowString(1,5,"-");
	LCD_ShowString(1,8,"-");
	LCD_ShowString(2,3,"-");
	LCD_ShowString(2,6,"-");

	/*
		如果读取时间为一个大于59并且不动的数,
		则芯片有可能是处于写保护状态,在此处加上
		DS1302_WriteByte(0x8E,0x00);
		即可解除芯片写保护
	*/
	DS1302_WriteByte(0x8E,0x00);//解除芯片写保护
	
	//写入初始时间
	DS1302_WriteByte(RTC_WRITE_SECOND,set_time[0]);
	DS1302_WriteByte(RTC_WRITE_MINUTES,set_time[1]);
	DS1302_WriteByte(RTC_WRITE_HOUR,set_time[2]);
	DS1302_WriteByte(RTC_WRITE_DATE,set_time[3]);
	DS1302_WriteByte(RTC_WRITE_MONTH,set_time[4]);
	//DS1302_WriteByte(RTC_WRITE_DAY,set_time[5]);
	DS1302_WriteByte(RTC_WRITE_YEAR,set_time[6]);

	while(1){
		set_time[0] = DS1302_ReadByte(RTC_READ_SECONDS);
		set_time[1] = DS1302_ReadByte(RTC_READ_MINUTES);
		set_time[2] = DS1302_ReadByte(RTC_READ_HOUR);
		set_time[3] = DS1302_ReadByte(RTC_READ_DATE);
		set_time[4] = DS1302_ReadByte(RTC_READ_MONTH);
		//set_time[5] = DS1302_ReadByte(RTC_READ_DAY);
		set_time[6] = DS1302_ReadByte(RTC_READ_YEAR);
		
		LCD_ShowHexNum(2,7,set_time[0],2);
		LCD_ShowHexNum(2,4,set_time[1],2);
		LCD_ShowHexNum(2,1,set_time[2],2);
		LCD_ShowHexNum(1,9,set_time[3],2);
		LCD_ShowHexNum(1,6,set_time[4],2);
		//LCD_ShowHexNum(2,1,set_time[5],2);
		LCD_ShowHexNum(1,3,set_time[6],2);
	}
}

注:普中A2的P3_4接口,没有上拉电阻,向DS1302写完数据后需要将其置 0,才可正常进行下次的读操作。

2、可设置时间的时钟

10、嗡鸣器(ULN2003A)

在这里插入图片描述
在这里插入图片描述
达林顿晶体管(UNLN2003A):
在这里插入图片描述

在这里插入图片描述

独立按键使得LED亮起,并想0.2s的喇叭 50HZ的声音

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

void bi(){
	int count = 0;
	for(count = 0; count < 100; ++count){
		P2_5=0;
		Delay(1);
		P2_5=1;
		Delay(1);
	}
}

void main(){
	while(1){
		//open led 
		if(P3_1==0){
			P2_0=~P2_0;
			bi();
		}else if(P3_0==0){
			P2_1=~P2_1;
			bi();
		}else if(P3_2==0){
			P2_2=~P2_2;
			bi();
		}else if(P3_3==0){
			P2_3=~P2_3;
			bi();
		}
	}
}

演奏小星星

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

#define FULL_TIME 65535

unsigned char flag = 0;
unsigned long count_max = 0;	//用于计算终结的时间
unsigned long count_now = 0;					//用于计算终结的时间
unsigned char pitch = 1;					//输入一个 1-36 的音高

unsigned long code dorimi[36] = {
	61718,	61925,	62134,	62320,	62505,	62670,	62832,	62984,	63125,	63262,	63389,	63511,	
	63623,	63730,	63831,	63927,	64018,	63863,	64184,	64259,	64332,	64399,	64462,	64523,
	64579,	64633,	64684,	64732,	64776,	64819,	64859,	64897,	64933,	64967,	64999,	65029
};

unsigned long code time_long[6] = {
	125000,		//125ms		(1/16)
	250000,		//250ms		(1/8)
	500000,		//500ms 	(1/4)
	1000000,	//1s 			(1/2)
	2000000,	//2s			(1)
	4000000,	//4s			(2)
};

unsigned char code small_star[] = {
	12,2,12,2,
	19,2,19,2,
	21,2,21,2,
	19,3,17,2,
	17,2,
	16,2,16,2,
	14,2,14,2,
	12,3,
	19,2,19,2,
	17,2,17,2,
	16,2,16,2,
	14,3,
	19,2,19,2,
	17,2,17,2,
	16,2,16,2,
	14,3,
	12,2,12,2,
	19,2,19,2,
	21,2,21,2,
	19,3,
	17,2,17,2,
	16,2,16,2,
	14,2,14,2,
	13,3
};

unsigned int TH0_s=0;
unsigned int TL0_s=0;
void timing_begin(){
	//这个寄存器的名字叫 TMOD,用来设置计时器的工作模式
	TMOD=TMOD&0xF0;
	TMOD=TMOD|0x01;
	TF0=0;				//打开通向中断的开关
	TR0=1;				//允许计数/计时
	TH0_s=dorimi[pitch]/256;
	TL0_s=dorimi[pitch]%256+1;
	TH0=TH0_s;
	TL0=TL0_s;
	
	
	/* 
	TH0 TL0 总共可计数65535
	65535 - 64535 =1000,即 1ms
	(经过12的分频器,12M变成1M)
	 */
	
//	TH0=12000/256;
//	TL0=12000%256+1;
	ET0=1;	//总断的分开关
	EA=1;	//中断的总开关
	PT0=0;	//设置中断的优先级别
	
	flag=1; //计时开始标志
	count_now = 0;
}

void Timer0_Routine() interrupt 1{
	TH0=TH0_s;
	TL0=TL0_s;
	
	P2_5=~P2_5;			//震动
	
	count_now++;
	if(count_now>=count_max){
		TR0=0;				//停止计时
		flag=0; 			//计时结束标志
	}
}

//放声
void bi(unsigned char pitch_cur,unsigned char time){//音高 |	和一个音执行的时长
	unsigned int x = 0;
	
	pitch = pitch_cur;												//设置音高
	count_max=time_long[time]/(65535-dorimi[pitch]); 	//计算需要执行多少的周期	
	timing_begin();
	while(flag){}
}

void main(){
	unsigned int i=0;
	for (i=0;i<sizeof(small_star);i+=2){
		bi(small_star[i],small_star[i+1]);
	}
	
	while(1){}
}

11、存储器(I2C、AT24C02)

AT24C02是内存256byte大小的存储器:
在这里插入图片描述
AT24C02的内部电路图:
在这里插入图片描述

I2C总线:
在这里插入图片描述
在这里插入图片描述
I2C的时序图
在这里插入图片描述
在这里插入图片描述

AT24C02的时序图:
在这里插入图片描述

保存一个字节到AT24C02中,然后读取出来

i2c.h
----------------------------------------------------------
#ifndef __I2C_H__
#define __I2C_H__

//起始条件
void I2C_Start(void);
//结束条件
void I2C_Stop(void);
//发送一个字节
void I2C_SendByte(unsigned char Byte);
//接受一个字节的数据
unsigned char I2C_ReceiveByte(void);
//发送应答
void I2C_SendAck(unsigned char AckBit);
//接受应答
unsigned char I2C_ReceiveAck(void);

#endif

----------------------------------------------------------

i2c.c
----------------------------------------------------------
#include <REGX52.H>
#include "i2c.h"

sbit I2C_SCL=P2^1;
sbit I2C_SDA=P2^0;

//起始条件
//SCL高电平期间,SDA由高到低
void I2C_Start(void){
	I2C_SDA=1;
	I2C_SCL=1;
	I2C_SDA=0;
	I2C_SCL=0;
}

//结束条件
//SCL高电平期间,SDA由低到高
void I2C_Stop(void){
	I2C_SDA=0;
	I2C_SCL=1;
	I2C_SDA=1;
}

//发送一个字节
//SDA的数据提前准备好,SCL在高电平时接受数据
void I2C_SendByte(unsigned char Byte){
	unsigned char i;
	for(i=0;i<8;i++){
		I2C_SDA=Byte&(0x80>>i);	//由高到低一位一位的传递
		I2C_SCL=1;		
		I2C_SCL=0;
	}
}

//接受一个字节的数据
//SDA做好接受数据的准备,SCL在高电平时接受数据
unsigned char I2C_ReceiveByte(void){
	unsigned char i,Byte=0x00;
	I2C_SDA=1;	//断开连接,准备接受数据
	for(i=0;i<8;i++){
		I2C_SCL=1;
		if(I2C_SDA){Byte|=(0x80>>i);}		//由高到低一位一位的接受数据
		I2C_SCL=0;
	}
	
	return Byte;
}

//发送应答
//在接受完一个字节的数据后进行,0是应答,1是非应答
void I2C_SendAck(unsigned char AckBit){
	I2C_SDA=AckBit;	//准备好要发送的应答
	I2C_SCL=1;			//开始发送
	I2C_SCL=0;			//发送结束
}

//接受应答
//在发送完一字节的数据后进行,0是应答,1是非应答
unsigned char I2C_ReceiveAck(void){
	unsigned char AckBit;
	I2C_SDA=1;			//准备好接受应答
	I2C_SCL=1;			//开始接受
	AckBit=I2C_SDA;	//接受应答
	I2C_SCL=0;			//结束接受
	return AckBit;	//将接受到的应答返回
}

----------------------------------------------------------

at24c02.h
----------------------------------------------------------
#ifndef __AT24C02_H__
#define __AT24C02_H__


//写一个字节到指定的位置
void  AT24C02_WriteByte(unsigned char WordAddress,Data);

//从指定的位置读一个字节出来
unsigned char AT24C02_ReadByte(unsigned char WordAddress);

#endif
----------------------------------------------------------

at24c02.c
----------------------------------------------------------
#include <REGX52.H>
#include "I2C.h"
#include "at24c02.h"

#define AT24C02_ADDRESS 0xA0

/*
	发送的八位数据:
	xxxx             		xxx                 		x
	前四位是设备编号 		可配置地址(对应A0~A2) 		最后一位是设置读写(0是写,1是读)
	AT24C02的是1010		原理图中A0~A2是接地的
*/

//写一个字节到指定的位置
void  AT24C02_WriteByte(unsigned char WordAddress,Data){
	I2C_Start();		//开始
	I2C_SendByte(AT24C02_ADDRESS);
	I2C_ReceiveAck();								//写到AT24C02
	I2C_SendByte(WordAddress);
	I2C_ReceiveAck();								//写入的地址
	I2C_SendByte(Data);
	I2C_ReceiveAck();								//写入的具体数据
	I2C_Stop();			//结束
}

//从指定的位置读一个字节出来
unsigned char AT24C02_ReadByte(unsigned char WordAddress){
	unsigned char Data;
	//一阶段
	I2C_Start();	//开始
	I2C_SendByte(AT24C02_ADDRESS);
	I2C_ReceiveAck();								//操作AT24C02
	I2C_SendByte(WordAddress);
	I2C_ReceiveAck();								//操作的地址
	
	//二阶段
	I2C_Start();	//开始
	I2C_SendByte(AT24C02_ADDRESS|0x01);
	I2C_ReceiveAck();								//读AT24C02
	Data=I2C_ReceiveByte();
	I2C_SendAck(1);									//读取数据
	I2C_Stop();		//结束
	
	return Data;
}
----------------------------------------------------------

main.c
----------------------------------------------------------
#include <REGX52.H>
#include "LCD1602.h"
#include "i2c.h"
#include "at24c02.h"
#include "Delay.h"

unsigned char Data;

void main(){
	LCD_Init();
	//LCD_ShowString(1,1,"Hello");
	
	AT24C02_WriteByte(1,120);
	Delay(5);
	
	Data=AT24C02_ReadByte(1);
	LCD_ShowNum(2,1,Data,3);
	
	while(1){}
}
----------------------------------------------------------

2h计时器(到了时间嗡鸣器会响),时、分、秒、10毫秒(有暂停、保存功能)


print_number.h
----------------------------------------------------------
#ifndef __PRINT_NUMBER_H__
#define __PRINT_NUMBER_H__

void print_number(unsigned char index,unsigned char value);
void print_null();

#endif
----------------------------------------------------------

print_number.c
----------------------------------------------------------
#include <REGX52.H>
#include "Delay.h"
#include "print_number.h"

/*
	0-9	   为:0-9
	10 		 为:空
	11-21  为:带点的0-9
*/
unsigned char code number_array[21]={
			0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F,
			0x00,
			0xBF,0x86,0xDB,0xCF,0xE6,0xED,0xFD,0x87,0xFF,0xEF
	};


//(index)位置 和 (value)数字
void print_number(unsigned char index,unsigned char value){
	if(value>20||value<0||index>7||index<0)return;

	P2_2=0;
	P2_3=0;
	P2_4=0;//显示下个数字前先熄灯,消除残影

	switch (index){
		case 0:
			P2_2=1;
			P2_3=1;
			P2_4=1;
			break;
		case 1:
			P2_2=0;
			P2_3=1;
			P2_4=1;
			break;
		case 2:
			P2_2=1;
			P2_3=0;
			P2_4=1;
			break;
		case 3:
			P2_2=0;
			P2_3=0;
			P2_4=1;
			break;
		case 4:
			P2_2=1;
			P2_3=1;
			P2_4=0;
			break;
		case 5:
			P2_2=0;
			P2_3=1;
			P2_4=0;
			break;
		case 6:
			P2_2=1;
			P2_3=0;
			P2_4=0;
			break;
		case 7:
			P2_2=0;
			P2_3=0;
			P2_4=0;
			break;
		default:
			break;
	}
	P0=number_array[value];
	//Delay(1);//显示一毫秒
}

void print_null(){
	P2_2=0;
	P2_3=0;
	P2_4=0;//显示下个数字前先熄灯,消除残影
}
----------------------------------------------------------

i2c.h
----------------------------------------------------------
#ifndef __I2C_H__
#define __I2C_H__

//起始条件
void I2C_Start(void);
//结束条件
void I2C_Stop(void);
//发送一个字节
void I2C_SendByte(unsigned char Byte);
//接受一个字节的数据
unsigned char I2C_ReceiveByte(void);
//发送应答
void I2C_SendAck(unsigned char AckBit);
//接受应答
unsigned char I2C_ReceiveAck(void);

#endif
----------------------------------------------------------

i2c.c
----------------------------------------------------------
#include <REGX52.H>
#include "i2c.h"

sbit I2C_SCL=P2^1;
sbit I2C_SDA=P2^0;

//起始条件
//SCL高电平期间,SDA由高到低
void I2C_Start(void){
	I2C_SDA=1;
	I2C_SCL=1;
	I2C_SDA=0;
	I2C_SCL=0;
}

//结束条件
//SCL高电平期间,SDA由低到高
void I2C_Stop(void){
	I2C_SDA=0;
	I2C_SCL=1;
	I2C_SDA=1;
}
----------------------------------------------------------

at24c02.h
----------------------------------------------------------
#ifndef __AT24C02_H__
#define __AT24C02_H__


//写一个字节到指定的位置
void  AT24C02_WriteByte(unsigned char WordAddress,Data);

//从指定的位置读一个字节出来
unsigned char AT24C02_ReadByte(unsigned char WordAddress);


#endif
----------------------------------------------------------

at24c02.c
----------------------------------------------------------
#include <REGX52.H>
#include "I2C.h"
#include "at24c02.h"

#define AT24C02_ADDRESS 0xA0

/*
	发送的八位数据:
	xxxx             		xxx                 		x
	前四位是设备编号 		可配置地址 		最后一位是设置读写(0是写,1是读)
*/

//写一个字节到指定的位置
void  AT24C02_WriteByte(unsigned char WordAddress,Data){
	I2C_Start();		//开始
	I2C_SendByte(AT24C02_ADDRESS);
	I2C_ReceiveAck();								//写到AT24C02
	I2C_SendByte(WordAddress);
	I2C_ReceiveAck();								//写入的地址
	I2C_SendByte(Data);
	I2C_ReceiveAck();								//写入的具体数据
	I2C_Stop();			//结束
}

//从指定的位置读一个字节出来
unsigned char AT24C02_ReadByte(unsigned char WordAddress){
	unsigned char Data;
	//一阶段
	I2C_Start();	//开始
	I2C_SendByte(AT24C02_ADDRESS);
	I2C_ReceiveAck();								//操作AT24C02
	I2C_SendByte(WordAddress);
	I2C_ReceiveAck();								//操作的地址
	
	//二阶段
	I2C_Start();	//开始
	I2C_SendByte(AT24C02_ADDRESS|0x01);
	I2C_ReceiveAck();								//读AT24C02
	Data=I2C_ReceiveByte();
	I2C_SendAck(1);									//读取数据
	I2C_Stop();		//结束
	
	return Data;
}

----------------------------------------------------------

main.c
----------------------------------------------------------
#include <REGX52.H>
#include "i2c.h"
#include "at24c02.h"
#include "Delay.h"
#include "print_number.h"

unsigned char Data;
unsigned char time_begin_flag = 1;

//当前时间
unsigned char now_time_array[4] = {0,    0,     0,     0};
																	//hour munite second ms
																	//0-23 0-59   0-59   0-99

unsigned char print_number_array[8]={1,2,3,4,5,6,7,8};//当前时间显示表

/*
	开始计时
*/
void timer0_init(){
	//这个寄存器的名字叫 TMOD,用来设置计时器的工作模式
	TMOD=TMOD&0xF0;
	TMOD=TMOD|0x01;
	TF0=0;				//打开通向中断的开关
	TR0=1;				//允许计数/计时
	TH0=64535/256;
	TL0=64535%256+1;
	/* 
	TH0 TL0 总共可计数65535
	65535 - 64535 =1000,即 1ms
	(经过12的分频器,12M变成1M)
	 */
	
//	TH0=12000/256;
//	TL0=12000%256+1;
	ET0=1;	//总断的分开关
	EA=1;	//中断的总开关
	PT0=0;	//设置中断的优先级别
}



//初始化
void init_time_print(){
	unsigned char tens = 0;
	unsigned char ones = 0;

	//设置ram中的时刻表(从ROM中拿数据)
	Data=AT24C02_ReadByte(0);
	now_time_array[0]=Data;
	Data=AT24C02_ReadByte(1);
	now_time_array[1]=Data;
	Data=AT24C02_ReadByte(2);
	now_time_array[2]=Data;
	Data=AT24C02_ReadByte(3);
	now_time_array[3]=Data;
	
	//设置显示的时刻表	
		//更新 hour
	tens = now_time_array[1]/10;
	ones = now_time_array[1]%10;
	print_number_array[0]=tens;
	print_number_array[1]=ones+11;					
		//更新 munite
	tens = now_time_array[1]/10;
	ones = now_time_array[1]%10;
	print_number_array[2]=tens;
	print_number_array[3]=ones+11;
		//更新 s
	tens = now_time_array[2]/10;
	ones = now_time_array[2]%10;
	print_number_array[4]=tens;
	print_number_array[5]=ones+11;
		//更新 ms
	tens = now_time_array[3]/10;
	ones = now_time_array[3]%10;
	print_number_array[6]=tens;
	print_number_array[7]=ones+11;
}


/*
	一毫秒进入一次这个中断函数
*/
unsigned int T0Count=0;
unsigned char cur_number = 0;
unsigned char key_status[4]={0,0,0,0};
void Timer0_Routine() interrupt 1{
	TH0=64535/256;
	TL0=64535%256+1;
	

	//数码管的操作
	//每两毫秒进入一次,数码管函数
	if(0==T0Count%2){//两毫秒进入一次循环	
		cur_number=	T0Count%16;
		print_number(cur_number/2,print_number_array[cur_number/2]);
	}
	
	//加时间的操作
	//每10毫秒加一次时间
	if(0==T0Count%10){
		if(time_begin_flag){
			now_time_array[3]++;
		}
	}
	
	//加时间的操作
	//每20ms检查一次按键的状态
	//三个个按键的作用分别是:暂停、开始、保存、重置为0
	if(0==T0Count%20){
		if(0==P3_1 && key_status[0]==1){//暂停
			time_begin_flag=0;
		}
		if(0==P3_0 && key_status[1]==1){//开始
			time_begin_flag=1;
		}
		if(0==P3_2 && key_status[2]==1){//保存
			TR0=0;//停止计时
			
			//将RAM中的数据存入ROM
			AT24C02_WriteByte(0,now_time_array[0]);
			Delay(5);
			AT24C02_WriteByte(1,now_time_array[1]);
			Delay(5);
			AT24C02_WriteByte(2,now_time_array[2]);
			Delay(5);
			AT24C02_WriteByte(3,now_time_array[3]);
			Delay(5);
			
			//初始化
			init_time_print();
			TR0=1;//开始计时
		}
		if(0==P3_3 && key_status[3]==1){//重置
			TR0=0;//停止计时
			
			//写入,将时间归零
			AT24C02_WriteByte(0,0);
			Delay(5);
			AT24C02_WriteByte(1,0);
			Delay(5);
			AT24C02_WriteByte(2,0);
			Delay(5);
			AT24C02_WriteByte(3,0);
			Delay(5);
			
			//初始化
			init_time_print();
			TR0=1;//开始计时
		}
		
		key_status[0]=P3_1;
		key_status[1]=P3_0;
		key_status[2]=P3_2;
		key_status[3]=P3_3;
		
		if(T0Count==65519){//65520可用被40整除,40是8,10和20的最小公倍数
			T0Count=0;
			return;
		}
	}
	
	T0Count++;
	
}

//嗡鸣器
void bi(){
	int count = 0;
	while(1){
		P2_5=0;
		Delay(1);
		P2_5=1;
		Delay(1);
	}
}

void main(){
	unsigned char tens = 0;
	unsigned char ones = 0;
	unsigned char now_time_array_3 = 0;

	//初始化
	init_time_print();
	
	timer0_init();//开启计时函数

	
	//now_time_array_3 = now_time_array[3];
	//开始计时
	while(1){
		if(now_time_array_3 == now_time_array[3]){
			continue;														//时间没有跟新
		}else{
			now_time_array_3=now_time_array[3];	//更新为现在的ms数
		}
		
		if(now_time_array[3]>99){				//ms			进位了
			now_time_array[3]=0;
			now_time_array[2]++;
			
			if(now_time_array[2]>59){			//s 			进位了
				now_time_array[2]=0;
				now_time_array[1]++;
				
				if(now_time_array[1]>59){		//munite 	进位了
					now_time_array[1]=0;
					now_time_array[0]++;
					
					//到2h时,嗡鸣器开始响亮
					if(2==now_time_array[0]){
						TR0=0;//停止计时
						bi();
					}
					
					if(now_time_array[0]>23){	//hour 		进位了
						now_time_array[0]=0;
						
					}
					//更新 hour
					tens = now_time_array[1]/10;
					ones = now_time_array[1]%10;
					print_number_array[0]=tens;
					print_number_array[1]=ones+11;					
				}
				//更新 munite
				tens = now_time_array[1]/10;
				ones = now_time_array[1]%10;
				print_number_array[2]=tens;
				print_number_array[3]=ones+11;
			}
			//更新s
			tens = now_time_array[2]/10;
			ones = now_time_array[2]%10;
			print_number_array[4]=tens;
			print_number_array[5]=ones+11;
		}
		//更新ms
		tens = now_time_array[3]/10;
		ones = now_time_array[3]%10;
		print_number_array[6]=tens;
		print_number_array[7]=ones+11;
	}
}
----------------------------------------------------------

12、温度检测 (1-Wire(单总线)、DS18B20))

简介:
在这里插入图片描述
在这里插入图片描述

电路原理图:
在这里插入图片描述
AMR中的数据结构:
在这里插入图片描述
在这里插入图片描述

51中的电路结构:
在这里插入图片描述

时序图:
在这里插入图片描述
注:因为单总线没有时钟同步,所以在通信的过程中是不能被打断的。
在这里插入图片描述

显示当前温度:

OneWire.h
----------------------------------------------------------
#ifndef __ONE_Wire_H__
#define __ONE_Wire_H__

//初始化
unsigned char OneWire_Init(void);
//发送一位数据
void OneWire_SendBit(unsigned char Bit);
//接受从机的一位数据
unsigned char OneWire_ReceiveBit(void);
//发送一个字节的数据
void OneWire_SendByte(unsigned char Byte);
//接受一个字节的数据
unsigned char OneWire_ReceiveByte(void);

#endif
----------------------------------------------------------

OneWire.c
----------------------------------------------------------
#include <REGX52.H>
#include "OneWire.h"

//单总线就是靠这一根线来通讯的
sbit OneWire_DQ=P3^7;

//初始化
//初始化可以结束上条指定,开始新的指令
//初始化后,从机 会进行响应,没有从机的话就没有响应
//低电平,0,代表响应
//高电平,1,代表无响应
unsigned char OneWire_Init(void){
	unsigned char i;
	unsigned char AckBit;
	OneWire_DQ=1;					//确认已经释放了对总线的释放
	OneWire_DQ=0;
	i = 247;while(--i);		//Delay 500us,最少拉低480us,这里拉低了500us
	OneWire_DQ=1;					//准备好接受从机发送数据
	i = 32;while(--i);		//Delay 70us		要等待15-60us,从机拉低60-240us.
												//							这里等待了70us,开始取从机发的数据 
	AckBit=OneWire_DQ;		//这里是在接受数据
	i=247;while(--i);			//Dealy 500us 这里是在等待从机释放总线
	return AckBit;
}

//发送一位数据
void OneWire_SendBit(unsigned char Bit){
	unsigned char i;
	OneWire_DQ=0;				
	i=4;while(--i);			//Delay 10us			拉低10u后发送要传输的数据
	OneWire_DQ=Bit;
	i=24;while(--i);		//Delay 50us			
											//在传0时 保证拉低的总时长大于60;也小于120
											//在传1时 保证40us后从机可以读取到高电平;且使得整个时间片大于60us
	OneWire_DQ=1;
}

//接受从机的一位数据
unsigned char OneWire_ReceiveBit(void){
	unsigned char i;
	unsigned char Bit;
	OneWire_DQ=0;
	i=2;while(--i);			//Delay 5us		//拉低5us
	OneWire_DQ=1;				//准备好接受从机发送的数据
	i=2;while(--i);			//Delay 5us
	Bit=OneWire_DQ;			//等待5us后,接受数据
	i=24;while(--i);		//Delay 50us		使得整个过程大于60us
	return Bit;
}

//发送一个字节的数据
void OneWire_SendByte(unsigned char Byte){
	unsigned char i;
	for(i=0;i<8;i++){
		OneWire_SendBit(Byte&(0x01<<i));//由低到高一位一位的发送,和 I2C相反
	}
}

//接受一个字节的数据
unsigned char OneWire_ReceiveByte(void){
	unsigned char i;
	unsigned char Byte=0x00;
	for(i=0;i<8;i++){
		if(OneWire_ReceiveBit()){Byte|=(0x01<<i);}//由低到高一位一位的接受数据
	}
	
	return Byte;
}
----------------------------------------------------------

ds18b20.h
----------------------------------------------------------
#ifndef __DS18B20_H__
#define __DS18B20_H__

//将当前的温度信息写入到 RAM中 暂存起来
void DS18B20_ConvertT(void);
//读取当前温度信息(从RAM中一个字节一个字节的读取)
float DS18B20_ReadT(void);

#endif
----------------------------------------------------------

ds18b20.c
----------------------------------------------------------
#include <REGX52.H>
#include "OneWire.h"

#define DS18B20_SKIP_ROM					0xCC
#define DS18B20_CONVERT_T					0x44
#define DS18B20_READ_SCRATCHPAD		0xBE

//将当前的温度信息写入到 RAM中 暂存起来
void DS18B20_ConvertT(void){
	OneWire_Init();												//初始化
	OneWire_SendByte(DS18B20_SKIP_ROM);		//跳过从机选择(ROM指令),直接进入功能指令
	OneWire_SendByte(DS18B20_CONVERT_T);	//将温度信息写入到 RAM中
}

//读取当前温度信息(从RAM中一个字节一个字节的读取)
float DS18B20_ReadT(void){
	unsigned char TLSB,TMSB;
	int Temp;
	float T;
	OneWire_Init();//初始化
	OneWire_SendByte(DS18B20_SKIP_ROM);//跳过从机选择(ROM指令),直接进入功能指令
	OneWire_SendByte(DS18B20_READ_SCRATCHPAD);//读取RAM中的数据
	TLSB=OneWire_ReceiveByte();			//读取第一个字节
	TMSB=OneWire_ReceiveByte();			//读取第二个字节
	
	//对读出的数据进行处理,将其转换位浮点型
	Temp=(TMSB<<8)|TLSB;
	T=Temp/16.0;
	
	return T;
}
----------------------------------------------------------


main.c
----------------------------------------------------------
#include <REGX52.H>
#include "ds18b20.h"
#include "LCD1602.h"

float t;

void main(){
	LCD_Init();
	LCD_ShowString(1,1,"Temperature:");	
	
	while(1){
		DS18B20_ConvertT();
		t=DS18B20_ReadT();
		if(t>=0){
			LCD_ShowChar(2,1,'+');
		}else{
			LCD_ShowChar(2,1,'-');
		}
		LCD_ShowNum(2,2,t,3);
		LCD_ShowChar(2,5,'.');
		LCD_ShowNum(2,6,(unsigned long)(t*10000)%10000,4);
	}
}

----------------------------------------------------------

13、液晶屏的显示(LCD1602)

简介:
在这里插入图片描述
电路图:
在这里插入图片描述

在这里插入图片描述

时序图及指令集:
在这里插入图片描述
在这里插入图片描述

本程序使用的指令流程:
在这里插入图片描述

显示一个X

LCD1602.h
----------------------------------------------------------
#ifndef __LCD1602_H__
#define __LCD1602_H__

//用户调用函数:
//初始化函数
void LCD_Init();

//在LCD1602指定位置上显示一个字符
void LCD_ShowChar(unsigned char Line,unsigned char Column,char Char);

#endif
----------------------------------------------------------

LCD1602.c
----------------------------------------------------------
#include <REGX52.H>

//引脚配置
sbit LCD_RS=P2^6;	//指令、数据的选择
sbit LCD_RW=P2^5;	//读写选择
sbit LCD_EN=P2^7;	//使能

//用于传数据
#define lCD_DataPort P0

//LCD1602执行命令需要时间
void LCD_Delay(){
	unsigned char i,j;
	
	i=2;
	j=239;
	do{
		while(--j);
	}while(--i);
}

//输入一条指令
void LCD_WriteCommand(unsigned char Command){
	LCD_RS=0;								//指令
	LCD_RW=0;								//写
	lCD_DataPort=Command;		//命令的内容
	LCD_EN=1;								//数据有效
	LCD_Delay();
	LCD_EN=0;								//下降沿执行命令
	LCD_Delay();
}

//写入字节的数据
void LCD_WriteData(unsigned char Data){
	LCD_RS=1;								//数据
	LCD_RW=0;								//写
	lCD_DataPort=Data;			//数据的内容
	LCD_EN=1;								//数据有效
	LCD_Delay();
	LCD_EN=0;								//下降沿写入数据
	LCD_Delay();
}

//设置光标位置
void LCD_SetCursor(unsigned char Line,unsigned char Column){
	if(Line==1){
		LCD_WriteCommand(0x80|(Column-1));				//首位位1,第二位 0表示在第一行显示。后四位表示字符所在的行的位置
	}else if(Line==2){
		LCD_WriteCommand(0x80|(Column-1+0x40));		//首位位1,第二位 1表示在第二行显示。后四位表示字符所在的行的位置
	}
}

//初始化函数
void LCD_Init(){
	LCD_WriteCommand(0x38);	//八位数据接口,两行显示,5*8点阵
	LCD_WriteCommand(0x0c);	//显示开,光标关,闪烁关
	LCD_WriteCommand(0x06);	//数据读写操作后,光标自动加1,画面不动
	LCD_WriteCommand(0x01);	//清屏
}

//在LCD1602指定位置上显示一个字符
void LCD_ShowChar(unsigned char Line,unsigned char Column,char Char){
	LCD_SetCursor(Line,Column);		//设置光标位置(行列)
	LCD_WriteData(Char);					//显示字符
}

----------------------------------------------------------

main.c
----------------------------------------------------------
#include <REGX52.H>
#include "LCD1602.h"

void main(){
	//初始化函数
	LCD_Init();

	//在LCD1602指定位置上显示一个字符
	LCD_ShowChar(1,1,'X');
	
	while(1){}
}

----------------------------------------------------------

完整版:


LCD1602.h
----------------------------------------------------------
#ifndef __LCD1602_H__
#define __LCD1602_H__

//用户调用函数:
void LCD_Init();
void LCD_ShowChar(unsigned char Line,unsigned char Column,char Char);
void LCD_ShowString(unsigned char Line,unsigned char Column,char *String);
void LCD_ShowNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);
void LCD_ShowSignedNum(unsigned char Line,unsigned char Column,int Number,unsigned char Length);
void LCD_ShowHexNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);
void LCD_ShowBinNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);

#endif

----------------------------------------------------------

LCD1602.c
----------------------------------------------------------
#include <REGX52.H>

//引脚配置:
sbit LCD_RS=P2^6;
sbit LCD_RW=P2^5;
sbit LCD_EN=P2^7;
#define LCD_DataPort P0

//函数定义:
/**
  * @brief  LCD1602延时函数,12MHz调用可延时1ms
  * @param  无
  * @retval 无
  */
void LCD_Delay()
{
	unsigned char i, j;

	i = 2;
	j = 239;
	do
	{
		while (--j);
	} while (--i);
}

/**
  * @brief  LCD1602写命令
  * @param  Command 要写入的命令
  * @retval 无
  */
void LCD_WriteCommand(unsigned char Command)
{
	LCD_RS=0;
	LCD_RW=0;
	LCD_DataPort=Command;
	LCD_EN=1;
	LCD_Delay();
	LCD_EN=0;
	LCD_Delay();
}

/**
  * @brief  LCD1602写数据
  * @param  Data 要写入的数据
  * @retval 无
  */
void LCD_WriteData(unsigned char Data)
{
	LCD_RS=1;
	LCD_RW=0;
	LCD_DataPort=Data;
	LCD_EN=1;
	LCD_Delay();
	LCD_EN=0;
	LCD_Delay();
}

/**
  * @brief  LCD1602设置光标位置
  * @param  Line 行位置,范围:1~2
  * @param  Column 列位置,范围:1~16
  * @retval 无
  */
void LCD_SetCursor(unsigned char Line,unsigned char Column)
{
	if(Line==1)
	{
		LCD_WriteCommand(0x80|(Column-1));
	}
	else if(Line==2)
	{
		LCD_WriteCommand(0x80|(Column-1+0x40));
	}
}

/**
  * @brief  LCD1602初始化函数
  * @param  无
  * @retval 无
  */
void LCD_Init()
{
	LCD_WriteCommand(0x38);//八位数据接口,两行显示,5*7点阵
	LCD_WriteCommand(0x0c);//显示开,光标关,闪烁关
	LCD_WriteCommand(0x06);//数据读写操作后,光标自动加一,画面不动
	LCD_WriteCommand(0x01);//光标复位,清屏
}

/**
  * @brief  在LCD1602指定位置上显示一个字符
  * @param  Line 行位置,范围:1~2
  * @param  Column 列位置,范围:1~16
  * @param  Char 要显示的字符
  * @retval 无
  */
void LCD_ShowChar(unsigned char Line,unsigned char Column,char Char)
{
	LCD_SetCursor(Line,Column);
	LCD_WriteData(Char);
}

/**
  * @brief  在LCD1602指定位置开始显示所给字符串
  * @param  Line 起始行位置,范围:1~2
  * @param  Column 起始列位置,范围:1~16
  * @param  String 要显示的字符串
  * @retval 无
  */
void LCD_ShowString(unsigned char Line,unsigned char Column,char *String)
{
	unsigned char i;
	LCD_SetCursor(Line,Column);
	for(i=0;String[i]!='\0';i++)
	{
		LCD_WriteData(String[i]);
	}
}

/**
  * @brief  返回值=X的Y次方
  */
int LCD_Pow(int X,int Y)
{
	unsigned char i;
	int Result=1;
	for(i=0;i<Y;i++)
	{
		Result*=X;
	}
	return Result;
}

/**
  * @brief  在LCD1602指定位置开始显示所给数字
  * @param  Line 起始行位置,范围:1~2
  * @param  Column 起始列位置,范围:1~16
  * @param  Number 要显示的数字,范围:0~65535
  * @param  Length 要显示数字的长度,范围:1~5
  * @retval 无
  */
void LCD_ShowNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
{
	unsigned char i;
	LCD_SetCursor(Line,Column);
	for(i=Length;i>0;i--)
	{
		LCD_WriteData(Number/LCD_Pow(10,i-1)%10+'0');
	}
}

/**
  * @brief  在LCD1602指定位置开始以有符号十进制显示所给数字
  * @param  Line 起始行位置,范围:1~2
  * @param  Column 起始列位置,范围:1~16
  * @param  Number 要显示的数字,范围:-32768~32767
  * @param  Length 要显示数字的长度,范围:1~5
  * @retval 无
  */
void LCD_ShowSignedNum(unsigned char Line,unsigned char Column,int Number,unsigned char Length)
{
	unsigned char i;
	unsigned int Number1;
	LCD_SetCursor(Line,Column);
	if(Number>=0)
	{
		LCD_WriteData('+');
		Number1=Number;
	}
	else
	{
		LCD_WriteData('-');
		Number1=-Number;
	}
	for(i=Length;i>0;i--)
	{
		LCD_WriteData(Number1/LCD_Pow(10,i-1)%10+'0');
	}
}

/**
  * @brief  在LCD1602指定位置开始以十六进制显示所给数字
  * @param  Line 起始行位置,范围:1~2
  * @param  Column 起始列位置,范围:1~16
  * @param  Number 要显示的数字,范围:0~0xFFFF
  * @param  Length 要显示数字的长度,范围:1~4
  * @retval 无
  */
void LCD_ShowHexNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
{
	unsigned char i,SingleNumber;
	LCD_SetCursor(Line,Column);
	for(i=Length;i>0;i--)
	{
		SingleNumber=Number/LCD_Pow(16,i-1)%16;
		if(SingleNumber<10)
		{
			LCD_WriteData(SingleNumber+'0');
		}
		else
		{
			LCD_WriteData(SingleNumber-10+'A');
		}
	}
}

/**
  * @brief  在LCD1602指定位置开始以二进制显示所给数字
  * @param  Line 起始行位置,范围:1~2
  * @param  Column 起始列位置,范围:1~16
  * @param  Number 要显示的数字,范围:0~1111 1111 1111 1111
  * @param  Length 要显示数字的长度,范围:1~16
  * @retval 无
  */
void LCD_ShowBinNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
{
	unsigned char i;
	LCD_SetCursor(Line,Column);
	for(i=Length;i>0;i--)
	{
		LCD_WriteData(Number/LCD_Pow(2,i-1)%2+'0');
	}
}

----------------------------------------------------------

14、直流电机(PWM)

简介:
在这里插入图片描述

电路图:
在这里插入图片描述
注:第二张图的二极管是为了防止 电感器件(马达)击穿三极管。
下面程序用的P01口,接线是5v 、01口。
在这里插入图片描述

PWM:
因为直流电机的电路上经过的电路比较大,所以调节其运行工具不能通过加电阻的方式。
于是就是使用了PWM的方式(在一定时间间隔中加如电流)
在这里插入图片描述

LED呼吸灯:

#include <REGX52.H>


sbit LED=P2^0;

void delay(unsigned int time){
	while(time--){}
}

void main(){
	unsigned int i,j,k = 0;

	//变亮
	while(1){
		for(i=0;i<100;++i){
			for(j=0;j<20;++j){
				LED=0;
				delay(i);
			}
			for(j=0;j<20;++j){
				LED=1;
				delay(100-i);
			}
		}
		
		//变暗
		for(i=0;i<100;++i){
			for(j=0;j<20;++j){
				LED=1;
				delay(i);
			}
			for(j=0;j<20;++j){
				LED=0;
				delay(100-i);
			}
		}
	}
}

3档马达,每按一次K1(独立按键),就加一当的转速。

#include <REGX52.H>

sbit motor=P1^0;

void Timer0Init(void)		//100微秒@11.0592MHz
{
	
	TMOD=TMOD&0xF0;
	TMOD=TMOD|0x01; //设置定时器模式
	TL0 = 0xAE;		//设置定时初值
	TH0 = 0xFB;		//设置定时初值
	TF0 = 0;		//清除TF0标志
	TR0 = 1;		//定时器0开始计时
	
	ET0=1;	//总断的分开关
	EA=1;	//中断的总开关
	PT0=0;	//设置中断的优先级别
}

//PWM
unsigned int T0Count;//计数,在0-99之间
unsigned int Compare;//在0-99之间
void Timer0_Routine() interrupt 1{
	TL0 = 0xAE;		//设置定时初值
	TH0 = 0xFB;		//设置定时初值
	
	T0Count++;
	
	if(T0Count<Compare){//10ms 进入一次
		motor=1;
	}else{
		motor=0;
	}
	if(T0Count>100)T0Count=0;//重置
}

void Delay(unsigned int xms)
{
	unsigned char i, j;
	while(xms--)
	{
		i = 2;
		j = 239;
		do
		{
			while (--j);
		} while (--i);
	}
}
void main(){
	Compare = 0;
	
	Timer0Init();

	//变亮
	while(1){
		if(P3_1==0){
			Delay(20);//检测按下
			
			while(P3_1==0);
			Delay(20);//确认松手
			
			if(Compare<75){
				Compare+=25;
			}else{
				Compare=0;
			}
		}
		
		
//		Compare++;
//		if(Compare==100){
//			Compare=0;
//		}
	}
}

15、ADC、DAC(XPT2046)

ADC: 模拟信号转为数字信号
A:analog 模拟,
D:digital 数字
DAC:数字信号转为模拟信号

在这里插入图片描述
实现原理:
在这里插入图片描述
在这里插入图片描述

具体应用
(电路的接口连接)
在这里插入图片描述
在这里插入图片描述
(时序与指令)
在这里插入图片描述
在这里插入图片描述

ADC
显示模拟信号的值:

XPT2046.h
----------------------------------------------------------
#ifndef __XPT2046_H__
#define __XPT2046_H__

/*
	XP 		AIN0
	YP 		AIN1
	UBAT	AIN2
	AUX		AIN3
	
	制字的控制命令
	第7位		第6位	第5位	第4位	第3位		第2位			第1位		第0位
	S			A2		A1		A0		MODE		SER/DFR			PD1			PD0


	S设置必须为1 ,它叫控制字首位
	
	A2 A1 A0	:选择哪个作为输入端口	001(XP)	101(YP)	010(BAT)	110(AUX)
	
	MODE	:0 是12位的精度, 1 是8位精度
	SER/DFR		1是单端输入配置	0是差分输入模式
	
	PD1 PD0	00代表低功耗模式 11代表供电状态
*/

#define XPT2046_XP_8		0x9C
#define XPT2046_YP_8		0xDC
#define XPT2046_UBAT_8	0xAC
#define XPT2046_AUX_8		0xEC

#define XPT2046_XP_12		0x94
#define XPT2046_YP_12		0xD4
#define XPT2046_UBAT_12	0xA4
#define XPT2046_AUX_12	0xE4

unsigned int XPT2046_ReadAD(unsigned char Command);

#endif

----------------------------------------------------------

XPT2046.c
----------------------------------------------------------
#include <REGX52.H>

/*
	可以在51的原理图中查找到这里的连接依据
	
	CS是片选线
	DCLK是时钟线
	DIN是写命令的线
	DOUT是读数据的线
*/

sbit XPT2046_CS=P3^5;
sbit XPT2046_DCLK=P3^6;		//上升沿写入数据 下降沿读取数据
sbit XPT2046_DIN=P3^4;		
sbit XPT2046_DOUT=P3^7;


unsigned int XPT2046_ReadAD(unsigned char command){
	unsigned char i;
	unsigned int ADVAlue=0;
	
	XPT2046_DCLK=0;	//为在上升沿写数据做准备
	XPT2046_CS=0;		//命令开始

	//发送命令
	for(i=0;i<8;++i){
		XPT2046_DIN=command&(0x80>>i);
		XPT2046_DCLK=1;//写入
		XPT2046_DCLK=0;//为下次写入做准备
	}
	
	//接受数据
	for(i=0;i<16;++i){
		XPT2046_DCLK=1;
		XPT2046_DCLK=0;//下降沿后开始读数据
		if(XPT2046_DOUT){ADVAlue|=(0x8000>>i);}
	}
	
	XPT2046_CS=1;//命令结束
	return ADVAlue;
}

----------------------------------------------------------

main.c
----------------------------------------------------------
#include <REGX52.H>
#include "LCD1602.h"
#include "XPT2046.h"
#include "Delay.h"

void main(){
	unsigned int value=0;
	
	LCD_Init();
	LCD_ShowString(1,1,"AD1");
	LCD_ShowString(1,6,"NTC1");
	LCD_ShowString(1,11,"GR1");

	//变亮
	while(1){
		//分辨率为8	AD1	旋转
		value=XPT2046_ReadAD(XPT2046_XP_8);
		LCD_ShowNum(2,1,value/256,3);//16位中,后8位为0
		Delay(10);
		
		//分辨率为12
		//ADValue=XPT2046_ReadAD(XPT2046_XP_12);
		//LCD_ShowNum(2,1,ADValue/16,4);//16位中,后4位为0
		//Delay(10);

		//分辨率为8	NTC1	温度
		value=XPT2046_ReadAD(XPT2046_YP_8);
		LCD_ShowNum(2,6,value/256,3);//16位中,后8位为0
		Delay(10);
		
		//分辨率为8	GR1	光感
		value=XPT2046_ReadAD(XPT2046_UBAT_8);
		LCD_ShowNum(2,11,value/256,3);//16位中,后8位为0
		Delay(10);
	}
}

----------------------------------------------------------

DAC
呼吸灯(将PWM信号转为模拟信号)

#include <REGX52.H>

sbit DA=P2^1;

void Timer0Init(void)		//100微秒@11.0592MHz
{
	
	TMOD=TMOD&0xF0;
	TMOD=TMOD|0x01; //设置定时器模式
	TL0 = 0xF5;		//设置定时初值
	TH0 = 0xFF;		//设置定时初值
	TF0 = 0;		//清除TF0标志
	TR0 = 1;		//定时器0开始计时
	
	ET0=1;	//总断的分开关
	EA=1;	//中断的总开关
	PT0=0;	//设置中断的优先级别
}

//PWM
unsigned int T0Count;//计数,在0-99之间
unsigned int Compare;//在0-99之间
void Timer0_Routine() interrupt 1{
	TL0 = 0xF5;		//设置定时初值
	TH0 = 0xFF;		//设置定时初值
	
	T0Count++;
	
	if(T0Count<Compare){//10ms 进入一次
		DA=0;
	}else{
		DA=1;
	}
	if(T0Count>100)T0Count=0;//重置
}

void Delay(unsigned int xms)
{
	unsigned char i, j;
	while(xms--)
	{
		i = 2;
		j = 239;
		do
		{
			while (--j);
		} while (--i);
	}
}
void main(){
	unsigned int i=0;
	Compare = 0;
	
	Timer0Init();

	//变亮
	while(1){
		for(i=0,Compare=0;i<200;++i){
			if(i<100)Compare+=1;
			if(i>100)Compare-=1;
			Delay(1);
		}
	}
}

16、红外线(遥控器)

简介:
在这里插入图片描述
在这里插入图片描述

电路:
在这里插入图片描述
(外部中断接口)
在这里插入图片描述

中断寄存器:
在这里插入图片描述

NEC编码:
在这里插入图片描述
遥控器键值:
在这里插入图片描述

在LCD上输出地址码、控制码,音量大小


用于计算时间
Timer0.h
----------------------------------------------------------
#ifndef __TIMER0_H__
#define __TIMER0_H__

void Timer0_Init(void);
//设置从几开始计数
void Timer0_SetCounter(unsigned int Value);
//获取到现在计数到多大了
unsigned int Timer0_GetCounter(void);
//设置是否开始计时
void Timer0_Run(unsigned char Flag);

#endif
----------------------------------------------------------
Timer0.c
----------------------------------------------------------
#include <REGX52.H>

void Timer0_Init(void){
	TMOD &= 0xF0;		//
	TMOD |= 0x01;		//设置计时模式,16位重载
	
	TL0 = 0;				//
	TH0 = 0;				//从头开始计数
	
	TF0 = 0;				//打开通向中断的开关
	TR0 = 0;				//未开始计时
}

//设置从几开始计数
void Timer0_SetCounter(unsigned int Value){
	TH0=Value/256;
	TL0=Value%256;
}

//获取到现在计数到多大了
unsigned int Timer0_GetCounter(void){
	return (TH0<<8)|TL0;
}

//设置是否开始计时
void Timer0_Run(unsigned char Flag){
	TR0=Flag;
}

----------------------------------------------------------

开启外部中断(INT0)
Int0.h
----------------------------------------------------------
#ifndef __INT0_H__
#define __INT0_H__

//开启外部中断0(INT0  P3_2  K3)
void Ini0_Init(void);

#endif
----------------------------------------------------------
Int0.c
----------------------------------------------------------
#include <REGX52.H>

//开启外部中断0
void Ini0_Init(void){
	IT0=1;	//0:低电平触发,1:下降沿触发
	IE0=0;	//中断标志位,可以不进行设置
			//有中断来时,被设置位1,当响应后,会被硬件置0(下降沿触发模式中)
	
	EX0=1;	//中断使能
	EA=1;	//中断使能总开关
	PX0=1;	//选择最高优先级
}

/*
void Int0_Routine(void) interrupt 0{
	Num++;
}
*/
----------------------------------------------------------

红外线(遥控器数据解析)
IR.h
----------------------------------------------------------
#ifndef __IR_H__
#define __IR_H__

void IR_Init(void);
unsigned char IR_GetDataFlag(void);
unsigned char IR_GetRepeatFlag(void);
unsigned char IR_GetAddress(void);
unsigned char IR_GetCommand(void);

#endif
----------------------------------------------------------
IR.c
----------------------------------------------------------
#include <REGX52.H>
#include "Timer0.h"
#include "Int0.h"

unsigned int IR_Time;
unsigned char IR_State;

unsigned char IR_Data[4];
unsigned char IR_pData;

unsigned char IR_DataFlag;
unsigned char IR_RepeatFlag;
unsigned char IR_Address;
unsigned char IR_Command;

unsigned char IR_Temp;

void IR_Init(void){
	Timer0_Init();//开启计时
	Ini0_Init();//开启中断 INT0
}

//中断函数(下降沿进入)
void Int0_Routine(void) interrupt 0{
	/*
		IR_State 0:等待命令
						 1:检测有无 Start的时序
						 2:解析地址码和命令
	*/
	
	/*
		11.0592/12=0.9216
		
		Start
		9ms + 4.5ms = 13500 us
		13500 x 0.9216 = 12442			
		
		Repeat
		9ms + 2.25ms = 11250 us
		11250 x 0.9216 = 10368
	
		logical '0'
		560 + 560 = 1120
			1120 x 0.9216 = 1032
	
		logical '1'
		560 + 1690 = 2250
		2250 x 0.9216 = 2061
	*/

	if(IR_State==0){																							//等待命令
		Timer0_SetCounter(0);
		Timer0_Run(1);	//开启计时
		IR_State=1;
	}
	else if(IR_State==1){																					//检测有无 Start的时序 或 repeat 时序
		IR_Time=Timer0_GetCounter();
		Timer0_SetCounter(0);
		if(IR_Time>12442-500 && IR_Time<12442+500){//Start时序
			IR_State=2;																	
		}
		else if(IR_Time>10368-500 && IR_Time<10368+500){//repeat时序
			IR_RepeatFlag=1;
			Timer0_Run(0);
			IR_State=0;//等待下次命令
		}
		else{//识别不出来
			IR_State=1;
		}
	}
	else if(IR_State==2){																					//解析地址码和命令		
		IR_Time=Timer0_GetCounter();
		Timer0_SetCounter(0);
		if(IR_Time>2061-200 && IR_Time<2061+200){//数据 1的时序
			IR_Data[IR_pData/8]|=(0x01<<(IR_pData&7));
			IR_pData++;
		}
		else if(IR_Time>1032-200 && IR_Time<1032+200){	//数据 0的时序
			IR_Data[IR_pData/8]&=~(0x01<<(IR_pData%8));
			IR_pData++;
		}
		 
		else{//无法识别,回去寻找 Start的时序
			IR_pData=0;
			IR_State=1;
			return;
		}
		if(IR_pData>=32){//获取到了完整的4个字节,进行校验	
			IR_pData=0;
			if((IR_Data[0]==~IR_Data[1] && (IR_Data[2]==~IR_Data[3]))){//校验成功,装填数据
				IR_Address=IR_Data[0];
				IR_Command=IR_Data[2];
				IR_DataFlag=1;
			}
			
			Timer0_Run(0);
			IR_State=0;//回去等待下一条命令
		}
	}
}

unsigned char IR_GetDataFlag(void){//是否是重发命令
	IR_Temp = IR_DataFlag;
	IR_DataFlag=0;
	
	return IR_Temp;
}

unsigned char IR_GetRepeatFlag(void){
	IR_Temp = IR_RepeatFlag;
	IR_RepeatFlag=0;
	
	return IR_Temp;
}

unsigned char IR_GetAddress(void){
	IR_Temp = IR_Address;
	return IR_Temp;
}

unsigned char IR_GetCommand(void){
	IR_Temp = IR_Command;
	return IR_Temp;
}

----------------------------------------------------------


main.c
----------------------------------------------------------
#include <REGX52.H>
#include "LCD1602.h"
#include "IR.h"

unsigned char Address;
unsigned char Command;
unsigned int Num=0;
unsigned char* pData;

void main(){
	
	LCD_Init();
	IR_Init();//开启红外线的检测
	
	LCD_ShowString(1,1,"Adr");
	LCD_ShowString(1,5,"code");
	
	while(1){
		if(IR_GetDataFlag()||IR_GetDataFlag()){
			Address=IR_GetAddress();
			Command=IR_GetCommand();
			
			LCD_ShowHexNum(2,1,Address,2);
			LCD_ShowHexNum(2,5,Command,2);
			
			if(Command==0x15){
				Num--;
			}
			if(Command==0x09){
				Num++;
			}
			LCD_ShowNum(2,10,Num,3);
		}
	}
}

----------------------------------------------------------

四、其他

1、keil uVision5中文乱码的解决办法!

在这里插入图片描述

在这里插入图片描述

2、STC51 IO接口

P1、P2、P3: 准双向输出配置

P0:开漏输出配置
在这里插入图片描述
在这里插入图片描述
看不懂的原图:
在这里插入图片描述

3、电平标准、全双工

在这里插入图片描述

3.5、常见的通信协议:

在这里插入图片描述

4、sfr 和 sbit

在这里插入图片描述

5、关键字 code 的使用

/**
*code 可以让数组中的值存入 flash ,
*节省内存(ROM)空间,
*但其中的值不可改变
*/
unsigned char code map_array[24]={
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x42,0x7E,0x40,0x00,0x7E,0x08,0x14,0x22,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
};

6、BCD码

两位本来可以表示 0-F,但它只使用 0-9 的部分
在这里插入图片描述

7、存储器的分类

在这里插入图片描述

在这里插入图片描述

  • 23
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值