Timer实验3:定时器输出比较PWM:呼吸灯(CuBeMX版)+PWM相关函数解析

1、实验目的

结合定时器的输出比较功能,使其产生PWM波,控制两个小灯呈现为呼吸状态
有关定时器理论部分,详见【STM32学习笔记】Timer总结:从模式控制器、时基单元、输入捕获、输出比较

2、配置CuBeMX

1)选择MCU

使用STM32F407ZGT6

image-20250706162005447

2)配置时钟和调试接口

image-20250706162104454

image-20250706162129306

image-20250706170550927

3)配置定时器

通过查看该单片机的电路原理图,其两个LED灯连接方式如下:

image-20250706170140305

LED0对应PF9管脚

LED1对应PF10管脚

查看数据手册可得,PF9可对应TIM14_CH1,所以该实验选择定时器14,定时周期还设置为1ms,同时定时器14挂载在APB1上

image-20250706170837968

4)配置串口

配置串口,输出捕获/比较寄存器(CCR)的值

image-20250706232745401

串口的详细配置,详见:
【STM32学习笔记】串口总结:串口轮询模式、串口中断模式、串口DMA模式以及串口接收不定长数据

5)配置路径生成代码

image-20250706171229530

image-20250706171423905

3、PWM步骤

1)开启 TIM14 和 GPIO 时钟,配置 PF9 选择复用功能 AF9(TIM14)输出。

2)初始化 TIM14,设置 TIM14 的 ARR 和 PSC 等参数。

3)设置 TIM14_CH1 的 PWM 模式,使能 TIM14 的 CH1 输出。

HAL_StatusTypeDef HAL_TIM_PWM_ConfigChannel(TIM_HandleTypeDef *htim,
                                            TIM_OC_InitTypeDef *sConfig,
                                            uint32_t Channel)

4)使能 TIM14。

HAL_StatusTypeDef HAL_TIM_PWM_Start(TIM_HandleTypeDef *htim, uint32_t Channel);

5)修改 TIM14_CCR1 来控制占空比。

4、相关语句分析

1)开启PWM

/**
  * @brief  Starts the PWM signal generation.
  * @param  htim TIM handle
  * @param  Channel TIM Channels to be enabled
  *          This parameter can be one of the following values:
  *            @arg TIM_CHANNEL_1: TIM Channel 1 selected
  *            @arg TIM_CHANNEL_2: TIM Channel 2 selected
  *            @arg TIM_CHANNEL_3: TIM Channel 3 selected
  *            @arg TIM_CHANNEL_4: TIM Channel 4 selected
  * @retval HAL status
  */
HAL_StatusTypeDef HAL_TIM_PWM_Start(TIM_HandleTypeDef *htim, uint32_t Channel)

该函数输入参数上面已作解释

在该函数里主要包括了使能捕获/比较通道、使能定时器,如下图所示:

image-20250706175850164

展开使能捕获/比较通道函数TIM_CCxChannelCmd为:

/**
  * @brief  Enables or disables the TIM Capture Compare Channel x.
  * @param  TIMx to select the TIM peripheral
  * @param  Channel specifies the TIM Channel
  *          This parameter can be one of the following values:
  *            @arg TIM_CHANNEL_1: TIM Channel 1
  *            @arg TIM_CHANNEL_2: TIM Channel 2
  *            @arg TIM_CHANNEL_3: TIM Channel 3
  *            @arg TIM_CHANNEL_4: TIM Channel 4
  * @param  ChannelState specifies the TIM Channel CCxE bit new state.
  *          This parameter can be: TIM_CCx_ENABLE or TIM_CCx_DISABLE.
  * @retval None
  */
void TIM_CCxChannelCmd(TIM_TypeDef *TIMx, uint32_t Channel, uint32_t ChannelState)
{
  uint32_t tmp;

  /* Check the parameters */
  assert_param(IS_TIM_CC1_INSTANCE(TIMx));
  assert_param(IS_TIM_CHANNELS(Channel));

  tmp = TIM_CCER_CC1E << (Channel & 0x1FU); /* 0x1FU = 31 bits max shift */

  /* Reset the CCxE Bit */
  TIMx->CCER &= ~tmp;

  /* Set or reset the CCxE Bit */
  TIMx->CCER |= (uint32_t)(ChannelState << (Channel & 0x1FU)); /* 0x1FU = 31 bits max shift */
}

该函数的主要功能为使能或者关闭定时器的捕获/比较通道,是对 捕获/比较使能寄存器 (TIMx_CCER) 的操作,高级定时器的捕获/比较使能寄存器 (TIMx_CCER) 如下图,16位的寄存器每四位控制一个捕获/比较通道,其中CCxE控制OCx信号是否输出,该函数主要对此位进行操作。

image-20250706181435257

image-20250722232636669

输入的第二个参数为捕获/比较通道标识符,具体定义如下:

/** @defgroup TIM_Channel TIM Channel
  * @{
  */
#define TIM_CHANNEL_1                      0x00000000U                          /*!< Capture/compare channel 1 identifier      */
#define TIM_CHANNEL_2                      0x00000004U                          /*!< Capture/compare channel 2 identifier      */
#define TIM_CHANNEL_3                      0x00000008U                          /*!< Capture/compare channel 3 identifier      */
#define TIM_CHANNEL_4                      0x0000000CU                          /*!< Capture/compare channel 4 identifier      */
#define TIM_CHANNEL_ALL                    0x0000003CU                          /*!< Global Capture/compare channel identifier  */

输入的第三个参数为通道使能状态:

/** @defgroup Channel_CC_State TIM Capture/Compare Channel State
  * @{
  */
#define TIM_CCx_ENABLE                   0x00000001U                            /*!< Input or output channel is enabled */
#define TIM_CCx_DISABLE                  0x00000000U                            /*!< Input or output channel is disabled */
#define TIM_CCxN_ENABLE                  0x00000004U                            /*!< Complementary output channel is enabled */
#define TIM_CCxN_DISABLE                 0x00000000U                            /*!< Complementary output channel is enabled */
/**

有以上定义后,我们对TIM_CCxChannelCmd函数进行详细介绍:

  tmp = TIM_CCER_CC1E << (Channel & 0x1FU); /* 0x1FU = 31 bits max shift */
  • TIM_CCER_CC1E是通道1的使能位掩码(通常为0x0001)。
  • 结合上面TIM_CHANNEL_x的定义值,可计算目标通道的使能位位置:
    • TIM_CHANNEL_1 → 移位0位 → tmp = 0x0001
    • TIM_CHANNEL_2 → 移位4位 → tmp = 0x0010
    • TIM_CHANNEL_3 → 移位8位 → tmp = 0x0100
    • TIM_CHANNEL_4 → 移位12位 → tmp = 0x1000
  • 关键:STM32的CCER寄存器中,每个通道的使能位(CCxE)间隔4位(详见上面捕获/比较使能寄存器 (TIMx_CCER) 的图)。
  /* Reset the CCxE Bit */
  TIMx->CCER &= ~tmp;
  • 先清除当前通道的使能状态,为后续设置做准备。
/* Set or reset the CCxE Bit */
  TIMx->CCER |= (uint32_t)(ChannelState << (Channel & 0x1FU)); /* 0x1FU = 31 bits max shift */
  • ChannelState = TIM_CCx_ENABLE(通常为1):
    • 1左移相同位数后写入CCER,使能目标通道。
  • ChannelState = TIM_CCx_DISABLE(通常为0):
    • 左移结果为0,|=操作不改变寄存器值(保持第二步的清零状态)。

使能定时器函数已在前面实验介绍过,不展开了,详见:【STM32定时器】实验1:定时器基本用法(CuBeMX版)+启动函数解析中相关部分

2)设置通道的 PWM 模式函数—— HAL_TIM_PWM_ConfigChannel

/**
  * @brief  Initializes the TIM PWM  channels according to the specified
  *         parameters in the TIM_OC_InitTypeDef.
  * @param  htim TIM PWM handle
  * @param  sConfig TIM PWM configuration structure
  * @param  Channel TIM Channels to be configured
  *          This parameter can be one of the following values:
  *            @arg TIM_CHANNEL_1: TIM Channel 1 selected
  *            @arg TIM_CHANNEL_2: TIM Channel 2 selected
  *            @arg TIM_CHANNEL_3: TIM Channel 3 selected
  *            @arg TIM_CHANNEL_4: TIM Channel 4 selected
  * @retval HAL status
  */
HAL_StatusTypeDef HAL_TIM_PWM_ConfigChannel(TIM_HandleTypeDef *htim,
                                            TIM_OC_InitTypeDef *sConfig,
                                            uint32_t Channel)

