DS18B20是一款单总线驱动的温度传感器,占用口线少,驱动简单,只要按照协议发出符合时序要求的信号,就可以得到温度数据。
典型应用电路:
我这里使用的第一种应用电路,使用PB4连接DQ端。为了方便驳接,我选择的是现成的模块:
是用杜邦线连接到开发板上。
简单的读取温度值的步骤如下:
1、跳过ROM操作。
2、发送温度转换命令。
3、跳过ROM操作。
4、发送读取温度命令。
5、读取温度值。
DS18B20温度传感器的初始化
主器件首先发出一个480-960微秒的低电平脉冲,然后释放总线变为高电平,并在随后的480微秒时间内对总线进行检测,如果有低电平出现说明总线上有DS18B20温度传感器已做出应答。若无低电平出现一直都是高电平说明总线上无DS18B20温度传感器应答。 做为从器件的DS18B20温度传感器在一上电后就一直在检测总线上是否有480-960微秒的低电平出现,如果有,在总线转为高电平后等待15-60微秒后将总线电平拉低60-240微秒做出响应存在脉冲,告诉主机本器件已做好准备。若没有检测到就一直在检测等待。
DS18B20温度传感器的写操作
写周期最少为60微秒,最长不超过120微秒。写周期一开始主器件先把总线拉低1微秒表示写周期开始。随后若主器件想写0,则将总线置为低电平,若主器件想写1,则将总线置为高电平,持续时间最少60微秒直至写周期结束,然后释放总线为高电平至少1微秒给总线恢复 。而DS18B20温度传感器则在检测到总线被拉底后等待15微秒然后从15us到45us开始对总线采样,在采样期内总线为高电平则为1,若采样期内总线为低电平则为0。
工程使用80MHZ的主频,DS18B20的驱动程序如下:
#include "ds18b20.h"
// 初始化18B20用到的GPIO口:PB4,输出模式
static void gpio_config_DS18B20_Out(void) {
gpio_init_type gpio_initstructure;
crm_periph_clock_enable(CRM_GPIOB_PERIPH_CLOCK, TRUE);
gpio_default_para_init(&gpio_initstructure);
/* DATA引脚:PB5 */
gpio_initstructure.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;
gpio_initstructure.gpio_out_type = GPIO_OUTPUT_PUSH_PULL;
gpio_initstructure.gpio_mode = GPIO_MODE_OUTPUT;
gpio_initstructure.gpio_pins = DS18B20_DATA_PIN;
gpio_initstructure.gpio_pull = GPIO_PULL_UP;
gpio_init(GPIOB, &gpio_initstructure);
}
// 初始化18B20用到的GPIO口:PB4,输入模式
static void gpio_config_DS18B20_In(void) {
gpio_init_type gpio_initstructure;
gpio_default_para_init(&gpio_initstructure);
/* DATA引脚:PB5 */
gpio_initstructure.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;
gpio_initstructure.gpio_out_type = GPIO_OUTPUT_PUSH_PULL;
gpio_initstructure.gpio_mode = GPIO_MODE_INPUT;
gpio_initstructure.gpio_pins = DS18B20_DATA_PIN;
gpio_initstructure.gpio_pull = GPIO_PULL_UP;
gpio_init(GPIOB, &gpio_initstructure);
}
void Ds18b20Rst(void) {
gpio_config_DS18B20_Out();
gpio_bits_reset(DS18B20_PORT, DS18B20_DATA_PIN);
delay_us(750); //主机发送复位脉冲480us-960us
gpio_bits_set(DS18B20_PORT, DS18B20_DATA_PIN);
delay_us(25);//18b20等待15-60us
}
/*等待18b20响应
* 返回1:未检测到18b20
* 返回0:存在
*/
uint8_t Ds18b20Check(void) {
uint8_t retry=0;
// 输入模式
gpio_config_DS18B20_In();
while(gpio_input_data_bit_read(DS18B20_PORT, DS18B20_DATA_PIN) && retry < 200) {
retry++;
delay_us(1);
}
if(retry>=200)return 1;
else retry=0;
while(!gpio_input_data_bit_read(DS18B20_PORT, DS18B20_DATA_PIN) && retry < 240) {
retry++;
delay_us(1);
}
if(retry>240)return 1;
return 0;
}
/*从18b20读取一个位
* 返回值1/0
*/
uint8_t Ds18b20ReadBit(void) {
uint8_t data;
gpio_config_DS18B20_Out();
gpio_bits_reset(DS18B20_PORT, DS18B20_DATA_PIN);
delay_us(2);
gpio_bits_set(DS18B20_PORT, DS18B20_DATA_PIN);
// 输入模式
gpio_config_DS18B20_In();
delay_us(12);
if(gpio_input_data_bit_read(DS18B20_PORT, DS18B20_DATA_PIN)) data=1;
else data=0;
delay_us(50);
return data;
}
/*从18b20读取一个字节
* 返回值:读到的数据
*/
uint8_t Ds18b20ReadByte(void) {
uint8_t i,j,dat;
dat=0;
for(i=1;i<=8;i++) {
j=Ds18b20ReadBit();
dat=(j<<7)|(dat>>1);//低位在前
}
return dat;
}
/*写一个字节到Ds18b20
* dat:要写入的字节
*/
void Ds18b20WriteByte(uint8_t dat) {
uint8_t i;
uint8_t temp;
gpio_config_DS18B20_Out();
for(i=1;i<=8;i++) {
temp=dat&0x01;
dat=dat>>1;
if (temp) {
// 输出1
gpio_bits_reset(DS18B20_PORT, DS18B20_DATA_PIN);
delay_us(2);
gpio_bits_set(DS18B20_PORT, DS18B20_DATA_PIN);
delay_us(60);
} else {
// 输出0
gpio_bits_reset(DS18B20_PORT, DS18B20_DATA_PIN);
delay_us(60);
gpio_bits_set(DS18B20_PORT, DS18B20_DATA_PIN);
delay_us(2);
}
}
}
/*从Ds18b20得到温度值
* 返回值:温度值
*/
float Ds18b20GetTemp() {
uint8_t temp;
uint8_t TH=0,TL=0;
short tem;
float t;
Ds18b20Rst();
Ds18b20Check();
// 跳过ROM操作
Ds18b20WriteByte(0XCC);
// 发出转换指令
Ds18b20WriteByte(0X44);
Ds18b20Rst();
Ds18b20Check();
// 跳过ROM操作
Ds18b20WriteByte(0XCC);
// 读数据,低位在前
Ds18b20WriteByte(0XBE);
TL=Ds18b20ReadByte();//LSB
TH=Ds18b20ReadByte();//MSB
if(TH>7) {
// 如果数据是负的
TH=~TH;
TL=~TL;
// 负温度标志
temp=0;
} else {
// 正温度
temp=1;
}
tem=TH;//高八位
tem<<=8;
tem+=TL;//低八位
// 计算温度值
t=((float)tem*0.0625);
if(temp) return t;
else return -t;
}
在主程序中每间隔300ms读一次温度,得到的结果输出到SPI显示屏上。
只要时序没问题,很容易调通的。如果通讯失败,建议用逻辑分析仪截取通讯过程信号分析,通常是脉冲的时间不匹配造成的。
|