这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » DIY与开源设计 » 电子DIY » Let'sDo第4期【液体流量检测仪】03过程贴:使用定时器1实现LED每秒闪烁

共2条 1/1 1 跳转至

Let'sDo第4期【液体流量检测仪】03过程贴:使用定时器1实现LED每秒闪烁一次效果

菜鸟
2025-01-02 14:41:21     打赏

定时器是STM32F系列的一个不可或缺的外部定时器,由于定时器的功能比较多,真正学会定时器的运用还是需要花费一定的时间、精力的。记得当初自己刚刚接触定时器的时候,学到32定时器的时候,对自己的打击非常大,当时都想要放弃了,主要是这一部分的知识复杂太多了,不过现在我们使用STM32cube来学习32架构,比之前要方便很多了,之前的基本的使用标准库学习,出现了问题也不知道该如何解决,这里和大家分享一下,定时器的学习经历。

这次先和大家分享STM32定时器的原理、寄存器介绍、定时器配置,定时器的几个常用的功能(如定时器中断、定时器输出比较PWM波形、定时器输入捕获、定时器编码器模式应用等)在以后得章节中和大家分享。

一:STM32F103RB定时器基本介绍

高级控制定时器(TIM1)由一个16位的自动装载计数器组成,它由一个可编程的预分频器驱动。

它适合多种用途,包含测量输入信号的脉冲宽度(输入捕获),或者产生输出波形(输出比较PWM、嵌入死区时间的互补PWM等)。

使用定时器预分频器和RCC时钟控制预分频器,可以实现脉冲宽度和波形周期从几个微秒到几个毫秒的调节。

高级控制定时器(TIM1和TIM8)和通用定时器(TIMx)是完全独立的,它们不共享任何资源。它们可以同步操作。

高级定时器的基本功能如下所示:

16位向上、向下、向上/下自动装载计数器

16位可编程(可以实时修改)预分频器,计数器时钟频率的分频系数为1~65535之间的任意数值

多达4个独立通道:输入捕获,输出比较,PWM生成(边缘或中间对齐模式)单脉冲模式输出,死区时间可编程的互补输出

使用外部信号控制定时器和定时器互联的同步电路

允许在指定数目的计数器周期之后更新定时器寄存器的重复计数器

刹车输入信号可以将定时器输出信号置于复位状态或者一个已知状态如下事件发生时产生中断/DMA:更新:计数器向上溢出/向下溢出,计数器初始化(通过软件或者内部/外部触发)触发事件(计数器启动、停止、初始化或者由内部/外部触发计数);输入捕获;输出比较;刹车信号输入

支持针对定位的增量(正交)编码器和霍尔传感器电路触发输入作为外部时钟或者按周期的电流管理

二:定时器1的功能描述:

时基单元:可编程高级控制定时器的主要部分是一个16位计数器和与其相关的自动装载寄存器。这个计数器可以向上计数、向下计数或者向上向下双向计数。此计数器时钟由预分频器分频得到。计数器、自动装载寄存器和预分频器寄存器可以由软件读写,即使计数器还在运行读写仍然有效。

自动装载寄存器是预先装载的,写或读自动重装载寄存器将访问预装载寄存器。根据在TIMX CR1寄存器中的自动装载预装载使能位(ARPE)的设置,预装载寄存器的内容被立即或在每次的更新事件UEV时传送到影子寄存器。当计数器达到溢出条件(向下计数时的下溢条件)并当TIMX CR1寄存器中的UDIS位等于0时,产生更新事件。更新事件也可以由软件产生。随后会详细描述每一种配置下更新事件的产生。

计数器由预分频器的时钟输出CK CNT驱动,仅当设置了计数器TIMX CR1寄存器中的计数器使能位(CEN)时,CK CNT才有效。(更多有关使能计数器的细节,请参见控制器的从模式描述)。注意,在设置了TIMX CR寄存器的CEN位的一个时钟周期后,计数器开始计数。预分频器描述预分频器可以将计数器的时钟频率按1到65536之间的任意值分频。它是基于一个(在TIMX PSC寄存器中的)16位寄存器控制的16位计数器。因为这个控制寄存器带有缓冲器,它能够在运行时被改变。新的预分频器的参数在下一次更新事件到来时被采用。

这里我们使用定时器的计数器模式,来实现LED灯的翻转;

定时器1计数器模式分为:向上计数模式、向下计数模式、中央对其模式;这里我使用向上计数模式来实现功能;

①向上计数模式:计数器从0计数到自动加载值(TIMx_ARR),然后重新从0开始计数并且产生一个计数器溢出事件。

②向下计数模式:计数器从自动装入的值(TIMx_ARR)开始向下计数到0,然后从自动装入的值重新开始,并产生一个计数器向下溢出事件。

③中央对齐模式(向上/向下计数):计数器从0开始计数到自动装入的值-1,产生一个计数器溢出事件,然后向下计数到1并且产生一个计数器溢出事件;然后再从0开始重新计数。

向上计数模式

在向上计数模式中,计数器从0计数到自动加载值(TIMX ARR计数器的内容),然后重新从0开始计数并且产生一个计数器溢出事件。

