51单片机入门 案例均附源码

采用STC89C52RC单片机 

普中A2开发板

学习视频:江协科技51单片机入门教程

目录

一、创建工程和文件,进行代码编写

二、开始编写代码

三、将代码烧入单片机

四、尝试点亮LED

4.1 点亮LED

4.2 LED闪烁

4.3 LED流水灯

 4.4 流水灯Plus

 五、独立按键控制LED

5.1 独立按键控制LED亮灭

5.2  独立按键控制LED状态

5.3 独立按键控制LED显示二进制

 六、数码管

 6.1 静态数码管显示

6.2 动态数码管显示

七、模块化编程

7.1  综合数码管动态显示和延时函数实例

7.2 LCD1602调试工具 

八、矩阵键盘 

8.1 矩阵键盘

8.2 矩阵键盘电子密码锁 

九、定时器 

 9.1 定时器基本应用

9.2  定时器时钟

十、串口通信

10.1 串口向电脑发送数据

 10.2 电脑通过串口控制LED

 十一、LED点阵屏

11.1 点阵屏显示一个笑脸

11.2 点阵屏显示动画 

 十二、DS1302实时时钟

12.1 DS1302时钟 

12.2 DS1302可调时钟 

 十三、蜂鸣器

13.1 蜂鸣器播放提示音 

 13.2 蜂鸣器播放音乐

十四、AT24C02(I2C总线) 

12.1 AT24C02数据存储

12.2 秒表(定时器扫描按键数码管)

 十五、DS18B20温度传感器

15.1 DS18B20温度读取 

 15.2 DS18B20温度报警器 

十六、LCD1602 

十七、直流电机PWM 

17.1 LED呼吸灯

17.2 直流电机调速 

十八、AD/DA 

18.1 AD模数转换

18.2 DA数模转换

十九、红外遥控(外部中断) 

19.1 红外遥控

19.2 红外遥控电机调速 


一、创建工程和文件,进行代码编写

Project -> New..... -> 选择存放工程的目录 -> 选择型号AT89C52 -> 选择“否”

工程建好之后,新建文件:

二、开始编写代码

需要生成下载的.h文件:

勾选Create HEX File:

三、将代码烧入单片机

STC-ISP软件,将开发板接上电脑。

要先安装串口驱动,这样才能找到串口:

按顺序设置,点击下载以后,打开开发板的开关。

注意单片机型号要看具体的板子。

STC-ISP还能生成代码,比如生成延时代码

四、尝试点亮LED

4.1 点亮LED

#include <REGX52.H>

void main()
{
	//P2=0xFE;  //1111 1110  亮一个
	P2=0x55;	//0101 0101		隔一个亮一个
	while(1)
	{
		
	}
}

4.2 LED闪烁

#include <REGX52.H>
#include <INTRINS.H>

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

	_nop_();
	i = 4;
	j = 205;
	k = 187;
	do
	{
		do
		{
			while (--k);
		} while (--j);
	} while (--i);
}


void main()
{
	
	while(1)
	{
		P2=0xFE;
		Delay500ms();
		P2=0xFF;
		Delay500ms();
	}
}

4.3 LED流水灯

#include <REGX52.H>
#include <INTRINS.H>

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

	_nop_();
	i = 4;
	j = 205;
	k = 187;
	do
	{
		do
		{
			while (--k);
		} while (--j);
	} while (--i);
}

void main()
{
	while(1)
	{
		P2=0xFE;	//1111 1110
		Delay500ms();
		P2=0xFD;	//1111 1101
		Delay500ms();
		P2=0xFB;	//1111 1011
		Delay500ms();
		P2=0xF7;	//1111 0111
		Delay500ms();
		P2=0xEF;	//1110 1111
		Delay500ms();
		P2=0xDF;	//1101 1111
		Delay500ms();
		P2=0xBF;	//1011 1111
		Delay500ms();
		P2=0x7F;	//0111 1111
		Delay500ms();
	}
}

 4.4 流水灯Plus

改善了延时函数,不用每次都生成不一样的延时函数。将它改成可以传参数的,每次只需改变传参就能有不同的延时效果。 

#include <REGX52.H>

void Delay1ms(unsigned xms)		//@12.000MHz
{
	unsigned char i, j;
	while(xms)
	{
		i = 2;
		j = 239;
		do
		{
			while (--j);
		} while (--i);
			xms--;
	}
}

void main()
{
	while(1)
	{
		P2=0xFE;	//1111 1110
		Delay1ms(500);
		P2=0xFD;	//1111 1101
		Delay1ms(500);
		P2=0xFB;	//1111 1011
		Delay1ms(500);
		P2=0xF7;	//1111 0111
		Delay1ms(500);
		P2=0xEF;	//1110 1111
		Delay1ms(500);
		P2=0xDF;	//1101 1111
		Delay1ms(500);
		P2=0xBF;	//1011 1111
		Delay1ms(500);
		P2=0x7F;	//0111 1111
		Delay1ms(500);
	}
}

 五、独立按键控制LED

5.1 独立按键控制LED亮灭

k1按下LED1亮,松开灭

#include <REGX52.H>

void main()
{
//	P2=0xFE;
	while(1)
	{
		if(P3_1==0)
		{
			P2_0=0;
		}
		else
		{
			P2_0=1;
		}
	}
}

5.2  独立按键控制LED状态

按第一次亮,按第二次灭

#include <REGX52.H>

void Delay(unsigned int xms)		//@12.000MHz
{
	unsigned char i, j;
	while(xms)
	{
		i = 2;
		j = 239;
		do
		{
			while (--j);
		} while (--i);
		xms--;
	}
}

void main()
{
	while(1)
	{
		if(P3_1==0)
		{
			Delay(20);	//按键消抖
			while(P3_1==0);
			Delay(20);  //松手检测
			
			P2_0=~P2_0;		//P2_0默认为1(不亮)
		}
	}
}

5.3 独立按键控制LED显示二进制

#include <REGX52.H>

void Delay(unsigned int xms)		//@12.000MHz
{
	unsigned char i, j;
	while(xms--)
	{
		i = 2;
		j = 239;
		do
		{
			while (--j);
		} while (--i);
	}
}

void main()
{
	unsigned char LEDNum=0;
	while(1)
	{
		if(P3_1==0)
		{
			Delay(20);
			while(P3_1==0);
			Delay(20);
			
			LEDNum++;
			P2=~LEDNum;
		}
	}
}

5.4 独立按键控制LED移位

按K1右移,按K2左移。

#include <REGX52.H>

unsigned char LEDNum=0;

void Delay(unsigned int xms)		//@12.000MHz
{
	unsigned char i, j;
	while(xms--)
	{
		i = 2;
		j = 239;
		do
		{
			while (--j);
		} while (--i);
	}
}

void main()
{
	P2=~0x01;
	while(1)
	{
		if(P3_1==0)
		{
			Delay(20);
			while(P3_1==0);
			Delay(20);
			
			LEDNum++;
			if(LEDNum>=8)
				LEDNum=0;
			P2=~(0x01<<LEDNum);
		}
		if(P3_0==0)
		{
			Delay(20);
			while(P3_0==0);
			Delay(20);
			
			if(LEDNum==0)
				LEDNum=7;
			else
				LEDNum--;
			P2=~(0x01<<LEDNum);
		}
	}
}

 六、数码管

共阴极数码管的段码表:

012345678
0x3f0x060x5B0x4F0x660x6D0x7D0x070x7F
9ABCDEF
0x6F0x770x7C0x390x5E0x790x710X00

 6.1 静态数码管显示

#include <REGX52.H>

unsigned char NixieTable[]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F,0x77,0x7C,0x39,0x5E,0x79,0x71,0X00};
//0-F 
void Nixie(unsigned char Location,Number)
{
	switch(Location)
	{
		case 1:P2_4=1;P2_3=1;P2_2=1;break;
		case 2:P2_4=1;P2_3=1;P2_2=0;break;
		case 3:P2_4=1;P2_3=0;P2_2=1;break;
		case 4:P2_4=1;P2_3=0;P2_2=0;break;
		case 5:P2_4=0;P2_3=1;P2_2=1;break;
		case 6:P2_4=0;P2_3=1;P2_2=0;break;
		case 7:P2_4=0;P2_3=0;P2_2=1;break;
		case 8:P2_4=0;P2_3=0;P2_2=0;break;
	}
	P0=NixieTable[Number];
}
void main()
{
	Nixie(4,8); 
	while(1)
	{
		
	}
}

6.2 动态数码管显示

#include <REGX52.H>

unsigned char NixieTable[]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F,0x77,0x7C,0x39,0x5E,0x79,0x71,0X00};
//0-F 

void Delay(unsigned int xms)		//@12.000MHz
{
	unsigned char i, j;
	while(xms--)
	{
		i = 2;
		j = 239;
		do
		{
			while (--j);
		} while (--i);
	}
}

void Nixie(unsigned char Location,Number)
{
	switch(Location)
	{
		case 1:P2_4=1;P2_3=1;P2_2=1;break;
		case 2:P2_4=1;P2_3=1;P2_2=0;break;
		case 3:P2_4=1;P2_3=0;P2_2=1;break;
		case 4:P2_4=1;P2_3=0;P2_2=0;break;
		case 5:P2_4=0;P2_3=1;P2_2=1;break;
		case 6:P2_4=0;P2_3=1;P2_2=0;break;
		case 7:P2_4=0;P2_3=0;P2_2=1;break;
		case 8:P2_4=0;P2_3=0;P2_2=0;break;
	}
	P0=NixieTable[Number];
	Delay(1);	//消影
	P0=0x00;
}
void main()
{
	Nixie(4,8); 
	while(1)
	{
		Nixie(1,1);
		//Delay(20);
		Nixie(2,2);
		//Delay(20);
		Nixie(3,3);
		//Delay(20);
	}
}

七、模块化编程

7.1  综合数码管动态显示和延时函数实例

 用到五个文件:Nixie.h,Delay.h,Nixie.c,Delay.c,main.c。

//Nixie.h
#ifndef __NIXIE_H__
#define __NIXIE_H__

void Nixie(unsigned char Location,Number);

#endif


//Nixie.c
#include <REGX52.H>
#include "Delay.h"
unsigned char NixieTable[]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F,0x77,0x7C,0x39,0x5E,0x79,0x71,0X00};

void Nixie(unsigned char Location,Number)
{
	switch(Location)
	{
		case 1:P2_4=1;P2_3=1;P2_2=1;break;
		case 2:P2_4=1;P2_3=1;P2_2=0;break;
		case 3:P2_4=1;P2_3=0;P2_2=1;break;
		case 4:P2_4=1;P2_3=0;P2_2=0;break;
		case 5:P2_4=0;P2_3=1;P2_2=1;break;
		case 6:P2_4=0;P2_3=1;P2_2=0;break;
		case 7:P2_4=0;P2_3=0;P2_2=1;break;
		case 8:P2_4=0;P2_3=0;P2_2=0;break;
	}
	P0=NixieTable[Number];
	Delay(1);	
	P0=0x00;
}
//Delay.h
#ifndef __DELAY_H__
#define __DELAY_H__

void Delay(unsigned int xms);

#endif



//Delay.c
void Delay(unsigned int xms)		//@12.000MHz
{
	unsigned char i, j;
	while(xms--)
	{
		i = 2;
		j = 239;
		do
		{
			while (--j);
		} while (--i);
	}
}
//main.c
#include <REGX52.H>
#include "Delay.h"
#include "Nixie.h"

void main()
{
	while(1)
	{
		Nixie(1,1);
		Nixie(2,2);
		Nixie(3,3);
	}
}

7.2 LCD1602调试工具 

先提前准备LCD1602的代码文件,只需要学会使用函数就行了。

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

再将上节模块化的Delay.h导入工程 ,实现一个LCD602显示每一秒多加1的结果。(计数)

#include <REGX52.H>
#include "LCD1602.h"
#include "Delay.h"
int Result=0;
	
void main()
{
	LCD_Init();
//	LCD_ShowChar(1,1,'A');
//	LCD_ShowString(1,3,"Hello");
//	LCD_ShowNum(1,9,123,3);
//	LCD_ShowSignedNum(1,13,-66,2);
//	LCD_ShowHexNum(2,1,0xA8,2);
//	LCD_ShowBinNum(2,4,0xAA,8);
	
	while(1)
	{
		Result++;
		Delay(1000);
		LCD_ShowNum(1,1,Result,3);
	}
}

八、矩阵键盘 

8.1 矩阵键盘

实现:按下矩阵键盘,在LCD1602上显示数字。

//MatrixKey.h
#ifndef __MATRIXKEY_H__
#define __MATRIXKEY_H__

unsigned char MatrixKey();

#endif


//MatrixKey.c
#include <REGX52.H>
#include "Delay.H"

/**
  * @brief 矩阵键盘读取按键键码
  * @param 无
  * @retval KeyNumber 按下按键的键码值
		如果按键按下不放,程序会停留在此函数,松手的一瞬间,返回按键键码,没有按键按下时,返回0
  */
unsigned char MatrixKey()
{
	unsigned char KeyNumber=0;
	
	P1=0xFF;
	P1_3=0;
	if(P1_7==0){Delay(20);while(P1_7==0);Delay(20);KeyNumber=1;}
	if(P1_6==0){Delay(20);while(P1_6==0);Delay(20);KeyNumber=5;}
	if(P1_5==0){Delay(20);while(P1_5==0);Delay(20);KeyNumber=9;}
	if(P1_4==0){Delay(20);while(P1_4==0);Delay(20);KeyNumber=13;}

	P1=0xFF;
	P1_2=0;
	if(P1_7==0){Delay(20);while(P1_7==0);Delay(20);KeyNumber=2;}
	if(P1_6==0){Delay(20);while(P1_6==0);Delay(20);KeyNumber=6;}
	if(P1_5==0){Delay(20);while(P1_5==0);Delay(20);KeyNumber=10;}
	if(P1_4==0){Delay(20);while(P1_4==0);Delay(20);KeyNumber=14;}
	
	P1=0xFF;
	P1_1=0;
	if(P1_7==0){Delay(20);while(P1_7==0);Delay(20);KeyNumber=3;}
	if(P1_6==0){Delay(20);while(P1_6==0);Delay(20);KeyNumber=7;}
	if(P1_5==0){Delay(20);while(P1_5==0);Delay(20);KeyNumber=11;}
	if(P1_4==0){Delay(20);while(P1_4==0);Delay(20);KeyNumber=15;}
	
	P1=0xFF;
	P1_0=0;
	if(P1_7==0){Delay(20);while(P1_7==0);Delay(20);KeyNumber=4;}
	if(P1_6==0){Delay(20);while(P1_6==0);Delay(20);KeyNumber=8;}
	if(P1_5==0){Delay(20);while(P1_5==0);Delay(20);KeyNumber=12;}
	if(P1_4==0){Delay(20);while(P1_4==0);Delay(20);KeyNumber=16;}
	
	return KeyNumber;
}
//main.c
#include "LCD1602.h"
#include "MatrixKey.h"

unsigned char KeyNum;

void main()
{
	LCD_Init();
	LCD_ShowString(1,1,"MatrixKey:");
	while(1)
	{
		KeyNum=MatrixKey();
		if(KeyNum)
		{
			LCD_ShowNum(2,1,KeyNum,2);
		}
	}
}

8.2 矩阵键盘电子密码锁 

 要将所引用的文件全部导入工程,运行才不会出错。

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

unsigned char KeyNum;
unsigned int Password,count;

void main()
{
	LCD_Init();
	LCD_ShowString(1,1,"Password:");
	while(1)
	{
		KeyNum=MatrixKey();
		if(KeyNum)
		{
			if(KeyNum<=10)	//如果s1~s10按键按下,输入密码
			{
				if(count<4)	//如果输入次数小于4
				{
					Password*=10;				//密码左移一位
					Password+=KeyNum%10;		//获取一位密码
					count++;					//计次加一
				}
				LCD_ShowNum(2,1,Password,4);	//更新显示
			}
			
			if(KeyNum==11)	//如果s11按键按下,确认
			{
				if(Password==2345)				//如果密码正确
				{
					LCD_ShowString(1,14,"OK ");	//显示OK
					Password=0;					//密码清零
					count=0;					//计次清零
					LCD_ShowNum(2,1,Password,4);	//更新显示

				}
				else							//如果密码错误
				{
					LCD_ShowString(1,14,"ERR");	//显示ERR
					Password=0;					//密码清零
					count=0;					//计次清零
					LCD_ShowNum(2,1,Password,4);	//更新显示
				}
			}
			
			if(KeyNum==12)	//如果s12按键按下,取消
			{
				Password=0;					//密码清零
				count=0;					//计次清零
				LCD_ShowString(1,14,"   ");	
				LCD_ShowNum(2,1,Password,4);	//更新显示
			}
		}
	}
}	

九、定时器 

 9.1 定时器基本应用

实现:使用定时器、中断,按键控制LED流水灯左右移。

需要以下文件:

//Timer0.h
#ifndef __TIMER0_H__
#define __TIMER0_H__

void Timer0Init();

#endif


//Timer0.c
#include <REGX52.H>
/**
  * @brief 定时器0初始化,1毫秒@12.000MHz
  * @param 无
  * @retval 无
  */
void Timer0Init()
{
//	TMOD=0x01;	//0000 0001
	TMOD&=0xF0;		//把TMOD的低四位清零,高四位保持不变
	TMOD|=0x01;		//把TMOD的最低位置1,高四位保持不变
	TF0=0;
	TR0=1;
	TH0=0xFC;		//设置定时初值
	TL0=0x18;		//设置定时初值
	ET0=1;
	EA=1;
	PT0=0;
}

/*定时器中断函数模板,一般放在main.c
void Timer0_Routine() interrupt 1
{
	static unsigned int T0Count;
	TH0=0xFC;		//设置定时初值
	TL0=0x18;		//设置定时初值
	T0Count++;
	if(T0Count>=1000)
	{
		T0Count=0;
		
	}
}
*/
//Key.h
#ifndef __KEY_H__
#define __KEY_H__

unsigned char Key();

#endif



//Key.c
#include <REGX52.H>
#include "Delay.h"
/**
  * @brief 获取独立按键键码
  * @param 无 
  * @retval 按下按键的键码,范围:0~4,无按键按下时返回值为0
  */
unsigned char Key()
{
	unsigned char KeyNumber=0;
	
	if(P3_1==0){Delay(20);while(P3_1==0);Delay(20);KeyNumber=1;}
	if(P3_0==0){Delay(20);while(P3_0==0);Delay(20);KeyNumber=2;}
	if(P3_2==0){Delay(20);while(P3_2==0);Delay(20);KeyNumber=3;}
	if(P3_3==0){Delay(20);while(P3_3==0);Delay(20);KeyNumber=4;}
		
	return KeyNumber;
}
//main.c
#include <REGX52.H>
#include "Timer0.h"
#include "Key.h"
#include <INTRINS.H>

unsigned char KeyNum,LEDMode;

void main()
{
	P2=0xFE;
	Timer0Init();
	while(1)
	{
		KeyNum=Key();
		if(KeyNum)
		{
			if(KeyNum==1)
			{
				LEDMode++;
				if(LEDMode>=2) LEDMode=0;
			}
		}
	}
}

void Timer0_Routine() interrupt 1
{
	static unsigned int T0Count;
	TH0=0xFC;		//设置定时初值
	TL0=0x18;		//设置定时初值
	T0Count++;
	if(T0Count>=500)
	{
		T0Count=0;
		if(LEDMode==0)		//左移
			P2=_crol_(P2,1);
		if(LEDMode==1)		//右移
			P2=_cror_(P2,1);
	}
}

9.2  定时器时钟

显示:小时:分钟:秒钟 

使用以下文件,都是上文使用过的模块化函数,不再赘述,只贴上main.c:

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

unsigned char Sec,Min,Hour;

void main()
{
	LCD_Init();
	Timer0Init();
	
	LCD_ShowString(1,1,"Clock:");
	LCD_ShowString(2,1,"  :  :  ");
	while(1)
	{
		LCD_ShowNum(2,1,Hour,2);
		LCD_ShowNum(2,4,Min,2);
		LCD_ShowNum(2,7,Sec,2);
	}

}

void Timer0_Routine() interrupt 1
{
	static unsigned int T0Count;
	TH0=0xFC;		//设置定时初值
	TL0=0x18;		//设置定时初值
	T0Count++;
	if(T0Count>=1000)
	{
		T0Count=0;
		Sec++;
		if(Sec>=60)
		{
			Sec=0;
			Min++;
			if(Min>=60)
			{
				Min=0;
				Hour++;
				if(Hour>=24)
					{
						Hour=0;	
					}
			}
		}
	}
}

十、串口通信

常见通信接口比较
名称引脚定义通信方式特点
UARTTXD、RXD全双工、异步点对点通信
I2CSCL、SDA半双工、同步可挂载多个设备
SPISCLK、MOSI、MISO、CS全双工、同步可挂载多个设备
1-WireDQ半双工、异步可挂载多个设备

10.1 串口向电脑发送数据

实现:每隔一秒,串口向电脑发送一个字节的递增的数据。

实现效果:

准备以下文件:

//UART.h
#ifndef __UART_H__
#define __UART_H__

UART_Init();
UART_SendByte(unsigned char Byte);

#endif



//UART.c
#include <REGX52.H>

/**
  * @brief 串口初始化	4800bps@12.000MHz
  * @param 无
  * @retval 无
  */
void UART_Init()	
{
	SCON=0x40;
	PCON |= 0x80;	
	TMOD&=0x0F;		//设置定时器模式
	TMOD|=0x20;		//设置定时器模式
	TH1=0xF4;		//设置定时初值
	TL1=0xF4;		//设置定时初值
	ET1 = 0;		//禁止定时器1中断
	TR1 = 1;		//启动定时器1
}

/**
  * @brief 串口发送一个字节数据
  * @param Byte 要发送的一个字节数据
  * @retval 无
  */
void UART_SendByte(unsigned char Byte)
{
	SBUF=Byte;
	while(TI==0);
	TI=0;
}
//main.c
#include <REGX52.H>
#include "Delay.h"
#include "UART.h"

unsigned char Sec;

void main()
{
	UART_Init();
	while(1)
	{
		UART_SendByte(Sec);
		Sec++;
		Delay(100);
	}
}

 10.2 电脑通过串口控制LED

用到以下文件:

//UART.h
#ifndef __UART_H__
#define __UART_H__

UART_Init();
UART_SendByte(unsigned char Byte);

#endif



//UART.c
#include <REGX52.H>

/**
  * @brief 串口初始化	4800bps@12.000MHz
  * @param 无
  * @retval 无
  */
