前言:
每来一个TIM 时钟CNT计数器就记一个数,记到某一个程度就会产生溢出。然后ARR就会装载到CNT计数器里面
一:TIM
1:介绍
TIM(Timer)定时器
定时器可以对输入的时钟进行计数,并在计数值达到设定值时触发中断
16位计数器、预分频器、自动重装寄存器的时基单元,在72MHz计数时钟下可以实现最大59.65s的定时 (计数器、预分频器、自动重装寄存器构成时基单元)
不仅具备基本的定时中断功能,而且还包含内外时钟源选择、输入捕获、输出比较、编码器接口、主从触发模式等多种功能
根据复杂度和应用场景分为了高级定时器、通用定时器、基本定时器三种类型
时间计算:频率 = CK_PSC (72MHZ) / (PSC+1) / (arr+1) 周期= 1 / 频率
最大计数时间:72000 000/65536/65536=0.0167638063430786-----频率
周期:1/0.0167638063430786=59.6523235555556-------最大计数周期
PSC和ARR都为16 位 2^16-1
2:定时器的分类
STM32F103C8T6定时器资源:TIM1、TIM2、TIM3、TIM4
我们STM32F1C8T6系列中的定时器全部都是16位的,也就以为着最大计数位2^16-1=65535
高级定时器比通用定时器多了一个重复次数寄存器;
每个定时器都会有4个通道;定时器1和定时器2多了一个ETR通道;高级定时器1有3个互补通道:1,2,3。都涉及了MDA操作
3:计数器
计数器分为3种计数方式
自动重装载寄存器=自动重装载寄存器+1 可以看到了自动重装载的值(ARR) 加到36,再加一个数(实际就是加到了37);才溢出
自动重装载寄存器=自动重装载寄存器+1 可以看到了自动重装载的值(ARR) 减到0,再减一个数;才溢出 和上面的 递增计数模式一样
自动重装载寄存器=自动重装载寄存器; 和上面的2种计数方法不同,少了一个1;arr写多少就会在多少溢出
当计数器配置为中央对齐模式或编码器模式时,DIR位为只读。
3:基本定时器
A:介绍
注意我们使用的STM32F103C8T6这个型号的版主没有基本定时器,我们使用其他型号的STM32板子,来完成我们基本定时器的实验
1 : 预分频器+CNT计数器+自动重装载寄存器=时基单元
2 : 基本定时器只能选择内部时钟;他们都为16位的
预分频器 : 可以对72MHZ的计数时钟进行预分频处理; 对输入的基准频率提前进行一个分频的操作
eg : 预分频器写0,那就是不分频,或者说是1分频 ; 输出频率=输入频率=72MHz
预分频器写1,那就是2分频,输出频率(实际分频系数)=输入频率/2=36MHz
预分频器写2,那就是3分频,输出频率(实际分频系数)=输入频率/3= 24MHz
所以预分频器的值和实际的分频系数相差了1; 实际分频系数=预分频器的值+1
递增计数器 : 计数器可以对预分频后的计数时钟进行计数,预分频器每来一个上升沿计数器就+1
所以计数器的值在计时过程中会不断地自增运行,直到达到目标值(自动重装载寄存器)然后产生中断,然后在重新开始计数
自动重装载寄存器(固定值) : 储存的是我们的计数目标,产生中断的目标值,(当计数器达到目标值就产生中断) 自动重装载寄存器=自动重装载寄存器+1
流程: 基准时钟------->预分频器------>计数器<--------->自动重装载计数器
计数器不断自增,会和自动重装载寄存器比较,当两个的值相同时,产生更新事件中断和DMA请求;
cpu会响应更新中断
B:时间计算
溢出时间其实实际上就是周期
C:HAL库配置
要使能定时器定时的话,就必须打开中断使能也就是3,CNT==ARR溢出产生的中断
D:HAL库函数
UEV:只有一种事件引起中断或者DMA请求,即计数器上溢的更新事件(UEV),就是CNT=ARR是发生的溢出事件。
中断ISR:就是中断号,在汇编语言中写,比如下面的:
void TIM2_IRQHandler()
{}
4:通用定时器
A:介绍
外部时钟模式:常用情况-->A:对外部电平高低的变换进行一个计数
B:选择计数器时钟源
5:高级定时器
6:定时器的基本结构
7:HAL函数总结
其他通用操作函数
自动重装载值(AutoReloadPreload): X.Init.AutoReloadPreload,如果我们中间需要改变ARR的值需要写这个:
1:DISABLE(失能,关闭缓存区):如果我们中途需要改变ARR的值,关闭缓存区,ARR会直接变为改的数据,并CNT==改的数据时发生更新中断。
2:ENABLE(使能,打开缓存区):打开缓存区,ARR值不会立即改变,而是在下次把新的ARR值写入,当CNT=ARR时发生溢出
在代码总结事件中验证
中断处理
中断事件类型
HAL_TIM_IRQHandler和中断回调函数
二:定时中断功能
定时器有很多功能:07:HAL------定时器功能(输出比较PWM)
CSDNhttps://mp.csdn.net/mp_blog/creation/editor/137024445
08:HAL---定时器功能(输入捕获功能)
CSDNhttps://mp.csdn.net/mp_blog/creation/editor/137085008
三:代码
A:基本定时器
使用基本定时器控制LED,注意我们STM32C8T6这个板子没有基本定时器,需要使用其他的板子
这个是直接计算的周期
使用的是内部时钟源
TIM_HandleTypeDef g_timx_handle;
/* 定时器中断初始化函数 */
void btim_timx_int_init(uint16_t arr, uint16_t psc)
{
g_timx_handle.Instance = TIM6;
g_timx_handle.Init.Prescaler = psc;
g_timx_handle.Init.Period = arr;
HAL_TIM_Base_Init(&g_timx_handle);
//CNT==ARR溢出产生的中断
HAL_TIM_Base_Start_IT(&g_timx_handle);
}
/* 定时器基础MSP初始化函数 */
void HAL_TIM_Base_MspInit(TIM_HandleTypeDef *htim)
{
if (htim->Instance == TIM6)
{
__HAL_RCC_TIM6_CLK_ENABLE();
HAL_NVIC_SetPriority(TIM6_IRQn, 1, 3);
HAL_NVIC_EnableIRQ(TIM6_IRQn);
}
}
/* 定时器6中断服务函数 */
void TIM6_IRQHandler(void)
{
HAL_TIM_IRQHandler(&g_timx_handle);
}
/* 定时器溢出中断中断回调函数 */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if (htim->Instance == TIM6)
{
LED0_TOGGLE();
}
}
ETR刹车清除
A:ETR刹车
#include "stm32f1xx_hal.h"
#include "rcc.h"
#include "led.h"
#include "delay.h"
#include "OLED.h"
#include "wwdg.h"
#include "key.h"
#include "IC.h"
#include <stdarg.h>
#include "stdio.h"
#include "UART.h"
uint16_t num=0;
uint16_t zone_bit=1;
int main(void)
{
HAL_Init(); /* 初始化HAL库 */
sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
delay_init(72); /* 延时初始化 */
Uart_Init(115200);
Timer1_Init(2000-1,36000-1); // 0.5
while (1)
{
}
}
#include "stm32f1xx_hal.h"
#include "OLED.h"
#include <stdarg.h>
#include "stdio.h"
#include "UART.h"
TIM_HandleTypeDef TIM1_Handle;
TIM_OC_InitTypeDef TIM_OC_Init={0};
TIM_OC_InitTypeDef TIM_OC_Init4={0};
void Timer1_Init(uint16_t arr, uint16_t psc, uint8_t rep)
{
TIM_ClearInputConfigTypeDef TIM_ClearInputConfig={0};
TIM1_Handle.Instance = TIM1;
TIM1_Handle.Init.Prescaler = psc;
TIM1_Handle.Init.CounterMode = TIM_COUNTERMODE_UP;
TIM1_Handle.Init.Period = arr;
TIM1_Handle.Init.ClockDivision=TIM_CLOCKDIVISION_DIV1;
TIM1_Handle.Init.RepetitionCounter = rep; //重复计数器
TIM1_Handle.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE; //自动重装载值
HAL_TIM_PWM_Init(&TIM1_Handle);
__HAL_TIM_CLEAR_FLAG(&TIM1_Handle, TIM_FLAG_UPDATE);
//PWM1 --CNT <CCRX :输出有效电平
TIM_OC_Init.OCMode=TIM_OCMODE_PWM1; //输出比较的模式
TIM_OC_Init.Pulse=400; //比较值(CCR)
TIM_OC_Init.OCPolarity=TIM_OCPOLARITY_HIGH; //输出极性 (高电平有效)
TIM_OC_Init.OCFastMode=TIM_OCFAST_DISABLE; //快速模式
HAL_TIM_OC_ConfigChannel(&TIM1_Handle,&TIM_OC_Init,TIM_CHANNEL_1);
//HAL_TIM_PWM_ConfigChannel(&TIM1_Handle,&TIM_OC_Init,TIM_CHANNEL_1);
//PWM1 --CNT <CCRX :输出有效电平
TIM_OC_Init4.OCMode=TIM_OCMODE_PWM1; //输出比较的模式
TIM_OC_Init4.Pulse=400; //比较值(CCR)
TIM_OC_Init4.OCPolarity=TIM_OCPOLARITY_HIGH; //输出极性 (高电平有效)
TIM_OC_Init4.OCFastMode=TIM_OCFAST_DISABLE; //快速模式
HAL_TIM_PWM_ConfigChannel(&TIM1_Handle,&TIM_OC_Init4,TIM_CHANNEL_4);
TIM_ClearInputConfig.ClearInputState=ENABLE; //RTC清除的状态
TIM_ClearInputConfig.ClearInputSource=TIM_CLEARINPUTSOURCE_ETR; //选择清除源
TIM_ClearInputConfig.ClearInputPolarity=TIM_CLEARINPUTPOLARITY_INVERTED; //极性:不反向低电平触发
TIM_ClearInputConfig.ClearInputPrescaler=0; //分频因子:只能为0
TIM_ClearInputConfig.ClearInputFilter=0x8; //滤波器
HAL_TIM_ConfigOCrefClear(&TIM1_Handle,&TIM_ClearInputConfig,TIM_CHANNEL_1);
HAL_TIM_PWM_Start(&TIM1_Handle,TIM_CHANNEL_1);
HAL_TIM_PWM_Start(&TIM1_Handle,TIM_CHANNEL_4);
}
void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef *htim)
{
if(htim->Instance==TIM1)
{
__HAL_RCC_TIM1_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE() ;
GPIO_InitTypeDef GPIO_InitType;
GPIO_InitType.Pin = GPIO_PIN_8|GPIO_PIN_11;;
GPIO_InitType.Mode = GPIO_MODE_AF_PP;
GPIO_InitType.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOA,&GPIO_InitType);
GPIO_InitType.Pin = GPIO_PIN_12;
GPIO_InitType.Mode = GPIO_MODE_AF_INPUT;
GPIO_InitType.Pull = GPIO_PULLUP;
HAL_GPIO_Init(GPIOA,&GPIO_InitType);
}
}
按下为低电平
定时器ETR清除
可以和我们的高级定时器做下对比:
高级定时器: