1)设置RCC----》开启时钟
2)选择时钟模式---》配置时钟源
3)设置时基单元---》定时器初始化
4)使能中断---》中断输出配置
5)设置NVIC---》NVIC中断分组+NVIC配置
6)TIM使能

- /**
- * 函 数:定时中断初始化
- * 参 数:无
- * 返 回 值:无
- */
- void Timer_Init(void)
- {
- /*开启时钟*/
- RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); //开启TIM2的时钟
-
- /*配置时钟源*/
- TIM_InternalClockConfig(TIM2); //选择TIM2为内部时钟,若不调用此函数,TIM默认也为内部时钟
-
- /*时基单元初始化*/
- TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure; //定义结构体变量
- TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1; //时钟分频,选择不分频,此参数用于配置滤波器时钟,不影响时基单元功能
- TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; //计数器模式,选择向上计数
- /**
- 这里设置表示我们要计时1s,1s进入一次中断
- 72 000 000/(7200-1)/(1 0000-1)=1s
- */
- TIM_TimeBaseInitStructure.TIM_Period = 10000 - 1; //计数周期,即ARR的值
- TIM_TimeBaseInitStructure.TIM_Prescaler = 7200 - 1; //预分频器,即PSC的值
- TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0; //重复计数器,高级定时器才会用到
- TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure); //将结构体变量交给TIM_TimeBaseInit,配置TIM2的时基单元
-
- /*中断输出配置*/
- TIM_ClearFlag(TIM2, TIM_FLAG_Update); //清除定时器更新标志位
- //TIM_TimeBaseInit函数末尾,手动产生了更新事件
- //若不清除此标志位,则开启中断后,会立刻进入一次中断
- //如果不介意此问题,则不清除此标志位也可
-
- TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE); //开启TIM2的更新中断
-
- /*NVIC中断分组*/
- NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //配置NVIC为分组2
- //即抢占优先级范围:0~3,响应优先级范围:0~3
- //此分组配置在整个工程中仅需调用一次
- //若有多个中断,可以把此代码放在main函数内,while循环之前
- //若调用多次配置分组的代码,则后执行的配置会覆盖先执行的配置
-
- /*NVIC配置*/
- NVIC_InitTypeDef NVIC_InitStructure; //定义结构体变量
- NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn; //选择配置NVIC的TIM2线
- NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //指定NVIC线路使能
- NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; //指定NVIC线路的抢占优先级为2
- NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; //指定NVIC线路的响应优先级为1
- NVIC_Init(&NVIC_InitStructure); //将结构体变量交给NVIC_Init,配置NVIC外设
-
- /*TIM使能*/
- TIM_Cmd(TIM2, ENABLE); //使能TIM2,定时器开始运行
- }
1)内部时钟APB
2)外部时钟模式二ETR
3)内部触发输入时钟ITR0,ITR1...
4)外部时钟模式一TI1FP1来自于通道1,TI2FP2来自于通道2

触发控制器会将触发信号传输给其他定 时器的触发输入时钟

计数器实际上是与比较寄存器的影子寄存器进行比较的。
1)由图可知从通道1进入可能会被变为输入捕获通道1或者输入捕获通道2
2)上升沿/下降沿进行捕获,产生一个时间,然后进入捕获通道(获得计数值),

1)写入一个比较值
2)配置预分频器
3)计数值 VS 比较值(影子寄存器)比较--》触发比较事件
4)在转移到影子寄存器有一个缓冲作用
5)有8种输入模式

STM32F1xx中文参考手册.pdf · 林何/STM32F103C8 - 码云 - 开源中国 (gitee.com)
1)其实最多可以有17个定时器
2)功能差别
1)先学会定时器基本概念的使用
2)高级功能用到到时再去细看
3)设计本身的复杂性导致学习难度大,要有耐心
4)很多书面概念要搞清楚,需要不断前后对照
5)学习三宝:数据手册+外设库源码+例程
进行比较计数器和寄存器的值进行电平的反转。



定时器是基于计数器进行的,分频,然后走一格就计数一次。
PWM生成:就是输出比较产生的电流
单脉冲:表示就输出一次
向上:加法计数器【从x不断++到65535】
向下:减法计数器【从x不断--到0】




1)可以在运行的时候对预分频器的值进行修改
我们这里可以在运行时对其进行修改的是count计数器(可以在当前事件执行时进行更新,也可以等到下一次更新事件发生后在进行改变)
预分频器(预分频器的值只能在下一次更新事件到来时被采用。)
2)影子寄存器【预装载寄存器】:在硬件上实际上是存在的,但是没有给他分配地址(不需要程序进行干预)---实际上装的是ARR的值,在2种情况下才会被加载到影子寄存器中:
1.立即加载到影子寄存器【如果此时计数是300,则会立刻变成600】----》无缓存区
2.等这个时间计数结束后才会重新加载【只能等到执行到0或者600时才会被重新赋值】--》有缓存区



