systick定时器:
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
cpp
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;
}
2.TIM_ITConfig
cpp
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
cpp
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);
}
4.TIM2_IRQHandler
5.时钟编写
8.代码移植
1.先写定时器
如果不知道定时器的名字是什么可以去**"startup_stm32f10x_hd.s**"查看
再去"stm32f10x_it.c"中填写
2.RCC_Configuration
注意点:
TIM2和GPIOB所对应的APB对应不一样,所以一定要分开声明,要不然会出现错误。
TIM2---》APB2
GPIOB--》APB1
cpp
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
cpp
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
cpp
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);
}
5.APB的分频问题
TIM2接APB1,GPIOB接APB2
我们APB1不能超过36MHZ,所以要对72MHZ进行分频
cpp
//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 个频后产生个更新或者中断(也就是说定时时间到)
6.完整代码
main
cpp
#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);
}
stm32f10x_it.c
cpp
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了。