STM32项目实例:PWM呼吸灯

文摘   2024-10-14 07:01   河北  
与基本定时器相比,STM32F407微控制器通用定时器数量众多,功能强大,除具备基本的定时功能外,还可用于测量输入脉冲的频率和脉冲宽度以及输出PWM波形等场合,还具有编码器接口。

1 PWM输出模式

PWM输出模式是一种特殊的输出模式,在电力电子和电机控制领域得到广泛应用。STM32F407微控制器,除了基本定时器TIM6TIM7之外,其他的定时器都可以用来产生PWM输出,其中通用定时器能同时产生多达4路的PWM输出。

1.1 PWM简介

PWMPulse Width Modulation的缩写,中文意思就是脉冲宽度调制,简称脉宽调制。它是利用微处理器的数字输出来对模拟电路进行控制的一种非常有效的技术,因其控制简单、灵活和动态响应好等优点而成为电力电子技术最广泛应用的控制方式,其应用领域包括测量、通信、功率控制与变换,电动机控制、伺服控制、调光、开关电源,甚至包括某些音频放大器等。

PWM是一种对模拟信号电平进行数字编码的方法。通过高分辨率计数器的使用,方波的占空比被调制用来对一个具体模拟信号的电平进行编码。PWM信号仍然是数字的,因为在给定的任何时刻,满幅值的直流供电要么完全有(ON),要么完全无(OFF)。电压或电流源是以一种通(ON)或断(OFF)的重复脉冲序列被加到模拟负载上去的。通的时候即是直流供电被加到负载上,断的时候即是供电被断开。只要带宽足够,任何模拟值都可以使用PWM进行编码。

1.2 PWM输出模式的工作过程

STM32F407微控制器脉冲宽度调制模式可以产生一个由TIMx_ARR寄存器确定频率、由TIMx_CCRx寄存器确定占空比的信号,其产生原理如图1所示。

1 STM32F407微控制器PWM产生原理

下面以一个具体实例说明通用定时器PWM输出模式的工作过程。当通用定时器被设置为向上计数,自动重装载寄存器TIMx_ARR的预设值为84个捕获/比较寄存器TIMx_CCRx分别设为048和大于8时,通过用定时器的4PWM通道的输出时序OCxREF和触发中断时序CCxIF如图2所示。例如,在TIMx_CCR4情况下,当TIMx_CNT4时,OCxREF输出高电平;当TIMx_CNT4时,OCxREF输出低电平,并在比较结果改变时触发CCxIF中断标志。此PWM的占空比为4/(8+1)

2 向上计数模式PWM输出时序图

需要注意的是,在PWM输出模式下,脉冲计数器TIMx_CNT的计数模式有向上计数、向下计数和向上/向下计数(中央对齐)3种。以上仅介绍其中的向上计数方式,但是读者在掌握了通用定时器向上计数模式的PWM输出原理后,由此及彼,其他两种计数模式的PWM输出也就容易推出了。

1.3 PWM输出HAL库函数

PWM输出HAL库函数如表1所示,此处仅列出了相关函数,简要说明其功能,在后面生成PWM波的示例里,再结合CubeMX配置和初始化代码分析讲解这些函数的功能和使用。

1 PWM输出HAL库函数列表

函数名称

功能描述

TIM_PWM_Init()

生成PWM波的配置初始化

HAL_TIM_PWM_ConfigChannel()

配置PWM输出通道

HAL_TIM_PWM_Start()

启动生成PWM

HAL_TIM_PWM_Stop()

停止生成PWM

HAL_TIM_PWM_Start_IT()

以中断方式启动生成PWM

HAL_TIM_PWM_Stop_IT()

停止生成PWM

HAL_TIM_PWM_GetState()

返回定时器状态

__HAL_TIM_ENABLE_OCxPRELOAD()

使能CCR的预装载功能

__HAL_TIM_DISABLE_OCxPRELOAD()

失能CCR的预装载功能

__HAL_TIM_ENABLE_OCxFAST()

