|
发表于 2007-12-4 16:37:56
|
显示全部楼层
/*********************************************************************************************************
**定义引脚,用M16 PORTC.3 连接 DS18B20 2脚,模拟 1总线获取温度值
********************************************************************************************************/
#define DQ_18B20 (1<<3) // PC3,指定引脚
#define DQ_TO_0() (DDRC |= DQ_18B20) // PC3='0'
#define DQ_TO_1() (DDRC &= ~DQ_18B20) // PC3='float'
#define DQ_status() (PINC & DQ_18B20) // read PC3 pin
/*********************************************************************************************************
** 函数名称:ds18b20_config
** 功能描述:配置(使能)AVR与DS18B20的接口
** 输 入: 无
** 输 出: 无
** 范 例: ds18b20_config(void);
********************************************************************************************************/
void ds18b20_config(void)
{
DDRC &= ~DQ_18B20; // 输入模式(上电时为高电平)
PORTC &= ~DQ_18B20; // 输出锁存器写0,以后不再更改
}
/*********************************************************************************************************
** 函数名称:ds18b20_reset
** 功能描述:复位1-wire总线,并探测是否有温度芯片DS18B20(TO-92)
* 有返回1,没有返回0
** 输 入: 无
** 输 出: 1或0
** 范 例: ds18b20_reset(void);
********************************************************************************************************/
unsigned char ds18b20_reset(void)
{
bit bus_flag; //定义标记变量
DQ_TO_0(); // 设置1-wire总线为低电平(占领总线)...
delay_us(480); // 延迟490us~960us, 与硬件密切相关,但应尽可能选小值(480us),把抖动留给系统(比如在延迟期间发生中断导致延迟变长)。
#asm ("cli") // 下面这段时间要求比较严格,为保险起见,关中断
DQ_TO_1(); // 设置1-wire总线为高电平(释放总线)
delay_us(67); // 15至60US,最佳时间: 60us+7.5us!(忙延时,只是一种策略)
if (DQ_status() ) // 探测总线上是否有器件
{
bus_flag=0; // 复位单总线但没有发现有器件在线
}
else
{
bus_flag=1; // 复位单总线并发现有器件在线
}
#asm ("sei") // 退出临界代码区(开中断)
/* 保证Master(主机)释放总线的时间(不是说总线处于高电平的时间)不小于
480us即可,这一时间从读总线状态之前就开始了,所以这里把这个
时间计算在内。在Master释放总线的前半段,也是被动器件声明它
们在线之时。
*/
delay_us(490-67); // 490-67.5us
return(bus_flag);
}
/*********************************************************************************************************
** 函数名称:ds18b20_write
** 功能描述:写命令或数据到温度芯片DS18B20(发送一个字节)
** 输 入: dat
** 输 出: 无
** 范 例: ds18b20_write(dat);
********************************************************************************************************/
void ds18b20_write(unsigned char dat)
{
unsigned char count;
for (count=0;count<8;count++) // 每个字节共8位,一次发一位
{
#asm ("cli") // 保证绝对不会发生中断!
DQ_TO_0(); // 设置1-wire总线为低电平
delay_us(2); // 延迟 2us
if (dat & 0x01) // 并串转换,先低位后高位
{
DQ_TO_1(); // 如该位是1,释放总线,由上拉电阻输出高电平
}
else
{
DQ_TO_0(); //否则,输出第电平
}
dat >>= 1; // 右移,下一位做好准备
delay_us(62); // 延迟62us,(60us~120us实际不能到120us, 因为其它语句也用时间了!)
DQ_TO_1(); // 释放总线,由上拉电阻输出高电平
delay_us(2); // 2us
#asm ("sei") // 恢复系统中断
}
}
/*********************************************************************************************************
** 函数名称:ds18b20_read
** 功能描述:从温度芯片DS18B20读配置或数据(接收一个字节)
** 输 入: 无
** 输 出: dat
** 范 例: ds18b20_read();
********************************************************************************************************/
unsigned char ds18b20_read(void)
{
unsigned char count,dat;
dat = 0x00; // 数据接收准备
for(count=0; count<8; count++) // 每个字节共8位,一次收一位
{
#asm ("cli") // 保证绝对不会发生中断!
DQ_TO_0(); // 设置1-wire总线为低电平(拉低总线以同步),从总线拉低到读总线状态,不能大于15us!
delay_us(2); // 2us
DQ_TO_1(); // 设置1-wire总线为高电平(释放总线)
delay_us(4); // 4us
dat >>= 1; // 右移一位
if (DQ_status()) dat|=0x80; // 读取总线电平,先收低位再收高位
#asm("sei") // 恢复系统中断
delay_us(62); // 必须大于60us
}
return(dat);
}
/*********************************************************************************************************
** 函数名称:convert_T
** 功能描述:向DS18B20发出启动温度转换指令 (发送一个字节0x44)。
** 本电路中只有一个器件DS18B20,所以不需要多个器件的ID识别,
** 跳过之后,启动温度转换,但在启动后,用户应等待几百个
** 毫秒(>750ms),才能读到这次的转换值,
** 输 入: 无
** 输 出: 无
** 范 例: convert_T();
********************************************************************************************************/
void convert_T(void)
{
if (ds18b20_reset())
{ // 如果复位成功
ds18b20_write(0xcc); // 发送跳过多器件识别指令
ds18b20_write(0x44); // 发送启动温度转换指令
}
}
/*********************************************************************************************************
** 函数名称:read_T
** 功能描述:读取并计算转换后的温度值。向DS18B20发出读取温度值指令(发送一个字节0xbe)。
** 假定DS18B20一定是正确的,所以没有返回有关状态。当你故意把DS18B20从电路
** 中拔下而能让程序告诉你出错时,你可以自己修改这段代码!
** 输 入: 无
** 输 出: 无
** 范 例: read_T();
********************************************************************************************************/
void read_T(void)
{
//unsigned int value=0; //定义初值
unsigned char MSB; //温度高字节
unsigned char LSB; //温度低字节
unsigned long int t1=0; //温度整数部分数值
unsigned short int t2=0; //温度小数部分数值
//bit wendu_flag; //负温度标志
if (ds18b20_reset())
{ // 如果复位成功
ds18b20_write(0xcc); // 跳过多器件识别
ds18b20_write(0xbe); // 读暂存器
LSB=ds18b20_read(); // 低字节
MSB=ds18b20_read(); // 高字节
//value = (int)ds18b20_read(); // 低字节
//value += (int)(ds18b20_read())<<8; // 高字节
//高字节的前5位是符号,温度大于0,这5位为0,值乘0.0625即得实际温度.温度小于0,这5位为1,数值取反加1再乘0.0625即得实际温度
if ( (MSB & 0xF0)>0 ) //判断是否为负温度(高字节前四位是否为1)
{
wendu_flag=1; //置位,是负数
}
else
{
wendu_flag=0; //清标记,是正数
}
if (wendu_flag) //如果为负温度取反加1
{
MSB=~MSB; //高位取反
LSB=~LSB+1; //低位取反
}
t1 = MSB<<4; //得到温度整数部分
t1 |= (LSB>>4);
t2 = (LSB & 0x0F)*0.0625*10000; //得到温度小数部分并扩大10000倍
//计算各位数码管要显示的数值
//if(wendu_flag) //温度百位
//{
// Bit[1]=10; //如果为负温度则显示"-"
//}
//else
//{
// Bit[1]=t1/100; //温度百位
//}
//Bit[2]=t1%100/10; //温度十位
//Bit[3]=t1%10; //温度个位
time[0]=(t1%100); //温度整数(2位)
//Bit[4]=t2/1000; //第一位小数
//Bit[5]=t2%1000/100; //第二位小数
//time[0]=(t2/1000)+(t2%1000/100); //温度小数(2位)
//Bit[6]=t2%100/10; //第三位小数
//Bit[7]=t2%10; //第四位小数
}
}
-------------------------------
这是论坛里的,我以在M16调试通过。主要是在主函数里调用。
main
{
.....
#asm("sei")
while (1)
{
..........
//我用了马老师的状态机,不用在白等750ms...好多教程里都是delay_ms(),害人的!!!!!
5秒到,ds18b20_state=1,在中断里++time_750ms计时(ds18b20_state=2),到了750ms在执行read_T();
if (ds18b20_state) //5秒到,进入测温状态1
{
switch (ds18b20_state) //状态0:待命,状态1:发转换命令,状态2:读取温度值
{
case 1: //5秒到
ds18b20_state=2; //进入下个状态
time_750ms=0; //清750ms计时器
convert_T(); //向ds18b20发出转换温度命令
break;
case 2:
if (time_750ms>=75) //延时750ms,等待温度转换命令执行完毕
{
ds18b20_state=0; //完成温度检测,返回状态0
time_750ms=0; //清750ms计时器
read_T(); //获取温度值
}
break;
}
}
.....
} |
|