systick定时器:
【STM32】Systick定时器-CSDN博客
0.通用定时器框图
1.时钟源
2.控制器
3.输入捕获
计数器实际上是与比较寄存器的影子寄存器进行比较的。
4.输出比较
1.STM32的定时器学习要点
参考手册
STM32F1xx中文参考手册.pdf · 林何/STM32F103C8 - 码云 - 开源中国 (gitee.com)
1.通用定时器和其他的区别
1)其实最多可以有17个定时器
2)功能差别
2.STM32定时器的学习要点
1)先学会定时器基本概念的使用
2)高级功能用到到时再去细看
3)设计本身的复杂性导致学习难度大,要有耐心
4)很多书面概念要搞清楚,需要不断前后对照
5)学习三宝:数据手册+外设库源码+例程
2.通用定时器的数据手册
进行比较计数器和寄存器的值进行电平的反转。
1.TIMx主要功能
定时器是基于计数器进行的,分频,然后走一格就计数一次。
1.向上/向下
向上:加法计数器【从x不断++到65535】
向下:减法计数器【从x不断--到0】
2.TIM的所属APB
3.发生的中断/DMA
2.通用定时器框图
3.预分频器
1.时基单元
1)可以在运行的时候对预分频器的值进行修改
我们这里可以在运行时对其进行修改的是count计数器(可以在当前事件执行时进行更新,也可以等到下一次更新事件发生后在进行改变)
预分频器(预分频器的值只能在下一次更新事件到来时被采用。)
2)影子寄存器【预装载寄存器】:在硬件上实际上是存在的,但是没有给他分配地址(不需要程序进行干预)
1.1 TIMx_CR1_ARPE:修改位
1.2 TIMx_CR1_UDIS:中断允许位
1.3 TIMx_CR1_CEN:时钟和计数器的使能
注意点:
真正的计时器使能信号CNR_EN是在CEN的一个时钟周期后被设置(表示计数器是在一个周期后才开始计时)
2.预分频器描述
预分频器可以在工作时被改 变。但是新的预分频器参数只能在下一次更新事件到来时被采用。【因为预分频器中带有缓冲器】
2.1 计数器的时序图:1分频--2分频
2.2 计数器的时序图:1分频--4分频
4.计数器
1.向上计数器模式
1.与51单片机不同
2.基本概念
3.是否产生更新事件
4.计数器时序图
5.ARPE=0时的更新事件【立即模式-不缓存】
6.ARPE=1时的更新事件【下一周期改变--缓存】
2.向下计数模式
注意点:
向下计数模式没有【立即模式】,因为如果原来ff,然后现在走到05,然后修改为36,不可能重新回36在继续减,这是不合理的。
3.中央对齐模式(向上/向下计数)
在中央对齐模式,计数器从0开始计数到自动加载的值(TIMx_ARR寄存器)−1,产生一个计数器 溢出事件,然后向下计数到1并且产生一个计数器下溢事件;然后再从0开始重新计数。
1.注意点
1)在这个模式,不能写入TIMx_CR1中的DIR方向位。它由硬件更新并指示当前的计数方向。
2)注意加减的边界值问题:
加法计数器是寄存器值-1
减法计数器是减到1
2.计数器时序图
3.ARPE=1时的更新事件【下一周期更新--缓存】
5.时钟选择
1.内部时钟(CK_INT)--常规模式
1.使能
通过设置TIMx_SMCR寄存器的SMS=000,表示使用内部时钟【常规模式】。
2.外部时钟模式1:外部输入脚(TIx)
3.外部时钟模式2:外部触发输入(ETR)
4.内部触发输入(ITRx):一个定时器的输出作为另外一个定时器的输入
6.库函数
1.TIM_TimeBaseInit
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 countervalues immediately *///预分频器参数的改变TIMx->EGR = TIM_PSCReloadMode_Immediate;
}
2.TIM_ITConfig
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;}
}
7.定时器例程分析和编程实践
1.RCC_Configuration
TIM2接APB1,GPIOB接APB2
2.GPIO_Configuration
配置的是GPIOB
3.NVIC_Configuartion
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);
#endifNVIC_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);}
4.TIM2_IRQHandler
5.时钟编写
8.代码移植
1.先写定时器
如果不知道定时器的名字是什么可以去“startup_stm32f10x_hd.s”查看
再去“stm32f10x_it.c”中填写
2.RCC_Configuration
注意点:
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);
}
3.GPIO_Configuration
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);
}
4.NVIC_Configuration
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);
#endifNVIC_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);}
5.APB的分频问题
TIM2接APB1,GPIOB接APB2
我们APB1不能超过36MHZ,所以要对72MHZ进行分频
//72MHZ定时器的定义// 主频是72MHz,HCLK是72M,PCLK2是72M,PCLK1是36M// TIM2接在PCLK1下(APB1)所以源是36M,CK_CNT是36M/7200=5KHzTIM_TimeBaseStructure.TIM_Prescaler = 7199;//例如:时钟频率=72/(时钟预分频+1)TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;// 计数个数是10000,所以计数时间是10000*1/5000=2sTIM_TimeBaseStructure.TIM_Period = 9999;//自动重装载寄存器周期的值(定时时间)累计//0xffff 个频后产生个更新或者中断(也就是说定时时间到)
6.完整代码
main
#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=5KHzTIM_TimeBaseStructure.TIM_Prescaler = 7199;//例如:时钟频率=72/(时钟预分频+1)TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;// 计数个数是10000,所以计数时间是10000*1/5000=2s//注意点:我们计算出来的值是5,但是我们实际上是从0开始,所以我们这里要-1TIM_TimeBaseStructure.TIM_Period = 4;//自动重装载寄存器周期的值(定时时间)累计//0xffff 个频后产生个更新或者中断(也就是说定时时间到)TIM_TimeBaseStructure.TIM_ClockDivision = 0;//初始化定时器2TIM_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);
#endifNVIC_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);}
stm32f10x_it.c
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));}
}
9.问题解决:APB频率计算问题
cnt的计数公式
cnt=1s*频率值(HZ)
例如:
此时TIM2一定是分完频,结果为36MHZ
初始化:TIM_TimeBaseStructure.TIM_Prescaler = 7199;--》【最后要+1】
CK_CNT=36M/72 00=5KHZ
实际测试得到的是1KHZ的结果,说明前面分析有误
初始化:计数个数为1 0000个
所以:计数时间为:1 0000*1/5000=2s
结论:APB1时钟确实是36MHZ,但是APB1到定时器时钟那边中间被乘以2,所以又变成72MHZ了。