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

相关内容

热门资讯

常用商务英语口语   商务英语是以适应职场生活的语言要求为目的,内容涉及到商务活动的方方面面。下面是小编收集的常用商务...
六年级上册英语第一单元练习题   一、根据要求写单词。  1.dry(反义词)__________________  2.writ...
复活节英文怎么说 复活节英文怎么说?复活节的英语翻译是什么?复活节:Easter;"Easter,anniversar...
2008年北京奥运会主题曲 2008年北京奥运会(第29届夏季奥林匹克运动会),2008年8月8日到2008年8月24日在中华人...
英语道歉信 英语道歉信15篇  在日常生活中,道歉信的使用频率越来越高,通过道歉信,我们可以更好地解释事情发生的...
六年级英语专题训练(连词成句... 六年级英语专题训练(连词成句30题)  1. have,playhouse,many,I,toy,i...
上班迟到情况说明英语   每个人都或多或少的迟到过那么几次,因为各种原因,可能生病,可能因为交通堵车,可能是因为天气冷,有...
小学英语教学论文 小学英语教学论文范文  引导语:英语教育一直都是每个家长所器重的,那么有关小学英语教学论文要怎么写呢...
英语口语学习必看的方法技巧 英语口语学习必看的方法技巧如何才能说流利的英语? 说外语时,我们主要应做到四件事:理解、回答、提问、...
四级英语作文选:Birth ... 四级英语作文范文选:Birth controlSince the Chinese Governmen...
金融专业英语面试自我介绍 金融专业英语面试自我介绍3篇  金融专业的学生面试时,面试官要求用英语做自我介绍该怎么说。下面是小编...
我的李老师走了四年级英语日记... 我的李老师走了四年级英语日记带翻译  我上了五个学期的小学却换了六任老师,李老师是带我们班最长的语文...
小学三年级英语日记带翻译捡玉... 小学三年级英语日记带翻译捡玉米  今天,我和妈妈去外婆家,外婆家有刚剥的`玉米棒上带有玉米籽,好大的...
七年级英语优秀教学设计 七年级英语优秀教学设计  作为一位兢兢业业的人民教师,常常要写一份优秀的教学设计,教学设计是把教学原...
我的英语老师作文 我的英语老师作文(通用21篇)  在日常生活或是工作学习中,大家都有写作文的经历,对作文很是熟悉吧,...
英语老师教学经验总结 英语老师教学经验总结(通用19篇)  总结是指社会团体、企业单位和个人对某一阶段的学习、工作或其完成...
初一英语暑假作业答案 初一英语暑假作业答案  英语练习一(基础训练)第一题1.D2.H3.E4.F5.I6.A7.J8.C...
大学生的英语演讲稿 大学生的英语演讲稿范文(精选10篇)  使用正确的写作思路书写演讲稿会更加事半功倍。在现实社会中,越...
VOA美国之音英语学习网址 VOA美国之音英语学习推荐网址 美国之音网站已经成为语言学习最重要的资源站点,在互联网上还有若干网站...
商务英语期末试卷 Part I Term Translation (20%)Section A: Translate ...