当前位置: 首页 > 工具软件 > ta4j > 使用案例 >

华大单片机HC32L136J8TA读取DS18B20温度(源码+时钟配置)

秦哲瀚
2023-12-01
  • 免费的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)

}

 类似资料: