【STM32】定时器

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

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了。

相关推荐
美式小田2 小时前
单片机学习笔记 9. 8×8LED点阵屏
笔记·单片机·嵌入式硬件·学习
兰_博3 小时前
51单片机-独立按键与数码管联动
单片机·嵌入式硬件·51单片机
时光の尘3 小时前
C语言菜鸟入门·关键字·float以及double的用法
运维·服务器·c语言·开发语言·stm32·单片机·c
嵌入式大圣5 小时前
单片机结合OpenCV
单片机·嵌入式硬件·opencv
日晨难再6 小时前
嵌入式:STM32的启动(Startup)文件解析
stm32·单片机·嵌入式硬件
yufengxinpian7 小时前
集成了高性能ARM Cortex-M0+处理器的一款SimpleLink 2.4 GHz无线模块-RF-BM-2340B1
单片机·嵌入式硬件·音视频·智能硬件
__基本操作__8 小时前
历遍单片机下的IIC设备[ESP--0]
单片机·嵌入式硬件
网易独家音乐人Mike Zhou14 小时前
【卡尔曼滤波】数据预测Prediction观测器的理论推导及应用 C语言、Python实现(Kalman Filter)
c语言·python·单片机·物联网·算法·嵌入式·iot
zy张起灵14 小时前
48v72v-100v转12v 10A大功率转换电源方案CSM3100SK
经验分享·嵌入式硬件·硬件工程
PegasusYu16 小时前
STM32CUBEIDE FreeRTOS操作教程(九):eventgroup事件标志组
stm32·教程·rtos·stm32cubeide·free-rtos·eventgroup·时间标志组