STM32LL库编程系列第四讲——定时器输入捕获+超声波测距

第四讲——定时器输入捕获+超声波测距

往期文章
STM32LL库编程系列第一讲——Delay精准延时函数(详细,适合新手)
STM32LL库编程系列第二讲——蓝牙+USART串口通信(步骤详细、原理清晰)
STM32LL库编程系列第三讲——USART+DMA通信



前言

本文使用的控制板为本人自行设计,核心芯片为STM32F407VET6,有很大的通用性。
编程逻辑:利用定时器9的输入捕获功能捕获超声波回传信号的双边沿,从而计算高电平持续时间,结合声速得到测距,在利用DMA将测距信息发送到USRT3的DR寄存器,从而让上位机串口助手接收到。(对DMA、USART外设不了解可参考往期文章)
本文会对每一步操作详细说明,代码逻辑清晰,希望能对大家有所帮助!!


一、定时器输入捕获逻辑

在这里插入图片描述
如图所示,这是定时器的一个输入捕获通道,
TI1为输入信号,

f(DTS)是由TIMx_CR1寄存器的CKD设置的,指示定时器时钟 (CK_INT) 频率与数字滤波器所使用的采样时钟 (TIx) 之间的分频比,简单理解就是 f(DTS) = f(CK_INT) /n,n就是分频比。

ICF[3:0]设置定义 TI1 输入的采样频率和适用于 TI1 的数字滤波器带宽,简单理解就是ICF[3:0]=0~15,表示每采样到ICF[3:0]个TI1的电平信号,才会输出一个电平信号。比如ICF[3:0] = 4,则当4次采样到TI1为高电平或低电平,TI1F才为高电平或低电平。若4此采样中TI1有高电平也有低电平,则TI1F保持上一轮采样值,这样可以滤除高频干扰,故称为滤波器。

TI1F经过边沿检测器判断是上升沿还是下降沿,经过双通道输出符合要求的电平信号TI1FP1,再经过三通道输出IC1,最后经过一个分频器输出IC1PS。假设分频器为4,则表示4个IC1脉冲信号才输出一个IC1PS信号。
这里对定时器输入捕获模式进行简单讲解,下面可以具体操作了

二、使用CubeMX建立工程

这里只说明关于定时器部分的配置介绍,其他外设配置说明见往期文章
定位到TIM9的配置界面,打开内部时钟,将通道2配置为输入捕获模式,如图
在这里插入图片描述
参数设置如下
在这里插入图片描述
TIM9时钟频率为168MHz,这里进行168分频,从而 f(CK_INT) =1MHz。
向上计数
重装载值设置为20000,则最大计时时间为20000/1000000=20ms,声波传输距离为0.02*340=6.8m,从而最大测距距离为3.4m,足够了,我这是使用的超声波US100是非常普通常见的一款超神波模块,和蓝桥杯单片机的超生波模块几乎一样,因为功率低,一般距离大于3M,声波就衰减的无法检测了。
CKD就是上面介绍的定时器时钟 (CK_INT) 频率与数字滤波器所使用的采样时钟 (TIx) 之间的分频比,这里不分频。因为预分频和预装载值已经设置好需要的频率了。
使能重装载
设置边沿检测为双边沿检测
Prescaler Division Ratio为分频器分频系数,不分频。来一个我就要一个
Input Filter(4bits value)就是滤波器的带宽系数,具体解释可以看第六讲,当频率不高时可以适当滤波,高频率滤波可能会滤去脉冲信号,这里不滤波也可以使用,就先不滤波了。
使能全局中断,如图
在这里插入图片描述
设置超声波模块的信号触发引脚
在这里插入图片描述
编辑定时器引脚电平
在这里插入图片描述
设置串口通信引脚,如图
在这里插入图片描述
这里进行一点重要说明
对于输入引脚,如果没有外部上下拉,就一定要内部上下拉,避免空闲时误触发,具体上拉还是下拉根据需要的空闲状态而定。
而输出引脚可以不上下拉,当然,如果有空闲状态,最好拉置空闲状态。
配置LL库输出,至此CubeMX配置结束(这里跳了很多中间步骤,如果结合往期文章还是无法理解,结尾会给出工程下载地址,可以下下载查看)
在这里插入图片描述

