本帖最后由 PythonS 于 2018-11-1 10:10 編輯
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h> //中斷函數頭文件
//××××××××××××引腳宏定義×××××××××××××
//18B20定義
#define SET_DQ (PORTC) |= (1 <<4) // 18b20 高電平
#define CLR_DQ (PORTC) &=~(1 <<4) // 18b20 低電平
#define DQ_IN (PINC) & (1<<4) // 18b20信號輸入
#define SET_OUT (DDRC) |= (1<<4) //PC4定義成輸出
#define SET_IN (DDRC) &=~(1<<4) //PC4定義成輸入
//常量聲明
#define BAUD 9600
//全局變量聲明
unsigned char Temp_H,Temp_L,OK_Flag; //溫度高位,低位,復位成功標志
//函數聲明
void Delayus(unsigned int lus); //us延時函數
void Delayms(unsigned int lms); //ms延時函數
void Port_DS18b20(void); //DS18B20端口配置
void Port_Init(void); //端口初始化配置
void Usart_Init(void); //USART寄存器設置
void Usart_PutChar(unsigned char cTXData); //字節發送函數
void Usart_PutString(unsigned char *pcString); //字符串發送函數
unsigned char DS18B20_Init(void); //DS18B20初始化
unsigned char Read_18b20(void); //讀18b20
void Write_18b20(unsigned char dat); //寫18b20
int main(void)
{
unsigned char i;
unsigned int tempint,tempint1,tempint2,tempint3,tempint4;
//分別存儲溫度整數值,整數值的千,百,十,個位
unsigned int temppoint,temppoint1,temppoint2,temppoint3,temppoint4;
//分別存儲溫度小數值,小數值的千,百,十,個位
Port_Init(); //端口初始化
Usart_Init(); //串口初始化
Port_DS18b20(); //DS18B20端口初始化
tempint = 0; //變量初始化
temppoint=0;
Temp_H = 0;
Temp_L = 0;
OK_Flag = 0;
Usart_PutString("DS18B20 溫度測量實驗");
Usart_PutChar(0x0D);
Usart_PutChar(0x0A); //結尾發送回車換行
sei(); //使能全局中斷
while(1)
{
if(DS18B20_Init()) //判斷DS18B20復位是否成功
{
PORTC = 0x00;
}
else
{
PORTC = 0x01;
}
cli(); //關中斷
DS18B20_Init(); //初始化DS18B20
Write_18b20(0Xcc); //發送ROM指令,跳過ROM匹配
Write_18b20(0X44); // 發送溫度轉換命令
for(i=0;i<50;i++) //延時1S,等轉換完成
{
Delayms(20);
}
DS18B20_Init(); //初始化DS18B20
Write_18b20(0Xcc); //發送ROM指令,跳過ROM匹配
Write_18b20(0Xbe); //發送讀取暫存器指令
Temp_L = Read_18b20(); //獲得溫度的低位
Temp_H = Read_18b20(); //獲得溫度的高位
if(Temp_H & 0x08) //判斷溫度的正負
{
Temp_H = ~Temp_H; //負溫度。取反加1
Temp_L = ~Temp_L; //
SREG |= ~(1 << SREG_C); //清零進位位標志
Temp_L++; //溫度低字節加1
if(SREG & (1 << SREG_C)) //有進位嗎?
{
Temp_H++; //有進位,則溫度高字節加1
}
}
tempint = ((Temp_H << 4) & 0x70) | (Temp_L >> 4); //獲得溫度的整數位
tempint1 = tempint / 1000; //千位
tempint2 = tempint % 1000 / 100; //百位
tempint3 = tempint % 100 / 10; //十位
tempint4 = tempint % 10; //個位
temppoint = Temp_L & 0x0f; //取出溫度的小數位
temppoint = (temppoint * 625); //小數位乘以0.625得出溫度的小數位值,在此擴大1000
//倍,得出溫度的4位小數位,顯示的時候加小數點
temppoint1 = temppoint / 1000; //千位
temppoint2 = temppoint % 1000 / 100; //百位
temppoint3 = temppoint % 100 / 10; //十位
temppoint4 = temppoint % 10; //個位
Usart_PutString("當前環境溫度為:"); //發送溫度值到上位機
if(!(tempint1)) //高位為零,則不顯示
{
Usart_PutChar(' ');
if(!(tempint2))
{
Usart_PutChar(' ');
}
else
{
Usart_PutChar(tempint2 + 0x30);
}
if(!(tempint3))
{
Usart_PutChar(' ');
}
else
{
Usart_PutChar(tempint3 + 0x30);
}
//Usart_PutChar(tempint4 + 0x30);
}
else
{
Usart_PutChar(tempint1 + 0x30);
Usart_PutChar(tempint2 + 0x30);
Usart_PutChar(tempint3 + 0x30);
}
Usart_PutChar(tempint4 + 0x30);
Usart_PutChar('.'); //顯示小數點
Usart_PutChar(temppoint1 + 0x30); //顯示小數位
Usart_PutChar(temppoint2 + 0x30);
Usart_PutChar(temppoint3 + 0x30);
Usart_PutChar(temppoint4 + 0x30);
Usart_PutChar(' '); //不顯示,空一格
Usart_PutChar('o'); //顯示溫度的符號。由于實在找不到溫度那個再上面的小o,
Usart_PutChar('C'); //只好用普通的小寫o來代替了。
Usart_PutChar(0x0D);
Usart_PutChar(0x0A); //結尾發送回車換行
sei(); //開中斷
for(i=0;i<200;i++) //延時4S,再進行溫度轉換
{
Delayms(20);
}
}
}
//端口狀態初始化設置函數
void Port_Init()
{
PORTD = 0X00; //USART的發送接收端口分別為PD0和PD1
DDRD |= (1 << 3); //PD0為接收端口,置為輸入口;PD1為發送端口,置為輸出口
PORTB = 0x00;
DDRB = 0xff;
}
void Port_DS18b20()
{
DDRC &= ~(1<<4); // 輸入模式(上電時為高電平)
PORTC &= ~(1<<4); // 輸出鎖存器寫0,以后不再更改
}
//USART寄存器配置函數
void Usart_Init()
{
UCSR1A = 0X00;
UCSR1C |= (1 << UCSZ11) | (1 << UCSZ10); //異步,數據格式8,N,1
UBRR1L = (F_CPU / BAUD / 16 - 1) % 256; //波特率設置
UBRR1H = (F_CPU / BAUD / 16 - 1) / 256;
UCSR1B |= (1 << RXCIE1) | (1 << RXEN1) | (1 << TXEN1); //發送使能
}
//字節發送函數
void Usart_PutChar(unsigned char cTXData)
{
while( !(UCSR1A & (1 << UDRE1)) ); //只有數據寄存器為空時才能發送數據
UDR1 = cTXData; //發送數據送USART I/O數據寄存器-UDR
}
//接收中斷函數
ISR(USART1_RX_vect)
{
unsigned char Rev;
Rev = UDR1; //從USART I/O數據寄存器-UDR中讀出數據
Usart_PutChar(Rev); //將接收到的數據發送
}
void Usart_PutString(unsigned char *pcString)
{
while (*pcString)
{
Usart_PutChar(*pcString++);
}
}
//DS18B20初始化
unsigned char DS18B20_Init()
{
SET_OUT; //PA2設置為輸出口(相當于拉低數據線上的電平)
Delayus(490); //延時大于480us
SET_IN; //輸入 釋放數據線(相當于拉高數據線上的電平)
Delayus(68); //延時大于60US,
//while(DQ_IN); //可以用兩個while()死循環來判斷復位是否成功,當數據線被拉低,說明
//while(!(DQ_IN)); //18b20開始復位應答,當數據線變高,說明應答完畢
if(DQ_IN) //判斷DS18B20是否拉低數據線
{
OK_Flag = 0; // 數據線是高?復位失敗
}
else
{
OK_Flag = 1; // 數據線是低?復位成功
}
Delayus(422); //有復位應答信號后,應當再延時一段時間(480-68),以等待應答完畢
return OK_Flag; //返回復位標志
}
//從DS18B20讀取一個字節數據
unsigned char Read_18b20()
{
unsigned char i;
unsigned char dat = 0; // dat用于存儲讀到的數據,先清零
for(i = 0;i < 8;i++) //共讀8位數據,構成一個字節
{
SET_OUT; //定義為輸出(拉低數據線)
Delayus(2); //拉低2微秒
SET_IN; //定義成輸入,讀入數據(同時也相當于拉高數據線)
Delayus(4); //延時
dat = dat >> 1; //數據右移,讀順序:先低后高
if(DQ_IN) //讀數據,
{
dat |= 0x80; //如果是高,置1,右移數據
}
Delayus(62); //延時大于60us
}
return dat; //返回讀到的1字節數據
}
//向DS18B20寫1字節數據
void Write_18b20(unsigned char dat)
{
unsigned char i;
for(i = 0;i < 8;i++) //寫8次,一次寫1位,先寫低字節
{
SET_OUT; //拉低數據線2us,開始寫數據
Delayus(2); //
if(dat & 0x01) //寫數據
{
SET_IN; //寫1
}
else
{
SET_OUT; //寫0
}
dat >>= 1; //數據右移1位,先寫低位
Delayus(62); //延時大于60us
SET_IN; //拉高數據線
Delayus(2); //寫兩位數據的時間間隔
}
}
//us級別的延時函數
void Delayus(unsigned int lus)
{
while(lus--)
{
_delay_loop_2(4); //_delay_loop_2(1)是延時4個時鐘周期,參數為3則延時12
//個時鐘周期,本實驗用12M晶體,則12個時鐘周期為12/12=1us
}
}
//ms級別的延時函數
void Delayms(unsigned int lms)
{
while(lms--)
{
Delayus(1000); //延時1ms
}
}
|