一、说明
程序有HAL库和标准库版本,上拉电阻4.7K到10K都能正常读取数据,PA4引脚需要remap。
二、标准库函数
(1)DS18B20.c
#include "ds18b20.h"
#include "stm32f10x.h"
/**************************************************************************************
* 描 述 : 配置DS18B20用到的I/O口
* 入 参 : 无
* 返回值 : 无
**************************************************************************************/
static void DS18B20_GPIO_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(DS18B20_CLK|RCC_APB2Periph_AFIO, ENABLE);
GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE);
GPIO_InitStructure.GPIO_Pin = DS18B20_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(DS18B20_PORT, &GPIO_InitStructure);
GPIO_SetBits(DS18B20_PORT, DS18B20_PIN); //DS18B20数据引脚初始化配置为高电平输出
}
/**************************************************************************************
* 描 述 : 配置使DS18B20-DATA引脚变为输入模式
* 入 参 : 无
* 返回值 : 无
**************************************************************************************/
static void DS18B20_Mode_IPU(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = DS18B20_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //上拉输入
GPIO_Init(DS18B20_PORT, &GPIO_InitStructure);
}
/**************************************************************************************
* 描 述 : 配置使DS18B20-DATA引脚变为输出模式
* 入 参 : 无
* 返回值 : 无
**************************************************************************************/
static void DS18B20_Mode_Out_PP(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = DS18B20_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(DS18B20_PORT, &GPIO_InitStructure);
}
/**************************************************************************************
* 描 述 : 主机给从机发送复位脉冲
* 入 参 : 无
* 返回值 : 无
**************************************************************************************/
static void DS18B20_Rst(void)
{
DS18B20_Mode_Out_PP(); //主机设置为推挽输出
DS18B20_DATA_OUT(LOW); //主机至少产生480us的低电平复位信号
delay_us(750);
DS18B20_DATA_OUT(HIGH); //主机在产生复位信号后,需将总线拉高
delay_us(15); //从机接收到主机的复位信号后,会在15~60us后给主机发一个存在脉冲
}
/**************************************************************************************
* 描 述 : 检测从机给主机返回的存在脉冲
* 入 参 : 无
* 返回值 : 0:成功 1:失败
**************************************************************************************/
static u8 DS18B20_Presence(void)
{
u8 pulse_time = 0;
DS18B20_Mode_IPU(); //主机设置为上拉输入
/* 等待存在脉冲的到来,存在脉冲为一个60~240us的低电平信号
* 如果存在脉冲没有来则做超时处理,从机接收到主机的复位信号后,会在15~60us后给主机发一个存在脉冲
*/
while( DS18B20_DATA_IN() && pulse_time<100 )
{
pulse_time++;
delay_us(1);
}
if( pulse_time >=100 ) //经过100us后,存在脉冲都还没有到来
return 1; //读取失败
else //经过100us后,存在脉冲到来
pulse_time = 0; //清零计时变量
while( !DS18B20_DATA_IN() && pulse_time<240 ) // 存在脉冲到来,且存在的时间不能超过240us
{
pulse_time++;
delay_us(1);
}
if( pulse_time >=240 ) // 存在脉冲到来,且存在的时间超过了240us
return 1; //读取失败
else
return 0;
}
/**************************************************************************************
* 描 述 : 从DS18B20读取一个bit
* 入 参 : 无
* 返回值 : u8
**************************************************************************************/
static u8 DS18B20_Read_Bit(void)
{
u8 dat;
/* 读0和读1的时间至少要大于60us */
DS18B20_Mode_Out_PP();
/* 读时间的起始:必须由主机产生 >1us <15us 的低电平信号 */
DS18B20_DATA_OUT(LOW);
delay_us(10);
/* 设置成输入,释放总线,由外部上拉电阻将总线拉高 */
DS18B20_Mode_IPU();
if( DS18B20_DATA_IN() == SET )
dat = 1;
else
dat = 0;
/* 这个延时参数请参考时序图 */
delay_us(45);
return dat;
}
/**************************************************************************************
* 描 述 : 从DS18B20读一个字节,低位先行
* 入 参 : 无
* 返回值 : u8
**************************************************************************************/
u8 DS18B20_Read_Byte(void)
{
u8 i, j, dat = 0;
for(i=0; i<8; i++)
{
j = DS18B20_Read_Bit(); //从DS18B20读取一个bit
dat = (dat) | (j<<i);
}
return dat;
}
/**************************************************************************************
* 描 述 : 写一个字节到DS18B20,低位先行
* 入 参 : u8
* 返回值 : 无
**************************************************************************************/
void DS18B20_Write_Byte(u8 dat)
{
u8 i, testb;
DS18B20_Mode_Out_PP();
for( i=0; i<8; i++ )
{
testb = dat&0x01;
dat = dat>>1;
/* 写0和写1的时间至少要大于60us */
if (testb)
{
DS18B20_DATA_OUT(LOW);
delay_us(8); //1us < 这个延时 < 15us
DS18B20_DATA_OUT(HIGH);
delay_us(58); //58us+8us>60us
}
else
{
DS18B20_DATA_OUT(LOW);
/* 60us < Tx 0 < 120us */
delay_us(70);
DS18B20_DATA_OUT(HIGH);
/* 1us < Trec(恢复时间) < 无穷大*/
delay_us(2);
}
}
}
/**************************************************************************************
* 描 述 : 起始DS18B20
* 入 参 : 无
* 返回值 : 无
**************************************************************************************/
void DS18B20_Start(void)
{
DS18B20_Rst(); //主机给从机发送复位脉冲
DS18B20_Presence(); //检测从机给主机返回的存在脉冲
DS18B20_Write_Byte(0XCC); // 跳过 ROM
DS18B20_Write_Byte(0X44); // 开始转换
}
/**************************************************************************************
* 描 述 : DS18B20初始化函数
* 入 参 : 无
* 返回值 : u8
**************************************************************************************/
u8 DS18B20_Init(void)
{
DS18B20_GPIO_Config();
DS18B20_Rst();
return DS18B20_Presence();
}
/**************************************************************************************
* 描 述 : 从DS18B20读取温度值
* 入 参 : 无
* 返回值 : float
**************************************************************************************/
float DS18B20_Get_Temp(void)
{
u8 tpmsb, tplsb;
short s_tem;
float f_tem;
DS18B20_Rst();
DS18B20_Presence();
DS18B20_Write_Byte(0XCC); /* 跳过 ROM */
DS18B20_Write_Byte(0X44); /* 开始转换 */
DS18B20_Rst();
DS18B20_Presence();
DS18B20_Write_Byte(0XCC); /* 跳过 ROM */
DS18B20_Write_Byte(0XBE); /* 读温度值 */
tplsb = DS18B20_Read_Byte();
tpmsb = DS18B20_Read_Byte();
s_tem = tpmsb<<8;
s_tem = s_tem | tplsb;
if( s_tem < 0 ) /* 负温度 */
f_tem = (~s_tem+1) * 0.0625;
else
f_tem = (s_tem * 0.625);
//这样做的目的将小数点后第一位也转换为可显示数字
//同时进行一个四舍五入操作。
return f_tem;
}
/*************************************END OF FILE******************************/
(2)DS18B20.h
#ifndef __DS18B20_H
#define __DS18B20_H
#include "stm32f10x.h"
#include "delay.h"
#define HIGH 1
#define LOW 0
#define DS18B20_CLK RCC_APB2Periph_GPIOB
#define DS18B20_PIN GPIO_Pin_4
#define DS18B20_PORT GPIOB
//带参宏,可以像内联函数一样使用,输出高电平或低电平
#define DS18B20_DATA_OUT(a) if (a)
GPIO_SetBits(GPIOB,GPIO_Pin_4);
else
GPIO_ResetBits(GPIOB,GPIO_Pin_4)
//读取引脚的电平
#define DS18B20_DATA_IN() GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_4)
typedef struct
{
u8 humi_int; //湿度的整数部分
u8 humi_deci; //湿度的小数部分
u8 temp_int; //温度的整数部分
u8 temp_deci; //温度的小数部分
u8 check_sum; //校验和
}DS18B20_Data_TypeDef;
u8 DS18B20_Init(void);
float DS18B20_Get_Temp(void);
#endif /* __DS18B20_H */
(3) main.c
int main(void)
{
delay_init(); //延时函数初始化
NVIC_Configuration(); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
uart_init(115200);
DS18B20_Init();
while(1)
{
tempture = DS18B20_Get_Temp()/10;
delay_ms(1000);
printf("temp:%d rn",(int)tempture);
}
}
二、HAL库函数版本
(1)DS18B20.c
其他型号MCU注意延时即可。
/* ds18b20.c */
#include "ds18b20.h"
// 系统时钟频率(根据实际情况修改)
#define SYS_CLK_FREQ 72000000 // 72MHz
#define DELAY_CALIBRATION 10 // 校准系数(需根据实际测试调整)
// 微秒级延时函数
static void Delay_us(uint32_t us)
{
us *= (SYS_CLK_FREQ / 1000000) / DELAY_CALIBRATION;
while(us--) {
__NOP(); __NOP(); __NOP(); __NOP();
__NOP(); __NOP(); __NOP(); __NOP();
}
}
// 设置GPIO为开漏输出模式(带内部上拉)
static void GPIO_OutputMode(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = DS18B20_GPIO_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(DS18B20_GPIO_PORT, &GPIO_InitStruct);
}
// 设置GPIO为上拉输入模式
static void GPIO_InputMode(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = DS18B20_GPIO_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_PULLUP;
HAL_GPIO_Init(DS18B20_GPIO_PORT, &GPIO_InitStruct);
}
// 复位脉冲
uint8_t DS18B20_Reset(void)
{
GPIO_OutputMode();
// 拉低总线480-960us
HAL_GPIO_WritePin(DS18B20_GPIO_PORT, DS18B20_GPIO_PIN, GPIO_PIN_RESET);
Delay_us(480);
// 释放总线
GPIO_InputMode();
Delay_us(60); // 等待15-60us
// 检测存在脉冲
uint8_t status = HAL_GPIO_ReadPin(DS18B20_GPIO_PORT, DS18B20_GPIO_PIN);
Delay_us(420); // 等待剩余时间
return (status == GPIO_PIN_RESET);
}
// 写一位数据
static void WriteBit(uint8_t bit)
{
GPIO_OutputMode();
HAL_GPIO_WritePin(DS18B20_GPIO_PORT, DS18B20_GPIO_PIN, GPIO_PIN_RESET);
if(bit) {
Delay_us(5); // 1-15us
GPIO_InputMode();
Delay_us(55);
} else {
Delay_us(60); // 60-120us
GPIO_InputMode();
Delay_us(5);
}
}
// 读一位数据
static uint8_t ReadBit(void)
{
uint8_t bit = 0;
GPIO_OutputMode();
// 产生读时隙
HAL_GPIO_WritePin(DS18B20_GPIO_PORT, DS18B20_GPIO_PIN, GPIO_PIN_RESET);
Delay_us(2);
GPIO_InputMode();
Delay_us(12); // 等待15us前采样
bit = HAL_GPIO_ReadPin(DS18B20_GPIO_PORT, DS18B20_GPIO_PIN);
Delay_us(45); // 保持总时隙时间
return bit;
}
// 写一个字节
void DS18B20_WriteByte(uint8_t dat)
{
for(uint8_t i=0; i<8; i++) {
WriteBit(dat & 0x01);
dat >>= 1;
}
}
// 读一个字节
uint8_t DS18B20_ReadByte(void)
{
uint8_t byte = 0;
for(uint8_t i=0; i<8; i++) {
byte >>= 1;
if(ReadBit()) byte |= 0x80;
}
return byte;
}
// 初始化
uint8_t DS18B20_Init(void)
{
__HAL_RCC_GPIOB_CLK_ENABLE();
GPIO_InputMode();
return DS18B20_Reset();
}
// 启动温度转换
void DS18B20_StartConversion(void)
{
DS18B20_Reset();
DS18B20_WriteByte(0xCC); // Skip ROM
DS18B20_WriteByte(0x44); // Convert T
}
// 读取温度值
float DS18B20_ReadTemperature(void)
{
DS18B20_Reset();
DS18B20_WriteByte(0xCC); // Skip ROM
DS18B20_WriteByte(0xBE); // Read Scratchpad
uint8_t temp_l = DS18B20_ReadByte();
uint8_t temp_h = DS18B20_ReadByte();
int16_t temp = (temp_h << 8) | temp_l;
return temp * 0.0625f; // 正确转换系数
}
(2)DS18B20.h
/* ds18b20.h */
#ifndef __DS18B20_H
#define __DS18B20_H
#include "stm32f1xx_hal.h"
// 配置使用的GPIO端口和引脚
#define DS18B20_GPIO_PORT GPIOB
#define DS18B20_GPIO_PIN GPIO_PIN_6
// 函数声明
uint8_t DS18B20_Init(void);
void DS18B20_StartConversion(void);
float DS18B20_ReadTemperature(void);
#endif /* __DS18B20_H */
(3)main.c
int main(void)
{
DS18B20_Init();
while(1)
{
float temp;
DS18B20_StartConversion();
HAL_Delay(750); // 等待转换完成
temp = DS18B20_ReadTemperature();
printf("Temperature: %.2f Crn", temp);
}
}
阅读全文
328