STM32使用HAL库获取超声波传感器数据
创始人
2024-05-19 14:08:49
0

1.开发平台:

软件:Keil5 STM32CubeMX
硬件:STM32F103C8T6 HC-SR04

2.实现思路列举

类比51单片机的方法直接读取定时器数值中高电平时长

优点:实现起来很方便,有51单片机开发经验看起来很容易理解
缺点:个人感觉实时性一般,而且一旦超声波传感器Echo引脚出现问题,会导致程序卡死

自己在定时器中通过引脚状态进行计时,用结束时间-开始时间

优点:实现起来比前者还要方便,并且理论上更容易扩展,看起来也非常简洁
缺点:误差很大,精确度很低,实时性差

在定时器输入中断捕获回调函数中通过判断引脚上升沿和下降沿的变化的时间来读取高电平时长

优点: 实时性好,可以放在其它定时器中断中进行,比如放到系统滴答定时器10Hz的中断中执行,个人认为是对STM32定时器强大功能在计算高电平时间上最好的开发
缺点:代码实现难度大,需要对STM32定时器寄存器功能的深层理解(在这里发自内心感谢硬石科技提供的例程)

3.个人实现方法

按照最新的STM32CubeMX,进行编程即可(这里对于基础的调试配置,晶振配置,时钟树的配置不做赘述):
这里只需要选定:按照方向输入捕获 和 预分频 71 以及计数周期65535即可
在这里插入图片描述
这边的参数是默认的可以不用管
在这里插入图片描述
之后打开定时器中断
在这里插入图片描述
至于选择那个IO口作为Trig引脚,随意即可,然后上代码:
hc_sr04.c