第一个参数 htim 是定时器初始化句柄,也就是 TIM_HandleTypeDef 结构体指针类型,这 和 HAL_TIM_PWM_Init 函数调用时候参数保存一致即可。

第二个参数 sConfig 是 TIM_OC_InitTypeDef 结构体指针类型,这也是该函数最重要的参数。 该参数用来设置 PWM 输出模式,极性,比较值等重要参数。首先我们来看看结构体定义:

/**
  * @brief  TIM Output Compare Configuration Structure definition
  */
typedef struct
{
  uint32_t OCMode;        /*!< Specifies the TIM mode.
                               This parameter can be a value of @ref TIM_Output_Compare_and_PWM_modes */

  uint32_t Pulse;         /*!< Specifies the pulse value to be loaded into the Capture Compare Register.
                               This parameter can be a number between Min_Data = 0x0000 and Max_Data = 0xFFFF */

  uint32_t OCPolarity;    /*!< Specifies the output polarity.
                               This parameter can be a value of @ref TIM_Output_Compare_Polarity */

  uint32_t OCNPolarity;   /*!< Specifies the complementary output polarity.
                               This parameter can be a value of @ref TIM_Output_Compare_N_Polarity
                               @note This parameter is valid only for timer instances supporting break feature. */

  uint32_t OCFastMode;    /*!< Specifies the Fast mode state.
                               This parameter can be a value of @ref TIM_Output_Fast_State
                               @note This parameter is valid only in PWM1 and PWM2 mode. */


  uint32_t OCIdleState;   /*!< Specifies the TIM Output Compare pin state during Idle state.
                               This parameter can be a value of @ref TIM_Output_Compare_Idle_State
                               @note This parameter is valid only for timer instances supporting break feature. */

  uint32_t OCNIdleState;  /*!< Specifies the TIM Output Compare pin state during Idle state.
                               This parameter can be a value of @ref TIM_Output_Compare_N_Idle_State
                               @note This parameter is valid only for timer instances supporting break feature. */
} TIM_OC_InitTypeDef;

该结构体成员我们重点关注前三个。成员变量 OCMode 用来设置模式,也就是我们前面讲解的 7 种模式,这里我们设置为 PWM 模式 1。

成员变量 Pulse 用来设置捕获比较值。

成员变量 OCPolarity 用 来 设 置 输 出 极 性 是 高 还 是 低 。

第 三 个 参 数 Channel 用 来 选 择 定 时 器 的 通 道 , 取 值 范 围 为 TIM_CHANNEL_1~ TIM_CHANNEL_4。

5、整体代码

参数定义

/* USER CODE BEGIN PV */
char message[20];

/* USER CODE END PV */

main函数

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_TIM14_Init();
  MX_USART1_UART_Init();
  /* USER CODE BEGIN 2 */
	HAL_TIM_PWM_Start(&htim14, TIM_CHANNEL_1);

  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
		for(int i = 0; i<1000; i++){
			uint32_t cc_value = __HAL_TIM_GET_COMPARE(&htim14, TIM_CHANNEL_1);			//»ñÈ¡µ±Ç°¼Ä´æÆ÷Öµ
			sprintf(message, "%d\r\n", cc_value);
			HAL_UART_Transmit(&huart1, (uint8_t*)message, strlen(message), HAL_MAX_DELAY);			//´®¿Ú·¢ËÍ
			__HAL_TIM_SET_COMPARE(&htim14, TIM_CHANNEL_1, i);						//ÉèÖüĴæÆ÷Öµ
			
			HAL_Delay(9);  //ÑÓʱ10ms
			
		}
		for(int i = 999; i>=0; i--){
			uint32_t cc_value = __HAL_TIM_GET_COMPARE(&htim14, TIM_CHANNEL_1);
			sprintf(message, "%d\r\n", cc_value);
			HAL_UART_Transmit(&huart1, (uint8_t*)message, strlen(message), HAL_MAX_DELAY);
			__HAL_TIM_SET_COMPARE(&htim14, TIM_CHANNEL_1, i);
			HAL_Delay(9);
			
		}
		
    /* USER CODE END WHILE */

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

6、实验结果


呼吸灯就不上传了
image-20250706224344142

7、结语

学习记录,欢迎批评指正

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