1.2 TIMx_CR1_UDIS:中断允许位
1)计数器的结果是分频后的结果
2)只有在定时器开启时才可以使能计数器
注意点:
真正的计时器使能信号CNR_EN(定时器打开)是在CEN的一个时钟周期后被设置(表示计数器是在定时器打开后的一个周期才开始计时)



预分频器可以在工作时被改 变。但是新的预分频器参数只能在下一次更新事件到来时被采用。【因为预分频器中带有缓冲器】











注意点:
向下计数模式没有【立即模式】,因为如果原来ff,然后现在走到05,然后修改为36,不可能重新回36在继续减,这是不合理的。



在中央对齐模式,计数器从0开始计数到自动加载的值(TIMx_ARR寄存器)−1,产生一个计数器 溢出事件,然后向下计数到1并且产生一个计数器下溢事件;然后再从0开始重新计数。
1)在这个模式,不能写入TIMx_CR1中的DIR方向位。它由硬件更新并指示当前的计数方向。
2)注意加减的边界值问题:
加法计数器是寄存器值-1
减法计数器是减到1







通过设置TIMx_SMCR寄存器的SMS=000,表示使用内部时钟【常规模式】。















- void TIM_TimeBaseInit(TIM_TypeDef* TIMx, TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct)
- {
- uint16_t tmpcr1 = 0;
-
- /* Check the parameters */
- assert_param(IS_TIM_ALL_PERIPH(TIMx));
- assert_param(IS_TIM_COUNTER_MODE(TIM_TimeBaseInitStruct->TIM_CounterMode));
- assert_param(IS_TIM_CKD_DIV(TIM_TimeBaseInitStruct->TIM_ClockDivision));
-
- tmpcr1 = TIMx->CR1;
-
- if((TIMx == TIM1) || (TIMx == TIM8)|| (TIMx == TIM2) || (TIMx == TIM3)||
- (TIMx == TIM4) || (TIMx == TIM5))
- {
- /* Select the Counter Mode */
- tmpcr1 &= (uint16_t)(~((uint16_t)(TIM_CR1_DIR | TIM_CR1_CMS)));
- tmpcr1 |= (uint32_t)TIM_TimeBaseInitStruct->TIM_CounterMode;
- }
-
- if((TIMx != TIM6) && (TIMx != TIM7))
- {
- /* Set the clock division */
- tmpcr1 &= (uint16_t)(~((uint16_t)TIM_CR1_CKD));
- tmpcr1 |= (uint32_t)TIM_TimeBaseInitStruct->TIM_ClockDivision;
- }
-
- TIMx->CR1 = tmpcr1;
-
- /* Set the Autoreload value */
- //要计数的值
- TIMx->ARR = TIM_TimeBaseInitStruct->TIM_Period ;
-
- /* Set the Prescaler value */
- //预分频参数
- TIMx->PSC = TIM_TimeBaseInitStruct->TIM_Prescaler;
-
- if ((TIMx == TIM1) || (TIMx == TIM8)|| (TIMx == TIM15)|| (TIMx == TIM16) || (TIMx == TIM17))
- {
- /* Set the Repetition Counter value */
- TIMx->RCR = TIM_TimeBaseInitStruct->TIM_RepetitionCounter;
- }
-
- /* Generate an update event to reload the Prescaler and the Repetition counter
- values immediately */
- //预分频器参数的改变
- TIMx->EGR = TIM_PSCReloadMode_Immediate;
- }
- void TIM_ITConfig(TIM_TypeDef* TIMx, uint16_t TIM_IT, FunctionalState NewState)
- {
- /* Check the parameters */
- assert_param(IS_TIM_ALL_PERIPH(TIMx));
- assert_param(IS_TIM_IT(TIM_IT));
- assert_param(IS_FUNCTIONAL_STATE(NewState));
-
- if (NewState != DISABLE)
- {
- /* Enable the Interrupt sources */
- TIMx->DIER |= TIM_IT;
- }
- else
- {
- /* Disable the Interrupt sources */
- TIMx->DIER &= (uint16_t)~TIM_IT;
- }
- }
TIM2接APB1,GPIOB接APB2

配置的是GPIOB



- void NVIC_Configuration(void)
- {
- NVIC_InitTypeDef NVIC_InitStructure;
-
- #ifdef VECT_TAB_RAM
- /* Set the Vector Table base location at 0x20000000 */
- NVIC_SetVectorTable(NVIC_VectTab_RAM, 0x0);
- #else /* VECT_TAB_FLASH */
- /* Set the Vector Table base location at 0x08000000 */
- NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x0);
- #endif
-
- NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
- /* Enable the TIM1 Interrupt */
- NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
- NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
- NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
- NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
- NVIC_Init(&NVIC_InitStructure);
-
- }




