下载工程文件:
https://gitee.com/Joseph_Cooper/stm32-cube-mx-stm32-l151-c8-t6
MCU:STM32L151C8T6-A
IDE:MDK-Keil5
固件库:STM32Cube FW_L1 V1.10.1
有关STM32实现定时器输入捕获原理戳这里。
硬件设计采用CS102驱动的收发一体超声波探头作为传感器,产品型号为US-116
,可实现 25.5cm-350cm
的非接触测距功能,通过计算可知输出脉冲宽度大约为1.5~206ms
。
▲ 超声波板原理图
以下文字及图片来源于CS102数据手册。
工作原理:在 TRIG
管脚输入一个 10US
以上的高电平(一般建议 50US~100US
),芯片(TP,TN管脚
)便可发出 8 个 40KHZ
的超声波脉冲,然后(RP,RN
)检测回波信号。当检测到回波信号后,通过 ECHO
管脚输出,如下图所示:
▲ 测距时序图
根据 ECHO 管脚输出高电平的持续时间可以计算距离值。即距离值为:(高电平时间*340m/s)/2
。
当测量距离超过测量范围时,CS102
仍会通过 ECHO
管脚输出高电平的信号,高电平的宽度约为 33ms
。如下图所示:
▲ 超出测量范围时序图
测量周期:当芯片通过ECHO
管脚输出的高电平脉冲后,便可进行下一次测量,所以测量周期取决于测量距离,当测距很近时,ECHO
返回的脉冲宽度较窄,测量周期就很短;当测距较远时,ECHO
返回的脉冲宽度较宽,测量周期也就相应的变长。
最坏情况下,被测物体超出测量范围,此时返回的脉冲宽度最长,约为 33ms
,所以最坏情况下的测量周期稍大于 33ms
即可(取 40ms
足够)。
▲ US116主要技术参数
上图指出了本次实验使用的超声波模块的技术参数,值得注意的是探测距离为25.5-350cm
,也就是说在该范围之外的超声波数据可以认为错误数据而舍去。
将PA6
即TIM3_CH1
用于输入捕获。PA7
用以发送超声波触发信号。
▲ STM32L151官方文档截图
▲ CubeMX 输入捕获TIM配置图
为什么取32-1分频?
根据数据手册显示的数据来计算,高电平最高持续时间按40ms来计算则65536/(40/1000)=1,638,400Hz
也就是说定时器的时钟频率要小于这个频率,留下一部分冗余取整可取1MHz
,最小计时精度为1us
,若按照标准声速340m/s计算测量误差在±0.34mm
在接受范围内。
▲ CubeMX 打开TIM3全局中断
▲ CubeMX 设置输入捕获GPIO下拉
有一点疑问的是在CubeMX
的默认设置中GPIO
会被设置成复用推挽输出而手动将其更改为输入模式后将会导致捕获功能不可用。
▲ CubeMX 配置超声波触发信号发送引脚
实现us级延时
//实现简单的us级延时
void RCCdelay_us(uint32_t udelay)
{
__IO uint32_t Delay = udelay * 32 / 8;//(SystemCoreClock / 8U / 1000000U)
do
{
__NOP();
}
while (Delay --);
}
main.c
...
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_USART1_UART_Init();
MX_TIM3_Init();
/* USER CODE BEGIN 2 */
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
if(CapValReady == 1)
{
//25.5cm-350cm
USData = CapVal * 34000/1000000/2;//声速34000cm/s TIM3Freq:1000000Hz
if(USData <= 350 && USData >= 25.5)
{
printf("US:%.2fcm\r\n",USData);
}
__HAL_TIM_DISABLE(&htim3);
HAL_Delay(500);
__HAL_TIM_SET_COUNTER(&htim3,0);//设置定时器3计数值为0
__HAL_TIM_ENABLE(&htim3);
CapValReady = 0;
USTrig();
HAL_TIM_IC_Start_IT(&htim3,TIM_CHANNEL_1);//定时器3通道1输入捕获使能
}
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
...
tim.c
/* USER CODE BEGIN 0 */
uint8_t CapIndex = 0;
uint8_t CapValReady = 1;
float USData = 0;
uint16_t CapVal = 0;
/* USER CODE END 0 */
...
/* USER CODE BEGIN 1 */
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
if(htim->Instance == TIM3)
{
if(CapIndex == 0)
{
CapIndex = 1;
__HAL_TIM_SET_COUNTER(htim,0);//设置定时器3计数值为0
//清除原来的捕获极性
TIM_RESET_CAPTUREPOLARITY(htim,TIM_CHANNEL_1);
//定时器2通道1设置为下降沿捕获
TIM_SET_CAPTUREPOLARITY(htim,TIM_CHANNEL_1,TIM_ICPOLARITY_FALLING);
}
else
{
HAL_TIM_IC_Stop_IT(htim,TIM_CHANNEL_1);//关闭TIM2_CH1输入捕获
CapVal = __HAL_TIM_GET_COMPARE(htim,TIM_CHANNEL_1);
CapValReady = 1;
//清除原来的捕获极性
TIM_RESET_CAPTUREPOLARITY(htim,TIM_CHANNEL_1);
//定时器2通道1设置为上升沿捕获
TIM_SET_CAPTUREPOLARITY(htim,TIM_CHANNEL_1,TIM_ICPOLARITY_RISING);
CapIndex = 0;
}
}
}
/* USER CODE END 1 */
gpio.c
...
/* USER CODE BEGIN 2 */
//实现简单的us级延时
void RCCdelay_us(uint32_t udelay)
{
__IO uint32_t Delay = udelay * 32 / 8;//(SystemCoreClock / 8U / 1000000U)
do
{
__NOP();
}
while (Delay --);
}
void USTrig()
{
HAL_GPIO_WritePin(Trig_GPIO_Port,Trig_Pin,GPIO_PIN_SET);
RCCdelay_us(50);
HAL_GPIO_WritePin(Trig_GPIO_Port,Trig_Pin,GPIO_PIN_RESET);
}
/* USER CODE END 2 */
函数及宏定义介绍:
__HAL_TIM_SET_COUNTER
说明: 设置运行时的TIM计数器寄存器值。
参数:
__HANDLE__
:TIM handle.
__COUNTER__
: 指定计数器寄存器新值。
__HAL_TIM_GET_COMPARE
说明: 获取运行时的TIM捕获比较寄存器值。
参数:
__HANDLE__
:TIM handle.
__CHANNEL__
: 与捕获比较寄存器相关联的TIM通道此参数可以是以下值之一:
TIM_CHANNEL_1
:获取捕获/比较1寄存器值
TIM_CHANNEL_2
:获取捕获/比较2寄存器值
TIM_CHANNEL_3
:获取捕获/比较3寄存器值
TIM_CHANNEL_4
:获取捕获/比较4寄存器值
HAL_StatusTypeDef HAL_TIM_IC_Start_IT (TIM_HandleTypeDef * htim, uint32_t Channel)
说明: 在中断模式下启动TIM输入捕获测量。
参数:
htim
: 指向包含TIM模块配置信息的TIM_HandleTypeDef结构的指针。
Channel
: 将启用TIM通道。 此参数可以是以下值之一:
TIM_CHANNEL_1
:选择TIM通道1
TIM_CHANNEL_2
:选择TIM通道2
TIM_CHANNEL_3
:选择TIM通道3
TIM_CHANNEL_4
:选择TIM通道4
返回值:HAL status
HAL_StatusTypeDef HAL_TIM_IC_Stop_IT (TIM_HandleTypeDef * htim, uint32_t Channel)
说明: 在中断模式下停止TIM输入捕获测量。
参数:
htim
: 指向包含TIM模块配置信息的TIM_HandleTypeDef结构的指针。
Channel
: 将启用TIM通道。 此参数可以是以下值之一:
TIM_CHANNEL_1
:选择TIM通道1
TIM_CHANNEL_2
:选择TIM通道2
TIM_CHANNEL_3
:选择TIM通道3
TIM_CHANNEL_4
:选择TIM通道4
返回值:HAL status
▲ 实现效果图