void UART_Init()	
{
	SCON=0x50;
	PCON |= 0x80;	
	TMOD&=0x0F;		//设置定时器模式
	TMOD|=0x20;		//设置定时器模式
	TH1=0xF4;		//设置定时初值
	TL1=0xF4;		//设置定时初值
	ET1 = 0;		//禁止定时器1中断
	TR1 = 1;		//启动定时器1
	EA=1;
	ES=1;
}

/**
  * @brief 串口发送一个字节数据
  * @param Byte 要发送的一个字节数据
  * @retval 无
  */
void UART_SendByte(unsigned char Byte)
{
	SBUF=Byte;
	while(TI==0);
	TI=0;
}

/*串口中断函数模板
void UART_Routine() interrupt 4
{
	if(RI==1)		//如果是接收中断
	{
		
		RI=0;
	}
}
*/
//main.c
#include <REGX52.H>
#include "Delay.h"
#include "UART.h"

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

void UART_Routine() interrupt 4
{
	if(RI==1)		//如果是接收中断
	{
		P2=~SBUF;	//0才亮,所以需要取反,使发送的数据和LED对应
		UART_SendByte(SBUF);
		RI=0;
	}
}

 十一、LED点阵屏

要先将点阵屏上的J24跳帽插在GND和OE上(左边两个) 。

点阵屏试验结束别忘了插回右边两个。

11.1 点阵屏显示一个笑脸

要用到的文件:

//main.c
#include <REGX52.H>
#include "Delay.h"

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

#define MATRIX_LED_PORT P0

/**
  * @brief 74HC595写入一个字节
  * @param Byte 要写入的字节
  * @retval 无
  */
void _74HC595_WriteByte(unsigned char Byte)
{
	unsigned char i;
	for(i=0;i<8;i++)
	{
		SER=Byte&(0x80>>i);
		SCK=1;
		SCK=0;
	}
	RCK=1;
	RCK=0;
}

/**
  * @brief LED点阵屏显示一列数据
  * @param Column 要选择的列,范围:0~7,0在最左边
  * @param Data 选择列显示的数据,高位在上,1为亮,0为灭
  * @retval 无
  */
void MatrixLED_ShowColumn(unsigned char Column,Data)
{
	_74HC595_WriteByte(Data);
	MATRIX_LED_PORT=~(0x80>>Column);		//段选
	Delay(1);		
	MATRIX_LED_PORT=0xFF;	//位清零
}

void main()
{	
	SCK=0;
	RCK=0;
	while(1)
	{
		MatrixLED_ShowColumn(0,0x3C);
		MatrixLED_ShowColumn(1,0x42);
		MatrixLED_ShowColumn(2,0xA9);
		MatrixLED_ShowColumn(3,0x85);
		MatrixLED_ShowColumn(4,0x85);
		MatrixLED_ShowColumn(5,0xA9);
		MatrixLED_ShowColumn(6,0x42);
		MatrixLED_ShowColumn(7,0x3C);
	}
}

11.2 点阵屏显示动画 

实现:“Hello” 的流动显示。

使用文字取模软件提取点阵

使用以下模块化文件:

//MatrixLED.h
#ifndef __MATRIX_LED_H__
#define __MATRIX_LED_H__

void MatricLED_Init();
void MatrixLED_ShowColumn(unsigned char Column,Data);

#endif




//MatrixLED.c
#include <REGX52.H>
#include "Delay.h"

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

#define MATRIX_LED_PORT P0

/**
  * @brief 74HC595写入一个字节
  * @param Byte 要写入的字节
  * @retval 无
  */
void _74HC595_WriteByte(unsigned char Byte)
{
	unsigned char i;
	for(i=0;i<8;i++)
	{
		SER=Byte&(0x80>>i);
		SCK=1;
		SCK=0;
	}
	RCK=1;
	RCK=0;
}

/**
  * @brief 点阵屏初始化
  * @param 无
  * @retval 无
  */
void MatricLED_Init()
{
	SCK=0;
	RCK=0;
}

/**
  * @brief LED点阵屏显示一列数据
  * @param Column 要选择的列,范围:0~7,0在最左边
  * @param Data 选择列显示的数据,高位在上,1为亮,0为灭
  * @retval 无
  */
void MatrixLED_ShowColumn(unsigned char Column,Data)
{
	_74HC595_WriteByte(Data);
	MATRIX_LED_PORT=~(0x80>>Column);		//段选
	Delay(1);		
	MATRIX_LED_PORT=0xFF;	//位清零
}

code:定义数组时使用,这样放在Flash里面运行,不用占用RAM的空间。 但是这样不能够修改数组内容。

//main.c
#include <REGX52.H>
#include "Delay.h"
#include "MatrixLED.h"

unsigned char code Animation[]={					//点阵为32*8的,为了流畅前后加8个0
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0xFF,0x08,0x08,0x08,0xFF,0x00,
	0x0E,0x15,0x15,0x15,0x08,0x00,0x7E,0x01,
	0x01,0x02,0x00,0x7E,0x01,0x01,0x02,0x00,
	0x0E,0x11,0x11,0x11,0x0E,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
};

void main()
{	
	unsigned char i,Offset=3,Count=0;
	MatricLED_Init();
	while(1)
	{
		for(i=0;i<8;i++)
		{
			MatrixLED_ShowColumn(i,Animation[i+Offset]);
		}
		Count++;
		if(Count>10)
		{
			Count=0;
			Offset++;
			if(Offset>32)   //防溢出
			{
				Offset=0;
			}
		}
	}
}

 十二、DS1302实时时钟

BCD码转十进制:DEC=BCD/16*10+BCD%16;(2位BCD)

十进制码转BCD:BCD=DEC/10*16+DEC%10;(2位BCD)

12.1 DS1302时钟 

用到以下文件:

//DS1302.h
#ifndef __DS1302_H__
#define __DS1302_H__

extern unsigned char DS1302_Time[];

void DS1302_Init(void);
void DS1302_WriteByte(unsigned char Command,Data);
unsigned char DS1302_ReadByte(unsigned char Command);
void DS1302_SetTime(void);
void DS1302_ReadTime(void);

#endif
//DS1302.c
#include <REGX52.H>

//引脚定义
sbit DS1302_SCLK=P3^6;
sbit DS1302_IO=P3^4;
sbit DS1302_CE=P3^5;

//寄存器写入地址/指令定义
#define DS1302_SECOND 	0x80
#define DS1302_MINUTE 	0x82
#define DS1302_HOUR		0x84
#define DS1302_DATE 	0x86
#define DS1302_MONTH 	0x88
#define DS1302_DAY 		0x8A
#define DS1302_YEAR 	0x8C
#define DS1302_WP 		0x8E		

//时间数组,索引0~6分别为年、月、日、时、分、秒、星期
unsigned char DS1302_Time[]={24,9,26,15,59,55,5};

/**
  * @brief  DS1302初始化
  * @param  无
  * @retval 无
  */
void DS1302_Init()
{
	DS1302_CE=0;
	DS1302_SCLK=0;
}

/**
  * @brief  DS1302写一个字节
  * @param  Command 命令字/地址
  * @param  Data 要写入的数据
  * @retval 无
  */
void DS1302_WriteByte(unsigned char Command,Data)
{
	unsigned char i;
	DS1302_CE=1;
	for(i=0;i<8;i++)
	{
		DS1302_IO=Command&(0x01<<i);
		DS1302_SCLK=1;
		DS1302_SCLK=0;
	}
	for(i=0;i<8;i++)
	{
		DS1302_IO=Data&(0x01<<i);
		DS1302_SCLK=1;
		DS1302_SCLK=0;
	}
	DS1302_CE=0;
}

/**
  * @brief  DS1302读一个字节
  * @param  Command 命令字/地址
  * @retval 读出的数据
  */
unsigned char DS1302_ReadByte(unsigned char Command)
{
	unsigned char i,Data=0x00;
	Command|=0X01;	//将指令转换为读指令
	DS1302_CE=1;
	for(i=0;i<8;i++)
	{
		DS1302_IO=Command&(0x01<<i);
		DS1302_SCLK=0;
		DS1302_SCLK=1;
	}
	
	for(i=0;i<8;i++)
	{
		DS1302_SCLK=1;		//重复置1,过滤一个脉冲
		DS1302_SCLK=0;
		if(DS1302_IO){Data|=(0x01<<i);}
	}
	DS1302_IO=0;	//读取后将IO设置为0,否则读出的数据会出错
	DS1302_CE=0;
	return Data;
}

/**
  * @brief  DS1302设置时间,调用之后,DS1302_Time数组的数字会被设置到DS1302中
  * @param  无
  * @retval 无
  */
void DS1302_SetTime(void)
{
	DS1302_WriteByte(DS1302_WP,0x00);		//解除芯片写入保护
	DS1302_WriteByte(DS1302_YEAR,DS1302_Time[0]/10*16+DS1302_Time[0]%10);
	DS1302_WriteByte(DS1302_MONTH,DS1302_Time[1]/10*16+DS1302_Time[1]%10);
	DS1302_WriteByte(DS1302_DATE,DS1302_Time[2]/10*16+DS1302_Time[2]%10);
	DS1302_WriteByte(DS1302_HOUR,DS1302_Time[3]/10*16+DS1302_Time[3]%10);
	DS1302_WriteByte(DS1302_MINUTE,DS1302_Time[4]/10*16+DS1302_Time[4]%10);
	DS1302_WriteByte(DS1302_SECOND,DS1302_Time[5]/10*16+DS1302_Time[5]%10);
	DS1302_WriteByte(DS1302_DAY,DS1302_Time[6]/10*16+DS1302_Time[6]%10);
	DS1302_WriteByte(DS1302_WP,0x80);

}

/**
  * @brief  DS1302读取时间,调用之后,DS1302中的数据会被读取到DS1302_Time数组中
  * @param  无
  * @retval 无
  */
void DS1302_ReadTime(void)
{
	unsigned char Temp;
	Temp=DS1302_ReadByte(DS1302_YEAR);
	DS1302_Time[0]=Temp/16*10+Temp%16;	//BCD码转十进制后读取
	Temp=DS1302_ReadByte(DS1302_MONTH);
	DS1302_Time[1]=Temp/16*10+Temp%16;
	Temp=DS1302_ReadByte(DS1302_DATE);
	DS1302_Time[2]=Temp/16*10+Temp%16;
	Temp=DS1302_ReadByte(DS1302_HOUR);
	DS1302_Time[3]=Temp/16*10+Temp%16;
	Temp=DS1302_ReadByte(DS1302_MINUTE);
	DS1302_Time[4]=Temp/16*10+Temp%16;
	Temp=DS1302_ReadByte(DS1302_SECOND);
	DS1302_Time[5]=Temp/16*10+Temp%16;
	Temp=DS1302_ReadByte(DS1302_DAY);
	DS1302_Time[6]=Temp/16*10+Temp%16;
}
//main.c
#include <REGX52.H>
#include "LCD1602.h"
#include "DS1302.h"

void main()
{
	LCD_Init();
	DS1302_Init();
	LCD_ShowString(1,1,"  -  -  ");	//BCD码转十进制后读取
	LCD_ShowString(2,1,"  :  :  ");	
	
	DS1302_SetTime();	//设置时间
	
	while(1)
	{
		DS1302_ReadTime();	//读取时间
		LCD_ShowNum(1,1,DS1302_Time[0],2);
		LCD_ShowNum(1,4,DS1302_Time[1],2);
		LCD_ShowNum(1,7,DS1302_Time[2],2);
		LCD_ShowNum(2,1,DS1302_Time[3],2);
		LCD_ShowNum(2,4,DS1302_Time[4],2);
		LCD_ShowNum(2,7,DS1302_Time[5],2);
	}
}

