Fork me on GitHub

STM32-单总线通讯学习

STM32-单总线通讯学习

/**
  * @file     	BSP_1_WIRE.c
  * @author   	ydm
  * @version	V1.0
  * @date    	14-Dec-2021
  * @brief   	单总线协议驱动文件
  * @attention
  *  单总线协议的驱动文件,使用GPIO发出单总线的通讯时序,为单总线的设备提供基本驱动接口 \n
  * @htmlonly 
  * <span style="font-weight: bold">History</span> 
  * @endhtmlonly 
  * Version|Auther|Date|Describe
  * ------|----|------|-------- 
  * V1.0|ydm|14-Dec-2021|Create File
  * <h2><center>&copy;COPYRIGHT 2017 WELLCASA All Rights Reserved.</center></h2>
*/  

#include "BSP_1_Wire.h"
#include "delay.h"


/**  定义1_Wire的GPIO CLK  */
#define ONE_WIRE_GPIO_CLKENABLE      __HAL_RCC_GPIOA_CLK_ENABLE()

/**  1-wire总线输出高电平  */
#define ONE_WIRE_HIGH(One_Wire_bus_temp)				HAL_GPIO_WritePin(One_Wire_bus_temp->ONE_WIRE_PORT, One_Wire_bus_temp->ONE_WIRE_PIN, GPIO_PIN_SET)
/**  1-wire总线输出低电平  */
#define ONE_WIRE_LOW(One_Wire_bus_temp)				    HAL_GPIO_WritePin(One_Wire_bus_temp->ONE_WIRE_PORT, One_Wire_bus_temp->ONE_WIRE_PIN, GPIO_PIN_RESET)


/**
* @brief		单总线BUS的初始化函数.
* @details	    将对应的单总线BUS进行初始化. 
* @param[in]	ONE_WIRE_BUS 要将GPIO初始化的BUS
* @retval   1   初始化成功
* @retval   0   初始化失败
* @par 修改日志
* 		ydm于2021-12-14创建
*/
uint8_t BSP_1_WIRE_Init(PT_ONE_WIRE ONE_WIRE_BUS)
{
    BSP_1_WIRE_GPIO_Init(ONE_WIRE_BUS);
    BSP_1_WIRE_RST(ONE_WIRE_BUS);
    return BSP_1_WIRE_Check(ONE_WIRE_BUS);
}

/**
* @brief		单总线BUS的读取GPIO电平函数.
* @details	    读取GPIO的电平. 
* @param[in]	ONE_WIRE_BUS 要读取的BUS
* @retval   1   高电平
* @retval   0   低电平
* @par 修改日志
* 		ydm于2021-12-14创建
*/
uint8_t BSP_Ds18B20_Pin_Read(PT_ONE_WIRE ONE_WIRE_BUS)
{
	if(HAL_GPIO_ReadPin(ONE_WIRE_BUS->ONE_WIRE_PORT,ONE_WIRE_BUS->ONE_WIRE_PIN) == GPIO_PIN_RESET)
	{
		return 0;
	}
	else
	{
		return 1;
	}
}

/**
* @brief		单总线BUS的GPIO初始化函数.
* @details	    将对应的单总线BUS使用的GPIO进行初始化. 
* @param[in]	ONE_WIRE_BUS 要将GPIO初始化的BUS
* @retval
* @par 修改日志
* 		ydm于2021-12-14创建
*/
static void BSP_1_WIRE_GPIO_Init(PT_ONE_WIRE ONE_WIRE_BUS)
{
		ONE_WIRE_GPIO_CLKENABLE;
		GPIO_InitTypeDef GPIO_InitStructure;
		GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
		GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;
		GPIO_InitStructure.Pull = GPIO_NOPULL;
		GPIO_InitStructure.Pin = ONE_WIRE_BUS->ONE_WIRE_PIN;
		HAL_GPIO_Init(ONE_WIRE_BUS->ONE_WIRE_PORT, &GPIO_InitStructure);
}

