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库进行完善,总这样真的要逼得小编将来自己用寄存器写库了啊啊啊啊!

相关内容

热门资讯

国庆节趣事作文500字 国庆节趣事作文500字三篇  国庆节趣事作文500字一:  今天,是一个特别的日子。大家一定知道吧!...
节约型社会 Saving s... 节约型社会 Saving society  Recently,there has been an a...
国庆节优秀作文 国庆节优秀作文(精选34篇)  在平凡的学习、工作、生活中,许多人都写过作文吧,作文根据写作时限的不...
假如作文300字 假如作文300字五篇  在平平淡淡的日常中,大家都接触过作文吧,作文是从内部言语向外部言语的过渡,即...
买年货的作文 关于买年货的作文八篇  每一年的春节到来之前,家家户户都会准备好新年必备的年货,在这个特别的日子里,...
生活给我的启示作文 生活给我的启示作文  生活中,启示无处不在,也许是满树的春红,也许是寂寞的梧桐。生活总是在完美中留下...
小学生端午节的一天作文400... 小学生端午节的一天作文400字  在端午节这个古老的节日里,人们包粽子、赛龙舟,处处洋溢着浓浓的节日...
植树节的作文300字 植树节的作文300字四篇  在平凡的学习、工作、生活中,大家总少不了接触作文吧,借助作文可以提高我们...
织女牛郎鹊桥相会的历史典故 织女牛郎鹊桥相会的历史典故(通用11篇)  农历七月七日是民间的“乞巧”节,妇女们这晚在庭院中摆下酒...
季节作文300字 季节作文300字(通用99篇)  无论是身处学校还是步入社会,大家一定都接触过作文吧,作文要求篇章结...
寒假作文600字 寒假作文600字十篇  在日常学习、工作和生活中,大家对作文都不陌生吧,作文一定要做到主题集中,围绕...
快乐的春节作文1000字 精选快乐的春节作文1000字5篇  在学习、工作乃至生活中,许多人都有过写作文的经历,对作文都不陌生...
教师节作文 关于教师节作文(精选10篇)  在日常学习、工作抑或是生活中,大家都接触过作文吧,作文是人们把记忆中...
什么的春天作文 什么的春天作文大全  什么的春天作文(一)  春天到了,万物复苏,大地呈现一片欣欣向荣的景象。春天是...
围棋比赛作文 围棋比赛作文(通用35篇)  在平日的学习、工作和生活里,大家都写过作文吧,通过作文可以把我们那些零...
疫情的作文 有关疫情的作文(精选3篇)  疫情的作文  今年春节的时候,一个不受欢迎的不速之客——新型冠状病毒降...
有趣的实验作文500字 有趣的实验作文500字7篇  在平时的学习、工作或生活中,许多人都有过写作文的经历,对作文都不陌生吧...
承担责任作文 承担责任作文(通用21篇)  在学习、工作或生活中,大家都经常看到作文的身影吧,借助作文可以宣泄心中...
家乡的春节作文200字 【精选】家乡的春节作文200字3篇  在日复一日的学习、工作或生活中,大家都经常接触到作文吧,作文是...
写元旦作文 精选写元旦作文9篇  在现实生活或工作学习中,大家对作文都再熟悉不过了吧,借助作文可以宣泄心中的情感...