如果使用了重复计数器功能,在向上计数达到设置的重复计数次数(TIMX RCR)时,产生更新事件(UEV);否则每次计数器溢出时才产生更新事件。在TIMX EGR寄存器中(通过软件方式或者使用从模式控制器)设置UG位也同样可以产生一个更新事件。

设置TIMx CR1寄存器中的UDIS位,可以禁止更新事件;这样可以避免在向预装载寄存器中写入新值时更新影子寄存器。在UDIS位被清’0之前,将不产生更新事件。但是在应该产生更新事件时,计数器仍会被清0,同时预分频器的计数也被请0(但预分频器的数值不变)。此外,如果设置了TIMx CR1寄存器中的URS位(选择更新请求),设置UG位将产生一个更新事件UEV,但硬件不设置UIF标志(即不产生中断或DMA请求)。这是为了避免在捕获模式下清除计数器时,同时产生更新和捕获中断。

当发生一个更新事件时,所有的寄存器都被更新,硬件同时(依据URS位)设置更新标志位(TIMX SR寄存器中的UIF位)。

重复计数器被重新加载为TIMX RCR寄存器的内容。

自动装载影子寄存器被重新置入预装载寄存器的值(TIMX ARR),预分频器的缓冲区被置入预装载寄存器的值(TIMXPSC寄存器的内容)。

1.png

三:STM32cube MX软件配置如下:

3.1 配置定时器1的基本参数

2.png

3.2 使能定时器1的中断

3.png

3.3 系统时钟的配置,这里为了方便直接使用的默认时钟,系统时钟如下所示:

4.png

四:软件编码步骤如下:

1:使能定时器时钟。

2:初始化定时器,配置ARR,PSC。

3:开启定时器中断,配置NVIC。

4:使能定时器。

5: 编写中断服务函数。

五:软件编写:

5.1 定时器1 初始化:

/**
  * @brief TIM1 Initialization Function
  * @param None
  * @retval None
  */
static void MX_TIM1_Init(void)
{

  /* USER CODE BEGIN TIM1_Init 0 */

  /* USER CODE END TIM1_Init 0 */

  TIM_SlaveConfigTypeDef sSlaveConfig = {0};
  TIM_MasterConfigTypeDef sMasterConfig = {0};

  /* USER CODE BEGIN TIM1_Init 1 */

  /* USER CODE END TIM1_Init 1 */
  htim1.Instance = TIM1;
  htim1.Init.Prescaler = 799;
  htim1.Init.CounterMode = TIM_COUNTERMODE_UP;
  htim1.Init.Period = 9999;
  htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  htim1.Init.RepetitionCounter = 0;
  htim1.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
  if (HAL_TIM_Base_Init(&htim1) != HAL_OK)
  {
    Error_Handler();
  }
  sSlaveConfig.SlaveMode = TIM_SLAVEMODE_DISABLE;
  sSlaveConfig.InputTrigger = TIM_TS_ITR0;
  if (HAL_TIM_SlaveConfigSynchro(&htim1, &sSlaveConfig) != HAL_OK)
  {
    Error_Handler();
  }
  sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
  if (HAL_TIMEx_MasterConfigSynchronization(&htim1, &sMasterConfig) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN TIM1_Init 2 */
HAL_TIM_Base_Start_IT(&htim1); 
  /* USER CODE END TIM1_Init 2 */
}

以上是软件自动生成底层驱动代码,由于软件没有开启定时器的开始计数的功能,需要我们自行添加开启代码,如下所示:

  /* USER CODE BEGIN TIM1_Init 2 */
HAL_TIM_Base_Start_IT(&htim1); 
  /* USER CODE END TIM1_Init 2 */

在定时器初始话完成之后,添加开启定时器中断功能。在定时器回调函数中,添加LED灯闪烁的功能。代码如下:

/**
  * @brief  Period elapsed callback in non-blocking mode
  * @param  htim TIM handle
  * @retval None
  */
__weak void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
  /* Prevent unused argument(s) compilation warning */
  UNUSED(htim);

  /* NOTE : This function should not be modified, when the callback is needed,
            the HAL_TIM_PeriodElapsedCallback could be implemented in the user file
   */
}

上述函数为定时器的回调函数,为弱定义函数,我们可以将该函数拷贝出来,这里的函数在编译的时候就不会在编译了。

Tout(中断触发时间)=(ARR+1)(PSC+1)/定时器的时钟频率

/* USER CODE BEGIN 4 */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)  
{  
  /* USER CODE BEGIN Callback 0 */  
  
  /* USER CODE END Callback 0 */  
  
  /* USER CODE BEGIN Callback 1 */  
  if (htim->Instance == TIM1) {   
		HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5);
			
  }  
  /* USER CODE END Callback 1 */  
}

在软件工程中,添加LED灯的闪烁功能;

实物验证效果如下所示:

WeChat_20250102143454 00_00_00-00_00_30.gif

用示波器抓下,PA5脚的电平变化,可以看到电平每秒改变一次。

微信图片_20250102143731.jpg

至此,使用定时器完成LED每秒的闪烁的任务完成,测试代码如下:

02Time_LED.zip




关键词: 流量     检测仪     STM32     定时器1     LED闪烁    

专家
2025-01-02 16:38:49     打赏
2楼

谢谢分享


共2条 1/1 1 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]