#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include <stdbool.h>
#define DQ_DDR_IN DDRA &= ~(1<<PA0) // 设置为输入
#define DQ_DDR_OUT DDRA |= (1<<PA0) // 设置为输出
#define DQ_SET PORTA |= (1<<PA0) // 18b20 高电平
#define DQ_CLR PORTA &= ~(1<<PA0) // 18b20 低电平
#define DQ_IN PINA & (1<<PA0) // 18b20信号输入
/**
DS18B20初始化
*/
unsigned char ds18b20_init(void)
{
unsigned char is_reset_ok;
DQ_DDR_OUT; // PA2设置为输出口(相当于拉低数据线上的电平)
// 复位脉冲
DQ_SET;
DQ_CLR; // 输出低电平脉冲
_delay_us(500); //延时:480us~960us
DQ_SET;
// 应答总共480us,包括等待15~60us,应答60~240us,上拉。
_delay_us(100); // 延时大于60US,
// 检查复位是否成功
DQ_DDR_IN; //输入 释放数据线(相当于拉高数据线上的电平)
//while(DQ_R); //可以用两个while()死循环来判断复位是否成功,当数据线被拉低,说明
//while(!(DQ_R)); //18b20开始复位应答,当数据线变高,说明应答完毕
if(DQ_IN) //判断DS18B20是否拉低数据线
{
is_reset_ok = 0; // 数据线是高?复位失败
}
else
{
is_reset_ok = 1; // 数据线是低?复位成功
}
_delay_us(500); //有复位应答信号后,应当再延时一段时间,以等待应答完毕
return is_reset_ok; //返回复位标志
}
/**
向18b20写入一个字节
*/
void ds18b20_write_byte(unsigned char dat)
{
unsigned char i;
for(i = 0; i < 8; i++) //写8次,一次写1位,先写低字节
{
DQ_DDR_OUT; //拉低数据线2us,开始写数据
DQ_CLR;
_delay_us(10);
if(dat & 0x01) //写数据
{
DQ_SET;
}
_delay_us(100); //延时大于60us
DQ_SET;
dat >>= 1;
}
}
/**
从DS18B20读取一个字节数据
*/
unsigned char ds18b20_read_byte(void)
{
unsigned char i;
unsigned char dat = 0; // dat用于存储读到的数据,先清零
for(i = 0; i < 8; i++) //共读8位数据,构成一个字节
{
dat = dat >> 1; //数据右移,读顺序:先低后高
DQ_DDR_OUT; //定义为输出(拉低数据线)
DQ_CLR;
_delay_us(10); //拉低2微秒
DQ_SET;
DQ_DDR_IN; //定义成输入,读入数据(同时也相当于拉高数据线)
if(DQ_IN) //读数据,
{
dat |= 0x80; //如果是高,置1,右移数据
}
_delay_us(50); //延时大于60us
}
return dat; //返回读到的1字节数据
}
/**
从DS18B20读取温度信息到十进制字符数组
*/
void ds18b20_get_temperature(unsigned char temp[10])
{
unsigned char temp_h, temp_l; //温度高位,低位,复位成功标志
unsigned int temp_i; // 中间变量
temp_h = 0;
temp_l = 0;
temp_i = 0;
cli(); //关中断
ds18b20_init(); // 初始化ds18b20
ds18b20_write_byte(0xcc); // 跳过序列号
ds18b20_write_byte(0x44); // 发送温度转换命令
_delay_ms(10); // 延时,等转换完成
ds18b20_init(); // 初始化ds18b20
ds18b20_write_byte(0xcc); // 跳过序列号
ds18b20_write_byte(0xbe); // 发送读取暂存器指令
temp_l = ds18b20_read_byte(); //获得温度的低位
temp_h = ds18b20_read_byte(); //获得温度的高位
if(temp_h & 0x08) //判断温度的正负
{
//负温度。取反加1
temp_h = ~temp_h;
temp_l = ~temp_l;
//清零进位位标志
SREG |= ~(1 << 0);
//温度低字节加1
temp_l++;
if(SREG & (1 << 0)) //有进位吗?
{
temp_h++; //有进位,则温度高字节加1
}
// 负温度
temp[0] = '-';
}
else
{
// 正温度
temp[0] = '+';
}
temp_i = ((temp_h << 4) & 0x70) | (temp_l >> 4); //获得温度的整数位
temp[1] = temp_i / 1000 + 0x30; //千位
temp[2] = temp_i % 1000 / 100 + 0x30; //百位
temp[3] = temp_i % 100 / 10 + 0x30; //十位
temp[4] = temp_i % 10 + 0x30; //个位
temp[5] = '.'; //小数点
temp_i = temp_l & 0x0f; //取出温度的小数位
temp_i = (temp_i * 625); //小数位乘以0.625得出温度的小数位值,在此扩大1000倍,得出温度的4位小数位,显示的时候加小数点
temp[6] = temp_i / 1000 + 0x30; //千位
temp[7] = temp_i % 1000 / 100 + 0x30; //百位
temp[8] = temp_i % 100 / 10 + 0x30; //十位
temp[9] = temp_i % 10 + 0x30; //个位
sei(); //开中断
}