使能—个通道的快速模式

__HAL_TIM_DISABLE_OCxFAST()

失能一个通道的快速模式

HAL_TIM_PWM_PulseFinishedCallback()

产生输出比较事件对应的回调函数

2 PWM呼吸灯

2.1 项目分析

由开发板原理图可知,LED指示灯L7连接至微控制器的PF6引脚,通过查询数据手册可知该引脚具有的功能为PF6/TIM10_CH1/FSMC_NIORD/ADC3_IN4,可以使用其复用功能将TIM10_CH1生成的PWM波输出到引脚,通过引脚所连接的LED灯的亮度直观反映PWM波的占空比变化。项目实施的目标是在PF6引脚产生一个频率固定为10kHz,占空比循环改变,类似于呼吸灯效果的PWM方波。

已知TIM10是挂接在APB2总线上的,按照本书的常规配置,挂接在APB2总线上定时器的输入时钟频率为168MHz10kHz方波信号需要经过两次分频得到,一次是预分频,一次是周期计数分频,这两个分频系数的可以在较大范围设定,作者采用的具体数值分别为预分频系数为21,计数周期数为800

当设定计数周期数为800时,则捕获比较寄存器CCR1的数值可以在0~800之间变化,对应占空比为0%~100%,因为人的视觉分辨亮和很亮的能力比较弱,所以作者将CCR1寄存器的值限定在0~500之间变化,主程序中循环修改CCR1的数值,改变PWM波形占空比,形成一个呼吸灯的效果。因为LED指示灯采用共阳接法,所以将PWM输出极性设置为低,这样捕获/比较寄存器的数值直接对应LED指示灯的亮度。

2.2 项目实施

1.复制工程文件

复制第9章创建工程模板文件夹0901 BasicTimer到桌面,并将文件夹重命名为1001 PWMGenerate

2CubeMX配置

打开工程模板文件夹里面的Template.ioc文件,启动CubeMX配置软件,在左侧配置类别Categories下面的Timers列表中的找到TIM10定时器,打开其配置对话框,配置界面如图3所示。在模式设置部分,选中Activated复选框,启用定时器,此时Channel1下拉列表框处于可选择状态,并有如下选项可供选择:

  • Disable:失能通道。

  • Input Capture direct mode:直接模式输入捕获。

  • Output Compare No Output:输出比较,不输出到通道引脚。

  • PWM Generation No Output:生成PWM,不输出到通道引脚。

  • PWM Generation CH1:生成PWM,输出到通道引脚CH1。

  • Forced Output CH1:强制通道引脚CH1输出某个电平。
TIM10Channel1通道模式设置为PWM Generation CH1,即生成PWM信号输出至TIM10_CH1映射引脚PF6,此时One Pulse Mode(单脉冲模式)复选框依然无须选中,即使用定时器连续模式。

3 TIM10配置界面

参数配置选项里设置划分为Counter SettingsPWM Generation Channel1两部分,分别为定时器基本配置和PWM波形设置。

1Counter Settings设置
  • Prescaler:预分频值,16位寄存器,设置范围0~65535,对应分频系数1~65536。这里设置为21-1,实际分频系数21。

  • Counter Mode:计数模式,通用定时器可以有向上、向下和双向计数模式,但是TIM10只支持向上计数模式,所以此处设置为Up。

  • Counter Period:计数周期,设置的是自动重装载寄存器的值,这里设置为800-1,对应的计数值为800。

  • Internal Clock Division:内部时钟分频,是在定时器控制器部分对内部时钟进行分频,可以设置为1、2或4分频,对应选项为No Division、Division by 2和Division by 4,此处选择无分频(No Division),使得CK_PSC等于CK_INT。

  • auto-reload preload:是否启用定时器的预装载功能,不启用预装载功能,对自动重装载寄存器的修改立即生效,启用预装载功能,对自动重装载寄存器的修改在更新事件发生后才生效。如果不动态修改TIMx_ARR的值,这个设置对定时器工作无影响,此处选择Disable,即不启用预装载功能。

