前言:STM32 虽然内部自带了温度传感器,但是因为芯片温升较大等问题,与实际温度差别较大, 所以,本章我们将向大家介绍如何通过 STM32 来读取外部数字温度传感器的温度,来得到较 为准确的环境温度。在本章中,我们将学习使用单总线技术,通过它来实现 STM32 和外部温 度传感器(DS18B20)的通信,并把从温度传感器得到的温度显示在 TFTLCD 模块上。本章分为如下几个部分:
1、DS18B20 是由 DALLAS 半导体公司推出的一种的“一线总线”接口的温度传感器。与传统的热敏电阻等测温元件相比,它是一种新型的体积小、适用电压宽、与微处理器接口简单的数字化温度传感器。一线总线结构具有简洁且经济的特点,可使用户轻松地组建传感器网络, 从而为测量系统的构建引入全新概念,测量温度范围为-55~+125℃ ,精度为±0.5℃。现场温度直接以“一线总线”的数字方式传输,大大提高了系统的抗干扰性。它能直接读出被测温度,并且可根据实际要求通过简单的编程实现 9~l2 位的数字值读数方式。它工作在 3~5.5V 的电压范围,采用多种封装形式,从而使系统设计灵活、方便,设定分辨率及用户设定的报警温度存储在 EEPROM 中,掉电后依然保存。
2、DS18B20技术性能特征
(1)独特的单总线接口方式,DS18B20在与微处理器连接时仅需要一条口线即可实
现微处理器与DS18B20的双向通讯。大大提高了系统的抗干扰性。
(2)测温范围 -55℃~+125℃,精度为±0.5℃。
(3)支持多点组网功能,多个DS18B20可以并联在唯一的三线上,最多只能并联8个,
实现多点测温,如果数量过多,会使供电电源电压过低,从而造成信号传输的不稳定。
(4)工作电源: 3.0~5.5V/DC (可以数据线寄生电源)。
(5)在使用中不需要任何外围元件。
(6)测量结果以9~12位数字量方式串行传送。

1、DS18B20一共有三个引脚,分别是:

DSl8B20的另一个特点是不需要再外部供电下即可工作。当总线高电平时能量由单线上拉电阻经过DQ引脚获得。高电平同时充电一个内部电容,当总线低电平时由此电容供应能量。这种供电方法被称为“寄生电源”。另外一种选择是DSl8B20由接在VDD的外部电源供电(也是我们设计的连接方式)。2、DS18B20寄生电源
设计之前我们先阅读一下芯片手册,了解一下芯片的通信方式,控制方式,读写时序然后根据流程图规划我们的程序思路。
复位脉冲、应答脉冲、写0、写1、读0和读1。
所有这些信号,除了应答脉冲以外,都由主机发出同步信号。并且发送所有的命令和数据都是字节的低位在前。
(1)复位脉冲

单总线上的所有通信都是以初始化序列开始。主机输出低电平,保持低电平时间至少480 us,以产生复位脉冲。接着主机释放总线,4.7K的上拉电阻将单总线拉高,延时15~60 us,并进入接收模式(Rx)。接着DS18B20拉低总线60~240 us,以产生低电平应答脉冲。
(2)写时序

写时序包括写0时序和写1时序。所有写时序至少需要60us,且在2次独立的写时序之间至少需要1us的恢复时间,两种写时序均起始于主机拉低总线。
写0时序:主机输出低电平,延时60us,然后释放总线,延时2us。
写1时序:主机输出低电平,延时2us,然后释放总线,延时60us。
(3)读时序

单总线器件仅在主机发出读时序时,才向主机传输数据,所以,在主机发出读数据命令后,必须马上产生读时序,以便从机能够传输数据。所有读时序至少需要60us,且在2次独立的读时序之间至少需要1us的恢复时间。每个读时序都由主机发起,至少拉低总线1us。主机在读时序期间必须释放总线,并且在时序起始后的15us之内采样总线状态。
典型的读时序过程为:主机输出低电平延时2us,然后主机转入输入模式延时12us,然后读取单总线当前的电平,然后延时50us。