/**
* @brief		设置GPIO方向.
* @details	    设置GPIO是输出状态还是输入状态. 
* @param[in]	ONE_WIRE_BUS    要输出复位时序的BUS
* @param[in]	mode    状态是输入还是输出
* @retval   
* @par 修改日志
* 		ydm于2021-12-14创建
*/
static void ONE_WIRE_PinMode(PT_ONE_WIRE ONE_WIRE_BUS,int mode)
{	
		GPIO_InitTypeDef GPIO_InitStruct;	
		if(mode == ONE_WIRE_PinMode_IN)
		{			
				GPIO_InitStruct.Pin = ONE_WIRE_BUS->ONE_WIRE_PIN;
				GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
				GPIO_InitStruct.Pull = GPIO_NOPULL;
        GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
				HAL_GPIO_Init(ONE_WIRE_BUS->ONE_WIRE_PORT, &GPIO_InitStruct);	
		}
		else if(mode == ONE_WIRE_PinMode_OUT)
		{
				GPIO_InitStruct.Pin = ONE_WIRE_BUS->ONE_WIRE_PIN;
				GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD;
				GPIO_InitStruct.Pull = GPIO_NOPULL;
				GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
				HAL_GPIO_Init(ONE_WIRE_BUS->ONE_WIRE_PORT, &GPIO_InitStruct);		
		}
}

/**
* @brief		单总线复位时序输出.
* @details	    将对应的单总线BUS输出复位时序. 
* @param[in]	ONE_WIRE_BUS 要输出复位时序的BUS
* @retval
* @par 修改日志
* 		ydm于2021-12-14创建
*/
void BSP_1_WIRE_RST(PT_ONE_WIRE ONE_WIRE_BUS)
{
    ONE_WIRE_PinMode(ONE_WIRE_BUS,ONE_WIRE_PinMode_OUT);
    //拉低单总线480 ~ 960 us产生复位脉冲
    ONE_WIRE_LOW(ONE_WIRE_BUS);
    Delay_us(520);
    ONE_WIRE_HIGH(ONE_WIRE_BUS);
    Delay_us(15);
}

/**
* @brief		检测单总线检测是否有从设备相应.
* @details	    检测单总线上是否有从设备相应. 
* @param[in]	ONE_WIRE_BUS 要输出复位时序的BUS
* @retval   0   检测失败
* @retval   1   检测成功
* @par 修改日志
* 		ydm于2021-12-14创建
*/
uint8_t BSP_1_WIRE_Check(PT_ONE_WIRE ONE_WIRE_BUS)
{
    int i = 0;
    ONE_WIRE_PinMode(ONE_WIRE_BUS,ONE_WIRE_PinMode_IN);
    while((BSP_Ds18B20_Pin_Read(ONE_WIRE_BUS)) && (i<200))
    {
        i++;
        Delay_us(1);
    }

    if(i >= 200)
    {
        return 0;
    }
    else
    {
        i= 0;
    }

    //主机至少接收480us
    while((!BSP_Ds18B20_Pin_Read(ONE_WIRE_BUS)) && (i<240))
    {
        i++;
        Delay_us(1); 
    }
    if(i >= 240)
    {
        return 0;
    }
    else
    {
        return 1;
    }

}

/**
* @brief		向单总线上写一个字节.
* @details	    向单总线上写一个字节. 
* @param[in]	ONE_WIRE_BUS 要进行写操作的BUS
* @param[in]	data 要写入BUS的数据.
* @retval   
* @par 修改日志
* 		ydm于2021-12-14创建
*/
void BSP_OneWire_Write_Byte(PT_ONE_WIRE ONE_WIRE_BUS,uint8_t data)
{
    int i = 0;
    ONE_WIRE_PinMode(ONE_WIRE_BUS,ONE_WIRE_PinMode_OUT);
    for(i = 0 ; i < 8 ; i++)
    {
        ONE_WIRE_LOW(ONE_WIRE_BUS);
        Delay_us(2);
        if(data & 0x01)
        {
            ONE_WIRE_HIGH(ONE_WIRE_BUS);
        }
        else
        {
            ONE_WIRE_LOW(ONE_WIRE_BUS);
        }
        Delay_us(60);
        ONE_WIRE_HIGH(ONE_WIRE_BUS);
        Delay_us(2);
        data >>= 1;
    }
}