三、keil工程代码编写

1.电平触发测距工作原理

在这里插入图片描述
图 5.1 表明:只需要在 Trig/TX 管脚输入一个 10US 以上的高电平,系统便可发出 8 个 40KHZ 的超声波脉冲,然后检测回
波信号。当检测到回波信号后,模块还要进行温度值的测量,然后根据当前温度对测距结果进行校正,将校正后的结果通过
Echo/RX 管脚输出。
在此模式下,模块将距离值转化为 340m/s 时的时间值的 2倍,通过 Echo 端输出一高电平,可根据此高电平的持续时间来
计算距离值。即距离值为:(高电平时间*340m/s)/2。
注:因为距离值已经经过温度校正,此时无需再根据环境温度对超声波声速进行校正,即不管温度多少,声速选择340m/s 即可。

2.US100.c

这里放入超声波Trig/TX 管脚输入一个 10US 以上的高电平函数

#include "US100.h"
#include "delay.h"

void US100_Trig(GPIO_TypeDef *GPIOx, uint32_t PinMask)
{
	LL_GPIO_SetOutputPin(GPIOx,PinMask);
	delay_us(15);
	LL_GPIO_ResetOutputPin(GPIOx,PinMask);
}

这里稍微延时比10US多一点,避免漏检测。US100.h如下

#ifndef __US100_H
#define __US100_H

#include "main.h"
void US100_Trig(GPIO_TypeDef *GPIOx, uint32_t PinMask);

#endif

3.tim.c

再生成的初始化函数下面加入使能函数

  /* USER CODE BEGIN TIM9_Init 2 */
	LL_TIM_ClearFlag_CC2(TIM9);//清除通道2捕获标志位
	LL_TIM_EnableIT_CC2(TIM9);//使能通道2捕获中断
	LL_TIM_EnableIT_UPDATE(TIM9);//使能更新中断
	LL_TIM_CC_EnableChannel(TIM9,LL_TIM_CHANNEL_CH2);//使能TIM9_CH2捕获功能
  /* USER CODE END TIM9_Init 2 */

我们需要通道2的输入捕获功能,就得使能该功能和中断,使能更新中断为了到最大计数值依旧没有检测回传声波处理。

4.dma.c

编写USART3的DMA发送函数

/* USER CODE BEGIN 2 */
void usart3_DMA_init(void)
{
	//DMA接收配置
	LL_DMA_SetPeriphAddress(DMA1, LL_DMA_STREAM_3, (uint32_t)&(USART3->DR));//设置外设地址
	LL_DMA_SetMemoryAddress(DMA1, LL_DMA_STREAM_3, (uint32_t)US100B_DIST_cm);//设置内存地址
	LL_DMA_SetDataLength(DMA1, LL_DMA_STREAM_3, 6);//设置接受的数据长度

		//清除中断标志位
	LL_USART_ClearFlag_TC(USART3);
	LL_DMA_ClearFlag_TC3(DMA1);
	LL_DMA_EnableIT_TC(DMA1,LL_DMA_STREAM_3);
	LL_USART_EnableDMAReq_TX(USART3);//启用串口DMA发送模式
	
}
/* USER CODE END 2 */

只要开启了使能就会把US100B_DIST_cm传入USART3->DR中

5.stm32f4xx_it.c

定义参数

/* USER CODE BEGIN TD */
unsigned char US100B_DIST_cm[6]={0,0,0,'c','m',(u8)10};
u16 US100B_DIST;
/* USER CODE END TD */

编写TIM9中断服务函数

