STM32F103---标准库函数驱动DS18B20

思路说明:先打开DS18B20数据手册查询到以下图片内容按照顺序一步一步驱动。

 1.这个是DS18B20初始化的时序图,经过分析能得到以下代码(代码注释)。

void Init_DS18B20(void) 
{
	unsigned char t = 0;
	DS_DIR_OUT();  //让GPIO口为推挽输出模式
	GPIO_ResetBits(GPIOB,GPIO_Pin_9);  //发送复位脉冲 ds18b20 DQ管脚接到单片机的PB9
	delay_us(600); 		//延时(>480us) 
	GPIO_SetBits(GPIOB,GPIO_Pin_9);		//拉高数据线 
	delay_us(55); 				//等待(15~60us)	
	DS_DIR_IN(); //配置GPIO口为浮空输入模式
	while(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_9) == SET) //等待拉低
	{
		delay_us(1);
		t++;
		if(t >= 240)//如果超过240us还是高电平 代表ds18b20没发数据回来 丢失连接了
			return;   
	}
	t = 0;
	while(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_9) == RESET) //等待拉高
	{
		delay_us(1);
		t++;
		if(t >= 240)//如果超过240us还是低电平 代表ds18b20没发数据回来 丢失连接了
			return; 
	}
	printf("温度初始化完成!"); //我的printf函数是跟串口结合了,用于看ds18b20是否初始化完成
}
void DS_DIR_IN(void)  //让PB9为浮空输入模式
{
	GPIO_InitTypeDef GPIO_InitStruct;
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
	
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9;
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
	
	GPIO_Init(GPIOB,&GPIO_InitStruct);
}

void DS_DIR_OUT(void) //让PB9为推挽输出模式
{
	GPIO_InitTypeDef GPIO_InitStruct;
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
	
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9;
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
	
	GPIO_Init(GPIOB,&GPIO_InitStruct);
}

2.这个是DS18B20写读0/1的时序图 

 分析代码如下:

//单片机向DS18B20写一位  0  对应上图里面 左上方的图
void Write_Bit_0(void)
{
	GPIO_ResetBits(GPIOB,GPIO_Pin_9); //拉低
	delay_us(90);  //在60us --- 120us之间
	GPIO_SetBits(GPIOB,GPIO_Pin_9);  //拉高
	delay_us(10);  //拉高大于1us 这里选用10us
}
//单片机向DS18B20写一位 1
void Write_Bit_1(void)
{
	GPIO_ResetBits(GPIOB,GPIO_Pin_9); //拉低 
	delay_us(10);  //拉低大于1us
	GPIO_SetBits(GPIOB,GPIO_Pin_9);  //拉高
	delay_us(90); //我的理解是 拉高大于60us就是写1 所以选用90us
}

//单片机向DS18B20读一位 
unsigned char Read_Bit(void)
{
	DS_DIR_OUT(); //PB9为推挽输出模式
	GPIO_ResetBits(GPIOB,GPIO_Pin_9);  //拉低
	delay_us(10); //大于1us 选用10us
	DS_DIR_IN(); //PB9为浮空输入模式
	delay_us(10); //等待一会 用于后面判断该管脚返回的是高电平还是低电平 太大不好试过80us返回的数据就奇怪了
	if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_9) == SET)
	{
		return 1; //高电平返回1 
	}
	else
	{
		return 0; //低电平返回0
	}
}

通常我们对一个器件发送或者接受命令都是以1个字节的方式接受或发送。所以就用了一下代码:

//读一个字节
unsigned char ReadOneChar(void)  			
{
	unsigned char i=0; 		
	unsigned char dat=0; 
	for (i=0;i<8;i++) 		
	{
		dat = dat | (Read_Bit() << i); //DS18B20经手册查的是低位开始传输回来的,要获取一个完整的字节,这样子就可以。
	} 
	return(dat);
}

//写一个字节
void WriteOneChar(unsigned char dat) //dat要发送的数据
{ 
	unsigned char i=0; 		
	DS_DIR_OUT(); //推挽输出
	for(i=8;i>0;i--) 		//在15~60us之间对数据线进行采样,如果是高电平就写1,低写0发生。 
	{
		if((dat & 0x01) == 1)
		{
			Write_Bit_1();
		}
		else
		{
			Write_Bit_0();
		}
		dat >>= 1;
	} 
}

最后看到这张图了。按照这个顺序一步一步开始驱动并且获取温度数据。 

//读温度值(低位放tempL;高位放tempH;)
void ReadTemperature(void) 
{ 
	Init_DS18B20(); 					//初始化
	WriteOneChar(0xcc); 				//跳过读序列号的操作
	WriteOneChar(0x44); 				//启动温度转换
	delay_us(1000);					    //转换需要一点时间,延时 
	Init_DS18B20(); 					//初始化
	WriteOneChar(0xcc); 				//跳过读序列号的操作 
	WriteOneChar(0xbe); 				//读温度寄存器(头两个值分别为温度的低位和高位)	
	tempL=ReadOneChar(); 				//读出温度的低位LSB
	tempH=ReadOneChar(); 				//读出温度的高位MSB	
	if(tempH>0x7f)      				//最高位为1时温度是负
	{
		tempL=~tempL;					//补码转换,取反加一
		tempH=~tempH+1;       
		fg=0;      						//读取温度为负时fg=0
	}
	sdata = (tempH << 8) + tempL;
	sdata = (sdata * 0.0625) * 100;  //这里×100 用于保留两位小数了,因为我是unsigned int类型不是float。
	printf("%d  \r\n",sdata);
}

补充说明:在main.c文件里定义如下:

unsigned char tempL=0;         //设全局变量
unsigned char tempH=0; 
unsigned int sdata;            //温度的部分
unsigned char fg=1;                    //温度正负标志

int main()

{

        ReadTemperature(); //这样使用就可以了。
}

猜你喜欢

转载自blog.csdn.net/longjintao1/article/details/124415163