以STM32F103为主控 实现基于DS18B20控制风扇/LED的简易温控调速/调光器

电子说

1.4w人已加入

描述

一、DS18B20模块简介

DS18B20 数字温度传感器提供 9-Bit 到 12-Bit 的摄氏温度测量精度和一个用户可编程的非易失性且具有过温和低温触发报警的报警功能。DS18B20 采用的 1-Wire 通信即仅采用一个数据线(以及地)与微控制器进行通信。该传感器的温度检测范围为-55℃至+125℃,并且在温度范围超过-10℃至 85℃之外时还具有±0.5℃的精度。此外,DS18B20 可以直接由数据线供电而不需要外部电源供电。每片 DS18B20 都有一个独一无二的 64 位序列号,所以一个 1-Wire 总线上可连接多个DS18B20 设备。因此,在一个分布式的大环境里用一个微控制器控制多个 DS18B20 是非常简单的。这些特征使得其在 HVAC 环境控制,在建筑、设备及机械的温度监控系统,以及温度过程控制系统中有着很大的优势。
特性

  • 独特的 1-Wire 总线接口仅需要一个管脚来通信。
  • 每个设备的内部 ROM 上都烧写了一个独一无二的 64 位序列号。
  • 多路采集能力使得分布式温度采集应用更加简单。
  • 能够采用数据线供电;供电范围为 3.0V 至5.5V。
  • 温度可测量范围为:-55℃至+125℃(-67℉至+257℉)。
  • 温度范围超过-10℃至 85℃之外时具有±0.5℃的精度。
  • 内部温度采集精度可以由用户自定义为9-Bits 至 12-Bits。
  • 温度转换时间在转换精度为 12-Bits 时达到最大值 750ms。
  • 用户自定义非易失性的的温度报警设置。
  • 定义了温度报警搜索命令和当温度超过用户自定义的设定值时。

应用

  • 温度控制系统,工业系统,民用产品,温度传感器,或者任何温度检测系统中。

STM32F103

二、内部框图与引脚说明

STM32F103

VDD输入正;当采用“寄生电源”供电时,该引脚必须连接到地
GND输入负
DQ数据输入/输出。1-Wire 漏极开路接口引脚。当采用“寄生电源”供电方式时,同时向设备提供电源。

DS18B20的供电说明
DS18B20 可以通过 VDD 引脚由外部供电,或由“寄生电源”供电,这使得 DS18B20 可以不采用外部电源供电而实现其功能。“寄生电源”供电方式在远程温度检测或空间比较有限制的地方有很大的应用。上面内部框图展示的就是 DS18B20 的“寄生电源”控制电路,其由 DQ 口拉高时向其供电。总线拉高的时候为内部电容(Cpp)充电,当总线拉低是由该电容向设备供电。当 DS18B20 为“寄生电源”供电模式时,该 VDD 引脚必须连接到地。

在“寄生电源”供电模式下,只要工作在指定的时序下,则该 1-Wire 总线和 Cpp 可以提供给DS18B20 足够的电流来完成各种工作以及满足供电电压。然而,当DS18B20 正在进行温度转换或正将暂存寄存器中的值拷贝至 EEPROM 时,其工作电流将会高至1.5mA。通过 1-Wire 总线上的上拉电阻提供的电流将会引起不可接受的电压跌落,同时将会有很大部分电流由 Cpp 提供。为了保证 DS18B20 有足够的电流供应,有必要在 1-Wire 总线上提供一个强有力的上拉,不管此时在进行温度转换还是正将暂存寄存器中的值拷贝至 EEPROM 中。图 4中所示的由一个 MOSFET 直接将总线拉至高电平能够很好的实现。值得注意的是,1-Wire 总线必须在温度转换命令[44h]或暂存寄存器拷贝命令[48h]下达 10uS 后提供一个强有力的上拉,同时在整个温度转换期间(Tconv)或数据传(Twr=10ms)期间总线必须一直强制拉高。当强制拉高时该 1-Wire 总线上不允许有任何其他动作。

当然,DS18B20 也可以采用常规的通过外部电源连接至 VDD 引脚的供电方式,如图 5 所示。这种供电方式具有不需要上拉的MOSFET、该1-Wire总线在温度转换期间可执行其他动作的优点。

“寄生电源”供电方式在温度超过+100℃时不推荐使用,因为在超过该温度下时将会有很大的漏电流导致不能进行正常的通信。实际应用中,在类似的温度状态下强烈推荐该 DS18B20 由外部供电电源供电。

STM32F103

三、DS18B20 功能命令

DS18B20 包含了 SRAM 暂存寄存器和存储着过温和低温(TH 和 TL)温度报警寄存器及配置寄存器的非易失性 EEPROM。值得注意的是当 DS18B20的温度报警功能没有用到的时候,过温和低温(TH 和 TL)温度报警寄存器可以当做通用功能的存储单元。内存结构如下:

