用18B20温度控制板介绍华大HC32F030单片机 (三)
HC32F030系列单片机属于华大半导体公司“通用控制”产品系列的产品,便宜、小巧性能够用,备件易得性好,在国产器件风靡行业的今天,学习华大系列单片机是入门嵌入式行业的首选。
方案落实第一步:搞出电路图
下图是这次项目的电路板图纸,全部元件都在上面了,接下来笔者将分别介绍各个电路单元的功能。
两种温度传感器并存——NTC热敏电阻和18B20
NTC热敏电阻
NTC热敏电阻一种半导体材料,先了解下NTC热敏电阻的几个关键参数
项目 | 参数 |
---|---|
阻值(25℃) | 10kΩ |
B值(25℃/50℃) | 3950K |
工作温度 | -55℃~+125℃ |
最大稳态电流(25℃) | 440uA |
第一,NTC热敏电阻,即 Negative Temperature Coefficient thermistor,其阻值随着温度升高而降低,还有一种PTC热敏电阻,即Positive Temperature Coefficient thermistor,正温度系数的热敏电阻,其阻值随温度升高也升高;
第二,工作温度,-55℃~+125℃,这说明了可以测量的温度范围;
第三,B值的含义,即热敏常数,它表示阻值随温度变化的速度程度,B值越大,阻值随温度升高降低得越快;B值通常是在两个温度之间通过公式得出的,表中的B值是在25℃和50℃之间得出的。理论上,有了公式,可以算出热敏电阻在不同温度时的阻值,但是这只限于在25℃和50℃之间,在其他温度范围上就不准确了,因为热敏电阻的阻值和温度对应关系不是线性关系;
第四,最大稳态电流,在25℃下不要超过440uA,因为电阻要流过电流才能得到压降,其本身也要消耗功率,但是消耗功率太大就会影响精度,这也是阻值-温度呈非线性的原因。
这里要说明的是,热敏电阻作为一种半导体元件有很多技术参数供工程师们参考,为何这里只列四个?这里要重新对一下我们这个项目对温度的需求了:
1、温度是启停智能风扇的信号,这个温度在40℃左右较为合理,因为多媒体盒的工作场所不是恶劣的室外,而是稳定的家庭室内;
2、温度属于缓慢变化的模拟量,除非在锻造、淬火这种领域外,风扇控制类的设备对温度变化速度的响应可以不做特别高要求,所以大部分型号的热敏电阻的B值都可以接受;
3、我们这个项目可以让用户设定温度值,我们可以现在暂定三个温度值:30℃、40℃、50℃,这本身足够了,并且由于只设计了一个按钮,如果设置太多温度操作起来也不方便。我们只要保证这三个温度值下的电阻值稳定就行,措施如下:
①保证工作电流低于0.4mA,避免工作电流过大其自身要消耗功率。和它串联的电阻暂定1kΩ;
②规范焊接,焊接时间不要超过5s;
③ADC精度选最高位,并借助用18B20的温度值作为热敏电阻的采样参考值,18B20属于集成电路,按照数据手册正确使用即可得到相对精准的温度值,正所谓“清水煮杂面,你(18B20)吃我(热敏电阻)也见”
本项目热敏电阻的型号是:SDNT2012X103F3950FTF点击链接查看数据手册。
18B20
选用18B20传感器,是小型温控项目性价比很高的方案。
从商品角度说,市场上货量大,生产厂家多,虽然型号不同,但是基本能各个厂家平替;
功能上:测温范围在-55℃到125℃之间,能应对很多测温需求,用在零下或者高于50℃的场合,用金属壳封装带引线的型号,这种市面上也好买;
使用上,18B20属于单总线器件,一根线既作为控制输入,又做数据或者信号输出,外围电路简单(仅仅需要电源端加一个退耦电容,以及数据线上增加一个上拉电阻);
不足之处,其封装除了金属壳引线形式的,还有SO-8和TO-92,没见到有更小的封装的;并且价格在2-3元,不是十分便宜;
因为很多厂家都生产18B20,因此,如果你是初学单片机的入门工程师,各个厂家的数据手册是了解18B20的第一手资料,有的手册事无巨细,有的干净简洁,多多浏览能从不同的角度全面掌握这个芯片。
笔者在官方手册里挑选几个值得关注的信息:
1、在-10℃~70℃范围内精确度为±0.4°C,意思是如果真实温度是25℃,芯片读出来可能是在24.6℃到25.4℃之间的数据,实际这是该芯片的误差范围;
2、温度传感器的精度最高可以到9、10、11或12位,温度分辨率分别0.5℃、0.25℃、0.125℃和0.0625℃,意思是最高可以做到0.0625℃的最小步进,但是这和上面的误差±0.4℃没有关系。芯片在上电状态下默认的精度为12位,实际使用中,我们看到小数点后1位足够了。再者,位数越高,转换一次的时间越长,设置为12位分辨率下转换时间最长400ms,在程序中注意给转换留足时间;
3、18B20的数据引脚,也就是单总线引脚,是开漏形式的,必须要保证芯片不传输数据时保持高电平,所以必须在该引脚增加一个上拉电阻;
4、操作基本内容:
① 因为是一根线,作为主机的I/O端口要经常变化输入或输出的状态;
② 芯片上电默认是12位分辨率,没有必要改成其他分辨率,得到的数据乘以0.0625就是温度值;
③ 将以上步骤封装成库函数,在主程序里只调用读取数据的函数,并且读取数据的最小间隔1s,因为12位分辨率的最长转换时间是400ms
RESET 复位脉冲
//复位DS18B20
void DS18B20_Rst(void)
{
stc_gpio_cfg_t stcGpioCfg;
///< 打开GPIO外设时钟门控
Sysctrl_SetPeripheralGate(SysctrlPeripheralGpio, TRUE);
///< 端口方向配置->输出(其它参数与以上(输入)配置参数一致)
stcGpioCfg.enDir = GpioDirOut;
///< 端口上下拉配置->下拉
stcGpioCfg.enPu = GpioPuDisable;
stcGpioCfg.enPd = GpioPdEnable;
///关闭18B20总线端口
Gpio_ClrIO(TEMP_PORT, TEMP_PIN);
///初始化18B20总线端口
Gpio_Init(TEMP_PORT, TEMP_PIN, &stcGpioCfg);
Gpio_ClrIO(TEMP_PORT, TEMP_PIN); //DS18B20_DQ_OUT_Clr
delay10us(75ul);
Gpio_SetIO(TEMP_PORT, TEMP_PIN); //DS18B20_DQ_OUT_Set
delay10us(2ul);
}
Presence 芯片返回存在脉冲
//等待DS18B20的回应
//返回1:未检测到DS18B20的存在
//返回0:存在
uint8_t DS18B20_Check(void)
{
uint8_t retry = 0;
stc_gpio_cfg_t stcGpioCfg;
///< 打开GPIO外设时钟门控
Sysctrl_SetPeripheralGate(SysctrlPeripheralGpio, TRUE);
///< 端口方向配置->输入
stcGpioCfg.enDir = GpioDirIn;
///< 端口驱动能力配置->高驱动能力
stcGpioCfg.enDrv = GpioDrvL;
///< 端口上下拉配置->无
stcGpioCfg.enPu = GpioPuDisable;
stcGpioCfg.enPd = GpioPdDisable;
///< 端口开漏输出配置->开漏输出关闭
stcGpioCfg.enOD = GpioOdDisable;
///< 端口输入/输出值寄存器总线控制模式配置->AHB
stcGpioCfg.enCtrlMode = GpioAHB;
///初始化18B20总线端口
Gpio_Init(TEMP_PORT, TEMP_PIN, &stcGpioCfg);
while (Gpio_GetInputIO(TEMP_PORT, TEMP_PIN) && retry < 200)
{
retry++;
delay10us(1ul);
};
if(retry >= 200)return 1;
else retry = 0;
while (!Gpio_GetInputIO(TEMP_PORT, TEMP_PIN) && retry < 240)
{
retry++;
delay10us(1ul);
};
if(retry >= 240)return 1;
return 0;
}
CCh 跳过ROM指令
DS18B20_Write_Byte(0xcc);
温度转换指令
DS18B20_Write_Byte(0x44);// convert
}
写一个字节到芯片
//写一个字节到DS18B20
//dat:要写入的字节
void DS18B20_Write_Byte(uint8_t dat)
{
uint8_t j;
uint8_t testb;
stc_gpio_cfg_t stcGpioCfg;
///< 打开GPIO外设时钟门控
Sysctrl_SetPeripheralGate(SysctrlPeripheralGpio, TRUE);
///< 端口方向配置->输出(其它参数与以上(输入)配置参数一致)
stcGpioCfg.enDir = GpioDirOut;
///< 端口上下拉配置->下拉
stcGpioCfg.enPu = GpioPuDisable;
stcGpioCfg.enPd = GpioPdEnable;
///关闭18B20总线端口
Gpio_ClrIO(TEMP_PORT, TEMP_PIN);
///初始化18B20总线端口
Gpio_Init(TEMP_PORT, TEMP_PIN, &stcGpioCfg);
for (j = 1; j <= 8; j++)
{
testb = dat & 0x01;
dat = dat >> 1;
if (testb)
{
Gpio_ClrIO(TEMP_PORT, TEMP_PIN); //DS18B20_DQ_OUT_Clr
delay10us(1ul);
Gpio_SetIO(TEMP_PORT, TEMP_PIN); //DS18B20_DQ_OUT_Set
delay10us(6ul);
}
else
{
Gpio_ClrIO(TEMP_PORT, TEMP_PIN); //DS18B20_DQ_OUT_Clr
delay10us(6ul);
Gpio_SetIO(TEMP_PORT, TEMP_PIN); //DS18B20_DQ_OUT_Set
delay10us(1ul);
}
}
}
从芯片读取一个字节
//从DS18B20读取一个字节
//返回值:读到的数据
uint8_t DS18B20_Read_Byte(void) // read one byte
{
uint8_t i, j, dat;
dat = 0;
for (i = 1; i <= 8; i++)
{
j = DS18B20_Read_Bit();
dat = (j << 7) | (dat >> 1);
}
return dat;
}
得到温度值
//返回值:温度值 (-550~1250)
short DS18B20_Get_Temp(void)
{
uint8_t temp;
uint8_t TL, TH;
short tem;
DS18B20_Start (); // ds1820 start convert
DS18B20_Rst();
DS18B20_Check();
DS18B20_Write_Byte(0xcc);// skip rom
DS18B20_Write_Byte(0xbe);// convert
TL = DS18B20_Read_Byte(); // LSB
TH = DS18B20_Read_Byte(); // MSB
if(TH > 7)
{
TH = ~TH;
TL = ~TL;
temp = 0; //温度为负
} else temp = 1; //温度为正
tem = TH; //获得高八位
tem <<= 8;
tem += TL; //获得底八位
tem = (double)tem * 0.625; //转换
if(temp)return tem; //返回温度值
else return -tem;
}