主要由以下3部分组成: 64 位ROM,高速暂存器,存储器
(1)64 位ROM存储独有的序列号
ROM中的64位序列号是出厂前被光刻好的,它可以看作是该DS18B20的地址序列码,每个DS18B20的64位序列号均不相同。这样就可以实现一根总线上挂接多个DS18B20的目的。
(2)高速暂存器包含:
温度传感器
一个字节的温度上限和温度下限报警触发器(TH和TL)
配置寄存器允许用户设定9位,10位,11位和12位的温度分辨率,分别对应着温度的分辨率为:0.5°C,0.25°C,0.125°C,0.0625°C,默认为12位分辨率,
(3)存储器:
由一个高速的RAM和一个可擦除的EEPROM组成,EEPROM存储高温和低温触发器(TH和TL)以及配置寄存器的值,(就是存储低温和高温报警值以及温度分辨率)
(1)主机发送复位脉冲,DS18B20发送应答脉冲,主机选择ROM命令,我们使用SKIP ROM,主机发送控制功能脉冲。

(2)主机发送控制命令:主机发送44转换命令。这个命令开始温度转换。该任务结束。将执行温度转换,然后DS18B20将保持空闲。如果总线主根据此命令发出读时点,如果它忙于进行温度转换,DS18B20将在总线上输出0,;当温度转换完成时,它将返回1。
(3)主机发送控制命令:主机发送BE转换命令。读取将从字节0开始,并将继续通过scratchpad直到第9个字节(字节8,CRC)被读取。如果不是所有的位置都要被读取,主机可以在任何时候发出一个重置来终止读取。
DS18B20温度读取与计算
DS18B20采用16位补码的形式来存储温度数据,温度是摄氏度。当温度转换命令发布后,经转换所得的温度值以二字节补码形式存放在高速暂存存储器的第0和第1个字节。
高字节的五个S为符号位,温度为正值时S=0,温度为负值时S=1
剩下的11位为温度数据位,对于12位分辨率,所有位全部有效,对于11位分辨率,位0(bit0)无定义,对于10位分辨率,位0和位1无定义,对于9位分辨率,位0,位1,和位2无定义
(4) 总结:
DS18B20工作步骤
DS18B20的工作步骤可以分为三步:
其中第二步执行ROM指令,也就是访问每个DS18B20,搜索64位序列号,读取匹配的序列号值,然后匹配对应的DS18B20,如果我们仅仅使用单个DS18B20,可以直接跳过ROM指令。而跳过ROM指令的字节是0xCC。

不同开发板的单总线引脚不同,原子的开发版对应是PG9



ds18b20.h
#ifndef __DS18B20_H
#define __DS18B20_H
#include "stm32f4xx_hal.h"
//IO方向设置
#define DS18B20_IO_IN() {GPIOG->MODER&=~(3<<(6*2));GPIOG->MODER|=0<<(6*2);} //PG6输入模式
#define DS18B20_IO_OUT() {GPIOG->MODER&=~(3<<(6*2));GPIOG->MODER|=1<<(6*2);} //PG6输出模式
IO操作函数
#define DS18B20_OUT_LOW HAL_GPIO_WritePin(GPIOG, GPIO_PIN_6, GPIO_PIN_RESET) //数据端口 PG6
#define DS18B20_OUT_HIGH HAL_GPIO_WritePin(GPIOG, GPIO_PIN_6, GPIO_PIN_SET) //数据端口 PG6
#define DS18B20_DQ_IN HAL_GPIO_ReadPin(GPIOG, GPIO_PIN_6) //数据端口 PG6
uint8_t DS18B20_Init(void); //初始化DS18B20
short DS18B20_Get_Temp(void); //获取温度
void DS18B20_Start(void); //开始温度转换
void DS18B20_Write_Byte(uint8_t dat);//写入一个字节
uint8_t DS18B20_Read_Byte(void); //读出一个字节
uint8_t DS18B20_Read_Bit(void); //读出一个位
uint8_t DS18B20_Check(void); //检测是否存在DS18B20
void DS18B20_Reset(void); //复位DS18B20
#endif
ds18b20.c
(1)初始化DS18B20_Init函数
包含复位函数DS18B20_IReset和接收DS18B20_Check应答函数
参考芯片手册中复位和应答的介绍:
主机输出低电平,保持低电平时间至少480 us,以产生复位脉冲。接着主机释放总线,4.7K的上拉电阻将单总线拉高,延时15~60 us,并进入接收模式(Rx)。接着DS18B20拉低总线60~240 us,以产生低电平应答脉冲。
(2)因延时需要用到us,我们要写delay_us延时函数
因为HAL_Delay()毫秒级延时,无法达到us。在中断中调用延时函数有时候卡死在这里,从封装函数中可以发现函数中有中断获取系统时钟HAL_IncTick(void),由于优先级系统给的低,所以在高优先级的中断中无法产生这个低级的中断,导致程序卡死在HAL_Delay()中
由此建议直接不用HAL_Delay(),直接自己写延时函数
(3)实现读取一个位,再实现读取一个字节(参考芯片手册的读时序)
单总线器件仅在主机发出读时序时,才向主机传输数据,所以,在主机发出读数据命令后,必须马上产生读时序,以便从机能够传输数据。所有读时序至少需要60us,且在2次独立的读时序之间至少需要1us的恢复时间。每个读时序都由主机发起,至少拉低总线1us。主机在读时序期间必须释放总线,并且在时序起始后的15us之内采样总线状态。
典型的读时序过程为:主机输出低电平延时2us,然后主机转入输入模式延时12us,然后读取单总线当前的电平,然后延时50us。