STM32F103

当总线上的主设备通过 ROM 命令确定了哪个 DS18B20 能够进行通信时,主设备可以向其中一个 DS18B20 发送功能命令。这些命令使得主设备可以向 DS18B20 的暂存寄存器写入或者读出数据,初始化温度转换及定义供电模式。功能命令如下:

STM32F103

其中,暂存寄存器中的 Byte 4 包含着配置寄存器;通过改变表 2 中 R0 和 R1 的值来配置 DS18B20 的分辨率。上电默认为 R0=1 及 R1=1(12 位分辨率)。需要注意的是,转换时间与分辨率之间是有制约关系的。Bit 7 和 Bit 0 至 Bit 4 作为内部使用而保留使用,不可被写入。

STM32F103

四、DS18B20温度转换

DS18B20 的核心功能是直接温度-数字测量。其温度转换可由用户自定义为 9、10、11、12 位精度分别为 0.5℃、0.25℃、0.125℃、0.0625℃分辨率。值得注意的是,上电默认为 12 位转换精度。DS18B20 上电后工作在低功耗闲置状态下。主设备必须向 DS18B20 发送温度转换命令[44h]才能开始温度转换。温度转换后,温度转换的值将会保存在暂存存储器的温度寄存器中,并且DS18B20 将会恢复到闲置状态。如果 DS18B20 是由外部供电,当发送完温度转换命令[44h]后,主设备可以执行“读数据时序”,若此时温度转换正在进行DS18B20 将会响应“0”,若温度转换完成则会响应“1”。如果 DS18B20 是由“寄生电源”供电,该响应的技术将不能使用,因为在整个温度转换期间,总线必须强制拉高。

DS18B20 的温度输出数据是在摄氏度下校准的;若是在华氏度下应用的话,可以用查表法或者常规的数据换算。温度数据以一个 16 位标志扩展二进制补码数的形式存储在温度寄存器中。符号标志位(S)温度的正负极性:正数则 S=0,负数则 S=1。如果 DS18B20 被定义为12 位的转换精度,温度寄存器中的所有位都将包含有效数据。若为 11 位转换精度,则 bit 0 为未定义的。若为 10 位转换精度,则 bit 1 和 bit 0 为未定义的。 若为 9 位转换精度,则 bit 2、bit 1和 bit 0 为未定义的。以下为在 12 位转换精度下温度输出数据与相对应温度之间的关系表。

STM32F103

五、以STM32F103为主控实现温控调速/调光器

准备工作

STM32F103C8T6最小系统板,DS18B20测温模块,OLED屏幕模块,电机驱动板,直流电机,按键若干。

实现功能与接线说明

功能:设置上下限温度阈值,随检测温度变化,转速跟着变,可掉电保存。当检测温度超过设定最大温度阈值(过温)时,PC13指示灯快闪,电机全速转。当检测温度在最大温度阈值与最小温度阈值之间,检测温度越靠近上限阈值,电机转速越快,反之转速越慢,PC13指示灯常亮。当检测温度低于最小温度阈值,电机停止,PC13指示灯常灭。

STM32F103C8T6DS18B20
3.3VVDD
GNDGND
PA0DQ
PA6电机驱动板的PWM调速接口
PB8OLED->SCL
PB9OLED->SDA
PA8,PB12上限温度按键,设置上限温度阈值加减
PB13,PB15下限温度按键,设置下限温度阈值加减
PB14写入并保存上下限温度阈值按键,只有按下该按键才能掉电保存

STM32F103

代码示例

ds18b20.c

#include "ds18b20.h"

void DS18B20_DQ_OUT(void)
{
	GPIO_InitTypeDef GPIO_InitStructurn;
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	
	GPIO_InitStructurn.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_InitStructurn.GPIO_Pin = GPIO_Pin_0;
	GPIO_InitStructurn.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructurn);
	DS18B20_DQ_H
}

void DS18B20_DQ_IN(void)
{
	GPIO_InitTypeDef GPIO_InitStructurn;
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	
	GPIO_InitStructurn.GPIO_Mode = GPIO_Mode_IN_FLOATING;
	GPIO_InitStructurn.GPIO_Pin = GPIO_Pin_0;
	GPIO_Init(GPIOA, &GPIO_InitStructurn);
}

uint8_t OneWire_Init(void)
{
	uint8_t AckBit;
	
	DS18B20_DQ_OUT();
	DS18B20_DQ_L
	Delay_us(500);
	DS18B20_DQ_H
	Delay_us(70);
	
	DS18B20_DQ_IN();
	AckBit = DS18B20_DQ_R
	Delay_us(500);
	return AckBit;
}

void OneWire_WriteBit(uint8_t Bit)
{
	DS18B20_DQ_OUT();
	DS18B20_DQ_L
	Delay_us(10);
	
	if(Bit == 0) DS18B20_DQ_L
	else DS18B20_DQ_H
	
	Delay_us(50);
	DS18B20_DQ_H
}

uint8_t OneWire_ReadBit(void)
{
	uint8_t Bit;
	
	DS18B20_DQ_OUT();
	DS18B20_DQ_L
	Delay_us(5);
	DS18B20_DQ_H
	Delay_us(5);
	
	DS18B20_DQ_IN();
	Bit = DS18B20_DQ_R
	Delay_us(50);
	
	return Bit;
}

void DS18B20_WriteByte(uint8_t Byte)
{
	uint8_t i;
	
	for(i=0; i< 8; i++)
	{
		OneWire_WriteBit(Byte & (0x01<

main.c

#include "stm32f10x.h"                  // Device header
#include "ds18b20.h" 
#include "oled.h"
#include "key.h"
#include "timer.h"
#include "led.h"
#include "pwm.h"

float Temp;
int8_t TempH, TempL, TEMP_MIN, TEMP_MAX;
uint8_t KeyNum;
uint16_t PWM_MIN = 10;
uint16_t PWM_MAX = 900;
uint16_t pwm;

int main(void)
{
	Key_Init();
	Timer_Init();
	OLED_Init();
	LED_Init();
	PWM_Init();
	OLED_ShowString(1,5,"DS18B20");
	OLED_ShowString(2,1,"TH:     TL:");
	OLED_ShowString(3,1,"Temp:");
	
	DS18B20_Read_TH_TL(&TempH, &TempL);
	
	while(1)
	{
		switch(Key_Get())
		{
			case 1:
				TempH++;
				if(TempH >= 125) TempH = 125;
				break;
			case 2:
				TempH--;
				if(TempH <= TempL) TempH++;
				break;
			case 3:
				TempL++;
				if(TempL >= TempH) TempL--;
				break;
			case 4:
				TempL--;
				if(TempL <= -55) TempL = -55;
				break;
			case 5:
				DS18B20_Write_TH_TL(TempH, TempL);
				OLED_ShowString(4,2,"OK");
				Delay_ms(500);
				break;
		}
		
		
		OLED_ShowSignedNum(2,4,TempH, 3);
		OLED_ShowSignedNum(2,12,TempL, 3);
		OLED_ShowString(4,2,"  ");
		
		TEMP_MIN = TempL;
		TEMP_MAX = TempH;
		
		DS18B20_ConvertTemp();
		Delay_ms(100);
		Temp = DS18B20_TempRead();
		if(Temp < 0)
		{
			OLED_ShowString(3,6,"-");
			Temp = -Temp;
		}
		else
		{
			OLED_ShowString(3,6,"+");
		}
		
		OLED_ShowNum(3,7,Temp,3);
		OLED_ShowString(3,10,".");
		OLED_ShowNum(3,11,(uint32_t )(Temp*10000)%10000,4);
		
		if(Temp > TempH)
		{
			TIM_SetCompare1(TIM3, 1000);
			
			GPIO_ResetBits(GPIOC, GPIO_Pin_13);
			Delay_ms(100);
			GPIO_SetBits(GPIOC, GPIO_Pin_13);
			Delay_ms(100);
			
		}else if(Temp < TempL)
		{
			TIM_SetCompare1(TIM3, 0);
			
			GPIO_SetBits(GPIOC, GPIO_Pin_13);
			
		}else
		{
			pwm = PWM_MIN + (Temp - TEMP_MIN) * (PWM_MAX - PWM_MIN) / (TEMP_MAX - TEMP_MIN);
			
			TIM_SetCompare1(TIM3, pwm);
			
			GPIO_ResetBits(GPIOC, GPIO_Pin_13);
		}
	}
}

void TIM2_IRQHandler(void)
{
	if(TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)
	{
		KeyNum++;
		if(KeyNum > 10)
		{
			KeyLoop();
			KeyNum = 0;
		}
		
		TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
	}
}

效果展示

STM32F103

常见问题

Q:DS18B20初上电一直读取到85°C,温度没有变化?
A:DS18B20有一个存储数字温度结果的两个字节宽度的温度寄存器,在初始上电(未开启第一次转换前),里面是+85°C。即没有正确开启转换,数据就会固定85°不变。

Q:为什么一样的程序,有的DS18B20读取数据异常,有的就能正常读取到数据?
A:DS18B20时单总线通讯,对读写时序通讯时间很重要,超出几us就可能读取数据异常,且每个DS18B20都会有误差差异,做不到每个误差都一样的。如有的DS18B20误差大点,超出1us也可以读取到数据,有的误差小点就不行了。

以上两个问题很大概率都是读写时序不对导致的,不要手册说15us内释放总线,就15us卡极限释放,做事要留有余地,不要什么事都追求极限。

审核编辑 黄宇

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分