stm32基本定时器控制led闪烁(标准库)
定时器
stm32f1系列,一般有8个定时器,分为基本定时器,通用定时器和高级定时器。
基本定时器 TIM6 和 TIM7 是一个 16 位的只能向上计数的定时器,只能定时,没有外部IO。通用定时器TIM2/3/4/5 是一个 16 位的可以向上/下计数的定时器,可以定时,可以输出比较,可以输入捕捉,每个定时器有四个外部 IO。高级定时器 TIM1/8 是一个 16 位的可以向上/下计数的定时器,可以定时,可以输出比较,可以输入捕捉,还可以有三相电机互补输出信号,每个定时器有 8 个外部 IO。
基本定时器
定时器寄存器概述
CR1 (Control Register 1): 控制定时器的使能、计数模式选择等。
CEN
: 计数使能位,启用定时器。ARPE
位的作用是使能或禁止ARR的预装载功能当ARPE = 1:
自动重载值(ARR)在定时器的计数过程中可以随时被修改,但新设置的值将在下一个更新事件(计数器溢出)后生效。
当ARPE = 1:
启用预装载功能。在这种情况下,如果你修改了ARR的值,新值会在下一个更新事件前生效,而不是立即应用。这意味着新的自动重载值在当前计数周期内不会影响计数过程,只是在计数器达到当前ARR时使用新设置的值。
PSC (Prescaler Register): 分频器寄存器,用于设置定时器的时钟频率。定时器时钟频率为系统时钟频率除以(PSC + 1)。
CNT (Counter Register): 当前计数值寄存器,保持当前计数的值。
DIER(DMA/Interrupt Enable Register):用于配置定时器相关的中断和DMA请求。
SR (Status Register):状态寄存器: 用于标识定时器的状态和标志位,如溢出、更新等。
ARR (Auto-Reload Register): 自动重载寄存器,设置计数器的最大值。当计数器达到这个值后会复位并重新开始计数。
ARR它定义了定时器计数的上限值。当定时器的计数器(CNT)达到这个值时,会发生以下几种情况:
计数器重置:
当计数器的值达到ARR设定的值后,计数器会被重置为零(0)。
更新事件:
达到ARR值时,会产生一个更新事件,这通常用于触发中断或其他相关操作。定时器可以配置为在这种情况下产生中断。
周期性行为:
ARR的设置决定了定时器的运行周期。例如,如果将ARR设置为10000,而预分频器(PSC)设置为7200,那么计数器将在每1秒内完成一次完整的循环。这对于实现定时任务(如LED闪烁、数据采集等)非常有用。
当给PSC赋值后,会等到CNT重新变为0后才会将值赋给影子寄存器。
一些函数介绍
1. 初始化和配置
TIM_TimeBaseInit
初始化定时器的基本设置,包括预分频、计数模式等。
void TIM_TimeBaseInit(TIM_TypeDef* TIMx, TIM_TimeBaseInitTypeDef* TIM_TimeBaseStruct);
//TIM_TimeBaseStruct: 指向包含基本定时器设置的结构体的指针,该结构体定义如下:
typedef struct {
uint16_t TIM_Prescaler; // 预分频值
uint32_t TIM_CounterMode; // 计数模式 (TIM_CounterMode_Up, TIM_CounterMode_Down)
uint32_t TIM_Period; // 自动重载值
uint16_t TIM_ClockDivision; // 时钟分割 (TIM_CKD_DIV1, TIM_CKD_DIV2, TIM_CKD_DIV4)
uint32_t TIM_RepetitionCounter; // 重复计数值 (用于高级定时器)
}TIM_TimeBaseInitTypeDef;
2. 输出比较配置
TIM_OCInit
配置定时器的输出比较功能,用于生成 PWM 信号或其他类型的输出比较信号。
void TIM_OCInit(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);
//TIM_OCInitStruct: 指向包含输出比较设置的结构体的指针,该结构体定义如下:
typedef struct {
uint16_t TIM_OCMode; // 输出比较模式 (如 TIM_OCMode_Timing, TIM_OCMode_PWM1)
uint32_t TIM_OutputState; // 输出状态 (TIM_OutputState_Enable, TIM_OutputState_Disable)
uint32_t TIM_Pulse; // 比较值
uint16_t TIM_OCPolarity; // 输出极性 (TIM_OCPolarity_High, TIM_OCPolarity_Low)
uint16_t TIM_OCFastMode; // 快速模式 (ENABLE/DISABLE)
} TIM_OCInitTypeDef;
3. 启用或禁用定时器
TIM_Cmd
void TIM_Cmd(TIM_TypeDef* TIMx, FunctionalState NewState);
4. 中断管理
TIM_ITConfig
配置定时器的中断功能
void TIM_ITConfig(TIM_TypeDef* TIMx, uint16_t TIM_IT, FunctionalState NewState);
//TIM_IT: 要配置的中断类型(例如:TIM_IT_Update、TIM_IT_CC1 等)。
TIM_GetITStatus
检查特定定时器中断的状态。
FlagStatus TIM_GetITStatus(TIM_TypeDef* TIMx, uint16_t TIM_IT);
TIM_ClearITPendingBit
清除特定定时器的中断挂起位。
void TIM_ClearITPendingBit(TIM_TypeDef* TIMx, uint16_t TIM_IT);
5. 设置计数器和预分频器
TIM_SetCounter
void TIM_SetCounter(TIM_TypeDef* TIMx, uint32_t Counter);
//Counter: 要设置的计数器值。
TIM_PrescalerConfig
void TIM_PrescalerConfig(TIM_TypeDef* TIMx, uint16_t Prescaler,
TIM_PSCReloadMode ReloadMode);
//Prescaler: 新的预分频值。
//ReloadMode: 重新加载方式(例如:TIM_PSCReloadMode_Update)。
代码
timer.c
#include "timer.h"
void Timer_NVIC_Config(void){
NVIC_InitTypeDef NVIC_InitStructure;
/* 嵌套向量中断控制器组选择 */
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);
NVIC_InitStructure.NVIC_IRQChannel = TIM6_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
void Basic_Timer_Init(void){
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_InternalClockConfig(TIM6);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6,ENABLE);
TIM_TimeBaseStructure.TIM_Period = 1000 - 1;
TIM_TimeBaseStructure.TIM_Prescaler = 72 - 1;
TIM_TimeBaseInit(TIM6,&TIM_TimeBaseStructure);
TIM_ITConfig(TIM6,TIM_IT_Update,ENABLE);
TIM_Cmd(TIM6,ENABLE);
}
main.c
#include "stm32f10x.h"
#include "systick.h"
#include "usart.h"
#include "timer.h"
#include "LED.h"
uint16_t time = 0;
int main()
{
LED_ConfigInit();
// USART_Init_Config();
SystemClock_Config();
Timer_NVIC_Config();
Basic_Timer_Init();
while(1)
{
if(time == 1000)
{
time = 0;
GPIOB->ODR ^= GPIO_Pin_5;
// GPIO_Toggle(GPIOB,GPIO_Pin_5);
}
}
}
stm32f10x_it.c添加
extern uint16_t time;
void TIM6_IRQHandler(void){
if(TIM_GetITStatus(TIM6, TIM_IT_Update)!=RESET)
{
time++;
TIM_ClearITPendingBit(TIM6,TIM_IT_Update);
}
}
设置系统时钟
void SystemClock_Config(void)
{
// 启动 HSE(外部高速晶振)
RCC->CR |= RCC_CR_HSEON; // 开启 HSE
while (!(RCC->CR & RCC_CR_HSERDY)); // 等待 HSE 变为就绪状态
// 配置 Flash 延迟周期
FLASH->ACR |= FLASH_ACR_LATENCY_2; // 对于 72MHz,设置延迟周期为 2 WS
// 配置 PLL
RCC->CFGR |= RCC_CFGR_PLLSRC_HSE; // 选择 HSE 作为 PLL 输入
RCC->CFGR |= RCC_CFGR_PLLMULL9; // 设置 PLL 倍频因子为 9 (8 MHz * 9 = 72 MHz)
// 启动 PLL
RCC->CR |= RCC_CR_PLLON; // 开启 PLL
while (!(RCC->CR & RCC_CR_PLLRDY)); // 等待 PLL 就绪
// 将 PLL 作为系统时钟
RCC->CFGR |= RCC_CFGR_SW_PLL; // 选择 PLL 作为系统时钟
while ((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_PLL); // 等待 PLL 成为系统时钟
}
如果led的闪烁时间不对,就是时钟源不对,因为我用的正点的zet6和野火的板子不一样,也是搞了半天才解决(笑~),TIM_InternalClockConfig()作用是将定时器的时钟源设置为内部时钟。
作者:zhi_beiyou