【STM32】标准库与HAL库对照学习教程十--输入捕获实验
一、前言
本篇文章是对定时器功能中的输入捕获功能的讲解与配置,本篇从输入捕获原理讲解开始,一步一步让您学会输入捕获的知识与配置,您可以点击目录跳转您想要看的内容。 |
二、准备工作
- STM32F103开发板(我用的是普中的STM32F103ZE开发板)
- cubemx软件、keil 5(MDK)
- 开发板原理图
有关于定时器不了解的可以看这篇文章:【STM32】标准库与HAL库对照学习教程七–定时器中断
三、输入捕获介绍
1、简介
在定时器章节我们了解到通用定时器具有多种功能,输入捕获就是其中一种。 STM32F1除了基本定时器TIM6和TIM7,其他定时器都具有输入捕获功能。 输入捕获可以对输入的信号的上升沿,下降沿或者双边沿进行捕获,通常用于测量输入信号的脉宽、测量 PWM 输入信号的频率及占空比。 与输出比较一样,每个定时器的输入捕获有4个通道,分别映射到四个引脚上。
2、原理
在输入捕获模式下,捕获中断与定时器中断是打开的。
- ①输入捕获初始化。
- ②检测到边沿电平变化,触发捕获中断,在中断函数里,清空计算器TIMx_CNT的值。
- ③计算期间,可能会达到重装载值ARR触发定时器中断,在中断函数里,记录定时器计算溢出的次数为N。
- ④再次检测到边沿电平变化,触发中断,在中断函数里,得到计算器TIMx_CNT的值,记录到一个变量里(CCR)。
- ⑤高电平持续时间=(CCR+N*ARR)*计算周期 。.,ARR为定时器重装载值。
在拿一个对比图形
四、输入捕获结构
1、通道映射表
图片来源于STM32F1xx中文参考手册 116页
2、结构图
① 可以看到,捕获与比较的通道是相同的,所以在捕获与比较只能用一种,并且在图中可以看出通道1与通道2的捕获是可以互通的。 ②
五、输入捕获寄存器位
由上面的图2,可以知道,控制输入捕获的比较重要的几个位,分别是来自TIMx_CCMR1寄存器的ICF[3:0]、CC1S[1:0]、ICPS[1:0]位与TIMx_CCER寄存器的CC1P、CC1E位。
(1)ICF[3:0]
数字滤波器由一个事件计数器组成,假设我们是检测高电平,滤波N次,当连续N次采样检测,如果都是高电平,则说明这是一个有效的电平信号,这样便可以过滤掉那些因为某些而干扰产生的一些信号 。 输入捕获滤波器IC1F[3:0],这个用于设置采样频率和数字滤波器长度
(2)CC1S[1:0]
因为通道1与通道2是可以互通的,所以要使用这个位选择映射位置。
(3)ICPS[1:0]
捕获预分频器,表示遇到N个边沿时捕获一次。
(4)CC1P
配置通道是输入捕获还是输出比较。
(5)CC1E
捕获使能位。
图片来源于STM32F1xx中文参考手册 282页
六、硬件电路
本篇使用输入捕获检测按键引脚高电平的持续时间。 按键电路请参考自己开发板的原理图。
七、标准库配置输入捕获
1、配置步骤
- (1)使能定时器及端口时钟,并设置引脚复用器映射和引脚模式等
- (2)初始化定时器参数,包含自动重装值,分频系数,计数方式等
- (3)设置通用定时器的输入捕获参数,开启输入捕获功能
- (4)开启捕获和定时器溢出(更新)中断
- (5)设置定时器中断优先级,使能定时器中断通道
- (6)编写定时器中断服务函数
- (7)使能定时器
2、配置工程
标准库
(1)复制上一章的工程,并重命名为10、输入捕获实验。
(2)进入工程文件,进入APP文件,新建Input用来存放与输入捕获相关的文件。
(3)打开工程,新建文件,并命名为input.h与input.c。 ①
②
(4)添加文件到目录,并添加头文件路径。 ① ②
3、实验程序
input.h
#ifndef INPUT_H_
#define INPUT_H_
#include "stm32f10x.h"
extern volatile u16 TIM_Exceed;
extern volatile u8 TIM_IC_Edge;
extern volatile u16 TIM_IC_VAL;
void TIM5_CH1_Input_Init(u16 Psc,u16 Per);
#endif
input.c
#include "input.h"
volatile u16 TIM_Exceed = 0;
volatile u8 TIM_IC_Edge = 0;
volatile u16 TIM_IC_VAL = 0;
void TIM5_CH1_Input_Init(u16 Psc,u16 Per)
{
GPIO_InitTypeDef GPIO_InitStruct;
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
TIM_ICInitTypeDef TIM_ICInitStruct;
NVIC_InitTypeDef NVIC_InitStruct;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPD;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStruct);
TIM_TimeBaseInitStruct.TIM_Prescaler = Psc;
TIM_TimeBaseInitStruct.TIM_Period = Per;
TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInit(TIM5, &TIM_TimeBaseInitStruct);
TIM_ICInitStruct.TIM_Channel = TIM_Channel_1;
TIM_ICInitStruct.TIM_ICPrescaler = TIM_ICPSC_DIV1;
TIM_ICInitStruct.TIM_ICPolarity = TIM_ICPolarity_Rising;
TIM_ICInitStruct.TIM_ICFilter = 0;
TIM_ICInitStruct.TIM_ICSelection = TIM_ICSelection_DirectTI;
TIM_ICInit(TIM5, &TIM_ICInitStruct);
TIM_ITConfig(TIM5, TIM_IT_Update|TIM_IT_CC1, ENABLE);
NVIC_InitStruct.NVIC_IRQChannel = TIM5_IRQn;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 3;
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStruct);
TIM_Cmd(TIM5, ENABLE);
}
void TIM5_IRQHandler()
{
if(TIM_GetITStatus(TIM5, TIM_IT_Update))
{
if(TIM_IC_Edge == 1)
{
TIM_Exceed++;
}
}
if(TIM_GetITStatus(TIM5, TIM_IT_CC1))
{
if(TIM_IC_Edge == 0)
{
TIM_Cmd(TIM5, DISABLE);
TIM_OC1PolarityConfig(TIM5, TIM_OCPolarity_Low);
TIM_Exceed = 0;
TIM_SetCounter(TIM5, 0);
TIM_IC_Edge++;
TIM_Cmd(TIM5, ENABLE);
}
else
{
TIM_Cmd(TIM5, DISABLE);
TIM_IC_VAL = TIM_GetCapture1(TIM5);
TIM_OC1PolarityConfig(TIM5, TIM_OCPolarity_High);
TIM_IC_Edge++;
}
}
TIM_ClearITPendingBit(TIM5, TIM_IT_Update|TIM_IT_CC1);
}
main.c
#include "LED.h"
#include "Delay.h"
#include "System.h"
#include "input.h"
#include "usart.h"
#include "stdio.h"
int main()
{
u32 time;
SysTick_Init(72);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
LED_Init();
USART1_Init(9600);
TIM5_CH1_Input_Init(71,0xffff);
while(1)
{
if(TIM_IC_Edge == 2)
{
time = TIM_IC_VAL+TIM_Exceed*0xffff;
printf("高电平持续时间是 %d ms\r\n",time/1000);
TIM_IC_Edge = 0;
TIM_Cmd(TIM5, ENABLE);
}
}
}
4、实验效果
八、HAL库配置输入捕获
1、使用cubemx生成工程
(1) 打开cubemx,新建工程,选择自己的芯片。
(2) 配置RCC,选择外部高速时钟。
(3) 配置时钟树。
(4) 配置捕获通道。 ① ②
③打开中断 ④设置引脚为下拉
(5) 配置串口。
(6) 工程文件配置并生成工程 ①
②
2、相关函数
- HAL_TIM_ReadCapturedValue() ; 获取捕获通道计数器的函数
- HAL_TIM_IC_Start();开启输入捕获通道
- HAL_TIM_IC_Stop();关闭输入捕获通道
- HAL_TIM_IC_Start_IT();开启输入捕获通道与中断
- HAL_TIM_IC_Stop_IT();关闭输入捕获通道与中断
- __HAL_TIM_SET_COUNTER(HANDLE, COUNTER)设置计数器CNT的值,(HANDLE:时钟,COUNTER:设置的值)
- TIM_RESET_CAPTUREPOLARITY(HANDLE, CHANNEL);清除通道极性
- TIM_SET_CAPTUREPOLARITY(HANDLE, CHANNEL, POLARITY);设置通道极性
- __HAL_TIM_SET_CAPTUREPOLARITY(HANDLE, CHANNEL, POLARITY);清除通道极性,并设置通道极性
- HAL_TIM_PeriodElapsedCallback() ;定时器中断回调函数
- HAL_TIM_IC_CaptureCallback();捕获中断回调函数
Channel:
- TIM_CHANNEL_1
- TIM_CHANNEL_2
- TIM_CHANNEL_3
- TIM_CHANNEL_4
- TIM_CHANNEL_ALL
极性: - TIM_ICPOLARITY_RISING-上升沿
- TIM_ICPOLARITY_FALLING-下降沿
- TIM_ICPOLARITY_BOTHEDGE-上升下降沿
3、实验程序
①
#include<stdio.h>
②
int fputc(int ch, FILE *f)
{
HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xffff);
return ch;
}
uint8_t TIM5_CH1_Edge=0;
uint32_t TIM5_CH1_VAL=0;
uint32_t TIM5_CH1_OVER=0;
uint32_t time;
③
HAL_TIM_Base_Start_IT(&htim5);
HAL_TIM_IC_Start_IT(&htim5,TIM_CHANNEL_1);
if(TIM5_CH1_Edge == 2)
{
TIM5_CH1_Edge = 0;
time = TIM5_CH1_VAL + TIM5_CH1_OVER*0xffff;
printf("高电平持续时间为 %d ms\r\n", time/1000);
HAL_TIM_IC_Start_IT(&htim5,TIM_CHANNEL_1);
}
④ 回调函数
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if(htim == &htim5)
{
if(TIM5_CH1_Edge == 1)
{
TIM5_CH1_OVER++;
}
}
}
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
if(htim == &htim5)
{
if(TIM5_CH1_Edge == 0)
{
TIM5_CH1_Edge++;
TIM5_CH1_OVER = 0;
__HAL_TIM_SET_CAPTUREPOLARITY(&htim5,TIM_CHANNEL_1,TIM_ICPOLARITY_FALLING);
__HAL_TIM_SET_COUNTER(&htim5,0);
}
else
{
HAL_TIM_IC_Stop_IT(&htim5,TIM_CHANNEL_1);
TIM5_CH1_Edge++;
TIM5_CH1_VAL = HAL_TIM_ReadCapturedValue(&htim5,TIM_CHANNEL_1);
__HAL_TIM_SET_CAPTUREPOLARITY(&htim5,TIM_CHANNEL_1,TIM_ICPOLARITY_RISING);
}
}
}
4、实验效果
到这里就结束啦!
|