目录
前言:
目录
今天我们学习利用STM32CubeMX实现定时器中断、PWM输出(呼吸灯)以及定时器输入捕获三种操作。
工具:
- 芯片:STM32F103C8T6
- STM32CubeMx软件
- IDE: MDK-Keil软件
一、定时器中断
定时器简介
SMT32F1系列共有8个定时器:
高级定时器(TIM1、TIM8);通用定时器(TIM2、TIM3、TIM4、TIM5);基本定时器(TIM6、TIM7)。
基本定时器(TIM6、TIM7)功能:
16位向上、向下、向上/下自动装载计数器
16位可编程(可以实时修改)预分频器,计数器时钟率的分频系数为1~65535之间的任意数值
触发DAC的同步电路 注:此项是TIM6/7独有功能.
位于APB1总线上
通用定时器(TIM2~TIM5)的主要功能:
一、16位向上、向下、向上/下自动装载计数器
二、16位可编程(可以实时修改)预分频器,计数器时钟频率的分频系数为1~65535之间的任意数值
三、4 个独立通道(TIMx_CH1~4)可以用作:
测量输入信号的脉冲长度( 输入捕获)
输出比较
单脉冲模式输出
PWM输出(边缘或中间对齐模式)
四、支持针对定位的增量(正交)编码器和霍尔传感器电路
五、如下事件发生时产生中断/DMA:
更新:计数器向上溢出/向下溢出,计数器初 始化(通过软件或者内部/外部触发)
触发事件(计数器启动、停止、初始化或者由内 部/外部触发计数)
输入捕获
输出比较
六、位于APB1总线上
高级定时器(TIM1,TIM8)的主要功能:
高级定时器具有基本,通用定时器的所有的功能,
还具有控制交直流电动机所有的功能,
输出6路互补带死区的信号,刹车功能等等
位于APB2总线上
定时器计数模式:
通用定时器可以向上计数、向下计数、向上向下双向计数模式。
计数时钟选择:
内部时钟(TIMx_CLK)
外部时钟模式1:外部捕捉比较引脚(TIx)
外部时钟模式2:外部引脚输入(TIMx_ETR) 仅适用TIM2,3,4
内部触发输入(ITRx):使用一个定时器作为另一个定时器的预分频器,如可以配置一个定时器Timer1而作为另一个定时器Timer2的预分频器。
CubeMX配置
-
RCC选择外部高速时钟HSE
配置时钟如下图:
-
定时器设置:
定时器溢出时间计算为
这里我们 arr=9999 psc=7199 Tclk=72Mhz Tout=(10000*7200)/72us=1s
-
开启定时器中断
-
选择LED引脚,这里选的PA2
-
配置项目后生成代码
Keil代码
使用的函数:
- 中断处理函数
HAL_TIM_IRQHandler(&htim2);
- 定时器溢出中断回调函数
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
在中断回调函数中添加代码。
在main函数while上方初始化使能定时器2
HAL_TIM_Base_Start_IT(&htim2);
在main函数下重写回调函数
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
static unsigned char ledState = 0;
if (htim == (&htim2))
{
if (ledState == 0)
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_2,GPIO_PIN_RESET);
else
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_2,GPIO_PIN_SET);
ledState = !ledState;
}
}
硬件效果展示
二、PWM输出
PWM介绍
脉冲宽度调制(PWM),是英文“Pulse Width Modulation”的缩写,简称脉宽调制,是利用微处理器的数字输出来对模拟电路进行控制的一种非常有效的技术,广泛应用在从测量、通信到功率控制与变换的许多领域中。 |
STM32的每个通用定时器都有独立的4个通道可以用来作为:输入捕获、输出比较、PWM输出、单脉冲模式输出等。
STM32的定时器除了TIM6和TIM7(基本定时器)之外,其他的定时器都可以产生PWM输出。其中,高级定时器TIM1、TIM8可以同时产生7路PWM输出。
原理
向上计数模式:
- 当CNT<CCRx时,TIMx_CHx通道输出低电平
- 当CNT>CCRx时,TIMx_CHx通道输出高电平
一个PWM周期:
0~t1:CNT<CCRx,输出低电平
t1~t2:CNT>CCRx,输出高电平
t2:当CNT到达ARR时,定时器溢出,CNT归0,一个PWM周期结束
每个定时器有4个通道,每个通道都有一个捕获比较寄存器(即获取CNT的值,与CCRx进行比较),通过比较输出高低电平,以此来实现对脉宽的调制(PWM)
TIMx_ARR寄存器确定PWM频率
TIMx_CCRx寄存器确定占空比
PWM工作模式:
PWM模式1(向上计数): 计数器从0加到ARR(自动重装载值),计数器溢出,然后计数器归为0,继续加循环
PWM模式1(向下计数): 计数器从ARR(自动重装载值) 减到0,计数器溢出。然后计数器归为ARR,继续减循环
PWM输出模式:
- PWM模式1: 在向上计数时,当CNT<CCRx时通道x为有效电平,当CNT>CCRx时通道x为无效电平; 在向下计数时,当CNT>CCRx时通道x为无效电平,当CNT<CCRx时通道x为有效电平
- PWM模式2: 在向上计数时,当CNT<CCRx时通道x为无效电平,当CNT>CCRx时通道x为有效电平; 在向下计数时,当CNT>CCRx时通道x为有效电平,当CNT<CCRx时通道x为无效电平
- 有效电平可以是高电平也可以是低电平,具体得看CCER寄存器的CC1P位的值来确定。
CC1P=0 : 有效电平为高
CC1P=1 : 有效电平为低
CC1P=0,向上模式时,当CNT<CCRx时,输出电平为高
CC1P=1,向上模式时,当CNT<CCRx时,输出电平为低
CubeMX配置
配置基本与上文相同,需要稍作修改:
-
打开通道1,作为PWM输出IO口(此时PA0自动变为TIM2_CH1模式),同时关闭之前的PA2
-
关闭定时器中断
-
Parameter settings
- Internal Clock(内部时钟)
- 通道1选择:PWM Generation CH1(PWM输出通道1)
- Prtscaler (定时器分频系数) : 71
- Counter Mode(计数模式):Up(向上计数模式)
- Counter Period(自动重装载值) : 999
- CKD(时钟分频因子) :No Division (不分频 )
- auto-reload-preload(自动重装载) : Enable (使能)
- Mode :PWM模式1
- Pulse(占空比值) :0
- Fast Mode PWM脉冲快速模式(没啥用) :不使能
- PWM 极性(有效电平): 设置为高电平
PWM频率
ω =Tclk / ((arr+1)*(psc+1)) (单位Hz)
arr计数器值
psc预分频值
Tclk时钟频率
占空比
占空比 = CCRx / arr (单位%)
CCRx是可以预设的值
- 生成项目
Keil代码
在main.c文件添加变量
/* USER CODE BEGIN 1 */
uint16_t pwm=0;
/* USER CODE END 1 */
然后开启PWM的通道1
/* USER CODE BEGIN 2 */
HAL_TIM_PWM_Start(&htim2,TIM_CHANNEL_1);
/* USER CODE END 2 */
接着在while(1)写入代码
while (pwm< 499)
{
pwm++;
__HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_1, pwm); //通过修改比较值来改变占空比
// TIM3->CCR1 = pwmVal; 可通过操作寄存器来控制CCR1的值
}
while (pwm)
{
pwm--;
__HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_1, pwm); //通过修改比较值来改变占空比
// TIM3->CCR1 = pwmVal; 可通过操作寄存器来控制CCR1的值
HAL_Delay(1);
}
HAL_Delay(200);
编译烧录之
硬件效果展示
三、定时器输入捕获
输入捕获模式可以用来测量脉冲宽度或者测量频率。STM32的定时器,除了TIM6、TIM7,其他的定时器都有输入捕获的功能。 |
工作原理:
①先设置输入捕获为上升沿检测,
②记录发生上升沿时TIMx_CNT(计数器)的值
③配置捕获信号为下降沿捕获,当下降沿到来的时候发生捕获
④记录此时的TIMx_CN(计数器)T的值
⑤前后两次TIMx_CNT(计数器)的值之差就是高电平的脉宽。同时根据TIM的计数频率,我们就能知道高电平脉宽的准确时间。
CubeMX配置
只展示与前文不同的部分
使能串口1,用于调试打印
将TIM1配置为PWM输入模式,我是使用的PWM输入通道2,也就是PA9引脚。并使能捕获中断
生成项目。
Keil
程序修改与测试
uint32_t uiDutyCycle;
uint32_t uiCycle;
uint32_t uiFrequency;
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
if(htim->Instance == htim1.Instance)
{
switch(htim->Channel)
{
case HAL_TIM_ACTIVE_CHANNEL_1:
uiDutyCycle = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1); /* 占空比 */
break;
case HAL_TIM_ACTIVE_CHANNEL_2:
uiCycle = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_2); /* 周期 */
break;
default:break;
}
}
}
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_TIM2_Init();
MX_USART1_UART_Init();
MX_TIM4_Init();
HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1); /* 使能定时器4通道1输出PWM波 */
HAL_TIM_IC_Start_IT(&htim1, TIM_CHANNEL_1); /* 使能定时器2通道1的PWM输入捕获 */
HAL_TIM_IC_Start_IT(&htim1, TIM_CHANNEL_2); /* 使能定时器2通道2的PWM输入捕获 */
while(1)
{
HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);
HAL_Delay(500);
uiFrequency = 1000000 / uiCycle;
printf("占空:%dus 周期:%dus 频率:%dHz \r\n", uiDutyCycle, uiCycle, uiFrequency);
}
}
int fputc(int ch, FILE* fp)
{
HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, HAL_MAX_DELAY);
return ch;
}
参考博客
STM32F1x HAL库学习笔记(11)定时器配置及中断(溢出中断,PWM输出,输入捕获)
【STM32】HAL库 STM32CubeMX教程六----定时器中断
【STM32】HAL库——定时器PWM输出
【STM32】HAL库 STM32CubeMX教程八—定时器输入捕获
STM32CubeMX | 使用STM32定时器的PWM输入模式测量脉冲宽度和周期