如果不知道定时器的名字是什么可以去“startup_stm32f10x_hd.s”查看
再去“stm32f10x_it.c”中填写
注意点:
TIM2和GPIOB所对应的APB对应不一样,所以一定要分开声明,要不然会出现错误。
TIM2---》APB2
GPIOB--》APB1
- void RCC_Configuration(void)
- {
- //打开时钟
- //注意点:
- //TIM2和GPIOB的APB不一样,所以只能分开声明,要不然不起效果
- // RCC_APB2PeriphClockCmd(RCC_APB1Periph_TIM2 | RCC_APB2Periph_GPIOB, ENABLE);
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
- RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
- }
- void GPIO_Configuration(void)
- {
- GPIO_InitTypeDef GPIO_InitStructure;
-
- /* GPIOA Configuration: */
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_All;
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
- GPIO_Init(GPIOB, &GPIO_InitStructure);
-
- GPIO_WriteBit(GPIOB, GPIO_Pin_8, Bit_RESET);
- }
- void NVIC_Configuration(void)
- {
- NVIC_InitTypeDef NVIC_InitStructure;
-
- #ifdef VECT_TAB_RAM
- /* Set the Vector Table base location at 0x20000000 */
- NVIC_SetVectorTable(NVIC_VectTab_RAM, 0x0);
- #else /* VECT_TAB_FLASH */
- /* Set the Vector Table base location at 0x08000000 */
- NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x0);
- #endif
-
- NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
- /* Enable the TIM1 Interrupt */
- NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
- NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
- NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
- NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
- NVIC_Init(&NVIC_InitStructure);
-
- }


TIM2接APB1,GPIOB接APB2
我们APB1不能超过36MHZ,所以要对72MHZ进行分频
- //72MHZ定时器的定义
- // 主频是72MHz,HCLK是72M,PCLK2是72M,PCLK1是36M
- // TIM2接在PCLK1下(APB1)所以源是36M,CK_CNT是36M/7200=5KHz
- TIM_TimeBaseStructure.TIM_Prescaler = 7199;
- //例如:时钟频率=72/(时钟预分频+1)
- TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
- // 计数个数是10000,所以计数时间是10000*1/5000=2s
- TIM_TimeBaseStructure.TIM_Period = 9999;//自动重装载寄存器周期的值(定时时间)累计
- //0xffff 个频后产生个更新或者中断(也就是说定时时间到)
- #include "stm32f10x.h" // Device header
- /**
- 根据定时器2来实现数码管每隔1秒闪烁一次
- */
-
-
- void RCC_Configuration(void);
- void GPIO_Configuration(void);
- void NVIC_Configuration(void);
-
- int main(){
- //设置TIM2定时器
- TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
-
- RCC_Configuration(); //系统时钟配置
- NVIC_Configuration(); //NVIC配置
- GPIO_Configuration(); //通用IO端口配置
-
- //72MHZ定时器的定义
- // 主频是72MHz,HCLK是72M,PCLK2是72M,PCLK1是36M
- // TIM2接在PCLK1下(APB1)所以源是36M,CK_CNT是36M/7200=5KHz
- TIM_TimeBaseStructure.TIM_Prescaler = 7199;
- //例如:时钟频率=72/(时钟预分频+1)
- TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
- // 计数个数是10000,所以计数时间是10000*1/5000=2s
- //注意点:我们计算出来的值是5,但是我们实际上是从0开始,所以我们这里要-1
- TIM_TimeBaseStructure.TIM_Period = 4;//自动重装载寄存器周期的值(定时时间)累计
- //0xffff 个频后产生个更新或者中断(也就是说定时时间到)
- TIM_TimeBaseStructure.TIM_ClockDivision = 0;
- //初始化定时器2
- TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
-
- //打开中断,溢出中断
- TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
- //定时器打开
- TIM_Cmd(TIM2, ENABLE);
- while (1);
- //进入中断
-
- return 0;
- }
-
-
- void RCC_Configuration(void)
- {
- //打开时钟
- //注意点:
- //TIM2和GPIOB的APB不一样,所以只能分开声明,要不然不起效果
- // RCC_APB2PeriphClockCmd(RCC_APB1Periph_TIM2 | RCC_APB2Periph_GPIOB, ENABLE);
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
- RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
- }
-
-
- void GPIO_Configuration(void)
- {
- GPIO_InitTypeDef GPIO_InitStructure;
-
- /* GPIOA Configuration: */
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_All;
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
- GPIO_Init(GPIOB, &GPIO_InitStructure);
-
- //使亮数码管
- GPIO_WriteBit(GPIOB, GPIO_Pin_8, Bit_SET);
- }
-
- void NVIC_Configuration(void)
- {
- NVIC_InitTypeDef NVIC_InitStructure;
-
- #ifdef VECT_TAB_RAM
- /* Set the Vector Table base location at 0x20000000 */
- NVIC_SetVectorTable(NVIC_VectTab_RAM, 0x0);
- #else /* VECT_TAB_FLASH */
- /* Set the Vector Table base location at 0x08000000 */
- NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x0);
- #endif
-
- NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
- /* Enable the TIM1 Interrupt */
- NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
- NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
- NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
- NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
- NVIC_Init(&NVIC_InitStructure);
-
- }
- void TIM2_IRQHandler(void){
- if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET) //检测制定的中断是否发生
- {
- TIM_ClearITPendingBit(TIM2, TIM_IT_Update); //清除中断处理位。
- //LED的取反
- GPIO_WriteBit(GPIOB, GPIO_Pin_8, !GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_8));
- }
- }
cnt的计数公式
cnt=1s*频率值(HZ)
例如:
此时TIM2一定是分完频,结果为36MHZ
初始化:TIM_TimeBaseStructure.TIM_Prescaler = 7199;--》【最后要+1】
分频后的频率=36M/72 00=5KHZ
实际测试得到的是1KHZ的结果,说明前面分析有误
初始化:计数个数为1 0000个
计数时间=个数*f(1/周期)
所以:计数时间为:1 0000*1/5000=2s

