夜深人静学32系列14——基本定时器

定时器简介

关于定时器的基本内容,这里不做介绍,不明白的可以观看 “ 蓝桥杯单片机学习6——定时器/计数器&定时器实现秒表功能 ”,这里我们主要来介绍一下STM32的定时器。

在这里插入图片描述
根据功能的不同,STM32讲定时器分为常规定时器、专业定时器和内核定时器。
前面我们学习了专用定时器中的独立看门狗和窗口看门狗,现在我们主要学习常规定时器。

根据定时器功能的复杂程度,将常规定时器分类为基本定时器、通用定时器和高级定时器。高级的定时器拥有低级定时器的所有功能。

我们使用的NANO板上一共有八个定时器,分别为:

定时器分类
TIM1高级定时器
TIM2 ~ 5通用定时器
TIM9 ~ 11通用定时器

TIM2 ~ 5 和 TIM9 ~ 11之间存在一定的差异,所以我们将其分开来

在这里插入图片描述
遗憾的的是,我们所用的NANO板子上没有纯粹的基本定时器,只有通用定时器和高级定时器。

基本定时器(我们的NANO板上没有)

基本定时器 TIM6 和 TIM7 包含一个 16 位自动重载计数器,该计数器由可编程预分频器驱动。
此类定时器不仅可用作通用定时器以生成时基,还可以专门用于驱动数模转换器 (DAC)。实 际上,此类定时器内部连接到 DAC 并能够通过其触发输出驱动 DAC。(与51单片机的定时器相似)
这些定时器彼此完全独立,不共享任何资源。
在这里插入图片描述

功能框图

在这里插入图片描述
基本定时器主要由三个部分组成:

  • 第一个部分是时钟源根据定时器挂载在的总线而决定。
  • 第二部分为控制器,可以控制定时器的复位、使能(中断使能、定时器使能)、计数、触发等控制位。
  • 第三部分是定时器的计数单元,由预分频器、自动重装载寄存器和计数器玩部分完成。当计数器的值等于自动重装载寄存器影子寄存器的值时溢出,可选择触发中断、时间等一系列动作。

其计数原理如下:
在这里插入图片描述
总线时钟通过一个预分频器分频后作为计数器的时钟信号,计数器每经过一个脉冲加一/减一,当计数器的值等于自动重装载寄存器影子寄存器的值时,=溢出,产生中断/事件。

通过认为的配置预分频器和自动重装载寄存器的值,可以实现对给定时间的定时。

【注意】:
我们前面讲的自动重装载寄存器和预分频器实际上都有两个相同的寄存器,我们在进行操作的是一个寄存器,而实际起作用的是另一个寄存器,叫做影子寄存器。在每次溢出时,系统会自动将寄存器的值加载到影子寄存器之中,或者操作后直接加载到影子寄存器,这个由定时器的控制寄存器(TIMX_CR1)的ARPE位决定。

计数模式

在这里插入图片描述
STM32的定时器具有以上三种计数模式,在不同的触发条件会产生溢出。其中,递增计数模式最为常用。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
其中,ARR为自动重装载值,PSC为预分频寄存器的值。

定时器寄存器

控制寄存器1

在这里插入图片描述

中断使能寄存器

在这里插入图片描述

状态寄存器

在这里插入图片描述

计数器和预分频器

在这里插入图片描述

自动重装载寄存器

在这里插入图片描述

定时器溢出时间的计算

在这里插入图片描述
举个栗子:
我们使用NANO板子的定时器10/定时器11,APB2总线频率为100MHz,每1s溢出一次,则可设置ARR = 10000-1,PSC = 10000-1

中断配置步骤

在这里插入图片描述

相关HAL库函数介绍

在这里插入图片描述

在这里插入图片描述

基本定时器实战(这个可以有)

任务要求

  • 通过定时器11,以中断的方式控制LED0的闪烁,每1s改变一次LED状态
  • 开启独立看门狗,设置喂狗时间为1.25s以内,通过定时器10对独立看门狗每1s喂狗
  • 通过串口打印是否喂狗,
  • 系统LED1亮起,表示系统正常运行。
  • 每串口打印一次,改变LED1的状态。

CubeMX配置

TIM11(TIM10相同)
在这里插入图片描述

代码实现

mian.c

uint8_t IWDG_Flag;	//喂狗标志位

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_TIM11_Init();
  MX_IWDG_Init();
  MX_TIM10_Init();
  MX_USART1_UART_Init();
  /* USER CODE BEGIN 2 */
  HAL_TIM_Base_Start_IT(&htim10);	//开启定时10中断
  HAL_TIM_Base_Start_IT(&htim11);	//开启定时器11中断
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
     if(IWDG_Flag)		//喂狗完成后,串口打印,翻转LED2状态
     {
         printf("USART1:喂狗成功\r\n");
         HAL_GPIO_TogglePin(LED2_GPIO_Port,LED2_Pin);
          IWDG_Flag = 0;		//清除喂狗标志
     }         
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

tim.c
在这里插入图片描述
其他部分逻辑,前面已经介绍过了,这里不做展示。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
树形动态规划(Tree DP)是一种解决树状结构问题的算法思想。它利用了树这种特殊的数据结构的性质进行求解,常用来解决树的最优路径、最大值、最小值等类型的问题。 在夜深人静的时候写算法,我通常会采用以下步骤来完成树形dp的实现: 第一步是定义状态。我们首先需要确定问题的状态表示方式。对于树形dp来说,常用的状态表示方式是以节点为单位进行表示。我们可以定义dp[i]表示以节点i为根的子树的某种性质,比如最大路径和、最长路径长度等。 第二步是确定状态转移方程。根据问题的特点,我们需要找到状态之间的关系,从而确定状态转移方程。在树形dp中,转移方程常常与节点的子节点相关联。我们可以通过遍历节点的子节点,利用它们的状态来更新当前节点的状态,从而得到新的状态。 第三步是确定初始条件。在动态规划中,我们需要确定初始状态的值。对于树形dp来说,我们可以选择将叶节点作为初始状态,然后逐步向上更新,最终得到整棵树的最优解。 第四步是确定计算顺序。树形dp的计算通常是从根节点开始,自顶向下逐步计算,直到达到叶节点。因为树形dp的计算过程中需要利用到子节点的状态来更新当前节点的状态,所以必须按照计算顺序进行。 夜深人静时,写算法树形dp是相对较复杂的算法,需要仔细思考问题的状态表示方式,转移方程以及初始条件。在实现过程中,可以采用递归的方式进行代码编写,或者利用栈等数据结构进行迭代实现。 总的来说,夜深人静写算法树形dp需要耐心和细心,经过思考和实践,才能顺利解决树状结构问题。但是,一旦理解并掌握了树形dp的思想和方法,就能够高效地解决各种树形结构问题,提升算法的效率和准确性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不想写代码的我

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值