免费的C语言和linuxPDF文档:传送门
有一点C语言基础和华大单片机开发经验,应该就能看得懂就不多介绍了,有疑问可以留言,看到就回了;
关于协议具体解析:传送门@不脱发的程序猿
注(2021-05-21) :如果有时候数据采集不准确,可能和定时器中断有关,中断会打乱时序;
因此再读取温度的时候关闭中断,读取完成后开启中断(CMx关开全局中断网上有例程,自己找一下就好了);
#include "ds18b20.h"
uint8_t DS18B20_Init(void)
{
stc_gpio_config_t pstcGpioCfg;
DDL_ZERO_STRUCT(pstcGpioCfg);
Sysctrl_SetPeripheralGate(SysctrlPeripheralGpio, TRUE);
pstcGpioCfg.enDir = GpioDirOut;
pstcGpioCfg.enDrv = GpioDrvH; //< 端口驱动能力配置->高驱动能力
pstcGpioCfg.enPuPd = GpioNoPuPd; //无上下拉
pstcGpioCfg.enOD = GpioOdDisable; //< 端口开漏输出配置->开漏输出关闭
pstcGpioCfg.enCtrlMode = GpioFastIO; //< 端口输入/输出值寄存器总线控制模式配置->AHB
Gpio_Init(DS18B20DQ_PORT, DS18B20DQ_PIN, &pstcGpioCfg); //初始化引脚
Gpio_WriteOutputIO(DS18B20DQ_PORT, DS18B20DQ_PIN,TRUE); ///< 默认置高电平
}
static void DS18B20_IO_OUT(void)
{
stc_gpio_config_t pstcGpioCfg;
DDL_ZERO_STRUCT(pstcGpioCfg);
pstcGpioCfg.enDir = GpioDirOut;
pstcGpioCfg.enDrv = GpioDrvH; //< 端口驱动能力配置->高驱动能力
pstcGpioCfg.enPuPd = GpioNoPuPd; //无上下拉
pstcGpioCfg.enOD = GpioOdDisable; //< 端口开漏输出配置->开漏输出关闭
pstcGpioCfg.enCtrlMode = GpioFastIO; //< 端口输入/输出值寄存器总线控制模式配置->AHB
Gpio_Init(DS18B20DQ_PORT, DS18B20DQ_PIN, &pstcGpioCfg); //< 初始化引脚
Gpio_WriteOutputIO(DS18B20DQ_PORT, DS18B20DQ_PIN,FALSE);//< 默认置高电平
}
static void DS18B20_IO_IN(void)
{
stc_gpio_config_t pstcGpioCfg;
DDL_ZERO_STRUCT(pstcGpioCfg);
pstcGpioCfg.enDir = GpioDirIn;
pstcGpioCfg.enDrv = GpioDrvL;
pstcGpioCfg.enPuPd = GpioNoPuPd;
pstcGpioCfg.enOD = GpioOdDisable;
pstcGpioCfg.enCtrlMode = GpioFastIO;
Gpio_Init(DS18B20DQ_PORT, DS18B20DQ_PIN, &pstcGpioCfg);
}
//复位DS18B20
static void DS18B20_Rst(void)
{
DS18B20_IO_OUT(); ///< 配置端口输出
Gpio_WriteOutputIO(DS18B20DQ_PORT, DS18B20DQ_PIN,FALSE);///< 拉低DQ
__delay_us__(DELAY_700US); ///< 拉低700us
Gpio_WriteOutputIO(DS18B20DQ_PORT, DS18B20DQ_PIN,TRUE); ///< 拉高DQ
__delay_us__(DELAY_15US); ///< 拉高15us
}
//等待DS18B20的回应
//返回1:未检测到DS18B20的存在
//返回0:存在
static uint8_t DS18B20_Check(void)
{
uint8_t retry = 0;
DS18B20_IO_IN(); ///< 配置端口输入
while ((Gpio_GetInputIO(DS18B20DQ_PORT, DS18B20DQ_PIN) == 1) && (retry < 100)) ///< 最多200us
{
retry++;
__delay_us__(DELAY_2US); ///< 每次等待2us
}
if (retry >= 200)
{
return 1;
}
else
{
retry = 0;
}
while ((Gpio_GetInputIO(DS18B20DQ_PORT, DS18B20DQ_PIN) == 0) && (retry < 120)) ///< 最多240us
{
retry++;
__delay_us__(DELAY_2US); ///< 每次等待2us
}
if (retry >= 120)
{
return 1;
}
return 0;
}
//写一个字节到DS18B20
//dat:要写入的字节
static 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
{
Gpio_WriteOutputIO(DS18B20DQ_PORT, DS18B20DQ_PIN,FALSE);
__delay_us__(DELAY_2US); ///< 拉低2us
Gpio_WriteOutputIO(DS18B20DQ_PORT, DS18B20DQ_PIN,TRUE);
__delay_us__(DELAY_60US); ///< 拉高64us
}
else ///< 写入0
{
Gpio_WriteOutputIO(DS18B20DQ_PORT, DS18B20DQ_PIN,FALSE);
__delay_us__(DELAY_60US); ///< 拉低64us
Gpio_WriteOutputIO(DS18B20DQ_PORT, DS18B20DQ_PIN,TRUE);
__delay_us__(DELAY_2US); ///< 拉高2us
}
}
}
//从DS18B20读取一个位
//返回值:1/0
static uint8_t DS18B20_Read_Bit(void)
{
uint8_t data;
DS18B20_IO_OUT(); ///< 配置端口输出
Gpio_WriteOutputIO(DS18B20DQ_PORT, DS18B20DQ_PIN,FALSE);
__delay_us__(DELAY_2US); ///< 等待2us
Gpio_WriteOutputIO(DS18B20DQ_PORT, DS18B20DQ_PIN,TRUE);
DS18B20_IO_IN(); ///< 配置端口输入
__delay_us__(DELAY_12US); ///< 等待12us
if (Gpio_GetInputIO(DS18B20DQ_PORT, DS18B20DQ_PIN) == 1)
{
data = 1;
}
else
{
data = 0;
}
__delay_us__(DELAY_52US); ///< 等待52us
return data;
}
//从DS18B20读取一个字节
//返回值:读到的数据
static uint8_t DS18B20_Read_Byte(void)
{
uint8_t i = 0, j = 0, dat = 0;
uint8_t num1, num2;
for (i = 1; i <= 8; i++) ///< 一次读取一个字节,8位
{
j = DS18B20_Read_Bit();
num1 = j << 7;
num2 = dat >> 1;
dat = num1 | num2;
}
return dat;
}
// TL = DS18B20_Read_Byte(); ///< 读取LSB,低八位数据
// TH = DS18B20_Read_Byte(); ///< 读取MSB,高八位数据
//开始温度转换
static uint8_t DS18B20_Start(void)
{
DS18B20_Rst();
if (!DS18B20_Check())
{
DS18B20_Write_Byte(0xCC); ///< skip rom
DS18B20_Write_Byte(0x44); ///< convert
return 0;
}
else
{
return 1;
}
}
//读出温度
static float DS18B20_Read_Templet(void)
{
uint8_t TL = 0, TH = 0;
uint16_t tem = 0;
float temp = 0;
uint8_t temp_flag = 0;
//读取温度
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_flag = 0;
}
else ///< 温度为正
{
temp_flag = 1;
}
tem = TH;
tem <<= 8;
tem += TL;
temp = (float)tem / 16.0; ///< 转换数据
if (temp_flag)
{
return temp;
}
return -temp;
}
float DS18B20_Get_Templet(void)
{
float fget_temp;
uint8_t check_flag;
check_flag = DS18B20_Start(); //1
if(!check_flag)
{
DS18B20_Rst(); //2
DS18B20_Check(); //3
fget_temp = DS18B20_Read_Templet(); //4
PRINTF_B20("DS18B20_Get_temp = %f", fget_temp);
return fget_temp;
}
PRINTF_B20("DS18B20_Get_temp fail");
return 1;
}
#ifndef __DS18B20__
#define __DS18B20__
#include "global.h"
#define DS18B20DQ_PORT GpioPortB
#define DS18B20DQ_PIN GpioPin8
extern uint8_t DS18B20_Init(void); //初始化管脚
extern float DS18B20_Get_Templet(void); //获取温度
#endif
//华大单片机时钟不准,最好做验证
//延时函数要准确,避免数据不准确
//如果有逻辑分析仪 可以自己做测试
//没有的话,我把我的内部时钟配置源码放在下面了,用我的时钟配置应该没问题
#define DELAY_700US 3694
#define DELAY_200US 1050
#define DELAY_150US 789
#define DELAY_60US 310
#define DELAY_52US 267
#define DELAY_15US 71
#define DELAY_12US 55
#define DELAY_2US 1
void __delay_us__(const uint32_t ucus)
{
register uint32_t ucustemp = ucus;
while(ucustemp--)
{
__NOP();
}
}
#define DEBUG_18B20 1
#if DEBUG_18B20
#define PRINTF_B20(format, ...) printf(format "\r\n", ##__VA_ARGS__ )
#else
#define PRINTF_B20(format, ...)
#endif
//时钟切换内部时钟48MHZ
void SYSTEMCLKSWITCH(void)
{
stc_sysctrl_pll_config_t stcPLLCfg;
stc_sysctrl_clk_config_t stcCfg;
DDL_ZERO_STRUCT(stcPLLCfg);
DDL_ZERO_STRUCT(stcCfg);
Flash_WaitCycle(FlashWaitCycle1); //< 当前时钟源HCLK大于24M:此处设置FLASH 读等待周期为1 cycle(前面已经配置,此处无需重复配置)
/*< 切换时钟前配置PLL相关参数*/
Sysctrl_SetRCHTrim(SysctrlRchFreq4MHz); //PLL使用RCH24MHz作为时钟源,因此需要先设置RCH,之前已经设置
Sysctrl_ClkSourceEnable(SysctrlClkRCH, TRUE); //RCH使能,因RCH使能未关闭,此处可以不重复操作
stcPLLCfg.enInFreq = SysctrlPllInFreq4_6MHz; //RCH 24MHz
stcPLLCfg.enOutFreq = SysctrlPllOutFreq36_48MHz; //PLL 输出48MHz
stcPLLCfg.enPllClkSrc = SysctrlPllRch; //输入时钟源选择RCH
stcPLLCfg.enPllMul = SysctrlPllMul12; //4MHz x 12 = 48MHz
Sysctrl_SetPLLFreq(&stcPLLCfg);
///< 选择PLL作为HCLK时钟源;
stcCfg.enClkSrc = SysctrlClkPLL;
stcCfg.enHClkDiv = SysctrlHclkDiv1;
stcCfg.enPClkDiv = SysctrlPclkDiv1;
Sysctrl_ClkInit(&stcCfg);
Sysctrl_SetPLLStableTime(SysctrlPllStableCycle16384);
Sysctrl_ClkSourceEnable(SysctrlClkPLL, TRUE);
Sysctrl_SysClkSwitch(SysctrlClkPLL); //< 时钟切换
Sysctrl_ClkSourceEnable(SysctrlClkXTH, FALSE); //< 根据需要选择是否关闭原时钟(此处关闭XTH)
}