#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(); //开中断 }