(4) 写时序,写CCh和44h命令

写时序包括写0时序和写1时序。所有写时序至少需要60us,且在2次独立的写时序之间至少需要1us的恢复时间,两种写时序均起始于主机拉低总线。
写0时序:主机输出低电平,延时60us,然后释放总线,延时2us。
写1时序:主机输出低电平,延时2us,然后释放总线,延时60us。
#include "ds18b20.h"
uint32_t usctick = 0;
uint32_t time_delay = 0;
extern TIM_HandleTypeDef htim6;
//延时nus
//nus为要延时的us数.
//nus:0~190887435(最大值即2^32/fac_us@fac_us=168)
static uint8_t fac_us = 168; //这里主时钟为168M, 所以在1us内ticks会减168次
void delay_us(uint32_t nus)
{
uint32_t ticks;
uint32_t told,tnow,tcnt=0;
uint32_t reload=SysTick->LOAD; //LOAD的值
ticks=nus*fac_us; //1us需要的节拍数
told=SysTick->VAL; //刚进入时的计数器值
while(1)
{
tnow=SysTick->VAL;
if(tnow!=told)
{
if(tnow<told)tcnt+=told-tnow; //这里注意一下SYSTICK是一个递减的计数器就可以了.
else tcnt+=reload-tnow+told;
told=tnow;
if(tcnt>=ticks)break; //时间超过/等于要延迟的时间,则退出.
}
};
}
//复位DS18B20
void DS18B20_Reset(void)
{
DS18B20_IO_OUT(); //设置为输出
DS18B20_OUT_LOW ; //拉低DQ
delay_us(650); //拉低650us
DS18B20_OUT_HIGH ; //拉高DQ
delay_us(20); //20US
}
//等待DS18B20的回应
//返回1:未检测到DS18B20的存在
//返回0:存在
uint8_t DS18B20_Check(void)
{
uint8_t retry=0;
DS18B20_IO_IN(); //设置为输入
//等待DS18B20拉低总线回应,如果超过200us未拉低,则认为未回应
while ((DS18B20_DQ_IN == 1) && (retry<200))
{
retry++;
delay_us(1);
};
if(retry>=200)return 1; //DS18B20超时未拉低总线
else retry=0; //DS18B20拉低总线
while ( (DS18B20_DQ_IN == 0 ) && ( retry < 240) ) //测试拉低总线的时间是否在240us内
{
retry++;
delay_us(1);
};
if(retry>=240)return 1; //超过240us错误
return 0; //正确回应
}
//从DS18B20读取一个位
//返回值:1/0
uint8_t DS18B20_Read_Bit(void)
{
uint8_t data;
DS18B20_IO_OUT(); //设置为输出
DS18B20_OUT_LOW ; //拉低DQ
delay_us(3);
DS18B20_OUT_HIGH ; //拉高DQ
DS18B20_IO_IN(); //设置为输入
delay_us(12);
if(DS18B20_DQ_IN) data=1;
else data=0;
delay_us(50);
return data;
}
//从DS18B20读取一个字节
//返回值:读到的数据,先读数据的低位
uint8_t DS18B20_Read_Byte(void)
{
uint8_t i,j,dat;
dat=0;
for (i=0;i<8;i++)
{
j=DS18B20_Read_Bit();
dat=(j<<i)|dat;
}
return dat;
}
//写一个字节到DS18B20
//dat:要写入的字节
void DS18B20_Write_Byte(uint8_t dat)
{
uint8_t j;
uint8_t testb;
DS18B20_IO_OUT(); //设置为输出
for (j=1;j<=8;j++)
{
testb=dat&0x01;
dat=dat>>1;
if(testb) // 写1
{
DS18B20_OUT_LOW ; //拉低DQ
delay_us(2);
DS18B20_OUT_HIGH ; //拉高DQ
delay_us(60);
}
else //写0
{
DS18B20_OUT_LOW ; //拉低DQ
delay_us(60);
DS18B20_OUT_HIGH ; //拉高DQ
delay_us(2);
}
}
}
void DS18B20_Start(void)
{
//开始温度转换
DS18B20_Reset();
DS18B20_Check();
DS18B20_Write_Byte(0xcc); // skip rom
DS18B20_Write_Byte(0x44); // convert
//开始读取温度
DS18B20_Reset();
DS18B20_Check();
DS18B20_Write_Byte(0xcc); // skip rom
DS18B20_Write_Byte(0xbe); // convert
}
//初始化DS18B20的IO口 DQ 同时检测DS的存在
//返回1:不存在
//返回0:存在
uint8_t DS18B20_Init(void)
{
DS18B20_Reset();
return DS18B20_Check();
}
//从ds18b20得到温度值
//精度:0.1C
//返回值:温度值 (-550~1250)
short DS18B20_Get_Temp(void)
{
uint8_t temp;
uint8_t TL,TH;
short tem;
DS18B20_Start (); //开始转换读取
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;//转换 获得不带符号位的11位温度值
if(temp)return tem; //返回温度值
else return -tem;
}
main.c
int fputc(int ch, FILE *p)
{
while(!(USART1->SR & (1<<7)));
USART1->DR = ch;
return ch;
}
/* USER CODE END 0 */
/**
* @brief The application entry point.
*
* @retval None
*/
int main(void)
{
/* USER CODE BEGIN 1 */
int16_t temperature;
/* USER CODE END 1 */
/* MCU Configuration----------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_USART1_UART_Init();
MX_TIM6_Init();
/* USER CODE BEGIN 2 */
printf("this is DS18B20 test\n");
if(!DS18B20_Init())
{
printf(" DS18B20 is here\n");
}else
{
printf(" DS18B20 is not here\n");
}
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
temperature = DS18B20_Get_Temp();
if(temperature<0)
{
printf("-"); //显示负号
temperature=-temperature; //转为正数
}
printf("temperature = %d.%d\n",temperature/10,temperature%10);
HAL_Delay(1000);
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
我正在编写一个包含C扩展的gem。通常当我写一个gem时,我会遵循TDD的过程,我会写一个失败的规范,然后处理代码直到它通过,等等......在“ext/mygem/mygem.c”中我的C扩展和在gemspec的“扩展”中配置的有效extconf.rb,如何运行我的规范并仍然加载我的C扩展?当我更改C代码时,我需要采取哪些步骤来重新编译代码?这可能是个愚蠢的问题,但是从我的gem的开发源代码树中输入“bundleinstall”不会构建任何native扩展。当我手动运行rubyext/mygem/extconf.rb时,我确实得到了一个Makefile(在整个项目的根目录中),然后当
我已经在Sinatra上创建了应用程序,它代表了一个简单的API。我想在生产和开发上进行部署。我想在部署时选择,是开发还是生产,一些方法的逻辑应该改变,这取决于部署类型。是否有任何想法,如何完成以及解决此问题的一些示例。例子:我有代码get'/api/test'doreturn"Itisdev"end但是在部署到生产环境之后我想在运行/api/test之后看到ItisPROD如何实现? 最佳答案 根据SinatraDocumentation:EnvironmentscanbesetthroughtheRACK_ENVenvironm
我们的git存储库中目前有一个Gemfile。但是,有一个gem我只在我的环境中本地使用(我的团队不使用它)。为了使用它,我必须将它添加到我们的Gemfile中,但每次我checkout到我们的master/dev主分支时,由于与跟踪的gemfile冲突,我必须删除它。我想要的是类似Gemfile.local的东西,它将继承从Gemfile导入的gems,但也允许在那里导入新的gems以供使用只有我的机器。此文件将在.gitignore中被忽略。这可能吗? 最佳答案 设置BUNDLE_GEMFILE环境变量:BUNDLE_GEMFI
这似乎非常适得其反,因为太多的gem会在window上破裂。我一直在处理很多mysql和ruby-mysqlgem问题(gem本身发生段错误,一个名为UnixSocket的类显然在Windows机器上不能正常工作,等等)。我只是在浪费时间吗?我应该转向不同的脚本语言吗? 最佳答案 我在Windows上使用Ruby的经验很少,但是当我开始使用Ruby时,我是在Windows上,我的总体印象是它不是Windows原生系统。因此,在主要使用Windows多年之后,开始使用Ruby促使我切换回原来的系统Unix,这次是Linux。Rub
我正在玩HTML5视频并且在ERB中有以下片段:mp4视频从在我的开发环境中运行的服务器很好地流式传输到chrome。然而firefox显示带有海报图像的视频播放器,但带有一个大X。问题似乎是mongrel不确定ogv扩展的mime类型,并且只返回text/plain,如curl所示:$curl-Ihttp://0.0.0.0:3000/pr6.ogvHTTP/1.1200OKConnection:closeDate:Mon,19Apr201012:33:50GMTLast-Modified:Sun,18Apr201012:46:07GMTContent-Type:text/plain
无论您是想搭建桌面端、WEB端或者移动端APP应用,HOOPSPlatform组件都可以为您提供弹性的3D集成架构,同时,由工业领域3D技术专家组成的HOOPS技术团队也能为您提供技术支持服务。如果您的客户期望有一种在多个平台(桌面/WEB/APP,而且某些客户端是“瘦”客户端)快速、方便地将数据接入到3D应用系统的解决方案,并且当访问数据时,在各个平台上的性能和用户体验保持一致,HOOPSPlatform将帮助您完成。利用HOOPSPlatform,您可以开发在任何环境下的3D基础应用架构。HOOPSPlatform可以帮您打造3D创新型产品,HOOPSSDK包含的技术有:快速且准确的CAD
在应用开发中,有时候我们需要获取系统的设备信息,用于数据上报和行为分析。那在鸿蒙系统中,我们应该怎么去获取设备的系统信息呢,比如说获取手机的系统版本号、手机的制造商、手机型号等数据。1、获取方式这里分为两种情况,一种是设备信息的获取,一种是系统信息的获取。1.1、获取设备信息获取设备信息,鸿蒙的SDK包为我们提供了DeviceInfo类,通过该类的一些静态方法,可以获取设备信息,DeviceInfo类的包路径为:ohos.system.DeviceInfo.具体的方法如下:ModifierandTypeMethodDescriptionstatic StringgetAbiList()Obt
文章目录1.开发板选择*用到的资源2.串口通信(个人理解)3.代码分析(注释比较详细)1.主函数2.串口1配置3.串口2配置以及中断函数4.注意问题5.源码链接1.开发板选择我用的是STM32F103RCT6的板子,不过代码大概在F103系列的板子上都可以运行,我试过在野火103的霸道板上也可以,主要看一下串口对应的引脚一不一样就行了,不一样的就更改一下。*用到的资源keil5软件这里用到了两个串口资源,采集数据一个,串口通信一个,板子对应引脚如下:串口1,TX:PA9,RX:PA10串口2,TX:PA2,RX:PA32.串口通信(个人理解)我就从串口采集传感器数据这个过程说一下我自己的理解,
最近在学习CAN,记录一下,也供大家参考交流。推荐几个我觉得很好的CAN学习,本文也是在看了他们的好文之后做的笔记首先是瑞萨的CAN入门,真的通透;秀!靠这篇我竟然2天理解了CAN协议!实战STM32F4CAN!原文链接:https://blog.csdn.net/XiaoXiaoPengBo/article/details/116206252CAN详解(小白教程)原文链接:https://blog.csdn.net/xwwwj/article/details/105372234一篇易懂的CAN通讯协议指南1一篇易懂的CAN通讯协议指南1-知乎(zhihu.com)视频推荐CAN总线个人知识总
@作者:SYFStrive @博客首页:HomePage📜:微信小程序📌:个人社区(欢迎大佬们加入)👉:社区链接🔗📌:觉得文章不错可以点点关注👉:专栏连接🔗💃:感谢支持,学累了可以先看小段由小胖给大家带来的街舞👉微信小程序(🔥)目录自定义组件-behaviors 1、什么是behaviors 2、behaviors的工作方式 3、创建behavior 4、导入并使用behavior 5、behavior中所有可用的节点 6、同名字段的覆盖和组合规则总结最后自定义组件-behaviors 1、什么是behaviorsbehaviors是小程序中,用于实现