#include "hc_sr04.h"HC_SR04_define hc_Sr04;// 超声波传感器初始化
void HC_SR04_Init(void)
{	hc_Sr04.timPeriod = SR04TIM_PERIOD;	//初始化计数周期//获得Trig引脚hc_Sr04.TRIG = SR04_TRIG;	hc_Sr04.TRIG_PIN = SR04_TRIG_PIN;//hc_Sr04.tim = &SR04TIM;		//获取定时器hc_Sr04.channel = SR04TIM_CHANNEL;	//定时器通道1hc_Sr04.timITcc1 = SR04TIM_IT_CC;		//设定中断通道hc_Sr04.timSTRAT_ICPolarity = SR04TIM_STRAT_ICPolarity;		//中断捕获开始hc_Sr04.timEND_ICPolarity = SR04TIM_END_ICPolarity;				//中断捕获结束//获取锁相环时钟频率/预分频 = 定时器的工作频率hc_Sr04.ulTmrClk = HAL_RCC_GetHCLKFreq()/SR04TIM_PRESCALER; /* 启动定时器 */HAL_TIM_Base_Start_IT(&htim3);  /* 启动定时器3通道1输入捕获中断*/HAL_TIM_IC_Start_IT(&htim3,SR04TIM_CHANNEL);
}unsigned int i = 0;
//定时器中断捕获回调函数
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{HCSR04_CaptureHighLevel(&hc_Sr04);
}/* 获取超声波测量的距离 */
void getDistance(HC_SR04_define *sr04)
{//Trig引脚用来触发超声波测量HAL_GPIO_WritePin(sr04->TRIG,sr04->TRIG_PIN,GPIO_PIN_SET);delay_us(10);//触发信号延时10usHAL_GPIO_WritePin(sr04->TRIG,sr04->TRIG_PIN,GPIO_PIN_RESET);/* 完成测量高电平脉宽 */if(sr04->ucFinishFlag == 1){/* 计算高电平计数值 = 捕获到的电平的周期*定时器计数溢出值上限+捕获到高电平的数值*/sr04->ulTime = sr04->usCtr;/*distance是一次测量通过一次计算出的距离的值*//*距离distance=捕获的高电平的数值对于时钟取余然后得到的超声波反馈的时间*//*根据声速是每秒钟340m,因为声音发出后遇到物体返回回来,这是一个来回 所以具体计算的时候要除以2,因为捕获的时间是us,所以要除以1000*//*得到的距离是mm*/sr04->distance = ((sr04->ulTime%sr04->ulTmrClk)*340)/(1000*2);sr04->cmdis = sr04->distance/10.0f;		//计算cm的距离sr04->ucFinishFlag = 0;}
}//超声波捕获高电平
void HCSR04_CaptureHighLevel(HC_SR04_define *sr04)
{TIM_IC_InitTypeDef sConfigIC;if(sr04->ucStartFlag == 0){__HAL_TIM_SET_COUNTER(sr04->tim,0); // 清零定时器计数sr04->usPeriod = 0;			sr04->usCtr = 0;// 配置输入捕获参数,主要是修改触发电平sConfigIC.ICPolarity = sr04->timEND_ICPolarity;sConfigIC.ICSelection = TIM_ICSELECTION_DIRECTTI;sConfigIC.ICPrescaler = TIM_ICPSC_DIV1;sConfigIC.ICFilter = 0;HAL_TIM_IC_ConfigChannel(sr04->tim, &sConfigIC,sr04->channel);// 清除中断标志位__HAL_TIM_CLEAR_IT(sr04->tim,sr04->timITcc1);// 启动输入捕获并开启中断__HAL_TIM_ENABLE_IT(sr04->tim,sr04->timITcc1);TIM_CCxChannelCmd(sr04->tim->Instance,sr04->channel, TIM_CCx_ENABLE);__HAL_TIM_ENABLE(sr04->tim);sr04->ucStartFlag = 1;	i = 14;	}else{i = 15;// 获取定时器计数值sr04->usCtr = HAL_TIM_ReadCapturedValue(sr04->tim,sr04->channel);// 配置输入捕获参数,主要是修改触发电平sConfigIC.ICPolarity = sr04->timSTRAT_ICPolarity;sConfigIC.ICSelection = TIM_ICSELECTION_DIRECTTI;sConfigIC.ICPrescaler = TIM_ICPSC_DIV1;sConfigIC.ICFilter = 0;HAL_TIM_IC_ConfigChannel(sr04->tim, &sConfigIC, sr04->channel);// 清除中断标志位__HAL_TIM_CLEAR_IT(sr04->tim, sr04->timITcc1); // 启动输入捕获并开启中断__HAL_TIM_ENABLE_IT(sr04->tim,sr04->timITcc1);TIM_CCxChannelCmd(sr04->tim->Instance,sr04->channel, TIM_CCx_ENABLE);__HAL_TIM_ENABLE(sr04->tim);sr04->ucStartFlag = 0;			sr04->ucFinishFlag = 1;    }
}

hc_sr04.h

#ifndef __HC_SR04_H
#define __HC_SR04_H#include "header.h"#define SR04_TRIG GPIOA
#define SR04_TRIG_PIN GPIO_PIN_5#define SR04TIM_PRESCALER 71			//预分频71
#define SR04TIM_PERIOD 		0xFFFF	//定时器技术周期
#define SR04TIM htim3
#define SR04TIM_CHANNEL TIM_CHANNEL_1			//通道1
#define SR04TIM_IT_CC		TIM_IT_CC1					//中断1
#define SR04TIM_STRAT_ICPolarity        TIM_INPUTCHANNELPOLARITY_RISING          //测量的起始边沿
#define SR04TIM_END_ICPolarity    			TIM_INPUTCHANNELPOLARITY_FALLING         //测量的结束边沿typedef struct HC_SR04
{uint8_t   ucFinishFlag;		//捕获结束标志位uint8_t   ucStartFlag;		//捕获开始标志位uint16_t  usCtr;					//捕获时间uint16_t  usPeriod;				//捕获周期uint32_t  ulTmrClk;				//定时器工作频率uint32_t  ulTime;uint32_t distance;						//最终捕获距离float    cmdis;								//以厘米为单位的距离uint16_t     TRIG_PIN;		//方向控制引脚1GPIO_TypeDef *TRIG;			//方向控制GPIOTIM_HandleTypeDef *tim;	//需要用的定时器uint16_t timPeriod;			//定时器计数周期uint32_t channel;				//定时器通道uint32_t timITcc1;			//定时器中断计数1uint32_t timSTRAT_ICPolarity;uint32_t timEND_ICPolarity;
}HC_SR04_define;extern HC_SR04_define hc_Sr04;// 超声波传感器初始化
void HC_SR04_Init(void);
//超声波捕获高电平 只能放到定时器回调函数中使用
void HCSR04_CaptureHighLevel(HC_SR04_define *sr04);
/* 获取超声波测量的距离 */
void getDistance(HC_SR04_define *sr04);#endif //

使用非常简单,如果增加超声波传感器只需要再定义一个HC_SR04_define 数据结构型变量,然后按照初始化中的顺序对端口和初值进行赋值传递,之后放到回调函数中即可,获取距离

getDistance(&hc_Sr04);

4.对于HAL库更新的吐槽

其实以上代码的核心部分是我在20年的时候参照硬石电子的资料写的,但是如果使用的是写这篇文章时候的HAL库还按照硬石的资料去写,是不能实现的,主要原因如下:大家可以来对比一下,
这是19年的HAL_TIM_IC_Start_IT函数接下来看一下现在的

/*** @brief  Starts the TIM Input Capture measurement in interrupt mode.* @param  htim : TIM Input Capture handle* @param  Channel : TIM Channels to be enabled*          This parameter can be one of the following values:*            @arg TIM_CHANNEL_1: TIM Channel 1 selected*            @arg TIM_CHANNEL_2: TIM Channel 2 selected*            @arg TIM_CHANNEL_3: TIM Channel 3 selected*            @arg TIM_CHANNEL_4: TIM Channel 4 selected* @retval HAL status
*/
HAL_StatusTypeDef HAL_TIM_IC_Start_IT (TIM_HandleTypeDef *htim, uint32_t Channel)
{/* Check the parameters */assert_param(IS_TIM_CCX_INSTANCE(htim->Instance, Channel));switch (Channel){case TIM_CHANNEL_1:{/* Enable the TIM Capture/Compare 1 interrupt */__HAL_TIM_ENABLE_IT(htim, TIM_IT_CC1);}break;case TIM_CHANNEL_2:{/* Enable the TIM Capture/Compare 2 interrupt */__HAL_TIM_ENABLE_IT(htim, TIM_IT_CC2);}break;case TIM_CHANNEL_3:{/* Enable the TIM Capture/Compare 3 interrupt */__HAL_TIM_ENABLE_IT(htim, TIM_IT_CC3);}break;case TIM_CHANNEL_4:{/* Enable the TIM Capture/Compare 4 interrupt */__HAL_TIM_ENABLE_IT(htim, TIM_IT_CC4);}break;default:break;}/* Enable the Input Capture channel */TIM_CCxChannelCmd(htim->Instance, Channel, TIM_CCx_ENABLE);/* Enable the Peripheral */__HAL_TIM_ENABLE(htim);/* Return function status */return HAL_OK;
}

23年现在的

/*** @brief  Starts the TIM Input Capture measurement in interrupt mode.* @param  htim TIM Input Capture handle* @param  Channel TIM Channels to be enabled*          This parameter can be one of the following values:*            @arg TIM_CHANNEL_1: TIM Channel 1 selected*            @arg TIM_CHANNEL_2: TIM Channel 2 selected*            @arg TIM_CHANNEL_3: TIM Channel 3 selected*            @arg TIM_CHANNEL_4: TIM Channel 4 selected* @retval HAL status*/
HAL_StatusTypeDef HAL_TIM_IC_Start_IT(TIM_HandleTypeDef *htim, uint32_t Channel)
{uint32_t tmpsmcr;HAL_TIM_ChannelStateTypeDef channel_state = TIM_CHANNEL_STATE_GET(htim, Channel);HAL_TIM_ChannelStateTypeDef complementary_channel_state = TIM_CHANNEL_N_STATE_GET(htim, Channel);/* Check the parameters */assert_param(IS_TIM_CCX_INSTANCE(htim->Instance, Channel));/* Check the TIM channel state */if ((channel_state != HAL_TIM_CHANNEL_STATE_READY)|| (complementary_channel_state != HAL_TIM_CHANNEL_STATE_READY)){return HAL_ERROR;}/* Set the TIM channel state */TIM_CHANNEL_STATE_SET(htim, Channel, HAL_TIM_CHANNEL_STATE_BUSY);TIM_CHANNEL_N_STATE_SET(htim, Channel, HAL_TIM_CHANNEL_STATE_BUSY);switch (Channel){case TIM_CHANNEL_1:{/* Enable the TIM Capture/Compare 1 interrupt */__HAL_TIM_ENABLE_IT(htim, TIM_IT_CC1);break;}case TIM_CHANNEL_2:{/* Enable the TIM Capture/Compare 2 interrupt */__HAL_TIM_ENABLE_IT(htim, TIM_IT_CC2);break;}case TIM_CHANNEL_3:{/* Enable the TIM Capture/Compare 3 interrupt */__HAL_TIM_ENABLE_IT(htim, TIM_IT_CC3);break;}case TIM_CHANNEL_4:{/* Enable the TIM Capture/Compare 4 interrupt */__HAL_TIM_ENABLE_IT(htim, TIM_IT_CC4);break;}default:break;}/* Enable the Input Capture channel */TIM_CCxChannelCmd(htim->Instance, Channel, TIM_CCx_ENABLE);/* Enable the Peripheral, except in trigger mode where enable is automatically done with trigger */if (IS_TIM_SLAVE_INSTANCE(htim->Instance)){tmpsmcr = htim->Instance->SMCR & TIM_SMCR_SMS;if (!IS_TIM_SLAVEMODE_TRIGGER_ENABLED(tmpsmcr)){__HAL_TIM_ENABLE(htim);}}else{__HAL_TIM_ENABLE(htim);}/* Return function status */return HAL_OK;
}

我知道HAL库在向着更加高效更加方便的方向进行发展,我也是HAL库的忠实粉丝,但是我真的真的希望HAL库在更新的时候进行一下自测,因为我已经不是第一次遇上这种问题了,这几年来的嵌入式开发中,HAL库在我这出现了至少三次之前代码能用,之后代码不能用,结果发现是HAL库底层代码的变化导致的。对于以上更改的代码我的解决方法是,将该函数替换为:

    // 启动输入捕获并开启中断__HAL_TIM_ENABLE_IT(sr04->tim,sr04->timITcc1);TIM_CCxChannelCmd(sr04->tim->Instance,sr04->channel, TIM_CCx_ENABLE);__HAL_TIM_ENABLE(sr04->tim);

还是希望STM32官方能对HAL库进行完善,总这样真的要逼得小编将来自己用寄存器写库了啊啊啊啊!

相关内容

热门资讯

千岛湖景点导游词 千岛湖景点导游词  作为一位杰出的导游,就有可能用到导游词,一篇完整的导游词,其结构一般包括习惯用语...
南京中山陵导游词 南京中山陵导游词(精选5篇)  导读:南京中山陵美不胜收,远远望去,一大面山,郁郁葱葱。下面是小编整...
烟台的海导游词 烟台的海导游词15篇  作为一名专门为游客提供帮助的导游,很有必要精心设计一份导游词,导游词具有极强...
天津市古文化街导游词 天津市古文化街导游词  来自××的朋友大家好!  首先我代表青年旅行社欢迎各...
杭州西湖中英文导游词 杭州西湖中英文导游词  杭州西湖的导游词怎么写?下面小编为大家推荐2篇中英文的范文,希望对大家有帮助...
西安秦始皇兵马俑博物馆导游词 西安秦始皇兵马俑博物馆导游词  一篇完整的导游词,其结构一般包括习惯用语、概括介绍、重点讲解三个部分...
杭州山沟沟概况导游词 杭州山沟沟概况导游词  作为一名默默奉献的导游,总归要编写导游词,导游词作为一种解说的文体,它的作用...
我是大连小导游作文共60篇 我是大连小导游作文 第一篇亲爱的朋友们,旅途辛苦了,欢迎来到我的家乡—福州永泰,我是今天的小导游,大...
呼和浩特大昭寺导游词 呼和浩特大昭寺导游词  大昭寺成为藏式宗教建筑的千古典范,是旅游的胜地,吸引了很多游客来参观,导游要...
逍遥津导游词 逍遥津导游词位于合肥市旧城的东北角,是一座约20万平方米的城市公园。逍遥津古为淝水上的一个津渡。公园...
导游词开场白 导游词开场白导游词开场白有一句广告词说:心随我动,沟通无限,那我与在座各位朋友的沟通就从我的自我介绍...
苏州盘门三景导游词 苏州盘门三景导游词范例  苏州历史文化名城遐迩闻名在于她的历史悠久、人文荟萃、风景优美。盘门景区位于...
游长城导游词 游长城导游词  长城是世界七大奇迹之一。它像一条巨龙盘踞在中国北方的辽阔的土地上。它是中国古代劳动人...
颐和园导游词400字 颐和园导游词400字  一、颐和园简介  颐和园,中国清朝时期皇家园林,前身为清漪园,坐落在北京西郊...
辽宁省五女山导游词 辽宁省五女山导游词  作为一名具备丰富知识的导游,时常需要用到导游词,导游词的主要特点是口语化,此外...
介绍平遥古城导游词 介绍平遥古城导游词(通用15篇)  作为一位杰出的导游,往往需要进行导游词编写工作,导游词是导游员进...
太乙洞导游词 太乙洞导游词1、[洞口太乙神像]亲爱的游客,大家好!热烈欢迎观光!我们太乙洞有360万年历史,主洞全...
沈阳故宫导游词 沈阳故宫导游词  作为一名专门为游客提供帮助的导游,就有可能用到导游词,导游词具有注重口语化、精简凝...
广化寺导游词 广化寺导游词  广化寺,又称莆田南山广化寺,大家不妨来看看小编推送的广化寺导游词,希望给大家带来帮助...
学校的导游词 学校的导游词  作为一名专门引导游客、助人为乐的导游,就不得不需要编写导游词,导游词事实上是一种对旅...