建设电影网站需要多少钱,营销型网站建设的优势有哪些,怎么进电力建设公司网站,值得买 wordpress通用定时器 PWM 输出实验
本小节我们来学习使用通用定时器的 PWM 输出模式。
脉冲宽度调制(PWM)#xff0c;是英文“Pulse Width Modulation”的缩写#xff0c;简称脉宽调制#xff0c;是利用微处理器的数字输出来对模拟电路进行控制的一种非常有效的技术。我们可以让定时…通用定时器 PWM 输出实验
本小节我们来学习使用通用定时器的 PWM 输出模式。
脉冲宽度调制(PWM)是英文“Pulse Width Modulation”的缩写简称脉宽调制是利用微处理器的数字输出来对模拟电路进行控制的一种非常有效的技术。我们可以让定时器产生PWM在计数器频率固定时PWM 频率或者周期由自动重载寄存器TIMx_ARR的值决定其占空比由捕获/比较寄存器TIMx_CCRx的值决定。PWM 产生原理示意图如下图所示上图中定时器工作在递增计数模式纵轴是计数器的计数值 CNT横轴表示时。当CNTCCRx 时IO 输出低电平逻辑 0当 CNTCCRx 时IO 输出高电平逻辑 1当CNTARR 时定时器溢出CNT 的值被清零然后继续递增依次循环。在这个循环中改变 CCRx 的值就可以改变 PWM的占空比改变 ARR 的值就可以改变 PWM 的频率这就是 PWM 输出的原理。
定时器产生 PWM的方式有许多种下面我们以边沿对齐模式即递增计数模式/递减计数模式为例PWM 模式 1 或者 PWM 模式 2 产生 PWM 的示意图如下图所示TIM2/TIM3/TIM4/TIM5 寄存器
要使 STM32F429的通用定时器 TIMx产生 PWM 输出除了上一小节介绍的寄存器外我们还会用到另外 3 个寄存器来控制 PWM 输出。这三个寄存器分别是捕获/比较模式寄存器TIMx_CCMR1/2、 捕 获/比 较 使 能 寄 存 器 TIMx_CCER、 捕 获/比较寄存器TIMx_CCR1~4。接下来我们简单介绍一下这三个寄存器。捕获/比较模式寄存器 1/2TIMx_CCMR1/2
TIM2/TIM3/TIM4/TIM5的捕获/比较模式寄存器TIMx_CCMR1/2该寄存器一般有 2 个TIMx _CCMR1 和 TIMx _CCMR2。TIMx_CCMR1 控制 CH1 和 CH2而 TIMx_CCMR2 控制CH3 和 CH4。TIMx_CCMR2 寄存器描述如下图所示该寄存器的有些位在不同模式下功能不一样我们现在用到输出比较先看输出比较部分输入捕获在后面的实验再讲解。关于该寄存器的详细说明请参考《STM32F4xx 参考手册_V4中文版.pdf》第 432 页15.4.7 节。比如我们要让 TIM3 的 CH4 输出 PWM 波该寄存器的模式设置位 OC4M[2:0]就是对应着通道 4 的模式设置。总共可以配置 8 种模式我们使用的是 PWM 模式 1 或者 PWM 模式 2所以位 OC4M[2:0]设置为 110 或者 111。这两种 PWM模式的区别就是输出有效电平的极性相反。位 OC4PE 控制输出比较通道 4 的预装载使能实际就是控制 CCR4 寄存器是否进行缓冲。因为 CCR4 寄存器也是有影子寄存器的影子寄存器才是真正起作用的寄存器。CC4S[1:0]用于设置通道 4 的方向输入/输出默认设置为 0就是设置通道作为输出使用。捕获/比较使能寄存器TIMx_ CCER TIM2/TIM3/TIM4/TIM5 的捕获/比较使能寄存器该寄存器控制着各个输入输出通道的开关和极性。TIMx_CCER 寄存器描述如图 所示该寄存器比较简单要让 TIM3 的 CH4 输出 PWM 波这里我们要使能 CC4E 位该位是通道 4 输入/输出使能位要想 PWM 从 IO 口输出这个位必须设置为 1。CC4P 位是设置通道4 的输出极性我们默认设置 0。捕获/比较寄存器 1/2/3/4TIMx_ CCR1/2/3/4 捕获/比较寄存器TIMx_ CCR1/2/3/4该寄存器总共有 4 个分别对应 4 个通道CH1~CH4。我们使用的是通道 4所以来看看 TIMx_ CCR4 寄存器描述如图所示在输出模式下捕获/比较寄存器影子寄存器的值与 CNT 的值比较根据比较结果产生相应动作利用这点我们通过修改这个寄存器的值就可以控制 PWM 的占空比了。注意对于 TIM2 和 TIM5 来说该寄存器是 32 位有效的对其他定时器来说则是 16 位有效位。硬件设计
1. 例程功能
使用 TIM3 通道 4由 PB1 复用输出 PWM PB1 引脚连接了 LED0从而实现 PWM 输出控制 LED0 亮度。2. 硬件资源
1LED 灯 LED0: LED0 – PB1
2定时器 3 输出通道 4对应 PB13. 原理图
定时器属于 STM32F429 的内部资源只需要软件设置好即可正常工作。我们通过 LED0来间接指示定时器的 PWM 输出情况。程序设计
定时器的 HAL 库驱动
定时器在 HAL 库中的驱动代码在前面介绍基本定时器已经介绍了部分这里我们再介绍几个本实验用到的函数。1. HAL_TIM_PWM_Init 函数
定时器 PWM 输出基础工作参数初始化函数其声明如下
HAL_StatusTypeDef HAL_TIM_PWM_Init(TIM_HandleTypeDef *htim);函数描述
用于初始化定时器的基础工作参数即初始化 TIM_HandleTypeDef 结构体成员。函数形参
形参 1 是 TIM_HandleTypeDef 结构体类型指针变量基本定时器的时候已经介绍。函数返回值 HAL_StatusTypeDef 枚举类型的值。
注意事项 该函数实现的功能以及使用方法和 HAL_TIM_Base_Init 类似作用都是初始化定时器的ARR 和 PSC 等参数。为什么 HAL 库要提供这个函数而不直接让我们使用 HAL_TIM_Base_Init函数呢这是因为 HAL 库为定时器的针对 PWM 输出定义了单独的 MSP 回调函数HAL_TIM_PWM_MspInit所以当我们调用 HAL_TIM_PWM_Init 进行 PWM 初始化之后该函数内部会调用 MSP 回调函数 HAL_TIM_PWM_MspInit。当我们使用 HAL_TIM_Base_Init 初始化定时器参数的时候它内部调用的回调函数是 HAL_TIM_Base_MspInit这里大家注意区分。2. HAL_TIM_PWM_ConfigChannel 函数
定时器 PWM 模式通道配置函数。其声明如下HAL_StatusTypeDef HAL_TIM_PWM_ConfigChannel(TIM_HandleTypeDef *htim,TIM_OC_InitTypeDef *sConfig, uint32_t Channel);函数描述
该函数用于设置定时器的 PWM 输出模式及通道等参数。函数形参
形参 1 是 TIM_HandleTypeDef 结构体类型指针变量用于配置定时器基本参数。
形参 2 是 TIM_OC_InitTypeDef 结构体类型指针变量用于配置定时器输出比较参数。
下面重点来了解一下 TIM_OC_InitTypeDef 结构体指针类型其定义如下
typedef struct
{uint32_t OCMode; /* 输出比较模式选择寄存器的时候说过了共 8 种模式 */uint32_t Pulse; /* 设置比较值*/uint32_t OCPolarity; /* 设置输出比较极性 */uint32_t OCNPolarity; /* 设置互补输出比较极性 */uint32_t OCFastMode; /* 使能或失能输出比较快速模式 */uint32_t OCIdleState; /* 选择空闲状态下的非工作状态OC1 输出 */uint32_t OCNIdleState; /* 设置空闲状态下的非工作状态OC1N 输出 */
} TIM_OC_InitTypeDef;
我们重点关注前三个结构体成员。成员变量 OCMode 用来设置模式这里我们设置为PWM模式 1。成员变量 Pulse 用来设置捕获比较值。成员变量 TIM_OCPolarity用来设置输出极性。其他成员 TIM_OutputNStateTIM_OCNPolarityTIM_OCIdleState 和 TIM_OCNIdleState后面用到再介绍。形参 3 是定时器通道范围TIM_CHANNEL_1~4比如定时器 3 只有 4 个通道那选择范围就只有 TIM_CHANNEL_1~4所以要根据具体情况选择。函数返回值 HAL_StatusTypeDef 枚举类型的值。
3. HAL_TIM_PWM_Start 函数
定时器 PWM 输出启动函数其声明如下
HAL_StatusTypeDef HAL_TIM_PWM_Start(TIM_HandleTypeDef *htim, uint32_t Channel);函数描述
用于使能通道输出和启动计数器即启动 PWM 输出。函数形参
形参 1 是 TIM_HandleTypeDef 结构体类型指针变量。
形参 2 是定时器通道范围TIM_CHANNEL_1 到 TIM_CHANNEL_4。函数返回值 HAL_StatusTypeDef 枚举类型的值。
注意事项
HAL 库提供了单独使能定时器输出通道的函数其声明如下
void TIM_CCxChannelCmd(TIM_TypeDef *TIMx, uint32_t Channel, uint32_t ChannelState); HAL_TIM_PWM_Start 函数内部也是调用了该函数。4. HAL_TIM_ConfigClockSource 函数
配置定时器时钟源函数其声明如下
HAL_StatusTypeDef HAL_TIM_ConfigClockSource(TIM_HandleTypeDef *htim, TIM_ClockConfigTypeDef *sClockSourceConfig);函数描述
用于配置定时器时钟源。函数形参
形参 1 是 TIM_HandleTypeDef 结构体类型指针变量。
形参 2 是 TIM_ClockConfigTypeDef 结构体类型指针变量用于配置定时器时钟源参数。
TIM_ClockConfigTypeDef 定义如下
typedef struct
{uint32_t ClockSource; /* 时钟源 */uint32_t ClockPolarity; /* 时钟极性 */uint32_t ClockPrescaler; /* 定时器预分频器 */
uint32_t ClockFilter; /* 时钟过滤器 */
} TIM_ClockConfigTypeDef;函数返回值 HAL_StatusTypeDef 枚举类型的值。
注意事项
该函数主要配置 TIMx_SMCR 寄存器。默认情况下定时器的时钟源是内部时钟。本实验就是使用内部时钟的所以我们不用对时钟源进行初始化默认即可。这里只是让大家知道有这个函数可以设定时器的时钟源。比如用 HAL_TIM_ConfigClockSource 初始化选择内部时钟方法如下
TIM_HandleTypeDef timx_handle; /* 定时器 x 句柄 */
TIM_ClockConfigTypeDef sClockSourceConfig {0};
sClockSourceConfig.ClockSource TIM_CLOCKSOURCE_INTERNAL; /* 选择内部时钟 */
HAL_TIM_ConfigClockSource(timx_handle, sClockSourceConfig);
后面的定时器初始化凡是用到内部时钟我们都没有去进行初始化系统默认即可。定时器 PWM 输出模式配置步骤
1开启 TIMx 和相应通道输出的 GPIO 时钟配置该 IO 口的复用功能输出
首先开启 TIMx 的时钟然后配置 GPIO 为复用功能输出。本实验我们默认用到定时器 3通道 4对应 IO 是 PB1它们的时钟开启方法如下
__HAL_RCC_TIM3_CLK_ENABLE(); /* 使能定时器 3 */
__HAL_RCC_GPIOB_CLK_ENABLE(); /* 开启 GPIOB 时钟 */
IO 口复用功能是通过函数 HAL_GPIO_Init 来配置的。2初始化 TIMx,设置 TIMx 的 ARR 和 PSC 等参数
使用定时器的 PWM 输出功能时通过 HAL_TIM_PWM_Init 函数初始化定时器 ARR 和PSC 等参数。
注意该函数会调用HAL_TIM_PWM_MspInit 函数我们可以通过后者存放定时器和GPIO 时钟使能、GPIO 初始化、中断使能以及优先级设置等代码。3设置定时器为 PWM 模式输出比较极性比较值等参数
在 HAL 库中通过 HAL_TIM_PWM_ConfigChannel 函数来设置定时器为 PWM1模式或者PWM2 模式根据需求设置输出比较的极性设置比较值控制占空比等。4使能 TIMx使能 TIMx 的 CHy 输出
在 HAL 库中通过调用 HAL_TIM_PWM_Start 函数来使能 TIMx 的某个通道输出 PWM。5修改 TIM3_CCR4 来控制占空比
在经过以上设置之后PWM 其实已经开始输出了只是其占空比和频率都是固定的而我们可以通过修改比较值来控制 PWM的输出占空比。HAL库中提供一个修改占空比的宏定义
__HAL_TIM_SET_COMPARE (__HANDLE__, __CHANNEL__, __COMPARE__)
__HANDLE__是 TIM_HandleTypeDef 结构体类型指针变量__CHANNEL__对应 PWM 的输出通道__COMPARE__则是要写到捕获/比较寄存器TIMx_ CCR1/2/3/4的值。实际上该宏定义最终还是往对应的捕获/比较寄存器写入比较值来控制 PWM 波的占空比。如下解析比如我们要修改定时器 3 通道 4 的输出比较值控制占空比寄存器操作方法TIM3-CCR4 ledrpwmval; /* ledrpwmval 是比较值并且动态变化的 所以我们要周期性调用这条语句已达到及时修改 PWM 的占空比 */ __HAL_TIM_SET_COMPARE这个宏定义函数最终也是调用这个寄存器操作的所以说我们使用 HAL 库的函数其实就是间接操作寄存器的。程序流程图 这里我们只讲解核心代码详细的源码请大家参考本实验对应源码。通用定时器驱动源码包括两个文件gtim.c 和 gtim.h。
首先看 gtim.h 头文件的几个宏定义/* TIMX PWM 输出定义* 这里输出的 PWM 控制 LED0(RED)的亮度* 默认是针对 TIM2~TIM5*/#define GTIM_TIMX_PWM_CHY_GPIO_PORT GPIOB#define GTIM_TIMX_PWM_CHY_GPIO_PIN GPIO_PIN_1#define GTIM_TIMX_PWM_CHY_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOB_CLK_ENABLE(); }while(0) /* PB 口时钟使能 */#define GTIM_TIMX_PWM_CHY_GPIO_AF GPIO_AF2_TIM3 /* 端口复用到 TIM3 *//* TIMX REMAP 设置 */#define GTIM_TIMX_PWM TIM3 /* TIM3 */#define GTIM_TIMX_PWM_CHY TIM_CHANNEL_4 /* 通道 Y, 1 Y 4 */ #define GTIM_TIMX_PWM_CHY_CCRX TIM3-CCR4 /* 通道 Y 的输出比较寄存器 */ #define GTIM_TIMX_PWM_CHY_CLK_ENABLE() do{ __HAL_RCC_TIM3_CLK_ENABLE(); }while(0) /* TIM3 时钟使能 */可以把上面的宏定义分成两部分第一部分是定时器 3输出通道 4对应的 IO口的宏定义。第二部分则是定时器 3 输出通道 4 的相应宏定义这里的宏定义是定时器 3 通道 4 输出 PWM控制 LED0 的相关宏定义。
gtim.h 头文件就添加了这部分的程序下面看 gtim.c 的程序首先是通用定时器 PWM 输出初始化函数。/**
* brief 通用定时器 TIMX 通道 Y PWM 输出 初始化函数使用 PWM 模式 1
* note
* 通用定时器的时钟来自 APB1,当 PPRE1 ≥ 2 分频的时候
* 通用定时器的时钟为 APB1 时钟的 2 倍, 而 APB1 为 45M, 所以定时器时钟 90Mhz
* 定时器溢出时间计算方法: Tout ((arr 1) * (psc 1)) / Ft us.
* Ft 定时器工作频率,单位:Mhz
*
* param arr: 自动重装值
* param psc: 预分频系数
* retval 无
*/
void gtim_timx_pwm_chy_init(uint16_t arr, uint16_t psc)
{TIM_OC_InitTypeDef timx_oc_pwm_chy {0}; /* 定时器输出句柄 */g_timx_pwm_chy_handle.Instance GTIM_TIMX_PWM; /* 定时器 x */g_timx_pwm_chy_handle.Init.Prescaler psc; /* 预分频系数 */g_timx_pwm_chy_handle.Init.CounterMode TIM_COUNTERMODE_UP;/* 递增计数模式*/g_timx_pwm_chy_handle.Init.Period arr; /* 自动重装载值 */HAL_TIM_PWM_Init(g_timx_pwm_chy_handle); /* 初始化 PWM */timx_oc_pwm_chy.OCMode TIM_OCMODE_PWM1; /* 模式选择 PWM1 */timx_oc_pwm_chy.Pulse arr / 2; /* 设置比较值,此值用来确定占空比 */timx_oc_pwm_chy.OCPolarity TIM_OCPOLARITY_LOW; /* 输出比较极性为低 */ HAL_TIM_PWM_ConfigChannel(g_timx_pwm_chy_handle, timx_oc_pwm_chy,GTIM_TIMX_PWM_CHY); /* 配置 TIMx 通道 y *//* 开启对应 PWM 通道 */HAL_TIM_PWM_Start(g_timx_pwm_chy_handle, GTIM_TIMX_PWM_CHY);
}HAL_TIM_PWM_Init 初始化 TIM3 并设置 TIM3 的 ARR 和 PSC 等参数其次通过调用函数 HAL_TIM_PWM_ConfigChannel 设置 TIM3_CH4 的 PWM 模式以及比较值等参数最后通过调用函数 HAL_TIM_PWM_Start 来使能 TIM3 以及使能 PWM 通道 TIM3_CH4 输出。本实验我们使用 PWM 的 MSP 初始化回调函数 HAL_TIM_PWM_MspInit 来存放时钟、GPIO 的初始化代码其定义如下/**
* brief 定时器底层驱动时钟使能引脚配置此函数会被 HAL_TIM_PWM_Init()调用
* param htim:定时器句柄
* retval 无
*/
void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef *htim)
{if (htim-Instance GTIM_TIMX_PWM){GPIO_InitTypeDef gpio_init_struct;GTIM_TIMX_PWM_CHY_GPIO_CLK_ENABLE(); /* 开启通道 y 的 GPIO 时钟 */GTIM_TIMX_PWM_CHY_CLK_ENABLE(); /* 使能定时器时钟 */gpio_init_struct.Pin GTIM_TIMX_PWM_CHY_GPIO_PIN; /* 通道 y 的 GPIO 口 */gpio_init_struct.Mode GPIO_MODE_AF_PP; /* 复用推挽输出 */gpio_init_struct.Pull GPIO_PULLUP; /* 上拉 */gpio_init_struct.Speed GPIO_SPEED_FREQ_HIGH; /* 高速 */gpio_init_struct.Alternate GTIM_TIMX_PWM_CHY_GPIO_AF; /* IO 口 REMAP 设置, 是否必要查看头文件配置的说明! */HAL_GPIO_Init(GTIM_TIMX_PWM_CHY_GPIO_PORT, gpio_init_struct);}
} 该函数首先判断定时器寄存器基地址符合条件后开启对应的 GPIO 时钟和定时器时钟并且初始化 GPIO。上面是使用 HAL 库标准的做法我们亦可把 HAL_TIM_PWM_MspInit 函数里面的代码直接放到 gtim_timx_pwm_chy_init 函数里。这样做的好处是当一个项目中用到多个定时器时代码的移植性、可读性好方便管理。
在 main.c 里面编写如下代码int main(void)
{uint16_t ledrpwmval 0;uint8_t dir 1;HAL_Init(); /* 初始化 HAL 库 */sys_stm32_clock_init(360, 25, 2, 8); /* 设置时钟,180Mhz */delay_init(180); /* 延时初始化 */usart_init(115200); /* 初始化 USART */led_init(); /* 初始化 LED */gtim_timx_pwm_chy_init(500 - 1, 90 - 1); /* 90 000 000 / 90 1 000 000 1Mhz 的计数频率2Khz 的 PWM */while(1){delay_ms(10);if (dir)ledrpwmval; /* dir1 ledrpwmval 递增 */else ledrpwmval--; /* dir0 ledrpwmval 递减 */if (ledrpwmval 300)dir 0; /* ledrpwmval 到达 300 后方向为递减 */if (ledrpwmval 0)dir 1; /* ledrpwmval 递减到 0 后方向改为递增 *//* 修改比较值控制占空比 */__HAL_TIM_SET_COMPARE(g_timx_pwm_chy_handle, GTIM_TIMX_PWM_CHY,ledrpwmval);}
}本小节开头我们就说 PWM 频率由自动重载寄存器TIMx_ARR的值决定其占空比则由捕获/比较寄存器TIMx_CCRx的值决定。下面结合实际看看具体怎么计算
定时器 3 的时钟源频率为 2 倍 APB1 总线时钟频率即频率为 90MHz而调用gtim_timx_pwm_chy_init 初始化函数之后就相当于写入预分频寄存器的值为 89写入自动重载寄存器的值为 499。基本定时器讲的定时器溢出公式由公式得Tout ((arr1)*(psc1))/Tclk ((4991)*(891))/900000000.0005s再由频率是周期的倒数关系得到 PWM 的频率为 2000Hz。
占空比怎么计算的呢结合图 20.3.1我们分两种情况分析输出比较极性为低和输出比较极性为高它们的情况正好相反。因为在 main 函数中的比较值是动态变化的不利于我们计算占空比我们假设比较值固定为 200在本实验中可以调用如下语句得到。
__HAL_TIM_SET_COMPARE(g_timx_pwm_chy_handle, GTIM_TIMX_PWM_CHY, 200);
因为 LED0 是低电平有效所以我们在 gtim_timx_pwm_chy_init 函数中设置了输出比较极性为低那么当比较值固定为200时占空比 ((arr1) – CCR4)/ (arr1) (500-200)/50060%。
其中 arr 是写入自动重载寄存器TIMx_ARR的值CCR4 就是写入捕获/比较寄存器 4TIMx_CCR4的值。这里我们还需要提醒一下占空比是指在一个周期内高电平时间相对于总时间所占的比例。
另外一种情况设置了输出比较极性为高那么当比较值固定为 200 时占空比 CCR4 / (arr1) 200/50040%。可以看到输出比较极性为低和输出比较极性为高的占空比正好反过来。
在这里我们使用 DS100 示波器进行验证效果图如下图 所示这里把输出比较极性低和输出比较极性高的 PWM 波形都显示出来了。本实验默认设置PWM 模式 1、输出比较极性低当 CCR1 寄存器的值设置为 200 时对应的 PWM 波形如上图黄色的波形图。如果把输出比较极性设置为高对应的波形图就是绿色的波形图了。