12.2 DS1302可调时钟 

 用到以下文件:

//DS1302.h
#ifndef __DS1302_H__
#define __DS1302_H__

//外部可调用时间数组,索引0~6分别为年、月、日、时、分、秒、星期,设置为有符号的便于<0的判断
extern char DS1302_Time[];

void DS1302_Init(void);
void DS1302_WriteByte(unsigned char Command,Data);
unsigned char DS1302_ReadByte(unsigned char Command);
void DS1302_SetTime(void);
void DS1302_ReadTime(void);

#endif
//DS1302.c
#include <REGX52.H>

//引脚定义
sbit DS1302_SCLK=P3^6;
sbit DS1302_IO=P3^4;
sbit DS1302_CE=P3^5;

//寄存器写入地址/指令定义
#define DS1302_SECOND 	0x80
#define DS1302_MINUTE 	0x82
#define DS1302_HOUR		0x84
#define DS1302_DATE 	0x86
#define DS1302_MONTH 	0x88
#define DS1302_DAY 		0x8A
#define DS1302_YEAR 	0x8C
#define DS1302_WP 		0x8E		

//时间数组,索引0~6分别为年、月、日、时、分、秒、星期
char DS1302_Time[]={24,9,26,15,59,55,5};

/**
  * @brief  DS1302初始化
  * @param  无
  * @retval 无
  */
void DS1302_Init()
{
	DS1302_CE=0;
	DS1302_SCLK=0;
}

/**
  * @brief  DS1302写一个字节
  * @param  Command 命令字/地址
  * @param  Data 要写入的数据
  * @retval 无
  */
void DS1302_WriteByte(unsigned char Command,Data)
{
	unsigned char i;
	DS1302_CE=1;
	for(i=0;i<8;i++)
	{
		DS1302_IO=Command&(0x01<<i);
		DS1302_SCLK=1;
		DS1302_SCLK=0;
	}
	for(i=0;i<8;i++)
	{
		DS1302_IO=Data&(0x01<<i);
		DS1302_SCLK=1;
		DS1302_SCLK=0;
	}
	DS1302_CE=0;
}

/**
  * @brief  DS1302读一个字节
  * @param  Command 命令字/地址
  * @retval 读出的数据
  */
unsigned char DS1302_ReadByte(unsigned char Command)
{
	unsigned char i,Data=0x00;
	Command|=0X01;	//将指令转换为读指令
	DS1302_CE=1;
	for(i=0;i<8;i++)
	{
		DS1302_IO=Command&(0x01<<i);
		DS1302_SCLK=0;
		DS1302_SCLK=1;
	}
	for(i=0;i<8;i++)
	{
		DS1302_SCLK=1;		//重复置1,过滤一个脉冲
		DS1302_SCLK=0;
		if(DS1302_IO){Data|=(0x01<<i);}
	}
	DS1302_IO=0;	//读取后将IO设置为0,否则读出的数据会出错
	DS1302_CE=0;
	return Data;
}

/**
  * @brief  DS1302设置时间,调用之后,DS1302_Time数组的数字会被设置到DS1302中
  * @param  无
  * @retval 无
  */
void DS1302_SetTime(void)
{
	DS1302_WriteByte(DS1302_WP,0x00);		//解除芯片写入保护
	DS1302_WriteByte(DS1302_YEAR,DS1302_Time[0]/10*16+DS1302_Time[0]%10);
	DS1302_WriteByte(DS1302_MONTH,DS1302_Time[1]/10*16+DS1302_Time[1]%10);
	DS1302_WriteByte(DS1302_DATE,DS1302_Time[2]/10*16+DS1302_Time[2]%10);
	DS1302_WriteByte(DS1302_HOUR,DS1302_Time[3]/10*16+DS1302_Time[3]%10);
	DS1302_WriteByte(DS1302_MINUTE,DS1302_Time[4]/10*16+DS1302_Time[4]%10);
	DS1302_WriteByte(DS1302_SECOND,DS1302_Time[5]/10*16+DS1302_Time[5]%10);
	DS1302_WriteByte(DS1302_DAY,DS1302_Time[6]/10*16+DS1302_Time[6]%10);
	DS1302_WriteByte(DS1302_WP,0x80);

}

/**
  * @brief  DS1302读取时间,调用之后,DS1302中的数据会被读取到DS1302_Time数组中
  * @param  无
  * @retval 无
  */
void DS1302_ReadTime(void)
{
	unsigned char Temp;
	Temp=DS1302_ReadByte(DS1302_YEAR);
	DS1302_Time[0]=Temp/16*10+Temp%16;	//BCD码转十进制后读取
	Temp=DS1302_ReadByte(DS1302_MONTH);
	DS1302_Time[1]=Temp/16*10+Temp%16;
	Temp=DS1302_ReadByte(DS1302_DATE);
	DS1302_Time[2]=Temp/16*10+Temp%16;
	Temp=DS1302_ReadByte(DS1302_HOUR);
	DS1302_Time[3]=Temp/16*10+Temp%16;
	Temp=DS1302_ReadByte(DS1302_MINUTE);
	DS1302_Time[4]=Temp/16*10+Temp%16;
	Temp=DS1302_ReadByte(DS1302_SECOND);
	DS1302_Time[5]=Temp/16*10+Temp%16;
	Temp=DS1302_ReadByte(DS1302_DAY);
	DS1302_Time[6]=Temp/16*10+Temp%16;
}
//main.c
#include <REGX52.H>
#include "LCD1602.h"
#include "DS1302.h"
#include "Key.h"
#include "Timer0.h"

unsigned char KeyNum,MODE,TimeSetSelect,TimeSetFlashFlag;

void TimeShow(void)//时间显示功能
{
	DS1302_ReadTime();	//读取时间
	LCD_ShowNum(1,1,DS1302_Time[0],2);//显示年
	LCD_ShowNum(1,4,DS1302_Time[1],2);//显示月
	LCD_ShowNum(1,7,DS1302_Time[2],2);//显示日
	LCD_ShowNum(2,1,DS1302_Time[3],2);//显示时
	LCD_ShowNum(2,4,DS1302_Time[4],2);//显示分
	LCD_ShowNum(2,7,DS1302_Time[5],2);//显示秒
}

void TimeSet(void)//时间设置功能
{
	if(KeyNum==2)//按键2按下
	{
		TimeSetSelect++;	//设置选择位加1
		TimeSetSelect%=6;	//越界清零
	}
	if(KeyNum==3)//按键3按下
	{
		DS1302_Time[TimeSetSelect]++;//时间设置位数值加1
		if(DS1302_Time[0]>99){DS1302_Time[0]=0;}//年越界判断
		if(DS1302_Time[1]>12){DS1302_Time[1]=1;}//月越界判断
		if(DS1302_Time[1]==1 || DS1302_Time[1]==3 || DS1302_Time[1]==5 || DS1302_Time[1]==7 ||
			DS1302_Time[1]==8 || DS1302_Time[1]==10 || DS1302_Time[1]==12)	//日越界判断
		{
			if(DS1302_Time[2]>31){DS1302_Time[2]=1;}//大月
		}
		else if(DS1302_Time[1]==4 || DS1302_Time[1]==6 || DS1302_Time[1]==9 || 
			DS1302_Time[1]==11)
		{
			if(DS1302_Time[2]>30){DS1302_Time[2]=1;}//小月
		}
		else if(DS1302_Time[1]==2)
		{
			if(DS1302_Time[0]%4==0)
			{
				if(DS1302_Time[2]>29){DS1302_Time[2]=1;}//闰年2月
			}
			else
			{
				if(DS1302_Time[2]>28){DS1302_Time[2]=1;}//平年2月
			}
		}
		if(DS1302_Time[3]>24){DS1302_Time[3]=0;}//时越界判断
		if(DS1302_Time[4]>59){DS1302_Time[4]=0;}//分越界判断
		if(DS1302_Time[5]>59){DS1302_Time[5]=0;}//秒越界判断
	}
	if(KeyNum==4)//按键4按下
	{
		DS1302_Time[TimeSetSelect]--;//时间设置位数值减1
		if(DS1302_Time[0]<0){DS1302_Time[0]=99;}//年越界判断
		if(DS1302_Time[1]<1){DS1302_Time[1]=12;}//月越界判断
		if(DS1302_Time[1]==1 || DS1302_Time[1]==3 || DS1302_Time[1]==5 || DS1302_Time[1]==7 ||
			DS1302_Time[1]==8 || DS1302_Time[1]==10 || DS1302_Time[1]==12)//日越界判断
		{
			if(DS1302_Time[2]<1){DS1302_Time[2]=31;}//大月
			if(DS1302_Time[2]>31){DS1302_Time[2]=1;}
		}
		else if(DS1302_Time[1]==4 || DS1302_Time[1]==6 || DS1302_Time[1]==9 || 
			DS1302_Time[1]==11)
		{
			if(DS1302_Time[2]<1){DS1302_Time[2]=30;}//小月
			if(DS1302_Time[2]>30){DS1302_Time[2]=1;}
		}
		else if(DS1302_Time[1]==2)
		{
			if(DS1302_Time[0]%4==0)
			{
				if(DS1302_Time[2]<1){DS1302_Time[2]=29;}//闰年2月
				if(DS1302_Time[2]>29){DS1302_Time[2]=1;}
			}
			else
			{
				if(DS1302_Time[2]<1){DS1302_Time[2]=28;}//平年2月
				if(DS1302_Time[2]>28){DS1302_Time[2]=1;}
			}
		}
		if(DS1302_Time[3]<0){DS1302_Time[3]=23;}//时越界判断
		if(DS1302_Time[4]<0){DS1302_Time[4]=59;}//分越界判断
		if(DS1302_Time[5]<0){DS1302_Time[5]=59;}//秒越界判断
	}
	//更新显示,根据TimeSetSelect和TimeSetFlashFlag判断可完成闪烁功能
	if(TimeSetSelect==0 && TimeSetFlashFlag==1){LCD_ShowString(1,1,"  ");}
	else {LCD_ShowNum(1,1,DS1302_Time[0],2);}
	if(TimeSetSelect==1 && TimeSetFlashFlag==1){LCD_ShowString(1,4,"  ");}
	else {LCD_ShowNum(1,4,DS1302_Time[1],2);}
	if(TimeSetSelect==2 && TimeSetFlashFlag==1){LCD_ShowString(1,7,"  ");}
	else {LCD_ShowNum(1,7,DS1302_Time[2],2);}
	if(TimeSetSelect==3 && TimeSetFlashFlag==1){LCD_ShowString(2,1,"  ");}
	else {LCD_ShowNum(2,1,DS1302_Time[3],2);}
	if(TimeSetSelect==4 && TimeSetFlashFlag==1){LCD_ShowString(2,4,"  ");}
	else {LCD_ShowNum(2,4,DS1302_Time[4],2);}
	if(TimeSetSelect==5 && TimeSetFlashFlag==1){LCD_ShowString(2,7,"  ");}
	else {LCD_ShowNum(2,7,DS1302_Time[5],2);}
}

