📄 ds18b20.c
字号:
#include "inc/config.h"
#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之类。
* 我的实验电路的晶振是8.0000MHz
*/
#ifndef CPU_CRYSTAL
#define CPU_CRYSTAL (8.0000)
#endif
#define wait_us(us)\
_delay_loop_2((u16)((us)*CPU_CRYSTAL/4))
/*---------------- 函数原型声明 ------------------*/
// 1个初始化模块
void ds18b20_config(void); // 配置端口
// 3个基本模块
inline BOOL ds18b20_reset(void); // 复位DS18B20
void ds18b20_write(u08 dat); // 写字节到DS18B20
u08 ds18b20_read(void); // 读字节从DS18B20
// 2个应用模块
BOOL convert_T(void); // 启动温度转换
u08 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);
}
/*--------------------------------------------------------
* 复位函数冗余处理
*/
inline BOOL ds18b20_reset(void)
{
u08 Cnt = 0;
while(!(_ds18b20_reset() == SUCC))
{
Cnt++;
if(Cnt > 3)
return FAIL ;
}
return SUCC ;
}
/*--------------------------------------------------------
* 写命令或数据到温度芯片DS18B20(发送一个字节)
*/
void ds18b20_write(u08 dat)
{
u08 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读配置或数据(接收一个字节)
*/
u08 ds18b20_read(void)
{
u08 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的数据手册规定的。
* 因为温度转换是需要时间的嘛!(^_^)
*/
BOOL convert_is_succ = FALSE ;
BOOL convert_T(void)
{
if(ds18b20_reset()==SUCC) { // 如果复位成功
ds18b20_write(0xcc); // 跳过多器件识别
ds18b20_write(0x44); // 启动温度转换
convert_is_succ = TRUE;
return TRUE ;
}
else
{
convert_is_succ = FALSE;
return FALSE ;
}
}
/*********************************************************************************************************
** 函数名称: CRC8_Dallas
** 功能描述: DS18B20的CRC8校验
** 输 入: 需校验的值
** 输 出: 单字节CRC结果
** 全局变量: 无
** 调用模块:
********************************************************************************************************/
u08 CRC8_Dallas(u08 Data,u08 CRC_Dallas)
{
u08 i;
u08 flag ;
CRC_Dallas^=Data;
for (i = 0; i < 8; i++)
{
flag = ( CRC_Dallas & (1<<0) );
CRC_Dallas >>= 1;
if (flag) CRC_Dallas ^= 0x8c; // b10001100 es la palabra del CRC (x8 + x5 + x4 + 1)
// 7..43..0 junto con el 1 aplicado a f.
}
return CRC_Dallas;
}
/*-------------------------------------------------------
* 读取转换后的温度值
* 我假定DS18B20一定是正确的,所以没有返回有关状态。当你故意
* 把DS18B20从电路中拔下而能让程序告诉你出错时,你可以自己修
* 改这段代码!
*/
u08 read_T(void)
{
u16 value = 0xee01;
u08 valueL;
u08 valueH;
u08 crc8;
u08 Cnt = 0;
if(convert_is_succ == FALSE)
{
return FALSE;
}
while(ds18b20_reset()==SUCC)
{ // 如果复位成功
ds18b20_write(0xcc); // 跳过多器件识别
ds18b20_write(0xbe); // 读暂存器
valueL = ds18b20_read(); // 低字节
valueH = ds18b20_read(); // 高字节
value = ((uint16)valueH<<8) + valueL ;
crc8=CRC8_Dallas( valueL,0 );
crc8=CRC8_Dallas( valueH,crc8 );
crc8=CRC8_Dallas( ds18b20_read(),crc8 );
crc8=CRC8_Dallas( ds18b20_read(),crc8 );
crc8=CRC8_Dallas( ds18b20_read(),crc8 );
crc8=CRC8_Dallas( ds18b20_read(),crc8 );
crc8=CRC8_Dallas( ds18b20_read(),crc8 );
crc8=CRC8_Dallas( ds18b20_read(),crc8 );
crc8=CRC8_Dallas( ds18b20_read(),crc8 );
if(crc8 == 0)
{
break;
}
else
{
Cnt++ ;
if(Cnt > 3)
{
value = 0xee02 ;
break;
}
}
}
Temperature = value *10/16;
if( (value & 0xff00) == 0xee00 )
{
return FALSE ;
}
else
{
return TRUE ;
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -