软件:Keil5 STM32CubeMX
硬件:STM32F103C8T6 HC-SR04
优点:实现起来很方便,有51单片机开发经验看起来很容易理解
缺点:个人感觉实时性一般,而且一旦超声波传感器Echo引脚出现问题,会导致程序卡死
优点:实现起来比前者还要方便,并且理论上更容易扩展,看起来也非常简洁
缺点:误差很大,精确度很低,实时性差
优点: 实时性好,可以放在其它定时器中断中进行,比如放到系统滴答定时器10Hz的中断中执行,个人认为是对STM32定时器强大功能在计算高电平时间上最好的开发
缺点:代码实现难度大,需要对STM32定时器寄存器功能的深层理解(在这里发自内心感谢硬石科技提供的例程)
按照最新的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);
其实以上代码的核心部分是我在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库进行完善,总这样真的要逼得小编将来自己用寄存器写库了啊啊啊啊!
上一篇:前端CSS学习之路-css002
下一篇:SAP入门技术分享六:搜索帮助