/* USER CODE BEGIN TIM1_BRK_TIM9_IRQn 0 */
	if(LL_TIM_IsActiveFlag_CC2(TIM9))//检测到捕获事件
	{
		LL_TIM_ClearFlag_CC2(TIM9);
		if(LL_GPIO_IsInputPinSet(US100_OUTB_GPIO_Port,US100_OUTB_Pin)==SET)//检测为高电平,则上升沿捕获
		{
			LL_TIM_EnableCounter(TIM9);//开启定时器计数
		}
		//若一致都是高电平,在下一轮开启采集前会自动产生下降沿
		if(LL_GPIO_IsInputPinSet(US100_OUTB_GPIO_Port,US100_OUTB_Pin)==RESET)//检测为低电平,则下升沿捕获
		{
			LL_TIM_DisableCounter(TIM9);//关闭定时器计时
			US100B_DIST = LL_TIM_GetCounter(TIM9)*340/2/10000;
			US100B_DIST_cm[0] = (US100B_DIST/100)+48;
			US100B_DIST_cm[1] = (US100B_DIST/10%10)+48;
			US100B_DIST_cm[2] = (US100B_DIST%10)+48;
			LL_TIM_SetCounter(TIM9,0);//将计数器值清0
			LL_DMA_EnableStream(DMA1, LL_DMA_STREAM_3);//使能DMA数据流
		}
		
	}
	if(LL_TIM_IsActiveFlag_UPDATE(TIM9))
	{
		LL_TIM_ClearFlag_UPDATE(TIM9);
		LL_TIM_DisableCounter(TIM9);//关闭定时器计时
	}
  /* USER CODE END TIM1_BRK_TIM9_IRQn 0 */

当触发边沿中断后先清除中断标志位,再从电平位确定是上升沿中断还是下降沿中断,再上升沿中断里开启计数器计时,到下降沿中断
里关闭计数器计时,从而得到回传信号高电平持续时间,LL_TIM_GetCounter(TIM9)用来检测计数器值,经过换算得到测距距离US100B_DIST 单位是cm,将US100B_DIST 每一位分别存入数组,+48是位了转换为ASCII码。然后将将计数器值清0,为下次采集做准备。最后使能DMA数据流传输,将US100B_DIST_cm数组值自动传输到USART3->DR中。
在非循环模式下配置数据流,传输结束后(即要传输的数据数目达到零),除非软件重新对数据流编程并重新使能数据流(通过将 DMA_SxCR 寄存器中的 EN 位置 1),否则 DMA 即会停止传输(通过硬件将 DMA_SxCR 寄存器中的 EN 位清零)并且不再响应任何
DMA 请求。故每次都发送前都需要使能LL_DMA_EnableStream(DMA1, LL_DMA_STREAM_3);//使能DMA数据流这也充当发送开始标志

如果到了20ms还没有检测到下降沿,表示距离过远,没收到回传声波,从而进入更新中断,在更新中断里关闭定时器计时,此时会计数达到最大值会自动清0,在下一轮开启采集前会自动产生下降沿,从而得到的US100B_DIST =0。

编写DMA中断服务函数

void DMA1_Stream3_IRQHandler(void)
{
  /* USER CODE BEGIN DMA1_Stream3_IRQn 0 */
	if(LL_DMA_IsActiveFlag_TC3(DMA1))
	{
		LL_DMA_ClearFlag_TC3(DMA1);
	}
  /* USER CODE END DMA1_Stream3_IRQn 0 */

  /* USER CODE BEGIN DMA1_Stream3_IRQn 1 */

  /* USER CODE END DMA1_Stream3_IRQn 1 */
}

当DMA数据流传输完成时就会进入TC完成中断服务函数,在中断服务函数清除标志位即可。这里的TC中断什么都没干,那为什么要留着呢?我们打开DMA串口传输数据流可以看出当传输完成时TC会硬件置1,如果不开启中断将标志位置0,那么下次传输就无法进行了。所以DMA串口发送必须使能TC中断
在这里插入图片描述

6.main.c

加入DMA串口发送初始化

  /* USER CODE BEGIN 2 */
	usart3_DMA_init();
  /* USER CODE END 2 */
while (1)
  {
		US100_Trig(US100_INB_GPIO_Port, US100_INB_Pin);
		delay_ms(500);
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }

每500ms测距一次。

四、效果展示

如图
在这里插入图片描述
大家根据自己的控制板逻辑进行连接,从串口助手可以看到不断有数据回传。

五、工程下载

链接:https://pan.baidu.com/s/1ULmQgEzNXqGgtEjXVVVFAg?pwd=1234
提取码:1234


尾言

对于DMA的数据收发LL库编程网上资源特别少,有那么几个也都含糊其辞,本人也是花了大量时间查看手册才弄清逻辑,大家可以结合上文,对DMA进行学习。
时间匆忙,有错误之处还望大家指出,有疑问可以在评论区发表,我看到就会回复(文章发表短期内)。

  • 33
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值