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

相关推荐
Wallace Zhang10 分钟前
STM32F103_Bootloader程序开发11 - 实现 App 安全跳转至 Bootloader
stm32·嵌入式硬件·安全
GodKK老神灭14 分钟前
STM32 CCR寄存器
stm32·单片机·嵌入式硬件
杰克逊的日记9 天前
MCU编程
单片机·嵌入式硬件
Python小老六9 天前
单片机测ntc热敏电阻的几种方法(软件)
数据库·单片机·嵌入式硬件
懒惰的bit9 天前
STM32F103C8T6 学习笔记摘要(四)
笔记·stm32·学习
HX科技9 天前
STM32给FPGA的外挂FLASH进行升级
stm32·嵌入式硬件·fpga开发·flash·fpga升级
Suagrhaha9 天前
驱动入门的进一步深入
linux·嵌入式硬件·驱动
国科安芯10 天前
基于ASP4644多通道降压技术在电力监测系统中集成应用与发展前景
嵌入式硬件·硬件架构·硬件工程
Li Zi10 天前
STM32 ADC(DMA)双缓冲采集+串口USART(DMA)直接传输12位原始数据到上位机显示并保存WAV格式音频文件 收藏住绝对实用!!!
经验分享·stm32·单片机·嵌入式硬件
进击的程序汪10 天前
触摸屏(典型 I2C + Input 子系统设备)从设备树解析到触摸事件上报
linux·网络·嵌入式硬件