void main()
{
	LCD_Init();
	DS1302_Init();
	Timer0Init();
	LCD_ShowString(1,1,"  -  -  ");	//静态字符初始化显示
	LCD_ShowString(2,1,"  :  :  ");	
	
	DS1302_SetTime();	//设置时间
	
	while(1)
	{
		KeyNum=Key();//读取键码
		if(KeyNum==1)//按键1按下
		{
			if(MODE==0){MODE=1;TimeSetSelect=0;}//功能切换
			else if(MODE==1){MODE=0;DS1302_SetTime();}
		}
		switch(MODE)//根据不同的功能执行不同的函数
		{
			case 0:TimeShow();break;
			case 1:TimeSet();break;
		}
	}
}

void Timer0_Routine() interrupt 1
{
	static unsigned int T0Count;
	TH0=0xFC;		//设置定时初值
	TL0=0x18;		//设置定时初值
	T0Count++;
	if(T0Count>=500)//每500ms进入一次
	{
		T0Count=0;
		TimeSetFlashFlag=!TimeSetFlashFlag;//闪烁标志位取反
	}
}

 十三、蜂鸣器

采用无源蜂鸣器 

13.1 蜂鸣器播放提示音 

实现:按键按下,蜂鸣器发声一段时间。

注意:这里按键3会反复横跳,是因为P3_2同时也是红外接收模块的引脚,引脚冲突了。一直响是正常的。 把红外拔掉应该能解决。以及按键之前要把LCD1602拔掉,不然数码管也会反复横跳。

用到以下文件:

//Nixie.c
#include <REGX52.H>
#include "Delay.h"
unsigned char NixieTable[]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F,0x77,0x7C,0x39,0x5E,0x79,0x71,0X00};

void Nixie(unsigned char Location,Number)
{
	switch(Location)
	{
		case 1:P2_4=1;P2_3=1;P2_2=1;break;
		case 2:P2_4=1;P2_3=1;P2_2=0;break;
		case 3:P2_4=1;P2_3=0;P2_2=1;break;
		case 4:P2_4=1;P2_3=0;P2_2=0;break;
		case 5:P2_4=0;P2_3=1;P2_2=1;break;
		case 6:P2_4=0;P2_3=1;P2_2=0;break;
		case 7:P2_4=0;P2_3=0;P2_2=1;break;
		case 8:P2_4=0;P2_3=0;P2_2=0;break;
	}
	P0=NixieTable[Number];
	//Delay(1);	
	//P0=0x00;
}
//Buzzer.h
#ifndef __BUZZER_H__
#define __BUZZER_H__

void Buzzer_Time(unsigned int ms);

#endif



//Buzzer.c
#include <REGX52.H>
#include <INTRINS.H>

//蜂鸣器端口
sbit Buzzer=P2^5;
/**
  * @brief 蜂鸣器私有延时函数,延时500us
  * @param 无
  * @retval 无
  */
void Buzzer_Delay500us()		//@12.000MHz
{
	unsigned char i;

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

/**
  * @brief 蜂鸣器发声
  * @param ms 发声的时长
  * @retval 无
  */
void Buzzer_Time(unsigned int ms)
{
	unsigned int i;
	for(i=0;i<ms*2;i++)
	{
		Buzzer=!Buzzer;
		Buzzer_Delay500us();
	}
}
//main.c
#include <REGX52.H>
#include "Delay.h"
#include "Key.h"
#include "Nixie.h"
#include "Buzzer.h"

unsigned char KeyNum;

void main()
{
	Nixie(1,0);
	while(1)
	{
		KeyNum=Key();
		if(KeyNum)
		{
			Buzzer_Time(100);
			Nixie(1,KeyNum);
		}
	}
}

 13.2 蜂鸣器播放音乐

实现:播放天空之城。

用到以下文件:

Delay和Timer0没有修改,所以还是用上面用过的。只修改main.c。 

//main.c
#include <REGX52.H>
#include "Delay.h"
#include "Timer0.h"

//蜂鸣器端口定义
sbit Buzzer=P2^5;

//播放速度,值为四分音符的时长(ms)
#define SPEED 500

//音符与索引对应表,P:休止符,L:低音,M:中音,H:高音,下划线:升半音符号#
#define P 	0
#define L1 	1
#define L1_ 2
#define L2 	3
#define L2_ 4
#define L3 	5
#define L4 	6
#define L4_ 7
#define L5 	8
#define L5_ 9
#define L6 	10
#define L6_ 11
#define L7 	12
#define M1 	13
#define M1_ 14
#define M2 	15
#define M2_ 16
#define M3 	17
#define M4 	18
#define M4_ 19
#define M5 	20
#define M5_ 21
#define M6 	22
#define M6_ 23
#define M7 	24
#define H1 	25
#define H1_ 26
#define H2 	27
#define H2_ 28
#define H3 	29
#define H4 	30
#define H4_ 31
#define H5 	32
#define H5_ 33
#define H6 	34
#define H6_ 35
#define H7 	36

//索引与频率对照表
unsigned int FreqTable[]={
	0,
	63628,63731,63835,63928,64021,64103,64185,64260,64331,64400,64463,64528,
	64580,64633,64684,64732,64777,64820,64860,64898,64934,64968,65000,65030,
	65058,65085,65110,65134,65157,65178,65198,65217,65235,65252,65268,65283,
};

//乐谱
unsigned char code Music[]={
	//音符,时值,
	
	//1
	P,	4,
	P,	4,
	P,	4,
	M6,	2,
	M7,	2,
	
	H1,	4+2,
	M7,	2,
	H1,	4,
	H3,	4,
	
	M7,	4+4+4,
	M3,	2,
	M3,	2,
	
	//2
	M6,	4+2,
	M5,	2,
	M6, 4,
	H1,	4,
	
	M5,	4+4+4,
	M3,	4,
	
	M4,	4+2,
	M3,	2,
	M4,	4,
	H1,	4,
	
	//3
	M3,	4+4,
	P,	2,
	H1,	2,
	H1,	2,
	H1,	2,
	
	M7,	4+2,
	M4_,2,
	M4_,4,
	M7,	4,
	
	M7,	8,
	P,	4,
	M6,	2,
	M7,	2,
	
	//4
	H1,	4+2,
	M7,	2,
	H1,	4,
	H3,	4,
	
	M7,	4+4+4,
	M3,	2,
	M3,	2,
	
	M6,	4+2,
	M5,	2,
	M6, 4,
	H1,	4,
	
	//5
	M5,	4+4+4,
	M2,	2,
	M3,	2,
	
	M4,	4,
	H1,	2,
	M7,	2+2,
	H1,	2+4,
	
	H2,	2,
	H2,	2,
	H3,	2,
	H1,	2+4+4,
	
	//6
	H1,	2,
	M7,	2,
	M6,	2,
	M6,	2,
	M7,	4,
	M5_,4,
	
	
	M6,	4+4+4,
	H1,	2,
	H2,	2,
	
	H3,	4+2,
	H2,	2,
	H3,	4,
	H5,	4,
	
	//7
	H2,	4+4+4,
	M5,	2,
	M5,	2,
	
	H1,	4+2,
	M7,	2,
	H1,	4,
	H3,	4,
	
	H3,	4+4+4+4,
	
	//8
	M6,	2,
	M7,	2,
	H1,	4,
	M7,	4,
	H2,	2,
	H2,	2,
	
	H1,	4+2,
	M5,	2+4+4,
	
	H4,	4,
	H3,	4,
	H3,	4,
	H1,	4,
	
	//9
	H3,	4+4+4,
	H3,	4,
	
	H6,	4+4,
	H5,	4,
	H5,	4,
	
	H3,	2,
	H2,	2,
	H1,	4+4,
	P,	2,
	H1,	2,
	
	//10
	H2,	4,
	H1,	2,
	H2,	2,
	H2,	4,
	H5,	4,
	
	H3,	4+4+4,
	H3,	4,
	
	H6,	4+4,
	H5,	4+4,
	
	//11
	H3,	2,
	H2,	2,
	H1,	4+4,
	P,	2,
	H1,	2,
	
	H2,	4,
	H1,	2,
	H2,	2+4,
	M7,	4,
	
	M6,	4+4+4,
	P,	4,
	
	0xFF	//终止标志
};

unsigned char FreqSelect,MusicSelect;

void main()
{
	Timer0Init();
	while(1)
	{	
		if(Music[MusicSelect]!=0xFF)	//如果不是停止标志位
		{
			FreqSelect=Music[MusicSelect];	//选择音符对应的频率
			MusicSelect++;
			Delay(SPEED/4*Music[MusicSelect]);	//选择音符对应的时值
			MusicSelect++;
			TR0=0;
			Delay(5);		//音符间短暂停顿
			TR0=1;
		}
		else	//如果是停止标志位
		{
			TR0=0;
			while(1);
		}
	}
}

void Timer0_Routine() interrupt 1
{
	if(FreqTable[FreqSelect])	//如果不是休止符
	{
		/*取对应频率值的重装载值到定时器*/
		TH0=FreqTable[FreqSelect]/256;		//设置定时初值
		TL0=FreqTable[FreqSelect]%256;		//设置定时初值
		Buzzer=!Buzzer;	//翻转蜂鸣器IO口
	}
}

十四、AT24C02(I2C总线) 

AT24C02是一种可以实现掉电不丢失的存储器,可用于保存单片机运行时想要永久保存的数据信息。

  • 存储介质:E2PROM
  • 通讯接口:I2C总线
  • 容量:256字节

AT24C02的固定地址为1010,可配置地址本开发板上为000,所以SLAVE ADDRESS+W为0xA0,SLAVE ADDRESS+R为0xA1。 

I2C总线 (Inter IC BUS)是由Philips公司开发的一种通用数据总线。
两根通信线:SCL (Serial Clock)、SDA (Serial Data)
同步、半双工,带数据应答。

12.1 AT24C02数据存储

实现:key1加,key2减,key3写入,key4读出。

用到以下文件:

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

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

/**
  * @brief I2C开始
  * @param 无
  * @retval 无
  */
void I2C_Start(void)
{
	I2C_SDA=1;
	I2C_SCL=1;
	I2C_SDA=0;
	I2C_SCL=0;
}

/**
  * @brief I2C停止
  * @param 无
  * @retval 无
  */
void I2C_Stop(void)
{
	I2C_SDA=0;
	I2C_SCL=1;
	I2C_SDA=1;
}

/**
  * @brief I2C发送一个字节
  * @param Byte 要发送的字节
  * @retval 无
  */
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;
	}
}

/**
  * @brief I2C接收一个字节
  * @param 无
  * @retval 接收到的一个字节数据
  */
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;
}

/**
  * @brief I2C发送应答
* @param AckBit 应答位,0为应答,1为非应答
  * @retval 无
  */
void I2C_SendAck(unsigned char AckBit)
{
		I2C_SDA=AckBit;
		I2C_SCL=1;
		I2C_SCL=0;
}

/**
  * @brief I2C接收应答
* @param 无
  * @retval 接收到达应答位,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__

AT24C02_WriteByte(unsigned char WordAddress,Data);
unsigned char AT24C02_ReadByte(unsigned char WordAddress);

#endif



//AT24C02.c
#include <REGX52.H>
#include "I2C.h"

#define AT24C02_ADDRESS  0xA0

/**
  * @brief AT24C02写入一个字节
  * @param WordAddress 要写入的字节地址
  * @param Data 要写入的数据
  * @retval 无
  */
void AT24C02_WriteByte(unsigned char WordAddress,Data)
{
	I2C_Start();
	I2C_SendByte(AT24C02_ADDRESS);
	I2C_ReceiveAck();
	I2C_SendByte(WordAddress);
	I2C_ReceiveAck();
	I2C_SendByte(Data);
	I2C_ReceiveAck();
	I2C_Stop();
}

/**
  * @brief AT24C02读取一个字节
  * @param WordAddress 要读出字节的地址
  * @retval 要读出的数据
  */
unsigned char AT24C02_ReadByte(unsigned char WordAddress)
{
	unsigned char Data;
	I2C_Start();
	I2C_SendByte(AT24C02_ADDRESS);
	I2C_ReceiveAck();
	I2C_SendByte(WordAddress);
	I2C_ReceiveAck();
	I2C_Start();
	I2C_SendByte(AT24C02_ADDRESS|0x01);
	I2C_ReceiveAck();
	Data=I2C_ReceiveByte();
	I2C_SendAck(1);
	I2C_Stop();
	return Data;
}
//main.c
#include <REGX52.H>
#include "LCD1602.h"
#include "Key.h"
#include "AT24C02.h"
#include "Delay.h"

unsigned char KeyNum;
unsigned int Num;

void main()
{
	LCD_Init();
	LCD_ShowNum(1,1,Num,5);
	while(1)
	{
		KeyNum=Key();
		if(KeyNum==1)
		{
			Num++;
			LCD_ShowNum(1,1,Num,5);
		}
		if(KeyNum==2)
		{
			Num--;
			LCD_ShowNum(1,1,Num,5);
		}
		if(KeyNum==3)
		{
			AT24C02_WriteByte(0,Num%256);
			Delay(5);
			AT24C02_WriteByte(1,Num/256);
			Delay(5);
			LCD_ShowString(2,1,"Write OK");
			Delay(1000);
			LCD_ShowString(2,1,"        ");
		}
		if(KeyNum==4)
		{
			Num=AT24C02_ReadByte(0);
			Num|=AT24C02_ReadByte(1)<<8;
			LCD_ShowNum(1,1,Num,5);
			LCD_ShowString(2,1,"Read OK");
			Delay(1000);
			LCD_ShowString(2,1,"       ");
		}
	}
}

12.2 秒表(定时器扫描按键数码管)

实现:秒表功能。key1开始和暂停,key2清零,key3写入,key4读出。

注意!!!因为key3和红外接收引脚冲突,写入不成功一定要把红外接收拔掉!!

用到以下文件:

仅粘贴有修改的 ,未贴出的代码上文有:

//Key.h
#ifndef __KEY_H__
#define __KEY_H__

unsigned char Key(void);
void Key_Loop(void);

#endif



//Key.c
#include <REGX52.H>
#include "Delay.h"

unsigned char Key_KeyNumber;

unsigned char Key(void)
{
	unsigned char Temp=0;
	Temp=Key_KeyNumber;
	Key_KeyNumber=0;
	return Temp;
}

unsigned char Key_GetState()
{
	unsigned char KeyNumber=0;
	
	if(P3_1==0){KeyNumber=1;}
	if(P3_0==0){KeyNumber=2;}
	if(P3_2==0){KeyNumber=3;}
	if(P3_3==0){KeyNumber=4;}
		
	return KeyNumber;
}

void Key_Loop(void)
{
	static unsigned char NowState,LastState;
	LastState=NowState;
	NowState=Key_GetState();
	if(LastState==0 && NowState==1)//这里按下有效
	{
		Key_KeyNumber=1;
	}
	if(LastState==2 && NowState==0)//这里松手有效
	{
		Key_KeyNumber=2;
	}
	if(LastState==3 && NowState==0)
	{
		Key_KeyNumber=3;
	}
	if(LastState==4 && NowState==0)
	{
		Key_KeyNumber=4;
	}
}
//Nixie.h
#ifndef __NIXIE_H__
#define __NIXIE_H__

void Nixie_SetBuf(unsigned char Location,Number);
void Nixie_Scan(unsigned char Location,Number);
void Nixie_Loop();

#endif



//Nixie.c
#include <REGX52.H>
#include "Delay.h"

unsigned char Nixie_Buf[9]={0,10,10,10,10,10,10,10,10};

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

void Nixie_SetBuf(unsigned char Location,Number)
{
	Nixie_Buf[Location]=Number;
}

void Nixie_Scan(unsigned char Location,Number)
{
	P0=0x00;
	switch(Location)
	{
		case 1:P2_4=1;P2_3=1;P2_2=1;break;
		case 2:P2_4=1;P2_3=1;P2_2=0;break;
		case 3:P2_4=1;P2_3=0;P2_2=1;break;
		case 4:P2_4=1;P2_3=0;P2_2=0;break;
		case 5:P2_4=0;P2_3=1;P2_2=1;break;
		case 6:P2_4=0;P2_3=1;P2_2=0;break;
		case 7:P2_4=0;P2_3=0;P2_2=1;break;
		case 8:P2_4=0;P2_3=0;P2_2=0;break;
	}
	P0=NixieTable[Number];	
}

void Nixie_Loop(void)
{
	static unsigned char i=1;
	Nixie_Scan(i,Nixie_Buf[i]);
	i++;
	if(i>=9){i=1;}
}
//main.c
#include <REGX52.H>
#include "Timer0.h"
#include "Key.h"
#include "Nixie.h"
#include "Delay.h"
#include "AT24C02.h"

unsigned char KeyNum;
unsigned char Min,Sec,MiniSec;
unsigned char RunFlag;

void main()
{
	Timer0Init();
	while(1)
	{
		KeyNum=Key();
		if(KeyNum==1)
		{
			RunFlag=!RunFlag;
		}
		if(KeyNum==2)
		{
			Min=0;
			Sec=0;
			MiniSec=0;
		}
		if(KeyNum==3)
		{
			AT24C02_WriteByte(0,Min);
			Delay(5);
			AT24C02_WriteByte(1,Sec);
			Delay(5);
			AT24C02_WriteByte(2,MiniSec);
			Delay(5);
		}
		if(KeyNum==4)
		{
			Min=AT24C02_ReadByte(0);
			Sec=AT24C02_ReadByte(1);
			MiniSec=AT24C02_ReadByte(2);	
		}
		Nixie_SetBuf(1,Min/10);		
		Nixie_SetBuf(2,Min%10);				
		Nixie_SetBuf(3,11);
		Nixie_SetBuf(4,Sec/10);		
		Nixie_SetBuf(5,Sec%10);				
		Nixie_SetBuf(6,11);
		Nixie_SetBuf(7,MiniSec/10);		
		Nixie_SetBuf(8,MiniSec%10);				
	}
}

void Sec_Loop()
{
	if(RunFlag)
	{
		MiniSec++;
		if(MiniSec>=100)
		{
			MiniSec=0;
			Sec++;
			if(Sec>=60)
			{
				Sec=0;
				Min++;
				if(Min>=60)
				{
					Min=0;
				}
			}
		}
	}
}

void Timer0_Routine() interrupt 1
{
	static unsigned int T0Count1,T0Count2,T0Count3;
	TH0=0xFC;		//设置定时初值
	TL0=0x18;		//设置定时初值
	T0Count1++;
	if(T0Count1>=20)
	{
		T0Count1=0;
		Key_Loop();
	}
	T0Count2++;
	if(T0Count2>=2)
	{
		T0Count2=0;
		Nixie_Loop();
	}
	T0Count3++;
	if(T0Count3>=10)
	{
		T0Count3=0;
		Sec_Loop();
	}
}

 十五、DS18B20温度传感器

15.1 DS18B20温度读取 

实现:读取环境温度,并显示到LCD上。

用到以下文件:

 

//OneWire.h
#ifndef __ONEWIRE_H__
#define __ONEWIRE_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>

sbit OneWire_DQ=P3^7;

unsigned char OneWire_Init(void)		//@12.000MHz
{
	unsigned char i;
	unsigned char AckBit;
	OneWire_DQ=1;
	OneWire_DQ=0;
	i=247;while(--i);		//Delay 500us
	OneWire_DQ=1;
	i=32;while(--i);		//Delay 70us
	AckBit=OneWire_DQ;
	i=247;while(--i);		//Delay 500us
	return AckBit;
}

void OneWire_SendBit(unsigned char Bit)
{
	unsigned char i;
	OneWire_DQ=0;
	i=4;while(--i);			//Delay 10us
	OneWire_DQ=Bit;
	i=24;while(--i);		//Delay 50us
	OneWire_DQ=1;
}

unsigned char OneWire_ReceiveBit(void)
{
	unsigned char i;
	unsigned char Bit;
	OneWire_DQ=0;
	i=2;while(--i);			//Delay 5us
	OneWire_DQ=1;
	i=2;while(--i);			//Delay 5us
	Bit=OneWire_DQ;
	i=24;while(--i);		//Delay 50us
	return Bit;
}

void OneWire_SendByte(unsigned char Byte)
{
	unsigned char i;
	for(i=0;i<8;i++)
	{
		OneWire_SendBit(Byte&(0x01<<i));
	}
}

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__

void DS18B20_ConvertT(void);
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

void DS18B20_ConvertT(void)
{
	OneWire_Init();
	OneWire_SendByte(DS18B20_SKIP_ROM);
	OneWire_SendByte(DS18B20_CONVERT_T);
}

float DS18B20_ReadT(void)
{
	unsigned char TLSB,TMSB;
	int Temp;
	float T;
	OneWire_Init();
	OneWire_SendByte(DS18B20_SKIP_ROM);
	OneWire_SendByte(DS18B20_READ_SCRATCHPAD);
	TLSB=OneWire_ReceiveByte();
	TMSB=OneWire_ReceiveByte();
	Temp=(TMSB<<8)|TLSB;		//将高低八位组成一个数
	T=Temp/16.0;				//转化为实际温度
	return T;
}
//main.c
#include <REGX52.H>
#include "LCD1602.h"
#include "DS18B20.h"
#include "Delay.h"

unsigned char Ack;
float T;

void main()
{
	DS18B20_ConvertT();
	Delay(1000);
	LCD_Init();
	LCD_ShowString(1,1,"Temperature:");
	while(1)
	{
		DS18B20_ConvertT();
		T=DS18B20_ReadT();
		if(T<0)
		{
			LCD_ShowChar(2,1,'-');
			T=-T;
		}
		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);
	}
}

 15.2 DS18B20温度报警器 

实现:一个温度,显示最高和最低的阈值,超过阈值则报警。

用到以下文件:

//OneWire.h
#ifndef __ONEWIRE_H__
#define __ONEWIRE_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>

//引脚定义
sbit OneWire_DQ=P3^7;

/**
  * @brief  单总线初始化
  * @param  无
  * @retval 从机响应位,0为响应,1为未响应
  */
unsigned char OneWire_Init(void)		//@12.000MHz
{
	unsigned char i;
	unsigned char AckBit;
	EA=0;
	OneWire_DQ=1;
	OneWire_DQ=0;
	i=247;while(--i);		//Delay 500us
	OneWire_DQ=1;
	i=32;while(--i);		//Delay 70us
	AckBit=OneWire_DQ;
	i=247;while(--i);		//Delay 500us
	EA=1;
	return AckBit;
}

/**
  * @brief  单总线发送一位
  * @param  Bit 要发送的位
  * @retval 无
  */
void OneWire_SendBit(unsigned char Bit)
{
	unsigned char i;
	EA=0;
	OneWire_DQ=0;
	i=4;while(--i);			//Delay 10us
	OneWire_DQ=Bit;
	i=24;while(--i);		//Delay 50us
	OneWire_DQ=1;
	EA=1;
}