/**
* @brief		从单总线上读取一个bit.
* @details	    从单总线上读取一个bit. 
* @param[in]	ONE_WIRE_BUS 要读取bit的BUS
* @retval   0   读取到bit为0
* @retval   1   读取到bit为1
* @par 修改日志
* 		ydm于2021-12-14创建
*/
static uint8_t BSP_OneWire_Read_bit(PT_ONE_WIRE ONE_WIRE_BUS)
{
    uint8_t data = 0;
    ONE_WIRE_PinMode(ONE_WIRE_BUS,ONE_WIRE_PinMode_OUT);
    ONE_WIRE_LOW(ONE_WIRE_BUS);
    Delay_us(2);
    ONE_WIRE_PinMode(ONE_WIRE_BUS,ONE_WIRE_PinMode_IN);
    Delay_us(5);
    if(BSP_Ds18B20_Pin_Read(ONE_WIRE_BUS))
    {
        data = 1;
    }
    else
    {
        data = 0;
    }
    Delay_us(50);
    return data;
}

/**
* @brief		从单总线上读取一个字节.
* @details	    从单总线上读取一个字节. 
* @param[in]	ONE_WIRE_BUS 要读取字节的BUS
* @retval   data   读取到的data
* @par 修改日志
* 		ydm于2021-12-14创建
*/
uint8_t BSP_OneWire_Read_Byte(PT_ONE_WIRE ONE_WIRE_BUS)
{
    uint8_t i,data;
    data = 0;
    for (i=0;i<8;i++) 
    {
        data  >>= 1;
        ONE_WIRE_LOW(ONE_WIRE_BUS);
        Delay_us(3);
        ONE_WIRE_HIGH(ONE_WIRE_BUS);
        Delay_us(3);
        if(BSP_Ds18B20_Pin_Read(ONE_WIRE_BUS) == 0)
        {
            ;
        }
        else
        {
            data |= 0x80;
        }
        Delay_us(60);
    }
    return data;
}
/**
  * @file     	BSP_1_WIRE.h
  * @author   	ydm
  * @version	V1.0
  * @date    	14-Dec-2021
  * @brief   	单总线协议驱动文件
  * @attention
  *  单总线协议的驱动文件,使用GPIO发出单总线的通讯时序,为单总线的设备提供基本驱动接口 \n
  * @htmlonly 
  * <span style="font-weight: bold">History</span> 
  * @endhtmlonly 
  * Version|Auther|Date|Describe
  * ------|----|------|-------- 
  * V1.0|ydm|14-Dec-2021|Create File
  * <h2><center>&copy;COPYRIGHT 2017 WELLCASA All Rights Reserved.</center></h2>
*/  
#ifndef __BSP_1_WIRE_H
#define __BSP_1_WIRE_H

#include "main.h"

/** 定义单总线引脚方向为输出方向 */
#define					ONE_WIRE_PinMode_OUT   	        0x01
/** 定义单总线引脚方向为输入方向 */      
#define					ONE_WIRE_PinMode_IN   	        0x00

