一、前言
记得那是刚上大一的时候,学校电子设计创新实验室的学长们自发的组织单片机技能培训,每次周末都会安排几个小时的培训,那时候自学了C语言,开始了自己的单片机之旅,记得那时候拿着个51单片机控制LED跑流水灯、做个避障小车什么的还挺好玩的,大一下学期加入了实验室,在学长的建议下积累下来了当时的一些模块代码,做成了一套属于自己的库函数,整套代码注释非常详细,都是自己当时用心一个字一个字手敲上去的,通过那段时间对51单片机的学习,接触到了各种各样的常用传感器模块和单片机技术,受益良多。现在很难用到51单片机了,最近又找到了这套代码,于是分享出来,大家觉得有帮助的可以自己下载。
二、工程涉及到的模块
![]() | 1、较为精确的软件延时函数 2、定时器 3、中断 4、I2C协议 5、SPI协议 6、LCD1602液晶 7、LCD12864液晶 8、PCF8591 AD和DA模块 9、DS18B20温度传感器 10、DS1302时钟 11、IR红外解码 12、RS232串口通信 13、AT24C02 存储模块 14、数码管显示 15、PWM产生 16、超声波模块 17、TFT点阵屏显示 18、GUI绘图函数 19、ASCII字库显示、中文显示 20、图片显示。 21、NRF24L01 2.4G无线通信模块 22、TFT点阵屏贪吃蛇游戏 |
三、代码一览
这里贴上来一些代码预览一下:
外部中断
#include "interrupt.h"
/****************************************************
函数名称: Interrupt_Falling
函数简介: 配置外部中断下降沿触发函数
入口参数: Interrupt_Num 外部中断号
Interrupt_NVIC 中断优先级(NVIC_Low或NVIC_High)
出口参数: 无
其他: 下降沿触发是通常外部中断的用法,中断后要处
理的数据自己在exti.c中编写。
*****************************************************/
void Interrupt_Falling(u8 Interrupt_Num,NVIC_Select Interrupt_NVIC)
{
if(Interrupt_Num==0)
{
IT0=1; //下降沿触发
EX0=1; //开中断0
EA=1; //开总中断
PX0=Interrupt_NVIC; //外部中断0优先级 1高优先级,0低优先级
}
else
{
IT1=1; //下降沿触发
EX1=1; //开中断1
EA=1; //开总中断
PX1=Interrupt_NVIC; //外部中断1优先级 1高优先级,0低优先级
}
}
/****************************************************
函数名称: Interrupt_Stop
函数简介: 禁止中断函数
入口参数: Interrupt_Num 外部中断号
出口参数: 无
其他: 无
*****************************************************/
void Interrupt_Stop(u8 Interrupt_Num)
{
if(Interrupt_Num==0)
{
EX0=0; //关中断0
}
else
{
EX1=0; //关中断1
}
}
LCD1602
#ifndef _LCD1602_H
#define _LCD1602_H
#include "public.h"
/***********管脚定义**********************************************/
sbit LCD1602_E=P2^7;
sbit LCD1602_RS=P2^6;
sbit LCD1602_RW=P2^5;
#define LCD1602_Dataport P0
/**********宏定义命令控制*****************************************/
#define Com_ClearScreen 0x01 //清显示
#define Com_CursorReturn 0x02 //光标返回
#define Com_CursorAndDisplayMode 0x40 //置输入模式
#define Com_DisplayOnOff 0x80 //显示开/关控制
#define Com_CursorOrDisplayMove 0x10 //光标或字符移位
#define Com_SetFuntion 0x20 //置功能
#define Com_SetRamAdd 0x40 //置字符发生存贮器地址
#define Com_SetDDRamAddr 0x80 //置数据存贮器地址
/***********函数声明*****************************************/
void LCD1602_CheckBusy(void); //判忙函数
void LCD1602_WriteCom(uchar com); //写命令函数
void LCD1602_WriteDat(uchar dat); //写数据函数
void LCD1602_WriteAddress(uchar x,uchar y); //写地址函数
void LCD1602_WriteString(uchar x,uchar y,uchar *str); //写字符串函数
void LCD1602_Init(void); //初始化函数
#endif
#include "LCD1602.h"
/****************************************************
函数名称: LCD1602_CheckBusy
函数简介: 判忙函数
入口参数: 无
出口参数: 无
其他: 无
*****************************************************/
void LCD1602_CheckBusy(void)
{
LCD1602_Dataport=0x00;
LCD1602_E=0;
LCD1602_RS=0;
LCD1602_RW=1;
LCD1602_E=1;
while(LCD1602_Dataport&0x80);
LCD1602_E=0;
}
/****************************************************
函数名称: LCD1602_WriteCom
函数简介: 写命令函数
入口参数: com,待写入命令
出口参数: 无
其他: 无
*****************************************************/
void LCD1602_WriteCom(uchar com)
{
LCD1602_CheckBusy();
LCD1602_E=0;
LCD1602_RS=0;
LCD1602_RW=0;
LCD1602_Dataport=com;
LCD1602_E=1;
delay_ms(5);
LCD1602_E=0;
}
/****************************************************
函数名称: LCD1602_WriteDat
函数简介: 写数据函数
入口参数: dat,待写入数据
出口参数: 无
其他: 无
*****************************************************/
void LCD1602_WriteDat(uchar dat)
{
LCD1602_CheckBusy();
LCD1602_E=0;
LCD1602_RS=1;
LCD1602_RW=0;
LCD1602_Dataport=dat;
LCD1602_E=1;
delay_ms(5);
LCD1602_E=0;
}
/****************************************************
函数名称: LCD1602_WriteAddress
函数简介: 写地址函数
入口参数: x列地址0~15,y行地址0~1
出口参数: 无
其他: 无
*****************************************************/
void LCD1602_WriteAddress(uchar x,uchar y)
{
if(y==0)
x+=0x80;
else
x+=0xc0;
LCD1602_WriteCom(x);
}
/****************************************************
函数名称: LCD1602_WriteString
函数简介: 写字符串函数
入口参数: x列地址0~15,y行地址0~1,str待写入字符
串地址指针
出口参数: 无
其他: 注意计算好对应的显示位数,避免字符写入错
误的地址
*****************************************************/
void LCD1602_WriteString(uchar x,uchar y,uchar *str)
{
LCD1602_WriteAddress(x,y);
while(*str!=0)
{
LCD1602_WriteDat(*str);
str++;
}
}
/****************************************************
函数名称: LCD1602_Init
函数简介: 初始化函数
入口参数: 无
出口参数: 无
其他: 无
*****************************************************/
void LCD1602_Init(void)
{
LCD1602_WriteCom(0x38); //开显示
LCD1602_WriteCom(0x0c); //开显示不显示光标
LCD1602_WriteCom(0x06); //写一个指针加1
LCD1602_WriteCom(0x01); //清屏
LCD1602_WriteCom(0x80); //设置数据指针起点
}
IR红外解码
#ifndef _IR_H
#define _IR_H
#include "public.h"
/*************管脚定义***************************************/
sbit IR_DP=P3^2; //红外传感器数据线
/*************变量定义***************************************/
extern u8 IR_OK; //红外信号接收完成标志
extern u8 IR_Dat[4]; //存储红外解码的数据(用户码,用户反码,数据码,数据反码)
/*************函数声明***************************************/
void delay800us(void); //延时函数
void IR_Init(void); //红外接收初始化函数
//void IR_Interrupt0() interrupt 0; //外部中断0中断服务函数
#endif
#include "IR.h"
/****************************************************
注意:红外接收头信号线发出的电平信号和接收的是相反的
即接收到的红外信号编码其实和遥控发出来的红外信
号是各位取反的关系。
*****************************************************/
u8 IR_OK=0; //红外信号接收完成标志
u8 IR_Dat[4]={0,0,0,0}; //存储红外解码的数据(用户码,用户反码,数据码,数据反码)
/****************************************************
函数名称: delay800us
函数简介: 延时函数
入口参数: 无
出口参数: 无
其他: 800us延时可以区分出信号0和1
*****************************************************/
void delay800us(void)
{
unsigned char a,b,c;
for(c=1;c>0;c--)
for(b=2;b>0;b--)
for(a=197;a>0;a--);
}
/****************************************************
函数名称: IR_Init
函数简介: 红外接收初始化函数
入口参数: 无
出口参数: 无
其他: 调用此函数对红外接收做好配置,注意每接收一
次红外数据都要初始化一次(调用一次该函数)
*****************************************************/
void IR_Init(void)
{
u8 i;
IR_DP=1; //先把红外传感器数据线拉高
IT0=1; //中断0,下降沿触发
EX0=1; //开中断0
EA=1; //开总中断
for(i=0;i<4;i++)
{
IR_Dat[i]=0; //清除红外数据,注意一定要清除,否则中断中用移位的方式接收数据时会出错。
}
IR_OK=0; //接收完成标志置0(未接收)
}
/****************************************************
函数名称: IR_Interrupt0
函数简介: 外部中断0中断服务函数
入口参数: 无
出口参数: 无
其他: 调用此函数进行红外解码,注意使用红外解码时
在exti.c中注释掉相应的外部中断0,避免冲突
*****************************************************/
void IR_Interrupt0() interrupt 0
{
u8 i;
u16 temp; //等待时间控制变量
if(IR_DP==0&&IR_OK==0) //如果红外接收头数据线确实是低电平且红外数据未接收
{
EX0=0; //关外部中断0,防止中断不断进入
while(!IR_DP) //等待9ms的低电平过去
{
temp++;
if(temp>10000) //如果等待时间超出正常范围,直接退出中断,避免程序死在这里
{
EX0=1; //接收到错误信号后要重新打开中断,否则下次接收不了红外信号
return;
}
}
temp=0;
while(IR_DP) //等待4.5ms的高电平过去
{
temp++;
if(temp>10000) //如果等待时间超出正常范围,直接退出中断,避免程序死在这里
{
EX0=1; //接收到错误信号后要重新打开中断,否则下次接收不了红外信号
return;
}
}
temp=0;
for(i=0;i<32;i++)
{
IR_Dat[i/8]>>=1; //数据是从低位开始发送的,所以向右移位
while(!IR_DP) //等待0.56ms低电平过去,因为数据0和1前部分都是0.56ms低电平,无区分度
{
temp++;
if(temp>10000) //如果等待时间超出正常范围,直接退出中断,避免程序死在这里
{
EX0=1; //接收到错误信号后要重新打开中断,否则下次接收不了红外信号
return;
}
}
temp=0;
delay800us();
if(IR_DP==1) //800us过去,IR_DP为1则是信号1,为0则是信号0
{
IR_Dat[i/8]|=0x80; //读取计数次数,存入数组中
while(IR_DP)
{
temp++;
if(temp>10000) //如果等待时间超出正常范围,直接退出中断,避免程序死在这里
{
EX0=1; //接收到错误信号后要重新打开中断,否则下次接收不了红外信号
return;
}
}
temp=0;
}
}
if(i==32) //如果接收数据是32位完整的,表明是红外信号
{
for(i=0;i<4;i++)
{
IR_Dat[i]=~IR_Dat[i]; //数据取反,因为直接从红外接收头读到的信号与遥控的信号是各位取反的
}
IR_OK=1; //数据接收完成
}
}
}
RS232串口通信
#ifndef _RS232_H
#define _RS232_H
#include "public.h"
void RS232_Init(u16 Baudrate); //串口通信初始化函数
#endif
#include "RS232.h"
/****************************************************
函数名称: RS232_Init
函数简介: 串口通信初始化函数
入口参数: Baudrate 通信波特率
出口参数: 无
其他: 注意晶振频率为11.0592MHZ使用串口通信后定
时器1被占用,串口通信中断向量号为4。
*****************************************************/
void RS232_Init(u16 Baudrate)
{
SCON = 0x50; // 设定通信方式为方式1,允许接收,相当于SM1 = 1; REN = 1;(注意顺序不能颠倒!)
PCON = 0x00; //SMOD=0,该语句可去掉(波特率正常),若SMOD=1则(波特率加倍)
TMOD = 0x20; //设置TI定时器于工作方式2
switch (Baudrate) //确定波特率
{
case 300: {TH1 = 0xA0;TL1 = 0xA0;} break;
case 600: {TH1 = 0xD0;TL1 = 0xD0;} break;
case 1200: {TH1 = 0xE8;TL1 = 0xE8;} break;
case 2400: {TH1 = 0xF4;TL1 = 0xF4;} break;
case 3600: {TH1 = 0xF8;TL1 = 0xF8;} break;
case 4800: {TH1 = 0xFA;TL1 = 0xFA;} break;
case 7200: {TH1 = 0xFC;TL1 = 0xFC;} break;
case 9600: {TH1 = 0xFD;TL1 = 0xFD;} break;
case 14400: {TH1 = 0xFE;TL1 = 0xFE;} break;
case 28800: {TH1 = 0xFF;TL1 = 0xFF;} break;
}
TR1 = 1;//启动定时器1
EA = 1;//开总中断
ES = 1;//开串行口中断
}
超声波传感器
#ifndef _ChaoShengBo_H
#define _ChaoShengBo_H
#include "public.h"
/*************管脚定义**********************************************/
sbit ChaoShengBo_TRIG=P2^0; //触发控制信号输入
sbit ChaoShengBo_ECHO=P2^1; //回响信号输出
/*************变量声明**********************************************/
extern u8 ChaoShengBo_Flag; //定时器中断溢出标志
/*************函数声明**********************************************/
u16 ChaoShengBo_Measure(void); //超声波测距函数
#endif
#include "ChaoShengBo.h"
u8 ChaoShengBo_Flag=0; //定时器中断溢出标志
/****************************************************
函数名称: ChaoShengBo_Measure
函数简介: 超声波测距函数
入口参数: 无
出口参数: 0 测量失败 非0 测得的返回信号时间(单位us)
其他: 测试距离=(高电平时间×声速(340M/S))/2,该函数
测量返回值为时间(单位us),需要转换成怎样的
精度自己转换一下就可以了。
注意:该函数调用时占用定时器0,请在(public.h)
中使能相关的宏定义,失能其余的宏定义。
*****************************************************/
u16 ChaoShengBo_Measure(void) //0.17毫米每微秒
{
u16 i=20;
ChaoShengBo_Flag=0; //定时器溢出标志位清0
TR0=0;
TMOD=0x11; //定时器0工作方式1,16位计数器
TH0=TL0=0;
EA=1;
ChaoShengBo_TRIG=1;
while(i--); //只要保持10us以上即可启动一次信号发送
ChaoShengBo_TRIG=0;
while(ChaoShengBo_ECHO==0); //等待低电平过去,加上i做判断防止程序死在这里
TR0=1; //高电平到来,开启定时器测量
while(ChaoShengBo_ECHO==1); //定时器测量期间程序一直等待,加上i做判断防止程序死在这里
TR0=0; //测量完成,关闭定时器
if(ChaoShengBo_Flag!=1) //若定时器未溢出
{
return TH0*256+TL0; //返回测量值按12M晶振算,单位是us
}
else //若定时器溢出按理为65536*340/2/1000000=11.14112米,超出测量范围2cm~400cm
{
return 0; //定时器已经溢出,返回最大值表示测量失败
}
}
四、工程下载
《51单片机外设及常用传感器库》https://download.youkuaiyun.com/download/qq_34254642/12398307