当前位置: 首页 > article >正文

细说STM32单片机使用通用定时器生成固定占空比和可变占空比PWM波的方法

目录

一、本实例测试的目的

二、硬件和CubeMX项目配置

1、硬件开发板 

2、项目配置

(1)定时器TIM2_CH1

(2)时钟和Debug

(3) NVIC

(4)GPIO

3、输出固定占空比的PWM波源码

(1)启动定时器

(2)控制LED

(3)下载与测试

4、输出可变占空比的PWM波

(1)改变占空比的方法

5、输出可变占空比的PWM波源码

(1)新增变量

(2)启动定时器

(3)控制LED

(4)下载与测试


一、本实例测试的目的

        使用TIM2的CH1输出生成PWM波, 首先输出固定占空比的PWM波,然后再改动程序后输出可变占空比的PWM波。

二、硬件和CubeMX项目配置

1、硬件开发板 

        开发板型号NUCLEO-G474RE,MCU型号STM32G474RET6,该MCU有1个高精度定时器、3个高级控制定时器、7个通用定时器(TIM2~TIM5、TIM15~TIM17)和2个基础定时器。此外还有2个看门狗定时器和1个SysTick定时器。

        开发板上TIM2的CH1连接的引脚PA5。PA5引脚不能设置为GPIO_Output,而要设置为TIM2_CH1。在开发板上,PA5管脚本身就是指示灯LD2,高电平亮。本实验不仅要控制LD2开与关还要控制它的明亮程度。

        开发板上PB11设置为LED1,PB11输出高电平时LED1亮,否则,LED1灭。可以用TIM2的CH1输出PWM波控制LED1的开与关(以PWM的频率)。PB11引脚设置为GPIO_Output。

2、项目配置

(1)定时器TIM2_CH1

        Channel1选择PWM Generation CH1;Clock Source选择Internal Clock;

  • Prescaler,预分频寄存器值,设置为16999,所以预分频系数为17000。定时器使用内部时钟信号频率为170MHz,经过预分频后进入计数器的时钟频率就10kHz,即

  • Counter Period,计数周期(ARR的值),设置为199,所以一个计数周期是

        

  • Internal Clock Division,内部时钟分频,是在定时器控制器部分对内部时钟进行分频,可以设置为1、2或4分频,选项No Division就是无分频,使得CK_PSC等于CK INT。
  • auto-reload preload,自动重载预装载,即设置TIM2_CR1寄存器中的ARPE位。如果设置为Disable,就是不使用预装载,设置的新ARR的值立即生效;如果设置为Enabled, 设置的新ARR的值在下一个UEV事件时才生效。
  • Mode,PWM模式,选项有PWM Mode 1(PWM模式1)和PWM Mode 2(PWM模式2)。这两种模式的定义如下:

        PWM模式1:在递增计数模式下,只要CNT<CCR,通道就是有效状态,否则为无效状态。在递减计数模式下,只要CNT<CCR,通道就变为无效状态,否则为有效状态。

        PWM模式2:其输出与PWM模式1正好相反,例如,在递增计数模式下,只要CNT<CCR,通道就是无效状态,否则为有效状态。

  • Pulse,PWM脉冲宽度,就是设置32位的捕获/比较寄存器CCR的值。脉冲宽度的值应该小于计数周期的值,这里设置为50,因为计数器的时钟频率是10kHz,所以脉冲宽度为5ms。
  • Output compare preload,输出比较预装载。CCR有预装载功能,寄存器TIMx_CCMRy中的OCyPE(Output Capture y Preload Enable)位可以使能或禁用其预装载功能。这个参数就是设置这个位的值,设置为Enable时,修改CCR的值需要到下一个UEV事件时才生效,否则立刻生效。
  • Fast Mode,是否使用输出比较快速模式,就是设置寄存器TIMx_CCMR1中的OC1FE位,用于加快触发输入事件对CC输出的影响,一般设置为Disable即可。
  • CH Polarity,通道极性,就是CCR与CNT比较输出的有效状态,可以设置为高电平 (High)或低电平(Low)。通道极性和PWM模式的组合可以生成不同的PWM波形。 

(2)时钟和Debug

        HSE选择外部晶振,24MHZ,APB1 = APB2 = 170MHz;

        Debug选择Serial Wire;

(3) NVIC

        Time Base修改为0;

        TIM2 global interrupt设置为1;

(4)GPIO

        设置PB11为GPIO OUTPUT,默认高电平,PP,高速,别名LED1。

        经过这样的设置,在启动定时器TIM2后,在引脚PA5(TIM2_CH1通道)上输出的PWM 波形:通道极性为高,PWM模式为1。PWM波的周期为20ms,由ARR的值决定;高电平脉冲宽度为5ms,由CCR的值决定。

        同时扩展板上的PB11的LED1闪烁。

