花了1周時間從零基礎搞定C語言基礎,再通過51單片機程序仿真--PIC開發板實物操作,以下是本人學習過程中編寫的一個源程序
以下源程序是基于MPLAB IDE 8.73開發平臺,編譯器是PICC。
其它編譯器大同小異,
這是一個1602LCD的驅動顯示程序和DS18B20的驅動,此源程序實用性不大,只是給初學者一個參考,
溫度顯示范圍從-55度-+125度,精確到0.1度。單片機為PIC16F877A,晶振16MHZ
對于初學者來說,自己學會編寫一個驅動程序,也是一件快樂的事情。
********************************************************************************************************
#include
__CONFIG(HS&WDTDIS&LVPDIS);
//////////////////////////////////////////////////////////////
/****************LCD1602液晶顯示相關函數*********************/
//////////////////////////////////////////////////////////////
//1602液晶相關I/O設置
#define E RB3 //1602液晶的E腳接在RB3口上
#define RW RB4 //1602液晶的RW腳接在RB4口上
#define RS RB5 //1602液晶的RS腳接在RB5口上
//////////////////////////////////////////////////////////////
/****************DS18B20溫度檢測相關函數*********************/
//////////////////////////////////////////////////////////////
unsigned char temp1=0; //正負溫度判斷變量,temp1為1時表示負溫,為0時標識正溫
float temp=125.0; //溫度變量,默認125度
/***********************短延時函數***************************/
/*16MHZ晶振下,FOSE/4分頻,單個周期是0.25US*/
/*延時時間為:(10個固定周期2.5US)+(次數*【1.5US(5+1個周期)】=總延時時間)*/
void Delay(unsigned char t) //跳入這個循環需要3個指令周期
{ //跳出這個循環需要3個指令周期
while(t>0) //while第一次判斷語句要消耗3個指令周期,之后需要5個
t--; //t=t-1,消耗1個指令周期
} //也就是說t=0時需要10個指令周期
/***********************長延時函數***************************/
/*a=6,c=72時是1MS延時*/
void Time(unsigned int a) //跳入這個循環需要3個指令周期
{ //跳出這個循環需要3個指令周期
unsigned int b,c; //定義int型變量需要2個周期,這里有2個所以需要4個周期
asm("NOP");
asm("NOP");
for(b=0;a>b;b++) //第一次需要10個指令周期,之后需要11個指令周期
//定義int型變量需要2個指令周期,判斷需要9個指令周期,++操作需要1個指令周期
for(c=72;c>0;c--) ; //定義int需要2個指令周期,判斷需要7個指令周期,--操作需要1個指令周期(C=85,a=1是200US)
} //--循環體內部需要2+8*N+N個指令周期,也就是0.5US+(2US*N)+(0.25US*N)+NPLL(0.25US)
//////////////////////////////////////////////////////////////
/****************LCD1602液晶顯示相關函數*********************/
//////////////////////////////////////////////////////////////
/*******************LCD1602忙檢測函數************************/
void LCD1602_busy(void)
{
TRISD7=1; //將RD7口設置為輸入口,為讀做準備
RS=0; //RS=0、RW=1、E=1時,忙信號輸出到DB7,由RD7讀入
RW=1; //RS=0、RW=1、E=1時,忙信號輸出到DB7,由RD7讀入
E=1; //RS=0、RW=1、E=1時,忙信號輸出到DB7,由RD7讀入
while(RD7==1); //由RD7讀入1,表示1602液晶忙,需要等待
E=0; //讀完以后,恢復E的電平
TRISD7=0; //將RD7口設置為輸出口
}
/************LCD1602寫指令函數(不帶忙檢測)*****************/
void LCD1602_Write_com(unsigned char combuf)
{
RS=0; //選擇指令寄存器
RW=0; //選擇寫狀態
PORTD=combuf; //將命令字通過RD口送至DB
E=1; //E高電平將命令字寫入1602液晶
Delay(0); //添加一個空操作,使高電平至少維持1us
E=0; //寫完以后,恢復E的電平
}
/*************LCD1602寫指令函數(帶忙檢測)******************/
void LCD1602_Write_com_busy(unsigned char combuf)
{
LCD1602_busy(); //調用忙檢測函數
LCD1602_Write_com(combuf); //調用忙檢測函數
}
/*************LCD1602寫數據函數(帶忙檢測)******************/
void LCD1602_Write_data_busy(unsigned char databuf)
{
LCD1602_busy(); //調用忙檢測函數
RS=1; //選擇數據寄存器
RW=0; //選擇寫狀態
PORTD=databuf; //將命令字通過RD口送至DB
E=1; //E高電平將命令字寫入1602液晶
Delay(0); //添加一個空操作,使高電平至少維持1us
E=0; //寫完以后,恢復E的電平
}
/*****************LCD1602操作指定地址函數********************/
void LCD1602_Write_address(unsigned char x,unsigned char y)
{
x&=0x0f; //列地址限制在0-15間
y&=0x01; //行地址限制在0-1間
if(y==0) //如果是第一行
LCD1602_Write_com_busy(x|0x80); //將列地址寫入
else //如果是第二行
LCD1602_Write_com_busy((x+0x40)|0x80); //將列地址寫入
}
/**********************LCD1602初始化*************************/
void LCD1602_init(void)
{
Time(6); //延時1MS
LCD1602_Write_com(0x38); //8位數據總線,兩行顯示模式,5*7點陣顯示
Time(1); //延時約167US
LCD1602_Write_com(0x38); //8位數據總線,兩行顯示模式,5*7點陣顯示
Time(1); //延時約167US
LCD1602_Write_com(0x38); //8位數據總線,兩行顯示模式,5*7點陣顯示
LCD1602_Write_com_busy(0x38); //8位數據總線,兩行顯示模式,5*7點陣顯示
LCD1602_Write_com_busy(0x08); //顯示功能關,無光標
LCD1602_Write_com_busy(0x01); //清屏
LCD1602_Write_com_busy(0x06); //寫入新的數據后,光標右移,顯示屏不移動
LCD1602_Write_com_busy(0x0C); //顯示功能開,無光標
}
/***************LCD1602顯示指定地址內容函數******************/
void LCD1602_Disp(unsigned char x,unsigned char y,unsigned char buf)
{
LCD1602_Write_address(x,y); //先將地址信息寫入
LCD1602_Write_data_busy(buf); //再寫入要顯示的數據
}
/*********************LCD固定內容顯示函數********************/
void Fixed_display(void)
{
LCD1602_Disp(2,0,'D');
LCD1602_Disp(3,0,'S');
LCD1602_Disp(4,0,'1');
LCD1602_Disp(5,0,'8');
LCD1602_Disp(6,0,'B');
LCD1602_Disp(7,0,'2');
LCD1602_Disp(8,0,'0');
LCD1602_Disp(2,1,'T');
LCD1602_Disp(3,1,'e');
LCD1602_Disp(4,1,'m');
LCD1602_Disp(5,1,'p');
LCD1602_Disp(6,1,':');
LCD1602_Disp(11,1,'.');
LCD1602_Disp(13,1,0xdf);
LCD1602_Disp(14,1,'C'); //0x43
}
/*********************LCD動態內容顯示函數********************/
void Dynamic_Display(void)
{
unsigned char a=0,b=0,c=0,d=0;
if(temp1==1) //限制負溫溫度不低于零下55度
{
if(temp>55.0) temp=55.0;
}
else if(temp>125.0) temp=125.0; //限制正溫溫度不高于125度
d=(unsigned char)temp/100; //提取溫度百位數值
c=(unsigned char)temp%100/10; //提取溫度十位數值
b=(unsigned char)temp%10; //提取溫度個位數值
a=(unsigned char)(temp*10)%10; //提取溫度小數數值
if(d==0) //判斷百位顯示是否為0
{
if(temp1==1) //如果溫度為零下攝氏度時
{
if(c==0) //又如果溫度不低于零下十度時
{
c='-'-0x30; //十位顯示“-”負號
d='_'; //百位顯示“_”空格
}
else d='-'-0x30; //百位顯示“-”負號
}
else //否則溫度為正常攝氏度
{
d='_'; //百位顯示“_”空格
if(c==0) c='_'; //又如果十位為零時,十位顯示“_”空格
}
}
if(a>9) a=9;
LCD1602_Disp(8,1,d+0x30); //顯示溫度百位數值
LCD1602_Disp(9,1,c+0x30); //顯示溫度十位數值
LCD1602_Disp(10,1,b+0x30); //顯示溫度個位數值
LCD1602_Disp(12,1,a+0x30); //顯示溫度小數數值
}
//////////////////////////////////////////////////////////////
/****************DS18B20溫度檢測相關函數*********************/
//////////////////////////////////////////////////////////////
/******************DS18B20上電復位函數***********************/
unsigned char Reset(void)
{
unsigned char i;
TRISA4=0; //設置RA4位輸出口
RA4=0; //拉低DQ總線開始復位
Delay(200); //設定602.5US,DS18B20規定RESET復位時間不低于480US,不大于960US
//定時180-240之間
TRISA4=1; //設置RA4為輸入口,以釋放總線等電阻拉高總線
Delay(20); //設定62.5US,DS18B20規定等待間隔時間為60-240US
//定時20-60之間
if(RA4==0) //讀出數據并保存
i=0;
else i=1;
Delay(200); //維持約449.5US,符合總讀時隙不低于480US的要求
return i; //k=0為復位成功,k=1為復位失敗
}
/*******************DS18B20寫字節函數************************/
void WriteByte(unsigned char dat)
{
unsigned char i;
for(i=0;i<8;i++)
{
TRISA4=0; //設置RA4位輸出口
if(dat&0x01) //如果寫1
{
RA4=0; //拉低總線電平
Delay(1); //維持約4US,符合大于1US小于15US的規范
TRISA4=1; //設置RA4為輸入口,以釋放總線等電阻拉高總線
Delay(39); //維持約61US,符合不低于60US的要求
}
else //如果寫0
{
TRISA4=0; //設置RA4位輸出口
RA4=0; //拉低
Delay(39); //維持約61US,符合不低于60US的要求
TRISA4=1; //設置RA4為輸入口,以釋放總線等電阻拉高總線
Delay(0); //維持約2.5US,符合大于1US規范
}
dat=dat>>1; //寫入字節右移1位
}
}
/*******************DS18B20讀字節函數************************/
unsigned char ReadByte(void)
{
unsigned char i,buf=0;
for(i=1;i<=8;i++) //接收8次還原一個字節數據
{
buf=buf>>1; //接收前,先將接收緩沖區右移
TRISA4=0; //設置RA4位輸出口
RA4=0; //拉低
Delay(2); //維持7US,符合大于1US小于15US的規范
TRISA4=1; //設置RA4為輸入口,以釋放總線等電阻拉高總線 準備讀
Delay(2); //維持約7US,符合大于1US小于15US的規范
if(RA4==1) buf|=0x80; //讀出1位數據保存于buf中最高位
Delay(33); //維持約52US,符合總讀時隙不低于60US的要求
}
return buf; //退出的同時將接收緩沖區參數返回
}
/******************DS18B20啟動轉換函數***********************/
unsigned char Convert(void)
{
if(Reset()==0) //如果復位成功
{ Time(3); //延時0.5MS
WriteByte(0xcc); // 跳過多器件識別
WriteByte(0x44); // 啟動溫度轉換
return 1;
}
else
{
return 0;
}
}
/******************DS18B20讀溫度函數*************************/
void ReadFlash(void)
{
unsigned int i;
unsigned char Lsb,Msb;
if(Reset()==0)
{
Time(3); //延時0.5MS
WriteByte(0xcc); // 跳過多器件識別
WriteByte(0xbe); // 讀暫存器
Lsb=ReadByte(); // 低字節
Msb=ReadByte(); // 高字節
i=Msb;
i<<=8;
i=i|Lsb;
if(Msb>0x07) //如果判斷溫度為零下攝氏度時
{
temp1=1; //把零下標識符temp1置1
i=(~i)+1; //讀取溫度取反加1
temp=(float)i*0.0625; //轉換為真實溫度并把結果轉換成浮點數
}
else
{
temp1=0;
temp=(float)i*0.0625; //轉換為真實溫度并把結果轉換成浮點數
}
}
else //那么沒有采集到溫度就把初始溫度設置為125度
{
temp=125.0;
}
}
/*************************主函數*****************************/
void main(void)
{
TRISA=0xff; //初始化RA5-RA0的輸入輸出方向
PORTA=0x00; //初始化RA5-RA0數值
TRISB=0xc7; //初始化對RB口的初始方向進行設置 設置為輸出
TRISD=0x00; //初始化RD口的初始方向進行設置,設置為輸出
PORTB=0x00; //初始化RB口的數值
PORTD=0x00; //初始化RD口的數值
Time(6); //延時1MS秒,做內容切換顯示
LCD1602_init(); //初始化
LCD1602Fixed_display(); //LCD固定內容顯示
/**************************死循環****************************/
while(1) //死循環,單片機初始化后,將一直運行這個死循環
{
if(Convert()==1) //啟動轉換
{
ReadFlash(); //讀取溫度
Dynamic_Display(); //讀取內容顯示
}
}
}
********************************************************************************************************