一、單片機簡介
單片機是一種集成電路芯片,採用超大規模技術把具有數據處理能力(如算術運算,邏輯運算、數據傳送、中斷處理)的微處理器(CPU),隨機存取數據存儲器(RAM),只讀程序存儲器(ROM),輸入輸出電路(I/O口),可能還包括定時計數器,串行通信口(SCI),顯示驅動電路(LCD或LED驅動電路),脈寬調製電路(PWM),模擬多路轉換器及A/D轉換器等電路集成到一塊單塊芯片上,構成一個最小然而完善的計算機系統。這些電路能在軟件的控制下準確、迅速、高效地完成程序設計者事先規定的任務。 由此來看,單片機有着微處理器所不具備的功能,它可單獨地完成現代工業控制所要求的智能化控制功能,這是單片機最大的特徵。
然而單片機又不同於單板機,芯片在沒有開發前,它只是具備功能極強的超大規模集成電路,如果賦予它特定的程序,它便是一個最小的、完整的微型計算機控制系統,它與單板機或個人電腦(PC機)有着本質的區別,單片機的應用屬於芯片級應用,需要用戶瞭解單片機芯片的結構和指令系統以及其它集成電路應用技術和系統設計所需要的理論和技術,用這樣特定的芯片設計應用程序,從而使該芯片具備特定的功能。
不同的單片機有着不同的硬件特徵和軟件特徵,即它們的技術特徵均不盡相同,硬件特徵取決於單片機芯片的內部結構,用戶要使用某種單片機,必須瞭解該型產品是否滿足需要的功能和應用系統所要求的特性指標。這裏的技術特徵包括功能特性、控制特性和電氣特性等等,這些信息需要從生產廠商的技術手冊中得到。軟件特徵是指指令系統特性和開發支持環境,指令特性即我們熟悉的單片機的尋址方式。
二、設計任務和要求
(一).設計目的
1.掌握單片機拓展總線的工作原理和時序操作;
2.掌握單總線協議的基本特點及通信過程;
3.掌握數字溫度傳感器DS18B20的基本特點及單總線控制協議;
4.掌握單片機IO端口模擬單總線時序控制程序的編寫方法;
5.掌握LCD液晶顯示器的顯示驅動方法。
(二).設計要求
1.單片機P0.0和DS18B20的數據端相連;
2.編寫單片機通過IO端口模擬單總線時序控制DS18B20的程序,讀出溫度;
3.在1602字符點陣液晶顯示模塊上顯示溫度;
4.單片機拓展總線的地址線和LCD的控制線的編寫方法。
三、設計方案的總體設計框圖
3.1硬件電路框圖
3.2硬件電路概述
硬件:PC機,
nKDE—51單片機實驗教學系統
1)將CPU板上的單片機P1.0~P1.3(J2或J6的1~4號引腳)和模擬總線接口IO板上的J4相連。
2)將CPU板上的單片機P2(J3或J7)和模擬總線接口IO板上的J5相連,注意P2.0和J5的第1腳相連。
3)將CPU板上的單片機P0.0(J1或J5的1號引腳)和模擬總線接口IO板上DS18B20的DBUS(J9的1號引腳)相連;將CPU板上的COM1和PC機相連。
4)將CPU板上的單片機P3.7(J4或J8的8號引腳)和基本IO板上的蜂鳴器驅動位(J8的最低位)相連。
5)將CPU板上的單片機P0(J1或J5)和基本IO板上的LED燈(J4)相連。
3.3主控電路
主程序需要調用2個子程序,分別爲;
1).實時溫度顯示子程序;驅動數碼管把實時溫度值送出在LED數碼管顯示
2).溫度顯示、報警子程序;1602字符點陣液晶顯示溫度,當溫度超過設定的報警溫度值時產生報警,即驅動蜂鳴器鳴叫
3.4顯示電路
溫度顯示模塊需要調用10個子程序,分別爲;
1).LCM延時程序
2).查詢LCM的忙標誌/當前AC地址程序
3).LCM清屏程序
4).向LCM寫入控制字程序
5).向LCM寫入數據程序
6).LCM初始化程序
7).移動光標道X行、Y行程序
8).在指定位置顯示一個字符程序
9).從指定的位置開始顯示字符串程序
10).將指定位置顯示的字符閃爍
3.5報警溫度調節電路
當溫度小於22攝氏度時,只顯示溫度。溫度大於等於22攝氏度小於23攝氏度,二極管全亮,顯示屏警告。溫度大於23攝氏度小於25攝氏度時,流水燈低速率循環亮,蜂鳴器Be一聲間隔響。溫度大於25攝氏度小於27攝氏度時,流水燈高速率循環亮,蜂鳴器Be一聲間隔響,間隔時間變短。溫度大於27攝氏度時,二極管常亮,蜂鳴器長響。
3.6溫度傳感器DS18B20測溫原理
數字溫度傳感器模塊
1.DS18B20性能
1).獨特的單線接口僅需一個端口引腳進行通信
2).簡單的多點分佈應用
3).無需外部器件
4).可通過數據線供電
5).零待機功耗
6).測溫範圍-55~+125℃,以0.5℃遞增
7).可編程的分辨率爲9~12位,對應的可分辨溫度分別爲0.5℃、0.25℃、0.125℃和0.0625℃
8).溫度數字量轉換時間200ms,12位分辨率時最多在750ms內把溫度轉換爲數字
9).應用包括溫度控制、工業系統、消費品、溫度計和任何熱感測系統
10).負壓特性:電源極性接反時,傳感器不會因發熱而燒燬,但不能正常工作
2.DS18B20時序圖
主機使用時間隙來讀寫DS18B20的數據位和寫命令字的位。
初始化時序如下圖:
3. 7硬件電路連接引腳說明
1)將CPU板上的單片機P1.0~P1.3(J2或J6的1~4號引腳)和模擬總線接口IO板上的J4相連。
2)將CPU板上的單片機P2(J3或J7)和模擬總線接口IO板上的J5相連,注意P2.0和J5的第1腳相連。
3)將CPU板上的單片機P0.0(J1或J5的1號引腳)和模擬總線接口IO板上DS18B20的DBUS(J9的1號引腳)相連;將CPU板上的COM1和PC機相連。
4)將CPU板上的單片機P3.7(J4或J8的8號引腳)和基本IO板上的蜂鳴器驅動位(J8的最低位)相連。
5)將CPU板上的單片機P0(J1或J5)和基本IO板上的LED燈(J4)相連。
3.8單片機硬件連接實物圖及實驗結果
連接實物圖:
實驗結果:
(1)低於22攝氏度時,正常:LED燈不亮,蜂鳴器不響,顯示屏不警報
(2)22~23攝氏度時,二極管常亮,顯示屏警報,蜂鳴器不響
(3) 溫度大於23攝氏度小於25攝氏度時,流水燈低速率循環亮,蜂鳴器Be一聲間隔響。溫度大於25攝氏度小於27攝氏度時,流水燈高速率循環亮,蜂鳴器Be一聲間隔響,間隔時間變短。
(4) 溫度大於27攝氏度時,二極管常亮,蜂鳴器長響。
(5)串口顯示:超過21攝氏度,警報,低於21攝氏度,警報消除
四、系統軟件算法設計
4.1主程序
主程序mian.c需要調用2個子程序,分別爲:
1、實時溫度顯示子程序:驅動數碼管把實時溫度值送出在LED數碼管顯示
2、溫度顯示、報警子程序:1602字符點陣液晶顯示溫度,當溫度超過設定的報警溫度值時產生報警,即驅動蜂鳴器鳴叫。
見附錄一;
4.2 子程序
1、實時溫度顯示子程序:驅動數碼管把實時溫度值送出在LED數碼管顯示
2、溫度顯示、報警子程序:1602字符點陣液晶顯示溫度,當溫度超過設定的報警溫度值時產生報警,即驅動蜂鳴器鳴叫。
見附錄二;
五、總結與體會
5.1 課程設計過程中遇到的問題及解決方法總結
(1)出現 requires ANSI-styleprototype
分析:函數聲明出錯,聲明函數時,需要注意大小寫的問題keil軟件中,無法自動識別。
解決:仔細查看,修改。
(2)多餘函數未調用,UNCALLEDSEGMENT,IGNORED FOR OVERLAY PROCESS
分析:教材上定義函數,應該都利用到了,此時應該思考,是否打錯問題。
解決:由於慣性思維,空函數(_nop()_)定義頭文件是,打的strings.h。
所以出現了,函數未調用,佔用多餘空間的問題。
(3)出現 illegal pointerconversion 非法指針轉換
分析:可疑指針轉換
解決:查看被調用的函數原型,檢查入口參數是否是與原型一致的函數。
(4)顯示屏無法正常顯示
分析;無法顯示的幾種原因:
1)程序錯誤;2)器件損壞;3)電路鏈接錯誤;4)串口鏈接失敗
解決:1)將程序導入其他單片機系統,運行成功,排除。
2)換線和顯示屏,還是顯示不出來。
3)仔細查看電路鏈接,發現控制顯示屏的IO 板上J4與單片機J1相連接,正確連接之後,顯示屏正常顯示。
(5)串口顯示雜亂
分析:可以通過換行,使得想要查詢的內容,即溫度,更清晰的看見。
解決:修改程序,進行換行處理:
printf("\r\nTemperature code HI=%02bX,LO=%02bX ",tmph,tmpl);
if(t.z>=22)
printf("\nWarning!!!Temperature = %d.%04d",t.z,t.x) ; //大於22度溫度警告
else
printf("\r\n Temperature = %d.%04d",t.z,t.x);
5.2 課程設計體會
在本次課程設計中,讓我受益匪淺,無論是專業知識上還是學習態度上,都有很大改變。
專業知識上,之前都是在課上聽老師講課,和對着課本記憶各種指令,程序,用法,通過這次課設,真正接觸到了單片機,它各個接口的含義,在課程設計中漸漸得到更深的瞭解,記憶也更加深刻。
學習態度上,首先,不能慣性思維,在接觸到新的知識的時候,仔細研究,而不是根據以往經驗,泛泛而談;其次,不能粗心大意,課程設計的程序很多,稍作不注意就會拼寫錯誤,或者大小寫出問題,以至於Target not creat;最後,遇到問題,要善於去解決,,在每次弄清楚自己的錯誤,解決運行成功的時候,很是滿足,很有成就感,相信做其他事也是這樣,不斷探索,會有意想不到的收穫。
這次課程設計,得到了老師和同學的幫助,很是感謝。
六、參考文獻
[1]李廣弟等.單片機基礎[M].北京航空航天出版社,2001.
[2]王東峯等.單片機C語言應用100例[M].電子工業出版社,2009.
[3]陳海宴.51單片機原理及應用[M].北京航空航天大學出版社,2010.
[4]劉守義等.單片機技術基礎[M].西安電子科技大學出版社,2007.
附錄一:
main.c:
#include <reg51.h>
#include <intrins.h>
#include <stdio.h>
#include "lcm16x2p.h"
#include "DS18B20.h"
#define OSC 11059200 //晶振頻率
#define BAUDRATE 9600 //波特率
#define LCM_DB P2
sbit BUZ_CON = P3^7;
sbit LED =P0;
void main(void){
unsigned charucTH,ucTL,Ticks; //中斷程序
unsigned int i;
unsigned char tmph,tmpl;
Temp t;
unsigned charstrTemp[6]; //顯示到屏幕的溫度數據
LCM_BLC = 0; //開背光
TMOD = 0x21; //選擇方式2作爲波特率發生器
SCON = 0x50; //串口方式1,允許中斷
PCON |= 0x80; //SMOD=1
TL1 = 256 -(OSC/12/16/BAUDRATE);
TH1 = 256 -(OSC/12/16/BAUDRATE);
TR1 = 1; //啓動定時器
TI = 1; //TI有效
//檢測DS18B20溫度傳感器是否存在並復位傳感器
if(DSReset())
printf("\r\Temp sensor ResetOK!");
else printf("\r\Temp Sensor Notready!");
while(1){
DSReset(); //復位傳感器
Delay(12);
DSWriteByte(SkipROM); //跳讀 省時
DSWriteByte(StartConvert); //溫度轉換
for(i=0;i<40000;i++);
DSReset();
Delay(12);
DSWriteByte(SkipROM);
DSWriteByte(ReadMemory); //讀RAM程序
tmpl = DSReadByte();
tmph = DSReadByte();
printf("\r\nTemperature code HI=%02bX,LO=%02bX ",tmph,tmpl);
DSReadTemp(&t);
//準備輸出到顯示屏的數據
strTemp[0]=t.z/10+0x30; //十位
strTemp[1]=t.z%10+0x30; //個位
strTemp[2]='.'; //小數點
strTemp[3]=t.x/1000+0x30; //十分位
strTemp[4]=t.x%1000/100+0x30; //百分位
strTemp[5]='C';
if(t.z>=22)
printf("\nWarning!!!Temperature= %d.%04d ",t.z,t.x) ; //大於22度溫度警告
else
printf("\r\nTemperature = %d.%04d",t.z,t.x);
if(t.z>=22&&t.z<23){
P0=0x00; //大於22小於23度LED燈全亮
}
else
{
P0=0xff; //燈滅
}
if(t.z>=23){
BUZ_CON=0;
LCMDelay(100);
BUZ_CON=1;
P0=0xfe;
LCMDelay(60);
P0=0xfd;
LCMDelay(60);
P0=0xfb;
LCMDelay(60);
P0=0xf7;
LCMDelay(60);
P0=0xef;
LCMDelay(60);
P0=0xdf;
LCMDelay(60);
P0=0xbf;
LCMDelay(60);
P0=0x7f;
LCMDelay(60);
P0=0xff; //大於23度流水燈亮,蜂鳴器響
if(t.z>=25)
{
BUZ_CON=0;
LCMDelay(100);
BUZ_CON=1;
P0=0xfe;
LCMDelay(20);
P0=0xfd;
LCMDelay(20);
P0=0xfb;
LCMDelay(20);
P0=0xf7;
LCMDelay(20);
P0=0xef;
LCMDelay(20);
P0=0xdf;
LCMDelay(20);
P0=0xbf;
LCMDelay(20);
P0=0x7f;
LCMDelay(20);
P0=0xff; //大於25度流水燈速率變快蜂鳴器間隔時間變短
}
if(t.z>=27)
{
BUZ_CON=0;
P0=0x00; //大於27度時,流水燈長亮,蜂鳴器長響
}
}
else{
BUZ_CON=1; //蜂鳴器不響
}
/* 初始化*/
EA = 0; // 停止所有中斷
Ticks = 0;
ucTH =(65536-OSC/12/20)/256; // 計算 50ms 定時的時間常數
ucTL =(65536-OSC/12/20)%256;
TMOD = 0x01; // T0:模式 1,16 位定時器
TH0 = ucTH;
TL0 = ucTL;
ET0 = 1; // T0 允許中斷
TR0 = 1; // 啓動定時器
EA = 1; // 打開總中斷允許
/*輸出溫度數據到顯示屏 */
LCMInit(); //初始化LCM顯示器
LCMClear(); //清屏
if(t.z>=22){
LCMDisplayString(0,0,"TempWarning!!!"); //大於22度警告
LCMDisplayString(1,0,"Temp:");
LCMDisplayString(1,6,strTemp);
}
else{
LCMDisplayString(0,0,"Temperature:");
LCMDisplayString(1,0,strTemp);
}
//閃爍(刷新)
for(i = 0; i < 16;i++)
LCMBlink(0,i,BLINK);
for(i = 0; i < 16;i++)
LCMBlink(1,i,BLINK);
}
}
附錄二:
DS18B20.h
#ifndef __DS18B20H__
#define __DS18B20H__
#define ReadROM 0x33
#define MatchROM 0x55
#define ReadMemory 0xBE
#define SkipROM 0xCC
#define SearchROM 0xF0
#define StartConvert 0x44 //變換指令
typedef struct{
int z; //整數部分
int x; //小數部分
}Temp;
extern unsigned char ucTH,ucTL,Ticks; //外部引用
void Delay(unsigned int);
bit DSReset(void);
void DSWriteByte(char);
unsigned char DSReadByte(void);
void DSReadTemp(Temp *t);
#endif
DS18B20.c
#include "DS18B20.h"
#include <reg51.h>
#include <intrins.h>
sbit DQ = P0^0;
/*
Delay
通過循環計時
參數:int,表示要延時的 毫秒 數
*/
void Delay(unsigned int i)
{
while(i--);
}
/*
DSReset
復位DS18B20並返回是否存在
*/
bit DSReset(void)
{
bit x;
DQ = 1;
Delay(8);
DQ = 0; // 主機拉低總線
Delay(80); // 延時約 500 個機器週期
DQ = 1; // 主機釋放總線
Delay(8); // 延時 60 個機器週期
x = DQ;
Delay(20);
if(x == 0)
return 1;
else return 0;
}
/*
DSWriteByte
向 DS18820 寫入一個字節
*/
void DSWriteByte(unsigned char c)
{
unsigned char ic;
for( ic = 0; ic < 8;ic++ )
{
DQ = 0; //主機拉低總線,開始寫位
DQ = c&0x01;
Delay(5); //延時60個機器週期
DQ = 1; //釋放總線
c >>= 1;
}
}
/*
DSReadByte
從溫度傳感器讀出一個字節
返回:讀出的字節
*/
unsigned char DSReadByte(void)
{
unsigned char c,ic;
c = 0;
for(ic = 0; ic < 8;ic++)
{
DQ = 0;
c >>= 1; //右移
DQ = 1;
if(DQ )
c |= 0x80;
Delay(4);
}
return c;
}
/*
DSReadTemp
讀出溫度
參數:溫度(由整數和小數部分構成)
*/
void DSReadTemp(Temp *t)
{
unsigned char tmpl,tmph;
DSReset();
Delay(12);
DSWriteByte(SkipROM);
DSWriteByte(StartConvert);
DSReset();
Delay(12);
DSWriteByte(SkipROM);
DSWriteByte(ReadMemory);
tmpl = DSReadByte();
tmph = DSReadByte();
t->z =(int)((tmph&0x07)<<4 | (tmpl&0xf0)>>4);
t->x =(int)((tmpl&0x0f)*625);
}
/*
T0ISR
50ms中斷服務程序
*/
void T0ISR(void) interrupt 1
{
unsigned charucTH,ucTL,Ticks;
TH0 = ucTH;
TL0 = ucTL;
TR0 = 1;
Ticks ++;
if(Ticks == 20)
{
Ticks = 0;
}
}
lcm16x2p.h
#ifndef LCM16X2_H
#define LCM16X2_H
#define BUSYFLAG 0x80 //忙標誌
#define BLINK 0x01 //閃爍
#define NOBLINK 0x00 //不閃爍
sbit LCM_RS = P1 ^ 0;
sbit LCM_RW = P1 ^ 1;
sbit LCM_E = P1 ^ 2;
sbit LCM_BLC = P1 ^ 3;
unsigned char LCMReadState(void);
void LCMDelay(int);
void LCMWriteCmd(unsigned char);
void LCMWriteData(unsigned char);
void LCMInit(void);
void LCMClear(void);
void LCMGotoXY(unsigned char, unsigned char);
void LCMDisplayString(unsigned char, unsigned char, unsigned char*);
void LCMBlink(unsigned char, unsigned char, unsigned char);
#endif
lcm16x2p.c
#include <reg51.h>
#include <intrins.h> //因爲程序中出現了_nop_()空指令
#include "lcm16x2p.h"
#define LCM_DB P2
/*
LCMDelay
通過循環方式延時
int 表示要延時的毫秒數
*/
void LCMDelay(int ms) {
unsigned int dataDelayConst = 125;
unsigned int i, cnt;
cnt = DelayConst * ms;
for (i = 0; i < cnt;i++);
}
/*
LCMReadState
查詢LCM的忙標誌/當前AC地址
返回:BYTE,最高bit爲1表示忙,爲0表示閒
*/
unsigned char LCMReadState(void){
unsigned char state;
LCM_E = 0; //E爲總線週期有效顯示,低電平無效
LCM_RS = 0; //RS爲寄存器選擇線,低電平指令寄存器操作
LCM_RW = 1; //RW爲讀寫選擇線,高電平讀操作
LCM_E = 1; //高電平有效
_nop_(); //空指令,較爲精確的控制延遲時間
_nop_();
state = LCM_DB;
LCM_E = 0;
return state;
}
/*
LCMClear
清屏
*/
void LCMClear(void){
LCMDelay(1);
LCM_E = 0;
LCM_RS = 0; //指令寄存器
LCM_RW = 0; //寫操作
LCM_DB = 0x01;
LCM_E = 1;
_nop_(); //空指令,較爲精確的控制延遲時間
_nop_();
LCM_E = 0;
LCMDelay(1);
}
/*
LCMWriteCmd
向LCM寫入控制字
BYTE,命令字節。寫入前不判斷忙表示(因爲初始化過程中不能判斷)
*/
void LCMWriteCmd(unsigned char cmd){
LCMDelay(1);
LCM_E = 0;
LCM_RS = 0;
LCM_RW = 0;
LCM_DB = cmd;
LCM_E = 1;
_nop_();
_nop_();
LCM_E = 0;
}
/*
LCMWriteData
向LCM寫入數據
BYTE,將要寫入數據
*/
void LCMWriteData(unsigned char dc){
LCM_RS = 1;
LCM_RW = 0;
LCM_DB = dc;
LCM_E = 1; //高電平有效
_nop_();
_nop_();
LCM_E = 0;
}
/*
LCMInit
初始化LCM
DWORD,晶體頻率(Hz),供計算延時參數
*/
void LCMInit(void)
{
LCMDelay(60); //延時60ms,等待LCM復位
LCMWriteCmd(0x38); //功能設置:8位接口,2行,5x7字符點陣
LCMDelay(5); //延時
LCMWriteCmd(0x38); //第二次
LCMDelay(1); //延時
LCMWriteCmd(0x38); //此後可以通過監測忙標誌判斷指令執行情況
while(LCMReadState()& BUSYFLAG);
LCMWriteCmd(0x08); //關閉顯示
while(LCMReadState()& BUSYFLAG);
LCMWriteCmd(0x01); //清屏
while(LCMReadState()& BUSYFLAG);
LCMWriteCmd(0x06); //顯示地址自動增量,整體不移位
while(LCMReadState()& BUSYFLAG);
LCMWriteCmd(0x0e); //開顯示,開光標,不閃爍
while(LCMReadState()& BUSYFLAG);
}
/*
LCMGotoXY
移動光標到X行Y列
BYTE,X行(0,1)Y列(橫向,取值0~0x0f)
*/
void LCMGotoXY(unsigned char x, unsigned char y) // x;行(0~1)y;列(0~F)
{
unsigned char cmd;
if(x == 0){
cmd = 0x80 | y;
}
else{
cmd = 0x80 | 0x40| y;
}
LCMWriteCmd(cmd);
while(LCMReadState()& BUSYFLAG);
}
/*
LCMDisplayChar
在指定位置顯示一個字符
X行(0,1)Y列(橫向,取值0~0x0f),ch表示將要顯示的字符(ASSCII碼)
*/
void LCMDisplayChar(unsigned char x, unsigned char y, unsigned charch){
LCMGotoXY(x,y);
LCMWriteData(ch);
}
/*
LCMDisplayString
從指定位置開始顯示字符
X行(0,1)Y列(橫向,取值0~0x0f),*str爲指向將要顯示的字符串的指針
*/
void LCMDisplayString(unsigned char x, unsigned char y, unsignedchar* str){
unsigned char ptr;
ptr = 0;
while(*(str+ptr) != 0)
{
LCMDisplayChar(x, (y+ptr), *(str+ptr));
ptr++;
}
}
/*
LCMBlink
將指定位置顯示的字符閃爍(同時關閉光標)
X行(0,1)Y列(橫向,取值0~0x0f),cmd=BLINK:閃爍, NOBLINK:不閃爍
*/
void LCMBlink(unsigned char x, unsigned char y, unsigned char cmd)
{
LCMGotoXY(x,y);
if(cmd == BLINK)
{
LCMWriteCmd(0x0d);
}
else
{
LCMWriteCmd(0x0c);
}
}