3、输出固定占空比的PWM波源码

(1)启动定时器

/* USER CODE BEGIN 2 */
  HAL_TIM_Base_Start_IT(&htim2);				//以中断方式启动TIM2
  HAL_TIM_PWM_Start_IT(&htim2,TIM_CHANNEL_1);	//TIM2通道1,启动生成PWM
/* USER CODE END 2 */

        MX_TIM2_Init()是定时器TIM2的初始化函数。要启动TIM2的PWM波输出,需要先执行函数HAL_TIM_Base_Start_IT()启动定时器,再执行函数HAL_TIM_PWM_Start IT()启动CH1的PWM波输出。 

(2)控制LED

         控制LED1(PB11)的开与关。

/* USER CODE BEGIN 4 */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
	if (htim->Instance == TIM2)
		HAL_GPIO_TogglePin (LED1_GPIO_Port,LED1_Pin);
}
/* USER CODE END 4 */

        控制LD2(PA5),自动生成。

void HAL_TIM_MspPostInit(TIM_HandleTypeDef* htim)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};
  if(htim->Instance==TIM2)
  {
  /* USER CODE BEGIN TIM2_MspPostInit 0 */

  /* USER CODE END TIM2_MspPostInit 0 */

    __HAL_RCC_GPIOA_CLK_ENABLE();
    /**TIM2 GPIO Configuration
    PA5     ------> TIM2_CH1
    */
    GPIO_InitStruct.Pin = GPIO_PIN_5;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_PULLUP;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF1_TIM2;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

  /* USER CODE BEGIN TIM2_MspPostInit 1 */

  /* USER CODE END TIM2_MspPostInit 1 */
  }

}

(3)下载与测试

        下载到开发板连续运行,用示波器可以观察PA5引脚上输出的固定占空比的PWM波形。用眼睛可以看到闪烁的LED1(PB11)和恒亮的LD2(PA5)。改变PWM高电平脉冲宽度可以改变LD2的亮度,因为PA5引脚为高电平时LED亮,所以PWM脉冲宽度越大,LD2越亮。在函数MX_TIM2_Init()的代码里,直接修改PWM参数结构体变量sConfigOC的成员变量的赋值,可以观察不同参数取值的影响,例如,修改PWM模式参数OCMode、有效极性参数OCPolarity、脉冲宽度参数Pulse等。

//sConfigOC成员
sConfigOC.OCMode = TIM_OCMODE_PWM1;
sConfigOC.Pulse = 50;
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;

4、输出可变占空比的PWM波

(1)改变占空比的方法

        LD2的亮度是由PWM波的占空比控制的,例1的程序输出PWM波是固定占空比,所以 LD2的亮度是固定的。如果在程序运行过程中动态修改PWM的占空比,就可以使LD2从暗到明、从明到暗地变化,形成一种呼吸的效果。LED1的亮度不受影响。

        在程序中动态改变PWM波的占空比,就是要修改寄存器TIMx_CCR1的值。下图是动态修改PWM波的占空比的示意图。在本示例中,PWM波的周期是200个时钟周期,在发生比较匹配事件时,会产生TIM_IT_CC1中断事件(置位CCyF中断标志位),可以在此中断里修改CCR的值。

 

        CCR是有预装载功能的。如果禁止CCR预装载功能,对CCR的修改立即生效;如果启用了CCR预装载功能,设置新的CCR值需要在下一个UEV事件时才生效。参数Output compare preload控制是否启用CCR的预装载功能,本例里需要设置为 Enable。

        如果CCR的值等于定时器计数器的值,就会置位中断事件标志位CCyIF,即产生TIM_IT_CCy中断事件。生成PWM波是输出比较,HAL_TIM_PWM_PulseFinishedCallback() 是对应的回调函数,可以在这个回调函数里修改CCR的值。当CCR预装载功能使能时,在下次UEV事件时对CCR的修改就会生效,从而可以动态地改变PWM波的占空比。

        使用宏函数__HAL_TIM_SET_COMPARE()可以设置CCR的值,其原型定义为:        

__HAL_TIM_SET_COMPARE(__HANDLE__, __CHANNEL__, __COMPARE__)

        其中,__HANDLE__是定时器对象指针,__CHANNEL__是定时器通道,__COMPARE__是需要为CCR设置的值。例如,将TIM2的CH1通道的CCR设置为0x0037的语句为:

__HAL_TIM_SET_COMPARE(&htim2,TIM_CHANNEL_1,0x0037);

        另外,宏函数__HAL_TIM_GET_COMPARE()可以获取CCR当前的值,其函数原型定义如下,这个函数返回结果是16位或32位整数,与具体定时器的CCR的长度有关。

__HAL_TIM_GET_COMPARE(__HANDLE__ ,__CHANNEL__)

5、输出可变占空比的PWM波源码

(1)新增变量

/* USER CODE BEGIN PV */
uint16_t pulseWidth = 50; //脉宽
uint8_t dirInc = 1;       //脉宽变化方向,1=递增,0=递减
/* USER CODE END PV */

(2)启动定时器

  /* USER CODE BEGIN 2 */
  HAL_TIM_Base_Start_IT(&htim2);				//以中断方式启动TIM2
  HAL_TIM_PWM_Start_IT(&htim2,TIM_CHANNEL_1);	//TIM2通道1,启动生成PWM
  /* USER CODE END 2 */

(3)控制LED

         控制LED1开与关的频率不变,亮度不受影响。LD2的亮度受PWM占空比的影响,呈现呼吸状态。

/* USER CODE BEGIN 4 */
void HAL_TIM_PWM_PulseFinishedCallback(TIM_HandleTypeDef *htim)
 {
    HAL_GPIO_TogglePin (LED1_GPIO_Port,LED1_Pin);
	if (htim->Instance != TIM2)
		return;
	if(dirInc == 1) //脉宽递增
	{
		pulseWidth++;
		if (pulseWidth >= 195)
		{
			pulseWidth = 195;
			dirInc=0; //脉宽递减
		}
	}
	else
	{
		pulseWidth--;
		if (pulseWidth <= 5)
		{
			pulseWidth = 5;
			dirInc=1;//脉宽递增
		}
	}
	__HAL_TIM_SET_COMPARE(&htim2,TIM_CHANNEL_1,pulseWidth);//设置CCR的值
}
/* USER CODE END 4 */

 

void HAL_TIM_MspPostInit(TIM_HandleTypeDef* htim)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};
  if(htim->Instance==TIM2)
  {
  /* USER CODE BEGIN TIM2_MspPostInit 0 */

  /* USER CODE END TIM2_MspPostInit 0 */

    __HAL_RCC_GPIOA_CLK_ENABLE();
    /**TIM2 GPIO Configuration
    PA5     ------> TIM2_CH1
    */
    GPIO_InitStruct.Pin = GPIO_PIN_5;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_PULLUP;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF1_TIM2;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

  /* USER CODE BEGIN TIM2_MspPostInit 1 */

  /* USER CODE END TIM2_MspPostInit 1 */
  }

}

(4)下载与测试

        该方法实现了按脉宽递增或递减方向修改CCR的值,因为开启了CCR预装载功能,所以新设置的CCR的值在下一个UEV事件时才生效。下载并运行此程序,我们就可以观察到LD2 由明到暗,再由暗到明的循环往复变化效果。


http://www.kler.cn/news/306633.html

相关文章:

  • leetcode 230.二叉搜索树中第k小的元素
  • VMware Fusion虚拟机Mac版 安装Ubuntu操作系统教程
  • YOLOv8目标检测——迁移学习
  • 55页可编辑PPT | 集团制造企业数字化转型顶层设计方案
  • k8s中的认证授权
  • LeetCode双周赛139
  • 鸿蒙开发入门day19-使用NDK接口构建UI(一)
  • 中间件之RocketMQ
  • react js 使用 useEffect 钩子
  • C++函数在库中的地址
  • 【机器学习随笔】概率论与实际问题的对应
  • PHP技术深度探索:构建高效安全的Web应用实践
  • ChatGPT提示词-中文版(awesome-chatgpt-prompts中文版)
  • java重点学习-JVM组成
  • 多张GPU卡
  • 【C++】c++ 11
  • 【Git】初识Git
  • 检查Tomcat是否启动成功
  • Baumer工业相机堡盟工业相机如何通过BGAPISDK获取相机接口数据吞吐量(C语言)
  • 【YashanDB知识库】YAS-02025 no free space in virtual memory pool
  • 初识时序数据库InfluxDB
  • 【ARM】中断的处理
  • 中间件安全(一)
  • 基于Selenium的新闻爬取技术实操
  • 【AIGC cosplay】让大模型扮演求职者,我当hr来面试
  • 语言哲学(Philosophy of Language)
  • PMP--一模--解题--81-90
  • Python 常用模块(二):json模块
  • 拒绝低效!开发者必备AI工具助你事半功倍!
  • WPF利用Path自定义画头部导航条(TOP)样式