一、定时器简介

STM32F1 系列中,除了互联型的产品,共有 8 个定时器,分为基本定时器,通用定时器和高级定时器。
基本定时器 TIM6TIM7 是一个 16 位的只能向上计数的定时器,只能定时,没有外部 IO。
通用定时器 TIM2/3/4/5 是一个 16 位的可以向上/下计数的定时器,可以定时,可以输出比较,可以输入捕捉,每个定时器有四个外部 IO。
高级定时器 TIM1/8 是一个 16 位的可以向上/下计数的定时器,可以定时,可以输出比较,可以输入捕捉,还可以有三相电机互补输出信号,每个定时器有 8 个外部 IO。

二、新建工程

1. 打开 STM32CubeMX 软件,点击“新建工程”

2. 选择 MCU 和封装

3. 配置时钟
RCC 设置,选择 HSE(外部高速时钟) 为 Crystal/Ceramic Resonator(晶振/陶瓷谐振器)

选择 Clock Configuration,配置系统时钟 SYSCLK 为 72MHz
修改 HCLK 的值为 72 后,输入回车,软件会自动修改所有配置

4. 配置调试模式
非常重要的一步,否则会造成第一次烧录程序后续无法识别调试器
SYS 设置,选择 Debug 为 Serial Wire

5. 配置GPIO
GPIO 设置,在右边图中找到 LED 灯对应引脚,选择 GPIO_Output,输出低电平点亮,可以添加自定义标签

三、TIM6基本定时器

3.1 参数配置

Timers 中选择 TIM6 设置,并勾选 Activated 激活

Parameter Settings 进行具体参数配置。

Tclk 即内部时钟CK_INT,经过APB1预分频器后分频提供,如果APB1预分频系数等于1,则频率不变,否则频率乘以2,库函数中APB1预分频的系数是2,即PCLK1=36M,如图所以定时器时钟Tclk=36*2=72M。

定时器溢出时间:

Tout = 1 / (Tclk / (psc + 1)) ∗ (arr + 1)

  • 定时器时钟Tclk:72MHz
  • 预分频器psc:71
  • 自动重装载寄存器arr:999

即 Tout = 1/(72MHz/(71+1))∗(999+1) = 1ms

  • Prescaler(时钟预分频数):72-1 则驱动计数器的时钟 CK_CNT = CK_INT(即72MHz)/(71+1) = 1MHz
  • Counter Mode(计数模式):Up(向上计数模式) 基本定时器只能是向上计数
  • Counter Period(自动重装载值):1000-1 则定时时间 1/CK_CLK*(999+1) = 1ms
  • auto-reload-preload(自动重装载):Enable(使能)
  • TRGO Parameters(触发输出):不使能 在定时器的定时时间到达的时候输出一个信号(如:定时器更新产生TRGO信号来触发ADC的同步转换)

3.2 配置NVIC

使能定时器中断

3.3 生成代码

输入项目名和项目路径

选择应用的 IDE 开发环境 MDK-ARM V5

每个外设生成独立的 ’.c/.h’ 文件
不勾:所有初始化代码都生成在 main.c
勾选:初始化代码生成在对应的外设文件。 如 GPIO 初始化代码生成在 gpio.c 中。
点击 GENERATE CODE 生成代码

3.4 修改中断回调函数

打开 stm32f1xx_it.c 中断服务函数文件,找到 TIM6 中断的服务函数 TIM6_IRQHandler()
中断服务函数里面就调用了定时器中断处理函数 HAL_TIM_IRQHandler()

打开 stm32f1xx_hal_tim.c 文件,找到定时器中断处理函数原型 HAL_TIM_IRQHandler(),其主要作用就是判断是哪个定时器产生哪种事件中断,清除中断标识位,然后调用中断回调函数 HAL_TIM_PeriodElapsedCallback()

/* NOTE: This function Should not be modified, when the callback is needed,
the HAL_GPIO_EXTI_Callback could be implemented in the user file
*/
这个函数不应该被改变,如果需要使用回调函数,请重新在用户文件中实现该函数。

HAL_TIM_PeriodElapsedCallback() 按照官方提示我们应该再次定义该函数,__weak 是一个弱化标识,带有这个的函数就是一个弱化函数,就是你可以在其他地方写一个名称和参数都一模一样的函数,编译器就会忽略这一个函数,而去执行你写的那个函数;而 UNUSED(htim) ,这就是一个防报错的定义,当传进来的定时器号没有做任何处理的时候,编译器也不会报出警告。其实我们在开发的时候已经不需要去理会中断服务函数了,只需要找到这个中断回调函数并将其重写即可而这个回调函数还有一点非常便利的地方这里没有体现出来,就是当同时有多个中断使能的时候,STM32CubeMX会自动地将几个中断的服务函数规整到一起并调用一个回调函数,也就是无论几个中断,我们只需要重写一个回调函并判断传进来的定时器号即可。

接下来我们就在 stm32f1xx_it.c 这个文件的最下面添加 HAL_TIM_PeriodElapsedCallback()

/* USER CODE BEGIN 1 */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{static uint32_t time = 0;if(htim->Instance == TIM6)  // 定时器6基地址{// 自定义应用程序time++;           // 每1ms进来1次if(time == 1000)  // 每1秒LED灯翻转一次{HAL_GPIO_TogglePin(LED_G_GPIO_Port,LED_G_Pin);time = 0;}}
}
/* USER CODE END 1 */

3.5 添加定时器启动函数

现在进入 main 函数并在 while 循环前加入开启定时器函数 HAL_TIM_Base_Start_IT(),这里所传入的 htim6 就是刚刚定时器初始化后的结构体。

/*** @brief  The application entry point.* @retval int*/
int main(void)
{/* USER CODE BEGIN 1 *//* USER CODE END 1 *//* MCU Configuration--------------------------------------------------------*//* Reset of all peripherals, Initializes the Flash interface and the Systick. */HAL_Init();/* USER CODE BEGIN Init *//* USER CODE END Init *//* Configure the system clock */SystemClock_Config();/* USER CODE BEGIN SysInit *//* USER CODE END SysInit *//* Initialize all configured peripherals */MX_GPIO_Init();MX_TIM6_Init();/* USER CODE BEGIN 2 */HAL_TIM_Base_Start_IT(&htim6);  /* USER CODE END 2 *//* Infinite loop *//* USER CODE BEGIN WHILE */while (1){/* USER CODE END WHILE *//* USER CODE BEGIN 3 */}/* USER CODE END 3 */
}

现在实验现象是每1秒LED灯翻转一次

3.6 HAL库与标准库代码比较

STM32CubeMX 使用 HAL 库生成的代码:

/*** @brief TIM6 Initialization Function* @param None* @retval None*/
static void MX_TIM6_Init(void)
{/* USER CODE BEGIN TIM6_Init 0 *//* USER CODE END TIM6_Init 0 */TIM_MasterConfigTypeDef sMasterConfig = {0};/* USER CODE BEGIN TIM6_Init 1 *//* USER CODE END TIM6_Init 1 */htim6.Instance = TIM6;htim6.Init.Prescaler = 72-1;htim6.Init.CounterMode = TIM_COUNTERMODE_UP;htim6.Init.Period = 1000-1;htim6.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;if (HAL_TIM_Base_Init(&htim6) != HAL_OK){Error_Handler();}sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;if (HAL_TIMEx_MasterConfigSynchronization(&htim6, &sMasterConfig) != HAL_OK){Error_Handler();}/* USER CODE BEGIN TIM6_Init 2 *//* USER CODE END TIM6_Init 2 */
}/*** @brief This function handles TIM6 global interrupt.*/
void TIM6_IRQHandler(void)
{/* USER CODE BEGIN TIM6_IRQn 0 *//* USER CODE END TIM6_IRQn 0 */HAL_TIM_IRQHandler(&htim6);/* USER CODE BEGIN TIM6_IRQn 1 *//* USER CODE END TIM6_IRQn 1 */
}void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{static uint32_t time = 0;if(htim->Instance == TIM6){// 自定义应用程序time++;if(time == 1000){HAL_GPIO_TogglePin(LED_G_GPIO_Port,LED_G_Pin);time = 0;}}
}HAL_TIM_Base_Start_IT(&htim6);

使用 STM32 标准库的代码:

/**@brief 定时器中断配置(使用TIM6基本定时器)@param 无@return 无
*/
void BASIC_TIM_Config(void)
{TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;/*可以从上图看出基本定时器和通用定时器使用APB1总线,高级定时器使用APB2总线。*/// 开启定时器时钟,即内部时钟 CK_INT=72MRCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6, ENABLE);/*预分频将输入时钟频率按1~65536之间的值任意分频,分频值决定了计数频率。计数值为计数的个数,当计数寄存器的值达到计数值时,产生溢出,发生中断。如系统时钟为72MHz,预分频 TIM_Prescaler = 71,计数值 TIM_Period = 1000,则 TIM_Period * (TIM_Prescaler + 1) / 72000000 = 0.001,即每1ms产生一次中断。*/// 自动重装载寄存器周的值(计数值)TIM_TimeBaseStructure.TIM_Period = 1000;// 累计 TIM_Period 个频率后产生一个更新或者中断// 时钟预分频数为 71,// 则驱动计数器的时钟 CK_CNT = CK_INT / (71+1)=1MTIM_TimeBaseStructure.TIM_Prescaler = 71;// 时钟分频因子 ,基本定时器没有,不用管//TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1;// 计数器计数模式,基本定时器只能向上计数,没有计数模式的设置//TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up;// 重复计数器的值,基本定时器没有,不用管//TIM_TimeBaseStructure.TIM_RepetitionCounter=0;/*完成时基设置*/// 初始化定时器TIM_TimeBaseInit(TIM6, &TIM_TimeBaseStructure);/*为了避免在设置时进入中断,这里需要清除中断标志位。如果是向上计数模式(基本定时器采用向上计数),则采用函数 TIM_ClearFlag(TIM6, TIM_FLAG_Update),清除向上溢出中断标志。*/// 清除计数器中断标志位TIM_ClearFlag(TIM6, TIM_FLAG_Update);// 使能计数器TIM_ITConfig(TIM6,TIM_IT_Update,ENABLE);// 开启计数器TIM_Cmd(TIM6, ENABLE);// 暂时关闭定时器的时钟,等待使用RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6, DISABLE);
}/**@brief NVIC初始化(使用TIM6基本定时器)@param 无@return 无
*/
void BASIC_TIM_NVIC_Config(void)
{NVIC_InitTypeDef NVIC_InitStructure;// 设置中断组为 0NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);// 设置中断来源NVIC_InitStructure.NVIC_IRQChannel = TIM6_IRQn ;// 设置主优先级为 0NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;// 设置抢占优先级为 3NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;NVIC_Init(&NVIC_InitStructure);
}#define BASIC_TIM_IRQHandler TIM6_IRQHandler
// 1ms发生一次中断,time 记录中断次数
uint16_t time;void BASIC_TIM_IRQHandler(void)
{if(TIM_GetITStatus(TIM6, TIM_IT_Update) != RESET){time++;TIM_ClearITPendingBit(TIM6, TIM_FLAG_Update);}
}RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6, ENABLE);

MX_TIM6_Init(); 对应 BASIC_TIM_Config();BASIC_TIM_NVIC_Config();
HAL_TIM_Base_Init(&htim6) 对应 TIM_TimeBaseInit(TIM6, &TIM_TimeBaseStructure)
HAL_TIM_Base_Start_IT(&htim6); 对应 RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6, ENABLE);

四、注意事项

用户代码要加在 USER CODE BEGIN NUSER CODE END N 之间,否则下次使用 STM32CubeMX 重新生成代码后,会被删除。


• 由 Leung 写于 2021 年 1 月 14 日

• 参考:STM32CubeMX系列教程3:基本定时器
    STM32CubeMX实战教程(四)——基本定时器(还是点灯)
    《嵌入式-STM32开发指南》第二部分 基础篇 - 第4章 定时器(HAL库)

STM32CubeMX学习笔记(5)——基本定时器接口使用相关推荐

  1. STM32CubeMX学习笔记(24)——通用定时器接口使用(电容按键检测)

    一.电容按键简介 电容器(简称为电容)就是可以容纳电荷的器件,两个金属块中间隔一层绝缘体就可以构成一个最简单的电容.如图 32-1(俯视图),有两个金属片,之间有一个绝缘介质,这样就构成了一个电容.这 ...

  2. STM32CubeMX学习笔记(38)——FSMC接口使用(TFT-LCD屏显示)

    一.TFT-LCD简介 TFT-LCD(Thin Film Transistor-Liquid Crystal Display) 即薄膜晶体管液晶显示器.TFT-LCD 与无源 TN-LCD. STN ...

  3. STM32CubeMX学习笔记(9)——I2C接口使用(读写EEPROM AT24C02)

    一.I2C简介 I2C(Inter-Integrated Circuit ,内部集成电路) 总线是一种由飞利浦 Philip 公司开发的串行总线.是两条串行的总线,它由一根数据线(SDA)和一根 时钟 ...

  4. STM32CubeMX学习笔记(22)——CRC接口使用

    一.CRC简介 CRC(Cyclic Redundancy Check),即循环冗余校验,是一种根据网络数据包或计算机文件等数据产生简短固定位数校验码的一种信道编码技术,主要用来检测或校验数据传输或者 ...

  5. STM32CubeMX学习笔记(15)——电源管理(PWR)低功耗睡眠模式

    一.低功耗模式简介 系统提供了多个低功耗模式,可在 CPU 不需要运行时(例如等待外部事件时)节省功耗.由用户根据应用选择具体的低功耗模式,以在低功耗.短启动时间和可用唤醒源之间寻求最佳平衡. 睡眠模 ...

  6. STM32CubeMX学习笔记(28)——FreeRTOS实时操作系统使用(任务管理)

    一.FreeRTOS简介 FreeRTOS 是一个可裁剪.可剥夺型的多任务内核,而且没有任务数限制.FreeRTOS 提供了实时操作系统所需的所有功能,包括资源管理.同步.任务通信等. FreeRTO ...

  7. STM32CubeMX学习笔记(25)——FatFs文件系统使用(操作SPI Flash)

    一.FatFs简介 FatFs 是面向小型嵌入式系统的一种通用的 FAT 文件系统.它完全是由 ANSI C 语言编写并且完全独立于底层的 I/O 介质.因此它可以很容易地不加修改地移植到其他的处理器 ...

  8. STM32CubeMX学习笔记(16)——电源管理(PWR)低功耗停止模式

    一.低功耗模式简介 系统提供了多个低功耗模式,可在 CPU 不需要运行时(例如等待外部事件时)节省功耗.由用户根据应用选择具体的低功耗模式,以在低功耗.短启动时间和可用唤醒源之间寻求最佳平衡. 睡眠模 ...

  9. STM32CubeMX学习笔记(27)——FatFs文件系统使用(操作SD卡)

    一.FatFs简介 FatFs 是面向小型嵌入式系统的一种通用的 FAT 文件系统.它完全是由 ANSI C 语言编写并且完全独立于底层的 I/O 介质.因此它可以很容易地不加修改地移植到其他的处理器 ...

  10. STM32CubeMX学习笔记——STM32H743_硬件I2C

    STM32CubeMX学习笔记--STM32H743_硬件I2C Github STM32CubeMX配置 Pinout配置 GPIO Clock Configuration配置 代码部分 main. ...

最新文章

  1. 在vscode中统一vue编码风格的方法
  2. 页面滑动至某处,固定导航。
  3. 转:ASP自动解压RAR文件
  4. Java如何读取JAR包外的properties文件及打成jar包后无法读取到jar包内的properties文件
  5. 剑指Offer #09 变态跳台阶(数列推导)
  6. ubuntu21.04截图快捷键
  7. 通过AVFoundation框架获取摄像头数据
  8. leetcode python3 简单题118. Pascal's Triangle
  9. 那是计算机房吗不它不是 英语,人教PEP版英语四年级下册Unit 1《My School》单元测试卷及答案.doc...
  10. backtrack5渗透 笔记
  11. Java RMI远程方法调用学习总结
  12. 富士康对夏普收购报价大幅缩水20多亿美元
  13. Citrix发布支持Framehawk技术的HDX协议,用户体验优势进一步扩大
  14. golang 将数据导入excel
  15. 常用的6个跨品种套利组合
  16. AM335X添加声卡驱动max98357a记录_基于TI官方PSDK
  17. java kryo_在java中使用kryo框架来实现高效序列化与反序列化 | 学步园
  18. Synchronized Lock 锁 同步
  19. NOI2015 Day1 T2 软件包管理器 树链剖分
  20. Spring Boot 3.0.0正式发布,Banner不再支持图片增强可观测性

热门文章

  1. 记一次js逆向详细过程
  2. 理解jquery的$.extend()、$.fn和$.fn.extend()
  3. 红米note7找android,红米Note 7
  4. 将两个有序列表合并,并保持顺序(一)
  5. 《图解密码技术》笔记5:公钥密码-用公钥加密,用私钥解密
  6. 无人机倾斜影像建模哪个软件好
  7. python数据提取和合并_用Python提取和合并Excel数据
  8. 如何限流,具体实现有哪些?
  9. Postgresql数组操作符及数组函数
  10. 《余世维 - 有效沟通》讲义zz