2PWM波形设置

  • Mode:PWM模式,选项有PWM Mode 1(PWM模式1)和PWM Mode 2(PWM模式2)。这两种模式PWM输出特性如下:

PWM模式1——在向上计数模式下,CNT<CCR通道输出有效状态,否则为无效状态;在向下计数模式下,CNT>CCR通道输出有效状态,否则为无效状态。图2是通道极性(有效状态)为高,PWM模式1下生成的PWM波形。

PWM模式2——其输出与PWM模式1正好相反,例如在向上计数模式下,只要CNT<CCR,通道就是无效状态,否则为有效状态。
  • Pulse:PWM脉冲宽度,就是设置16位捕获/比较寄存器CCR的值。脉冲宽度的值应小于计数周期的值,此处将其初始值设置为200,对应初始占空比为25%。

  • Output compare preload:是否启用CCR寄存器的预装载功能,设置为Disable,对CCR寄存器的修改立即生效,设置为Enable,对CCR寄存器修改在下一个UEV发生后才生效,此处设置为Enable。

  • Fast Mode:是否使用输出比较快速模式,用于加快触发输入事件对输出的影响,一般设置为Disable。

  • CH Polarity:通道极性,就是CCR与CNT比较输出的有效状态,可以设置为高电平High或低电平Low。对于共阳接法LED,通道极性设置为Low较为直观。

本项目无需使用定时器中断,所以不需要配置TIM10的全局中断。时钟配置和工程配置选项无须修改,单击GENERATE CODE按钮生成初始化工程。

3.初始化代码分析及用户程序编写

打开MDK-RAM文件夹下面的工程文件Template.uvprojx,将生成工程编译一下,没有错误和警告之后开始初始化代码分析和用户程序编写。

1)定时器初始化分析

用户在CubeMX中启用了某个定时器,系统会自动生成定时器初始化源文件tim.c和定时器初始化头文件tim.h,分别用于定时器初始化的实现和定义,此处仅展示TIM10相关代码。

头文件tim.h内容如下,其中省略了程序沙箱和部分注释。

#include "main.h"

extern TIM_HandleTypeDef htim10;

void MX_TIM10_Init(void);

void HAL_TIM_MspPostInit(TIM_HandleTypeDef *htim);

源文件tim.c内容如下,其中省略了程序沙箱和部分注释。

#include "tim.h"

TIM_HandleTypeDef htim7;

TIM_HandleTypeDef htim10;

void MX_TIM10_Init(void)

{

TIM_OC_InitTypeDef sConfigOC = {0};

htim10.Instance = TIM10;

htim10.Init.Prescaler = 21-1;

htim10.Init.CounterMode = TIM_COUNTERMODE_UP;

htim10.Init.Period = 800-1;

htim10.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;

htim10.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;

if (HAL_TIM_Base_Init(&htim10) != HAL_OK)

{    Error_Handler();  }

if (HAL_TIM_PWM_Init(&htim10) != HAL_OK)

{    Error_Handler();  }

sConfigOC.OCMode = TIM_OCMODE_PWM1;

sConfigOC.Pulse = 200;

sConfigOC.OCPolarity = TIM_OCPOLARITY_LOW;

sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;

if (HAL_TIM_PWM_ConfigChannel(&htim10, &sConfigOC, TIM_CHANNEL_1) != HAL_OK)

{    Error_Handler();  }

HAL_TIM_MspPostInit(&htim10);

}

void HAL_TIM_Base_MspInit(TIM_HandleTypeDef* tim_baseHandle)

{

if(tim_baseHandle->Instance==TIM10)

{

__HAL_RCC_TIM10_CLK_ENABLE();  /* TIM10 clock enable */

}

}

void HAL_TIM_MspPostInit(TIM_HandleTypeDef* timHandle)

{

GPIO_InitTypeDef GPIO_InitStruct = {0};

if(timHandle->Instance==TIM10)

{

__HAL_RCC_GPIOF_CLK_ENABLE();

/**TIM10 GPIO Configuration    PF6     ------> TIM10_CH1    */

GPIO_InitStruct.Pin = GPIO_PIN_6;

GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;

GPIO_InitStruct.Pull = GPIO_NOPULL;

GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;

GPIO_InitStruct.Alternate = GPIO_AF3_TIM10;

HAL_GPIO_Init(GPIOF, &GPIO_InitStruct);

}

}

上述代码由CubeMX生成,一般情况下无需任何修改。tim.h文件主要用于外部变量和初始化函数声明。

tim.c文件中首先定义了TIM_HandleTypeDef型外设对象变量htim10,用来表示TIM10。函数MX_TIM10_Init()用于定时器TIM10的初始化,包括定时器基本参数初始化和PWM生成参数初始化,初始化代码和图3配置是一一对应的。

函数HAL_TIM_Base_MspInit()是重新实现的MSP函数,由HAL_TIM_Base_Init()函数内部调用,其功能只有一个即开启TIM10时钟。

函数HAL_TIM_MspPostInit()是在函数MX_TIM10_Init()中最后调用的,其功能是对引脚PF6进行GPIO初始化,将其复用为TIM10_CH1输出引脚。

2)主程序分析及用户程序编写

首先打开main.c文件,其部分代码如下:

/********** main.c   Source   File   **********/

#include "main.h"

#include "tim.h"

#include "gpio.h"

#include "fsmc.h"

/* USER CODE BEGIN PV */

uint16_t *SEG_ADDR=(uint16_t *)(0x68000000);

uint8_t smgduan[11]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0xbf};

uint8_t smgwei[6]={0xfe,0xfd,0xfb,0xf7,0xef,0xdf},SmgBuff[6];

/* USER CODE END PV */

void SystemClock_Config(void);

int main(void)

{

/* USER CODE BEGIN 1 */

uint8_t dir=1;

uint16_t Duty=0;

/* USER CODE END 1 */

HAL_Init();

SystemClock_Config();  /* Configure the system clock */

MX_GPIO_Init();

MX_FSMC_Init();

MX_TIM7_Init();

MX_TIM10_Init();   //定时器TIM10初始化

/* USER CODE BEGIN WHILE */

SmgBuff[0]=0;SmgBuff[1]=1;   //显示学号前面两位

SmgBuff[2]=2;SmgBuff[3]=3;   //显示学号中间两位

SmgBuff[4]=4;SmgBuff[5]=5;   //显示学号最后两位

HAL_TIM_Base_Start_IT(&htim7);

HAL_TIM_PWM_Start(&htim10,TIM_CHANNEL_1);

while(1)

{

HAL_Delay(6);

if(dir==1)

{

if(++Duty==500) dir=0;

}

else

{

if(--Duty==0) dir=1;

}

//设置比较寄存器TIM10_CCR1数值

__HAL_TIM_SetCompare(&htim10,TIM_CHANNEL_1,Duty);

/* USER CODE END WHILE */

}

}

新生成初始化代码和用户编写代码均作加粗显示,以示区别于前期项目已有代码,本项目还需要将学生学号显示于数码管上,所以定时器TIM7及其中断刷新数码部分程序仍然需要,但该部分内容相对于第9章项目来说并无区别,所以未将其相同部分代码贴出。

main函数中,首先定义两个变量,一个是方向变量dir,一个占空比变量Duty,在无限程序中先让占空比增加,当增加到500时,再让占空比减少,并将占空比数值实时更新到TIM10的捕获比较寄存器CCR1,以实现L7指示灯的PWM呼吸灯效果。

4.下载调试
编译工程,直到没有错误为止,下载程序到开发板,复位运行,检查实验效果。
3 使用教材
4教程下载
STM32项目实例:PWM呼吸灯.docx



人工智能科学与技术
分享教学成果 | 传播前沿科技| 推荐优秀图书
 最新文章