#ifdef __cplusplus
extern "C" {
#endif

/** 
 * @brief		单总线BUS结构体.
 * @details	    记录单总线的基础信息. 
 */
typedef struct One_Wire_Opr
{
    uint16_t        ONE_WIRE_PIN;        /*!< 使用到的GPIO的PIN */
    GPIO_TypeDef*   ONE_WIRE_PORT;       /*!< 使用到的GPIO的PORT */
}ONE_WIRE , *PT_ONE_WIRE;

/**
* @brief		单总线BUS的初始化函数.
* @details	    将对应的单总线BUS进行初始化. 
* @param[in]	ONE_WIRE_BUS 要将GPIO初始化的BUS
* @retval   1   初始化成功
* @retval   0   初始化失败
* @par 修改日志
* 		ydm于2021-12-14创建
*/
uint8_t BSP_1_WIRE_Init(PT_ONE_WIRE ONE_WIRE_BUS);

/**
* @brief		单总线BUS的读取GPIO电平函数.
* @details	    读取GPIO的电平. 
* @param[in]	ONE_WIRE_BUS 要读取的BUS
* @retval   1   高电平
* @retval   0   低电平
* @par 修改日志
* 		ydm于2021-12-14创建
*/
uint8_t BSP_Ds18B20_Pin_Read(PT_ONE_WIRE ONE_WIRE_BUS)

/**
* @brief		单总线BUS的GPIO初始化函数.
* @details	    将对应的单总线BUS使用的GPIO进行初始化. 
* @param[in]	ONE_WIRE_BUS 要将GPIO初始化的BUS
* @retval
* @par 修改日志
* 		ydm于2021-12-14创建
*/
static void BSP_1_WIRE_GPIO_Init(PT_ONE_WIRE ONE_WIRE_BUS);

/**
* @brief		设置GPIO方向.
* @details	    设置GPIO是输出状态还是输入状态. 
* @param[in]	ONE_WIRE_BUS    要输出复位时序的BUS
* @param[in]	mode    状态是输入还是输出
* @retval   
* @par 修改日志
* 		ydm于2021-12-14创建
*/
static void ONE_WIRE_PinMode(PT_ONE_WIRE ONE_WIRE_BUS,int mode);

/**
* @brief		单总线复位时序输出.
* @details	    将对应的单总线BUS输出复位时序. 
* @param[in]	ONE_WIRE_BUS 要输出复位时序的BUS
* @retval
* @par 修改日志
* 		ydm于2021-12-14创建
*/
void BSP_1_WIRE_RST(PT_ONE_WIRE ONE_WIRE_BUS);

/**
* @brief		检测单总线检测是否有从设备相应.
* @details	    检测单总线上是否有从设备相应. 
* @param[in]	ONE_WIRE_BUS 要输出复位时序的BUS
* @retval   0   检测失败
* @retval   1   检测成功
* @par 修改日志
* 		ydm于2021-12-14创建
*/
uint8_t BSP_1_WIRE_Check(PT_ONE_WIRE ONE_WIRE_BUS);

/**
* @brief		向单总线上写一个字节.
* @details	    向单总线上写一个字节. 
* @param[in]	ONE_WIRE_BUS 要进行写操作的BUS
* @param[in]	data 要写入BUS的数据.
* @retval   
* @par 修改日志
* 		ydm于2021-12-14创建
*/
void BSP_OneWire_Write_Byte(PT_ONE_WIRE ONE_WIRE_BUS,uint8_t data);

/**
* @brief		从单总线上读取一个bit.
* @details	    从单总线上读取一个bit. 
* @param[in]	ONE_WIRE_BUS 要读取bit的BUS
* @retval   0   读取到bit为0
* @retval   1   读取到bit为1
* @par 修改日志
* 		ydm于2021-12-14创建
*/
static uint8_t BSP_OneWire_Read_bit(PT_ONE_WIRE ONE_WIRE_BUS);


/**
* @brief		从单总线上读取一个字节.
* @details	    从单总线上读取一个字节. 
* @param[in]	ONE_WIRE_BUS 要读取字节的BUS
* @retval   data   读取到的data
* @par 修改日志
* 		ydm于2021-12-14创建
*/
uint8_t BSP_OneWire_Read_Byte(PT_ONE_WIRE ONE_WIRE_BUS);

#ifdef __cplusplus
}
#endif

#endif // !__BSP_1_WIRE_H

