?? avrvs ds18b20.c
字號:
// AVR微控制器 VS 溫度芯片DS18B20
//日期:2006年1月6日 人氣:2 查看:[大字體 中字體 小字體]
/*
********************************************************
* 文件: ds18b20.c
* 功能: AVR微控制器 VS 溫度芯片DS18B20(TO-92)
* 工具: WinAVR20040404(AVR-GCC)
* 作者: 孤欲化境(qjy_dali)
* E-mail: qjy_dali@sohu.com
* 日期: 6/02/2004
* 版本: 1.41
* 聲明: 你可隨意地拷貝,復制或修改這個程序,但請你注明
* 你的修改。本作者不對這個程序的后果負責,無論是明
* 確的,還是隱含的(^_^)。自由軟件不是萬能的,但它
* 的確是令人振奮的。支持一下GCC!!!
********************************************************
*/
#ifndef _DS18B20_C_
#define _DS18B20_C_
/* 我的一線溫度芯片DS18B20被連接到AVR微控制器ATmega8
* -16PI(PDIP28)的PD3(INT1)引腳,但是我只是用了PD3功能而
* 沒有使用中斷INT1功能。DS18B20(TO-92)的連接如下:
* +-------------+-------------------------------------+
* | Pin-1(GND) | GND(ground) |
* +-------------+-------------------------------------|
* | Pin-2(DQ) | 通過240歐姆的電阻連接到ATmega8的 |
* | | PD3引腳,同時用一個3K的電阻上拉到 |
* | | VCC(5V)。 |
* +-------------+-------------------------------------+
* | Pin-3(VD) | GND(ground) |
* +-------------+-------------------------------------+
* 說明:很顯然,我采用的是"總線竊電"模式,這是DS-
* 18B20數據手冊認可的工作模式之一。串聯240歐姆電阻的用意
* 是為了防止有缺陷的用戶程序損壞DS18B20的可能性。例如,
* 如果用戶沒有正確地用OC(集電極開路)或OD(漏極開路)結構去
* 驅動DS18B20,而是錯誤地選擇了推挽式結構,則DS18B20會立
* 即或在"被虐待"一段時間后"死翹翹"(^_^)。當然,240歐姆的
* 取值未必是最恰當的,設計者可自己去優化。這需要設計者仔細
* 閱讀數據手冊。
*
* 此外,我采用了DS18B20的默認精度(12位),并未修改,我
* 也沒有使用它的其它功能。簡而言之,這只是一個簡單的讀取溫度
* 的實例。用戶可以自己去改進,自己去研究數據手冊(我并未完全
* 看,只看了想看的一點點 ^_^ )。但是我可以肯定一點,這個程
* 序我是實踐過的!并且成功地讀取了溫度值!
*
* 請注意我是如何驅動DS18B20的:我是用AVR的方向寄存器
* 而不是輸出端口寄存器! 同時預先在輸出端口寄存器中寫入0。
* 這實際上相當于一個三態門:只不過輸入被接地,使它成了一
* 個OC門,使能端成了這個OC門的實際輸入!
*
* 1-wire總線的電氣特性與I2C總線相似,具有線與功能,所
* 以,總線上的任一設備都可在合適的時間強行拉低總線,但是總線
* 要呈現高電平,則必須是每一個設備都釋放了總線。就像我下面的
* 宏DQ_TO_1(),它只是釋放了總線,但不是說總線一定被強行驅動
* 至高電平,總線的高電平是由上拉電阻實現的。
*/
#define DQ_18B20 (1<<3) // PD3
#define DQ_TO_0() (DDRD |= DQ_18B20) // PD3='0'
#define DQ_TO_1() (DDRD &= ~DQ_18B20) // PD3='float'
#define DQ_status() (PIND & DQ_18B20) // read PD3 pin
/* 請認真檢查你的AVR微控制器的時鐘頻率! 特別注意:頻率
* 定義的單位是MHz! 并且請使用浮點數! 假如你的晶振是12MHz,
* 你應該寫成12.0000或12.0之類。
* 我的實驗電路的晶振是:11.0592MHz
*/
#ifndef CPU_CRYSTAL
#define CPU_CRYSTAL (11.0592)
#endif
/* 請包含WinAVR系統提供的延時頭文件"delay.h",其中給出
* 兩個延時模塊,我用16位的那個(16-bit count, 4 cycles/l-
* oop.),細節請看這個頭文件。
*/
#define wait_us(us) _delay_loop_2((INT16U)((us)*CPU_CRYSTAL/4))
/*---------------- 函數原型聲明 ------------------*/
// 1個初始化模塊
void ds18b20_config(void); // 配置端口
// 3個基本模塊
BOOL ds18b20_reset(void); // 復位DS18B20
void ds18b20_write(INT8U dat); // 寫字節到DS18B20
INT8U ds18b20_read(void); // 讀字節從DS18B20
// 2個應用模塊
void convert_T(void); // 啟動溫度轉換
INT16U read_T(void); // 讀取轉換值
/*-------------------------------------------------------
* 配置(使能)AVR與DS18B20的接口
*/
void ds18b20_config(void)
{
DDRD &= ~DQ_18B20; // 輸入模式(上電時為高電平)
PORTD &= ~DQ_18B20; // 輸出鎖存器寫0,以后不再更改
}
/*-------------------------------------------------------
* 復位1-wire總線,并探測是否有溫度芯片DS18B20(TO-92
* 封裝)掛在總線上,有返回SUCC,沒有返回FAIL
*/
BOOL ds18b20_reset(void)
{
BOOL bus_flag;
DQ_TO_0(); // 設置1-wire總線為低電平(占領總線)...
/* 現在延遲480us~960us, 與硬件密切相關,但應盡可能選小值(480us),
把抖動留給系統(比如在延遲期間發生中斷導致延遲變長)。
*/
wait_us(490); // 490us
cli(); // 下面這段時間要求比較嚴格,為保險起見,關中斷
DQ_TO_1(); // 設置1-wire總線為高電平(釋放總線)
/* 這個浮點數是由編譯器計算好的,而不是由你的MCU在運行時臨時計算的,
所以不會占用用戶MCU的時間,不必擔心(看看前面的宏你就可以確定了)
*/
wait_us(67.5); // 最佳時間: 60us+7.5us!(忙延時,只是一種策略)
// 探測總線上是否有器件
if(DQ_status()) bus_flag=FAIL; // 復位單總線但沒有發現有器件在線
else bus_flag=SUCC; // 復位單總線并發現有器件在線
sei(); // 退出臨界代碼區(開中斷)
/* 保證Master釋放總線的時間(不是說總線處于高電平的時間)不小于
480us即可,這一時間從讀總線狀態之前就開始了,所以這里把這個
時間計算在內。在Master釋放總線的前半段,也是被動器件聲明它
們在線之時。
*/
wait_us(490-67.5); // 490-67.5us
return(bus_flag);
}
/*--------------------------------------------------------
* 寫命令或數據到溫度芯片DS18B20(發送一個字節)
*/
void ds18b20_write(INT8U dat)
{
INT8U count;
// 每個字節共8位,一次發一位
for(count=0; count<8; count++) {
cli(); // 保證絕對不會發生中斷!
DQ_TO_0(); // 設置1-wire總線為低電平
wait_us(2); // about 2us
if(dat&0x01) DQ_TO_1(); // 并串轉換,先低位后高位
else DQ_TO_0();
dat >>= 1; // 下一位做好準備
// 60us~120us(實際不能到120us, 因為其它語句也用時間了!)
wait_us(62); // 62us
DQ_TO_1();
sei(); // 恢復系統中斷
wait_us(2); // 2us
}
}
/*---------------------------------------------------------
* 從溫度芯片DS18B20讀配置或數據(接收一個字節)
*/
INT8U ds18b20_read(void)
{
INT8U count,dat;
dat = 0x00; // 數據接收準備
// 每個字節共8位,一次收一位
for(count=0; count<8; count++) {
cli(); // 保證絕對不會發生中斷!
// 從總線拉低到讀總線狀態,不能大于15us!
DQ_TO_0(); // 設置1-wire總線為低電平(拉低總線以同步)
wait_us(2); // 2us
DQ_TO_1(); // 設置1-wire總線為高電平(釋放總線)
wait_us(4); // 4us
dat >>= 1;
if(DQ_status()) dat|=0x80; // 讀取總線電平,先收低位再收高位
sei(); // 恢復系統中斷
wait_us(62); // 必須大于60us
}
return(dat);
}
/*-------------------------------------------------------
* 我的電路中只有一個器件DS18B20,所以不需要多個器件的ID
* 識別,跳過之后,啟動溫度轉換,但在啟動后,用戶應等待幾百個
* 毫秒,才能讀到這次的轉換值,這是DS18B20的數據手冊規定的。
* 因為溫度轉換是需要時間的嘛!(^_^)
*/
void convert_T(void)
{
if(ds18b20_reset()==SUCC) { // 如果復位成功
ds18b20_write(0xcc); // 跳過多器件識別
ds18b20_write(0x44); // 啟動溫度轉換
}
}
/*-------------------------------------------------------
* 讀取轉換后的溫度值
* 我假定DS18B20一定是正確的,所以沒有返回有關狀態。當你故意
* 把DS18B20從電路中拔下而能讓程序告訴你出錯時,你可以自己修
* 改這段代碼!
*/
INT16U read_T(void)
{
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -