一、前言
記得那是剛上大一的時候,學校電子設計創新實驗室的學長們自發的組織單片機技能培訓,每次週末都會安排幾個小時的培訓,那時候自學了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.csdn.net/download/qq_34254642/12398307