一、STM32F103定时器分类及区别
共有8个定时器,它们是:TIM1~TIM8。STM32的定时器分为基本定时器、通用定时器和高等定时器。 TIM6、TIM7(基本定时器):基本定时器是只能向上计数的16位定时器,基本定时器只能有定时的功能,没有外部IO口,所以没有捕获和比较通道。 TIM2、TIM3、TIM4、TIM5(通用定时器):通用定时器是可以向上计数,也可以向下计数的16位定时器。通用定时器可以定时、输出比较、输入捕捉,每个通用定时器具有4个外部IO口。 TIM1、TIM8(高级定时器):高级定时器是是可以向上计数,也可以向下计数的16位定时器。高等定时器可以定时、输出比较、输入捕捉、还可以输出三相电机互补信号,每个高等定时器有8个外部IO口。
二、通用定时器主要功能
通用TIMx (TIM2、TIM3、TIM4和TIM5)定时器功能包括: ● 16位向上、向下、向上/向下自动装载计数器 ● 16位可编程(可以实时修改)预分频器,计数器时钟频率的分频系数为1~65536之间的任意 数值 ● 4个独立通道: ─ 输入捕获 ─ 输出比较 ─ PWM生成(边缘或中间对齐模式) ─ 单脉冲模式输出 ● 使用外部信号控制定时器和定时器互连的同步电路 ● 如下事件发生时产生中断/DMA: ─ 更新:计数器向上溢出/向下溢出,计数器初始化(通过软件或者内部/外部触发) ─ 触发事件(计数器启动、停止、初始化或者由内部/外部触发计数) ─ 输入捕获 ─ 输出比较 ● 支持针对定位的增量(正交)编码器和霍尔传感器电路
通用定时器计数模式
向上计数,向下计数,中心对齐
三、通用定时器工作过程
从时钟源产生框可以看到 定时器时钟有四种来源: ● 内部时钟(CK_INT) ● 外部时钟模式1:外部输入脚(TIx) (输入捕获的引脚) ● 外部时钟模式2:外部触发输入(ETR) ● 内部触发输入(ITRx):(定时器级联)使用一个定时器作为另一个定时器的预分频器,如可以配置一个定时器Timer1而作为另一个定时器Timer2的预分频器。 产生CK_PSC时钟,然后在从模式控制器内设置好计数模式(向上向下),再经过预分频器产生CK_CNT,若为CNT计数器向下计数,则当其计数到0时,自动重装载寄存器会重新为CNT计数器装载新值重新递减计数,并产生一个更新事件。
时基单元: ● 计数器寄存器(TIMx_CNT) ● 预分频器寄存器 (TIMx_PSC) ● 自动装载寄存器 (TIMx_ARR) 输入捕获:信号通过捕获通道进入,经滤波,经分频,经,,输入捕获与输出比较的四个通道其实是一个。
四、时钟源配置(选内部时钟)
由以上两张图,我们知道CK_CNT时钟来源于APB1,并且如果APB1预分频系数如果不为1则CK_INT=APB1*2(单位是频率),然后设置好预分频系数CK_PSC为N,得CK_CNT=CK_INT / (N+1)。 CK_INT到底是多少呢? 那以STM32F103RBT6为例,打开system_stm32f10x.c文件找到SystemInit函数,其注释如下: 翻译:设置系统时钟(系统时钟源、PLL倍增器因子、AHB/APBx预分频器和闪存设置) 找到函数,我们为其一一注释:
void SystemInit (void)
{
RCC->CR |= (uint32_t)0x00000001;
#ifndef STM32F10X_CL
RCC->CFGR &= (uint32_t)0xF8FF0000;
#else
RCC->CFGR &= (uint32_t)0xF0FF0000;
#endif
RCC->CR &= (uint32_t)0xFEF6FFFF;
RCC->CR &= (uint32_t)0xFFFBFFFF;
RCC->CFGR &= (uint32_t)0xFF80FFFF;
#ifdef STM32F10X_CL
RCC->CR &= (uint32_t)0xEBFFFFFF;
RCC->CIR = 0x00FF0000;
RCC->CFGR2 = 0x00000000;
#elif defined (STM32F10X_LD_VL) || defined (STM32F10X_MD_VL) || (defined STM32F10X_HD_VL)
RCC->CIR = 0x009F0000;
RCC->CFGR2 = 0x00000000;
#else
RCC->CIR = 0x009F0000;
#endif
#if defined (STM32F10X_HD) || (defined STM32F10X_XL) || (defined STM32F10X_HD_VL)
#ifdef DATA_IN_ExtSRAM
SystemInit_ExtMemCtl();
#endif
#endif
SetSysClock();
#ifdef VECT_TAB_SRAM
SCB->VTOR = SRAM_BASE | VECT_TAB_OFFSET;
#else
SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET;
#endif
}
[注]: 这里STM32F10X_CL宏定义问题,这玩意没有被定义,参考链接1,链接2.
上头函数SystemInit ()的功能主要是复位与时钟相关的寄存器,并执行SetSysClock()。
#if defined (STM32F10X_LD_VL) || (defined STM32F10X_MD_VL) || (defined STM32F10X_HD_VL)
#define SYSCLK_FREQ_24MHz 24000000
#else
#define SYSCLK_FREQ_72MHz 72000000
#endif
static void SetSysClock(void)
{
#ifdef SYSCLK_FREQ_HSE
SetSysClockToHSE();
#elif defined SYSCLK_FREQ_24MHz
SetSysClockTo24();
#elif defined SYSCLK_FREQ_36MHz
SetSysClockTo36();
#elif defined SYSCLK_FREQ_48MHz
SetSysClockTo48();
#elif defined SYSCLK_FREQ_56MHz
SetSysClockTo56();
#elif defined SYSCLK_FREQ_72MHz
SetSysClockTo72();
#endif
}
由此部分代码可知(如果宏定义STM3210X_HD),默认状态下,在函数SetSysClock()中将会执行函数SetSysClockTo72()。关于这个函数详细信息参考链接3,主要关注一下
- 设置HCLK,HCLK = SYSCLK
- 设置PCLK2,PCLK2 = HCLK
- 设置PCLK1,PCLK1 = HCLK / 2(即APB1预分频系数为2,不为1的哟!!!)
- 设置PLL时钟来源及PLL倍频因数
- 选择PLL作为系统时钟源,即PLLCLK = SYSCLK
到现在终于真相大白:CK_CNT=CK_INT /(N+1)=APB1*2 /(N+1)=72M / (N+1)(这里的N指PSC预分频器的预分频系数)。
五、通用定时器配置步骤
void MSTimerInit(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
TIM_TimeBaseStructure.TIM_Prescaler = 35999;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseStructure.TIM_Period = 0xFFFF;
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseStructure.TIM_RepetitionCounter = 0;
TIM_DeInit(TIM3);
TIM_TimeBaseInit(TIM3,&TIM_TimeBaseStructure);
TIM_ITConfig(
TIM3,
TIM_IT_Update ,
ENABLE
);
NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
TIM_Cmd(TIM3, ENABLE);
对于上面这段代码,定时器的溢出时间为=(重装载值+1)x(分频系数+1)/72M=(65535+1) x (35999+1) / 72M=65536 x 0.5ms
|