/**
  * @file     	BSP_DS18B20.h
  * @author   	ydm
  * @version	V1.0
  * @date    	14-Dec-2021
  * @brief   	DS18B20驱动文件
  * @attention
  *  DS18B20驱动文件,提供DS18B20需要的操作接口 \n
  * @htmlonly 
  * <span style="font-weight: bold">History</span> 
  * @endhtmlonly 
  * Version|Auther|Date|Describe
  * ------|----|------|-------- 
  * V1.0|ydm|14-Dec-2021|Create File
  * <h2><center>&copy;COPYRIGHT 2017 WELLCASA All Rights Reserved.</center></h2>
*/ 

#include "BSP_DS18B20.h"
#include "delay.h"
#include "string.h"

static PT_DS18B20 g_ptds18b20OprHead;

uint8_t Ds18b20Init(PT_DS18B20 pDs18b20)
{
	
	int i = 0;
	RegisterDs18b20Opr(pDs18b20);

	while((!BSP_1_WIRE_Init(pDs18b20->pDS18B20_OneWire)) && i<50)
	{
		i++;
		Delay_us(1);
	}

	if(i >= 50)
	{
		return 0;
	}
	else
	{
		return 1;
	}

}

static void RegisterDs18b20Opr(PT_DS18B20 pDs18b20)
{
	PT_DS18B20 ptTmp;

	if (!g_ptds18b20OprHead)
	{
			g_ptds18b20OprHead = pDs18b20;
			pDs18b20->ptNext = NULL;
	}
	else
	{
			ptTmp = g_ptds18b20OprHead;
			while (ptTmp->ptNext)
			{
					ptTmp = ptTmp->ptNext;
			}
			ptTmp->ptNext = pDs18b20;
			pDs18b20->ptNext = NULL;
	}
	
}

PT_DS18B20 GetDs18b20Opr(const char *pcName)
{
	PT_DS18B20 ptTmp = g_ptds18b20OprHead;
	
	while (ptTmp)
	{
			if (strcmp(ptTmp->name, pcName) == 0)
			{
					return ptTmp;
			}
			ptTmp = ptTmp->ptNext;
	}
	return NULL;
}


static void BSP_DS18B20_Start(PT_DS18B20 pDs18b20)
{
	BSP_1_WIRE_RST(pDs18b20->pDS18B20_OneWire);
	BSP_1_WIRE_Check(pDs18b20->pDS18B20_OneWire);
	Delay_us(1000);
	BSP_OneWire_Write_Byte(pDs18b20->pDS18B20_OneWire,0xcc);		//skip rom
	BSP_OneWire_Write_Byte(pDs18b20->pDS18B20_OneWire,0x44);		//convert
}

float BSP_DS18B20_GetTemp(const char *pcName)
{
	uint8_t TL = 0,TH = 0;
	int temp = 0;
	uint8_t CRC_Temp = 0;
	uint8_t buf[10] = {'\0'};
	float tem = 0.0f;
	PT_DS18B20 ptTmp = GetDs18b20Opr(pcName);
	BSP_DS18B20_Start(ptTmp);
	BSP_1_WIRE_RST(ptTmp->pDS18B20_OneWire);
	BSP_1_WIRE_Check(ptTmp->pDS18B20_OneWire);
	Delay_us(1000);
	BSP_OneWire_Write_Byte(ptTmp->pDS18B20_OneWire,0xcc);		//skip rom
	BSP_OneWire_Write_Byte(ptTmp->pDS18B20_OneWire,0xbe);		//read
//	TL=BSP_OneWire_Read_Byte(ptTmp->pDS18B20_OneWire); // LSB   
//	TH=BSP_OneWire_Read_Byte(ptTmp->pDS18B20_OneWire); // MSB  

	buf[0] =BSP_OneWire_Read_Byte(ptTmp->pDS18B20_OneWire); // LSB   
  buf[1] =BSP_OneWire_Read_Byte(ptTmp->pDS18B20_OneWire); // MSB 
	buf[2] = BSP_OneWire_Read_Byte(ptTmp->pDS18B20_OneWire); // Byte2
	buf[3] = BSP_OneWire_Read_Byte(ptTmp->pDS18B20_OneWire); // Byte3 
	buf[4] = BSP_OneWire_Read_Byte(ptTmp->pDS18B20_OneWire); // Byte4 
	buf[5] = BSP_OneWire_Read_Byte(ptTmp->pDS18B20_OneWire); // Byte5 
	buf[6] = BSP_OneWire_Read_Byte(ptTmp->pDS18B20_OneWire); // Byte6 
	buf[7] = BSP_OneWire_Read_Byte(ptTmp->pDS18B20_OneWire); // Byte7 
	buf[8]  = BSP_OneWire_Read_Byte(ptTmp->pDS18B20_OneWire); // CRC
	
	CRC_Temp = buf[8];
  temp=buf[1];
	temp <<= 8;
	temp|=buf[0];//拼接为16位数据

	if(BSP_DS18B20_CheckCRC(buf,8,CRC_Temp))
	{
		//校验成功
		if(temp < 0)//DS18B20存储数据为补码形式,当temp<0时需要转为原码
		{
			temp = ~temp;
			temp += 1;
		}
		tem = temp;
		tem = tem * 0.0625f;
		return tem;
	}
	else
	{
		return 0;
	}
}