结论:APB1时钟确实是36MHZ,但是APB1到定时器时钟那边中间被乘以2,所以又变成72MHZ了。


- /**
- * 函 数:定时中断初始化
- * 参 数:无
- * 返 回 值:无
- */
- void Timer_Init(void)
- {
- /*开启时钟*/
- RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); //开启TIM2的时钟
-
- /*配置时钟源*/
- TIM_InternalClockConfig(TIM2); //选择TIM2为内部时钟,若不调用此函数,TIM默认也为内部时钟
-
- /*时基单元初始化*/
- TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure; //定义结构体变量
- TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1; //时钟分频,选择不分频,此参数用于配置滤波器时钟,不影响时基单元功能
- TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; //计数器模式,选择向上计数
- /**
- 这里设置表示我们要计时1s,1s进入一次中断
- 72 000 000/(7200-1)/(1 0000-1)=1s
- */
- TIM_TimeBaseInitStructure.TIM_Period = 10000 - 1; //计数周期,即ARR的值
- TIM_TimeBaseInitStructure.TIM_Prescaler = 7200 - 1; //预分频器,即PSC的值
- TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0; //重复计数器,高级定时器才会用到
- TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure); //将结构体变量交给TIM_TimeBaseInit,配置TIM2的时基单元
-
- /*中断输出配置*/
- TIM_ClearFlag(TIM2, TIM_FLAG_Update); //清除定时器更新标志位
- //TIM_TimeBaseInit函数末尾,手动产生了更新事件
- //若不清除此标志位,则开启中断后,会立刻进入一次中断
- //如果不介意此问题,则不清除此标志位也可
-
- TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE); //开启TIM2的更新中断
-
- /*NVIC中断分组*/
- NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //配置NVIC为分组2
- //即抢占优先级范围:0~3,响应优先级范围:0~3
- //此分组配置在整个工程中仅需调用一次
- //若有多个中断,可以把此代码放在main函数内,while循环之前
- //若调用多次配置分组的代码,则后执行的配置会覆盖先执行的配置
-
- /*NVIC配置*/
- NVIC_InitTypeDef NVIC_InitStructure; //定义结构体变量
- NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn; //选择配置NVIC的TIM2线
- NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //指定NVIC线路使能
- NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; //指定NVIC线路的抢占优先级为2
- NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; //指定NVIC线路的响应优先级为1
- NVIC_Init(&NVIC_InitStructure); //将结构体变量交给NVIC_Init,配置NVIC外设
-
- /*TIM使能*/
- TIM_Cmd(TIM2, ENABLE); //使能TIM2,定时器开始运行
- }
- uint16_t Num; //定义在定时器中断里自增的变量
-
- /**
- * 函 数:TIM2中断函数
- * 参 数:无
- * 返 回 值:无
- * 注意事项:此函数为中断函数,无需调用,中断触发后自动执行
- * 函数名为预留的指定名称,可以从启动文件复制
- * 请确保函数名正确,不能有任何差异,否则中断函数将不能进入
- */
- void TIM2_IRQHandler(void)
- {
- if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET) //判断是否是TIM2的更新事件触发的中断
- {
- Num ++; //Num变量自增,用于测试定时中断
- TIM_ClearITPendingBit(TIM2, TIM_IT_Update); //清除TIM2更新事件的中断标志位
- //中断标志位必须清除
- //否则中断将连续不断地触发,导致主程序卡死
- }
- }