/**
  * @brief  单总线接收一位
  * @param  无
  * @retval 读取的位
  */
unsigned char OneWire_ReceiveBit(void)
{
	unsigned char i;
	unsigned char Bit;
	EA=0;
	OneWire_DQ=0;
	i=2;while(--i);			//Delay 5us
	OneWire_DQ=1;
	i=2;while(--i);			//Delay 5us
	Bit=OneWire_DQ;
	i=24;while(--i);		//Delay 50us
	EA=1;
	return Bit;
}

/**
  * @brief  单总线发送一个字节
  * @param  Byte 要发送的字节
  * @retval 无
  */
void OneWire_SendByte(unsigned char Byte)
{
	unsigned char i;
	for(i=0;i<8;i++)
	{
		OneWire_SendBit(Byte&(0x01<<i));
	}
}

/**
  * @brief  单总线接收一个字节
  * @param  无
  * @retval 接收的一个字节
  */
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;
}
//main.c
#include <REGX52.H>
#include "DS18B20.h"
#include "Delay.h"
#include "AT24C02.h"
#include "Key.h"
#include "LCD1602.h"
#include "Timer0.h"

float T,TShow;
char TLow,THigh;
unsigned char KeyNum;

void main()
{
	DS18B20_ConvertT();		//上电先转换一次温度,防止第一次读数据错误
	Delay(1000);			//等待转换完成
	THigh=AT24C02_ReadByte(0);	//读取温度阈值数据
	TLow=AT24C02_ReadByte(1);
	if(THigh>155 || TLow<-55 || THigh<=TLow)
	{
		THigh=20;			//如果阈值非法,则设为默认值
		TLow=15;
	}
	LCD_Init();
	LCD_ShowString(1,1,"T:");
	LCD_ShowString(2,1,"TH:");
	LCD_ShowString(2,9,"TL:");
	LCD_ShowSignedNum(2,4,THigh,3);
	LCD_ShowSignedNum(2,12,TLow,3);
	Timer0Init();
	
	while(1)
	{
		KeyNum=Key();
		
		/*温度读取及显示*/
		DS18B20_ConvertT();				//转换温度
		T=DS18B20_ReadT();				//读取温度
		if(T<0)			//如果温度小于0
		{
			LCD_ShowChar(1,3,'-');		//显示负号
			TShow=-T;	//将温度变为正数
		}
		else			//如果温度大于等于0
		{
			LCD_ShowChar(1,3,'+');		//显示正号
			TShow=T;
		}
		LCD_ShowNum(1,4,TShow,3);		//显示温度整数部分
		LCD_ShowChar(1,7,'.');			//显示小数点
		LCD_ShowNum(1,8,(unsigned long)(TShow*100),2);		//显示温度小数部分
		
		/*阈值判断及显示*/
		if(KeyNum)						//按键按下再刷新
		{
			if(KeyNum==1)				//K1按键,THigh自增
			{
				THigh++;
				if(THigh>125){THigh=125;}
			}
			if(KeyNum==2)				//K2按键,THigh自减
			{
				THigh--;
				if(THigh<=TLow){THigh++;}
			}
			if(KeyNum==3)				//K3按键,TLow自增
			{
				TLow++;
				if(TLow>=THigh){TLow--;}
			}
			if(KeyNum==4)				//K4按键,TLow自减
			{
				TLow--;
				if(TLow<-55){TLow=-55;}
			}
			LCD_ShowSignedNum(2,4,THigh,3);		//显示阈值数据
			LCD_ShowSignedNum(2,12,TLow,3);
			AT24C02_WriteByte(0,THigh);			//写入到At24C02中保存
			Delay(5);
			AT24C02_WriteByte(1,TLow);
			Delay(5);
		}
		if(T>THigh)				//越界判断
		{
			LCD_ShowString(1,13,"OV:H");
		}
		else if(T<TLow)
		{
			LCD_ShowString(1,13,"OV:L");
		}
		else
		{
			LCD_ShowString(1,13,"    ");
		}
	}
}

void Timer0_Routine() interrupt 1
{
	static unsigned int T0Count;
	TH0=0xFC;		//设置定时初值
	TL0=0x18;		//设置定时初值
	T0Count++;
	if(T0Count>=20)
	{
		T0Count=0;
		Key_Loop();		//每20ms调用一次按键驱动函数
	}
}

十六、LCD1602 

 实现:LCD1602各种显示。

用到以下文件:

//LCD1602.h
#ifndef __LCD1602_H__
#define __LCD1602_H__

void LCD_WriteCommand(unsigned char Command);
void LCD_Init(void);
void LCD_ShowChar(unsigned char Line,unsigned char Column,unsigned char Char);
void LCD_ShowString(unsigned char Line,unsigned char Column,unsigned 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 char Number,unsigned char Length);
void LCD_ShowBinNum(unsigned char Line,unsigned char Column,unsigned char Number,unsigned char Length);

#endif



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

//引脚定义
sbit LCD_RS=P2^6;
sbit LCD_RW=P2^5;
sbit LCD_E=P2^7;
#define LCD_DataPort P0

/**
  * @brief  LCD1602延时函数,12MHz调用可延时1ms
  * @param  无
  * @retval 无
  */
void LCD_Delay()		//@12.000MHz   1ms
{
	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_E=1;
	LCD_Delay();
	LCD_E=0;
	LCD_Delay();
}

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

/**
  * @brief  LCD1602初始化函数
  * @param  无
  * @retval 无
  */
void LCD_Init(void)
{
	LCD_WriteCommand(0x38);
	LCD_WriteCommand(0x0C);
	LCD_WriteCommand(0x06);
	LCD_WriteCommand(0x01);
}

/**
  * @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
	{
		LCD_WriteCommand(0x80|(Column-1)+0x40);
	}
}

/**
  * @brief  在LCD1602指定位置上显示一个字符
  * @param  Line 行位置,范围:1~2
  * @param  Column 列位置,范围:1~16
  * @param  Char 要显示的字符
  * @retval 无
  */
void LCD_ShowChar(unsigned char Line,unsigned char Column,unsigned 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,unsigned 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('0'+Number/LCD_Pow(10,i-1)%10);
	}
}

/**
  * @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('0'+Number1/LCD_Pow(10,i-1)%10);
	}
}

/**
  * @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 char Number,unsigned char Length)
{
	unsigned char i;
	unsigned char SingleNumber;
	LCD_SetCursor(Line,Column);
	for(i=Length;i>0;i--)
	{
		SingleNumber=Number/LCD_Pow(16,i-1)%16;
		if(SingleNumber<10)
		{
			LCD_WriteData('0'+SingleNumber);
		}
		else
		{
			LCD_WriteData('A'+SingleNumber-10);
		}
	}
}

/**
  * @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 char Number,unsigned char Length)
{
	unsigned char i;
	LCD_SetCursor(Line,Column);
	for(i=Length;i>0;i--)
	{
		LCD_WriteData('0'+Number/LCD_Pow(2,i-1)%2);
	}
}
//main.c
#include <REGX52.H>
#include "LCD1602.h"
#include "Delay.h"

void main()
{
	LCD_Init();         			//LCD初始化
	LCD_ShowChar(1,1,'A');			//在1行1列显示字符A
	LCD_ShowString(1,3,"Hello");	//在1行3列显示字符串Hello
	LCD_ShowNum(1,9,66,2);			//在1行9列显示数字66,长度为2
	LCD_ShowSignedNum(1,12,-88,2);	//在1行12列显示有符号数字-88,长度为2
	LCD_ShowHexNum(2,1,0xA5,2);		//在2行1列显示十六进制数字0xA5,长度为2
	LCD_ShowBinNum(2,4,0xA5,8);		//在2行4列显示二进制数字0xA5,长度为8
	LCD_ShowChar(2,13,0xDF);		//在2行13列显示编码为0xDF的字符
	LCD_ShowChar(2,14,'C');			//在2行14列显示字符C
	LCD_ShowString(1,16,"Welcome to China!");
	while(1)
	{
		LCD_WriteCommand(0x18);		//向左移屏,指令0x18
		Delay(500);					//实现循环流动屏
	}
}

十七、直流电机PWM 

17.1 LED呼吸灯

#include <REGX52.H>

sbit LED=P2^0;

void Delay(unsigned int t)
{
	while(t--);
}
void main()
{
	unsigned char Time,i;
	while(1)
	{
		for(Time=0;Time<100;Time++)
		{
			for(i=0;i<20;i++)
			{
				LED=0;
				Delay(Time);
				LED=1;
				Delay(100-Time);
			}			
		}
		for(Time=100;Time>0;Time--)
		{
			for(i=0;i<20;i++)
			{
				LED=0;
				Delay(Time);
				LED=1;
				Delay(100-Time);
			}			
		}
	}
}

17.2 直流电机调速 

实现:使用直流电机控制小风扇三档速度。

注意!!直流电机插在J47的5V和01引脚上。先把程序烧进去再插,不然烧不进去。

用以下文件,Key和Nixie使用最初始的:

//Timer0.h
#ifndef __TIMER0_H__
#define __TIMER0_H__

void Timer0Init();

#endif



//Timer0.c
#include <REGX52.H>
/**
  * @brief 定时器0初始化,100us@12.000MHz
  * @param 无
  * @retval 无
  */
void Timer0Init()
{
	TMOD&=0xF0;		//把TMOD的低四位清零,高四位保持不变
	TMOD|=0x01;		//把TMOD的最低位置1,高四位保持不变
	TF0=0;
	TR0=1;
	TH0=0xFF;		//设置定时初值
	TL0=0x9C;		//设置定时初值
	ET0=1;
	EA=1;
	PT0=0;
}
//main.c
#include <REGX52.H>
#include "Delay.h"
#include "Key.h"
#include "Timer0.h"
#include "Nixie.h"

sbit Motor=P1^0;

unsigned char Counter,Compare;
unsigned char KeyNum,Speed;

void main()
{
	Timer0Init();
	while(1)
	{
		KeyNum=Key();
		if(KeyNum==1)
		{
			Speed++;
			Speed%=4;
			if(Speed==0){Compare=0;}
			if(Speed==1){Compare=50;}
			if(Speed==2){Compare=75;}
			if(Speed==3){Compare=100;}
		}
		Nixie(1,Speed);
	}
}

void Timer0_Routine() interrupt 1
{
	TH0=0xFF;		//设置定时初值
	TL0=0x9C;		//设置定时初值	
	Counter++;
	Counter%=100;
	if(Counter<Compare)
	{
		Motor=1;
	}
	else
	{
		Motor=0;
	}
}

十八、AD/DA 

18.1 AD模数转换

用到以下:

实现:显示三个数据(ADJ  NTC  RG)。 

目前出现问题:8位显示ADJ正常,但是12位会出现乱闪且数值不能准确增减的问题。所以选择8位显示。

//XPT2046.h
#ifndef __XPT2046_H__
#define __XPT2046_H__

#define XPT2046_XP_8		0x9C	//0x8C
#define XPT2046_YP_8		0xDC
#define XPT2046_VBAT_8		0xAC
#define XPT2046_AUX	_8		0xEC

#define XPT2046_XP_12		0x94	//0x84
#define XPT2046_YP_12		0xD4
#define XPT2046_VBAT_12		0xA4
#define XPT2046_AUX	_12		0xE4

unsigned int XPT2046_ReadAD(unsigned char Command);

#endif





/XPT2046.c
#include <REGX52.H>
#include "Delay.H"

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;
		Delay(1);						//延时1。两处延时,使数值范围0-254
		XPT2046_DCLK=0;
	}	
	for(i=0;i<16;i++)
	{
		XPT2046_DCLK=1;
		Delay(1);						//延时2
		XPT2046_DCLK=0;
		if(XPT2046_DOUT){ADVAlue|=(0x8000>>i);}
	}
	XPT2046_CS=1;
	if(Command&0x08)
	{
		return ADVAlue>>8;
	}
	else
	{
		return ADVAlue>>4;
	}
}
//main.c
#include <REGX52.H>
#include "LCD1602.H"
#include "Delay.H"
#include "XPT2046.H"

unsigned int ADValue;

void main()
{
	LCD_Init();
	LCD_ShowString(1,1,"ADJ  NTC  RG");
	while(1)
	{
//		ADValue=XPT2046_ReadAD(XPT2046_XP_12);		//12位的显示有问题,8位的可以
//		LCD_ShowNum(2,1,ADValue,4);		
		ADValue=XPT2046_ReadAD(XPT2046_XP_8);
		LCD_ShowNum(2,1,ADValue,3);       
		ADValue=XPT2046_ReadAD(XPT2046_YP_8);
		LCD_ShowNum(2,6,ADValue,3);      
		ADValue=XPT2046_ReadAD(XPT2046_VBAT_8);
		LCD_ShowNum(2,11,ADValue,3);
		Delay(10);
	}
}

18.2 DA数模转换

使用以下文件(除了main函数内改写呼吸灯,其余文件全部复制17-2直流电机调速的代码,并且不需要用到数码管和按键):

实现:DA呼吸灯。

//main.c
#include <REGX52.H>
#include "Delay.h"
#include "Timer0.h"

sbit DA=P2^1;

unsigned char Counter,Compare;
unsigned char i;
void main()
{
	Timer0Init();
	while(1)
	{
		for(i=0;i<100;i++)
		{
			Compare=i;
			Delay(10);
		}
		for(i=100;i>0;i--)
		{
			Compare=i;
			Delay(10);
		}
	}
}

void Timer0_Routine() interrupt 1
{
	TH0=0xFF;		//设置定时初值
	TL0=0x9C;		//设置定时初值	
	Counter++;
	Counter%=100;
	if(Counter<Compare)
	{
		DA=1;
	}
	else
	{
		DA=0;
	}
}

十九、红外遥控(外部中断) 

19.1 红外遥控

用到以下:

实现:红外遥控。获取按键值以及NUM加减。

//Timer0.h
#ifndef __TIMER0_H__
#define __TIMER0_H__

void Timer0Init();
void Timer0_SetCounter(unsigned int Value);
unsigned int Timer0_GetCounter(void);
void Timer0_Run(unsigned char Flag);

#endif




//Timer0.c
#include <REGX52.H>

void Timer0Init()
{
	TMOD&=0xF0;		//把TMOD的低四位清零,高四位保持不变
	TMOD|=0x01;		//把TMOD的最低位置1,高四位保持不变
	TF0=0;
	TR0=0;			//定时器0不计时
	TH0=0;		//设置定时初值
	TL0=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.h
#ifndef __INT0_H__
#define __INT0_H__

void Int0_Init(void);

#endif



//Int0.c
#include <REGX52.H>

void Int0_Init(void)		//@12.000MHz
{
	IT0=1;
	IE0=0;
	EX0=1;
	EA=1;
	PX0=1;
}
//红外解码函数
//IR.h
#ifndef __IR_H__
#define __IR_H__

#define IR_POWER		0x45
#define IR_MODE			0x46
#define IR_MUTE			0x47
#define IR_START_STOP	0x44
#define IR_PREVIOUS		0x40
#define IR_NEXT			0x43
#define IR_EQ			0x07
#define IR_VOL_MINUS	0x15
#define IR_VOL_ADD		0x09
#define IR_0			0x16
#define IR_RPT			0x19
#define IR_USD			0x0D
#define IR_1			0x0C
#define IR_2			0x18
#define IR_3			0x5E
#define IR_4			0x08
#define IR_5			0x1C
#define IR_6			0x5A
#define IR_7			0x42
#define IR_8			0x52
#define IR_9			0x4A

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;

/**
  * @brief  红外遥控初始化
  * @param  无
  * @retval 无
  */
void IR_Init(void)	
{
	Timer0Init();
	Int0_Init();
}

/**
  * @brief  红外遥控获取收到数据帧标志位
  * @param  无
  * @retval 是否收到数据帧,1为收到,0为未收到
  */
unsigned char IR_GetDataFlag(void)
{
	if(IR_DataFlag)
	{
		IR_DataFlag=0;
		return 1;
	}
	return 0;
}

/**
  * @brief  红外遥控获取收到连发帧标志位
  * @param  无
  * @retval 是否收到连发帧,1为收到,0为未收到
  */
unsigned char IR_GetRepeatFlag(void)
{
	if(IR_RepeatFlag)
	{
		IR_RepeatFlag=0;
		return 1;
	}
	return 0;
}

/**
  * @brief  红外遥控获取收到的地址数据
  * @param  无
  * @retval 收到的地址数据
  */
unsigned char IR_GetAddress(void)
{
	return IR_Address;
}

/**
  * @brief  红外遥控获取收到的命令数据
  * @param  无
  * @retval 收到的命令数据
  */
unsigned char IR_GetCommand(void)
{
	return IR_Command;
}

//外部中断0中断函数,下降沿触发执行
void Int0_Routine(void) interrupt 0
{
	if(IR_State==0)				//状态0,空闲状态
	{
		Timer0_SetCounter(0);	//定时计数器清0
		Timer0_Run(1);			//定时器启动
		IR_State=1;				//置状态为1
	}
	else if(IR_State==1)		//状态1,等待Start信号或Repeat信号
	{
		IR_Time=Timer0_GetCounter();		//获取上一次中断到此次中断的时间
		Timer0_SetCounter(0);				//定时计数器清0
		//如果计时为13.5ms,则接收到了Start信号(判定值在12MHz晶振下为13500,在11.0592MHz晶振下为12442)
		if(IR_Time>13500*0.9216-500 && IR_Time<13500*0.9216+500)
		{
			IR_State=2;			//置状态为2
		}
		//如果计时为11.25ms,则接收到了Repeat信号(判定值在12MHz晶振下为11250,在11.0592MHz晶振下为10368)
		else if(IR_Time>11250*0.9216-500 && IR_Time<11250*0.9216+500)
		{
			IR_RepeatFlag=1;	//置收到连发帧标志位为1
			Timer0_Run(0);		//定时器停止
			IR_State=0;			//置状态为0
		}
		else//接收出错
		{
			IR_State=1;			//置状态为1
		}
	}
	else if(IR_State==2)		//状态2,接收数据
	{
		IR_Time=Timer0_GetCounter();	//获取上一次中断到此次中断的时间
		Timer0_SetCounter(0);			//定时计数器清0
		//如果计时为1120us,则接收到了数据0(判定值在12MHz晶振下为1120,在11.0592MHz晶振下为1032)
		if(IR_Time>1120*0.9216-500 && IR_Time<1120*0.9216+500)
		{
			IR_Data[IR_pData/8]&=~(0x01<<(IR_pData%8));		//数据对应位清0
			IR_pData++;			//数据位置指针自增
		}
		//如果计时为2250us,则接收到了数据1(判定值在12MHz晶振下为2250,在11.0592MHz晶振下为2074)
		else if(IR_Time>2250*0.9216-500 && IR_Time<2250*0.9216+500)
		{
			IR_Data[IR_pData/8]|=(0x01<<(IR_pData%8));		//数据对应位置1
			IR_pData++;			//数据位置指针自增
		}
		else//接收出错
		{
			IR_pData=0;			//数据位置指针清0
			IR_State=1;			//置状态为1
		}
		if(IR_pData>=32)		//如果接收到了32位数据
		{
			IR_pData=0;			//数据位置指针清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;					//置收到连发帧标志位为1
			}
			Timer0_Run(0);		//定时器停止
			IR_State=0;			//置状态为0
		}
	}
}
//main.c
#include <REGX52.H>
#include "Delay.H"
#include "LCD1602.H"
#include "IR.H"

unsigned char Num;
unsigned char Address;
unsigned char Command;

void main()
{
	LCD_Init();
	LCD_ShowString(1,1,"ADDR  CMD  NUM");
	LCD_ShowString(2,1,"00    00   000");
	
	IR_Init();
	
	while(1)
	{
		if(IR_GetDataFlag() || IR_GetRepeatFlag())
		{
			Address=IR_GetAddress();
			Command=IR_GetCommand();
			
			LCD_ShowHexNum(2,1,Address,2);
			LCD_ShowHexNum(2,7,Command,2);
			
			if(Command==IR_VOL_MINUS)
			{
				Num--;
			}
			if(Command==IR_VOL_ADD)
			{
				Num++;
			}
			LCD_ShowNum(2,12,Num,3);
		}
	}

}

19.2 红外遥控电机调速 

用到以下文件,将17-2电机调速的Motor模块化,移植19-1的红外遥控的代码(复制IR、Timer0、Int0)。

实现:红外遥控小风扇,三档调速。数码管显示当前是几档速度(1~3)。

//Motor.h
#ifndef __MOTOR_H__
#define __MOTOR_H__

void Motor_Init(void);
void Motor_SetSpeed(unsigned char Speed);

#endif



//Motor.c
#include <REGX52.H>
#include "Timer1.h"

sbit Motor=P1^0;

unsigned char Counter,Compare;

void Motor_Init(void)
{
	Timer1_Init();
}

void Motor_SetSpeed(unsigned char Speed)
{
	Compare=Speed;
}

void Timer1_Routine() interrupt 3
{
	TH1=0xFF;		//设置定时初值
	TL1=0x9C;		//设置定时初值	
	Counter++;
	Counter%=100;
	if(Counter<Compare)
	{
		Motor=1;
	}
	else
	{
		Motor=0;
	}
}
//Timer1.h
#ifndef __TIMER1_H__
#define __TIMER1_H__

void Timer1_Init();

#endif




//Timer1.c
#include <REGX52.H>
/**
  * @brief 定时器1初始化,100us@12.000MHz
  * @param 无
  * @retval 无
  */
void Timer1_Init()
{
	TMOD&=0x0F;		//把TMOD的低四位清零,高四位保持不变
	TMOD|=0x10;		//把TMOD的最低位置1,高四位保持不变
	TF1=0;
	TR1=1;
	TH1=0xFF;		//设置定时初值
	TL1=0x9C;		//设置定时初值
	ET1=1;
	EA=1;
	PT1=0;
}
//main.c
#include <REGX52.H>
#include "Delay.h"
#include "Key.h"
#include "Nixie.h"
#include "Motor.h"
#include "IR.h"

unsigned char Command,Speed;

void main()
{
	Motor_Init();
	IR_Init();
	while(1)
	{
		if(IR_GetDataFlag())
		{
			Command=IR_GetCommand();
			
			if(Command==IR_0){Speed=0;}
			if(Command==IR_1){Speed=1;}
			if(Command==IR_2){Speed=2;}
			if(Command==IR_3){Speed=3;}
			
			if(Speed==0){Motor_SetSpeed(0);}
			if(Speed==1){Motor_SetSpeed(50);}
			if(Speed==2){Motor_SetSpeed(75);}
			if(Speed==3){Motor_SetSpeed(100);}
		}
		Nixie(1,Speed);
	}
}

完结。 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值