static uint8_t BSP_DS18B20_CheckCRC(uint8_t* temp,uint8_t size,uint8_t CRC_Temp)
{
	uint8_t test = crcCalc(temp,size);
	if(test == CRC_Temp)
	{
		//校验成功
		return 1;
	}
	else
	{
		return 0;
	}
}

static uint8_t crcCalc(void *src, uint8_t size)
{
	uint8_t ret = 0;
	uint8_t *p;
	int i = 0;
	uint8_t pBuf = 0;
	p = (uint8_t*)src;
 
	while(size--)
	{
		pBuf = *p ++;
 
		for ( i = 0; i < 8; i ++ )
		{
			if ((ret ^ (pBuf)) & 0x01)
			{
				ret ^= 0x18;
				ret >>= 1;
				ret |= 0x80;
			}
			else
			{
				ret >>= 1;
			}
 
			pBuf >>= 1;
		}
	}
 
	return ret;
}


/**
  * @file     	BSP_DS18B20.h
  * @author   	ydm
  * @version	V1.0
  * @date    	14-Dec-2021
  * @brief   	DS18B20驱动文件
  * @attention
  *  DS18B20驱动文件,提供DS18B20需要的操作接口 \n
  * @htmlonly 
  * <span style="font-weight: bold">History</span> 
  * @endhtmlonly 
  * Version|Auther|Date|Describe
  * ------|----|------|-------- 
  * V1.0|ydm|14-Dec-2021|Create File
  * <h2><center>&copy;COPYRIGHT 2017 WELLCASA All Rights Reserved.</center></h2>
*/  
#ifndef __BSP_DS18B20_H
#define __BSP_DS18B20_H

#include "main.h"
#include "BSP_1_Wire.h"

#ifdef __cplusplus
extern "C" {
#endif

/** 
 * @brief		DS18B20结构体.
 * @details	    记录DS18B20的基础信息. 
 */
typedef struct Ds18B20Opr
{
    char *name;                                     /*!< DS18B20名称 */
    float temperature;
    struct One_Wire_Opr *pDS18B20_OneWire;
    struct Ds18B20Opr *ptNext;                      /*!< 链表指针 */
}DS18B20 , *PT_DS18B20;

uint8_t Ds18b20Init(PT_DS18B20 pDs18b20);
static void RegisterDs18b20Opr(PT_DS18B20 ptds18b20);
PT_DS18B20 GetDs18b20Opr(const char *pcName);
static void BSP_DS18B20_Start(PT_DS18B20 ptds18b20);
float BSP_DS18B20_GetTemp(const char *pcName);
static uint8_t BSP_DS18B20_CheckCRC(uint8_t* temp,uint8_t size,uint8_t CRC_Temp);
static uint8_t crcCalc(void *src, uint8_t size);

#ifdef __cplusplus
}
#endif

#endif // !__BSP_DS18B20_H

posted @   一地鸡毛-  阅读(685)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律
点击右上角即可分享
微信分享提示