大数跨境
0
0

STM32F407基础教程13——PWM之呼吸灯

STM32F407基础教程13——PWM之呼吸灯 Easy单片机
2025-08-08
0
导读:定时器PWM输出基本概念与库函数

一、PWM介绍

PWMPulse Width Modulation——脉冲宽度调制是利用微处理器的数字输出来对模拟电路进行控制的一种非常有效的技术,广泛应用在测量、通信功率控制与变换的许多领域中。

PWM技术中有个重要的概念,称为占空比(Duty Cycle),即在一个周期内,高电平占整个信号周期的百分比。PWM信号也就是占空比可变的周期性方波信号。

PWM是定时器输出比较典型的应用。除了基本定时器外,其他定时器都可以产生PWM信号输出。通用定时器可以同时产生4PWM,高级定时器可以同时产生多达7PWM

以通用定时器为例,每个定时器有4PWM输出通道TIMx_CH1TIMx_CH2TIMx_CH3TIMx_CH4每个通道都对应一个捕获/比较寄存器TIMx_CCR1TIMx_CCR2TIMx_CCR3TIMx_CCR4

二、PWM实现原理

计数器CNT的值与捕获/比较寄存器TIMx_CCRx的值相比较,根据比较结果决定输出电平高低,从而实现PWM信号输出。

现在假设定时器计数模式为向上计数定时器自动重装载寄存器TIMx_ARR值为ARR捕获/比较值寄存器TIMx_CCRx的值CCRx

计数器CNT的值从0计数到自动重载值ARR后又从0开始计数,故自动重装载寄存器TIMx_ARR的值决定了PWM信号的周期。

假设计数器值小于CCRx,输出低电平;计数器值大于CCRx,输出高电平。PWM信号输出效果见下图。

根据上图对比可以看出CCRx值不同,PWM信号的占空比也不同,即CCRx值决定了PWM信号的占空比

当然也可以设置为当计数值小于CCRx值时,输出高电平,计数值大于CCRx值时,输出低电平。效果见下图。

三、PWM输出通道结构

计数器CNT的值与捕获/比较寄存器CCRx的值相比较,根据比较结果通过引脚输出PWM信号。根据比较结果是输出高电平还是低电平则需通过相关寄存器进行设置。具体输出通道设置见下图。

1TIMx_CCMR1寄存器的OC1M[2:0],设置输出模式

110PWM模式1

在向上计数时,一旦TIMx_CNT < TIMx_CCR1时通道1为有效电平,否则为无效电平;在向下计数时,一旦TIMx_CNT > TIMx_CCR1时通道1为无效电平,否则为有效电平。

111PWM模式2

在向上计数时,一旦TIMx_CNT < TIMx_CCR1时通道1为无效电平,否则为有效电平;在向下计数时,一旦TIMx_CNT > TIMx_CCR1时通道1为有效电平,否则为无效电平。

2设置通道输出极性

0高电平有效

1低电平有效

3输出信号使能信号能否输出到对应引脚

0关闭

1开启

举例说明1向上计数,PWM模式1,低电平有效;

2)向上计数,PWM模式2,高电平有效;

两种方式输出信号效果见下图。

三、PWM相关库函数

1PWM输出初始化库函数

函数原型:

void TIM_OCxInit(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);x=1,2,3,4

函数参数:

TIMxTIM1~TIM5TIM8~TIM14

TIM_OCInitStruct:结构体指针

typedef struct

{

uint16_t TIM_OCMode; // PWM模式PWM模式1PWM模式2

uint16_t TIM_OutputState; // 设置比较输出使能/失能

uint16_t TIM_OutputNState; // 该参数仅用于高级定时器

uint16_t TIM_Pulse; // 写入捕获比较寄存器的值,0x00~0xFF

uint16_t TIM_OCPolarity; // 比较输出极性 

uint16_t TIM_OCNPolarity; //该参数仅用于高级定时器

uint16_t TIM_OCIdleState; //该参数仅用于高级定时器

uint16_t TIM_OCNIdleState; //该参数仅用于高级定时器

} TIM_OCInitTypeDef;

1TIM_OCMode:

TIM_OCMode_PWM1PWM模式1

TIM_OCMode_PWM2PWM模式2

2TIM_OutputState

TIM_OutputState_Enable比较输出使能

TIM_OutputState_Disable比较输出禁止

3)TIM_OCPolarity

TIM_OCPolarity_High:高电平有效

TIM_OCPolarity_Low:低电平有效

使用方法实例

TIM_OCInitTypeDef  TIM_OCInitStructure;//定义结构体

TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //PWM模式1

TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能TIM_OCInitStructure. TIM_Pulse=0;//比较值

TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //输出极性高电平 

TIM_OC2Init(TIM3, &TIM_OCInitStructure); //初始化TIM3比较输出通道2

2、设置比较值库函数,外部改变TIM_Pulse,即改变CCR的值

函数原型

void TIM_SetComparex(TIM_TypeDef* TIMx, uint16_t Compare); (x=1,2,3,4)

函数参数:

TIMxTTIM1~TIM5TIM8~TIM14

Compare:值范围0x00~0xFF

使用方法示例TIM_SetCompare1(TIM2,1000);//定时器2通道1,设定比较值100

3使能预装载寄存器库函数

函数原型

void TIM_OCxPreloadConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPreload);x=1,2,3,4

使用方法示例TIM_OC1PreloadConfig(TIM2, TIM_OCPreload_Enable);//使能定时器2通道1预装载寄存器

4使能自动重装载寄存器库函数

函数原型

void TIM_ARRPreloadConfig(TIM_TypeDef* TIMx, FunctionalState NewState);

使用方法示例TIM_ARRPreloadConfig(TIM2,ENABLE);

【说明】PWM应用中,我们经常需要在运行时改变占空比(CCRx寄存器)和周期(ARR寄存器),如果直接写入这些寄存器,新值会立即生效,这可能会导致当前输出信号异常。为了避免这种情况,我们需要使用预装载寄存器和影子寄存器的机制。写入的新值会下一个周期生效。

五、PWM使用步骤

1使能定时器时钟和GPIO口时钟

2配置PWM引脚,选择引脚、模式等

3配置时基单元,设置重装载值、预分频值、计数模式等

4配置比较输出结构体,调用TIM_OCxInit(TIMx,&TIM_OCInitStruct)函数完成初始化

5使能预装载寄存器

TIM_OCxPreloadConfig(TIMx, TIM_OCPreload_Enable);

6、使能自动重装载寄存器

7、TIM_ARRPreloadConfig(TIMx, ENABLE);

8、使能定时器TIM_Cmd(TIMx,ENABLE);

9不断改变比较值CCRx,达到不同的占空比效果

TIM_SetComparex(TIMx,Compare); 

示例代码:以定时器14、输出通道1为例,对应引脚PF9

void TIM14_PWM_Init(){GPIO_InitTypeDef  GPIO_InitStruct;  //GPIO口结构体TIM_TimeBaseInitTypeDef  TIM_TimeBaseStruct;//时基结构体TIM_OCInitTypeDef  TIM_OCInitStruct;//比较输出结构体/*1-开定时器和GPIO口时钟*/RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM14, ENABLE);RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF,ENABLE); /*2-配置定时器输入引脚*/GPIO_PinAFConfig(GPIOF,GPIO_PinSource9,GPIO_AF_TIM14);//复用功能配置GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9; //选择A2引脚GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF; //复用功能模式GPIO_InitStruct.GPIO_Speed = GPIO_Speed_100MHz; //输出速度GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;   //推挽输出GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP; // GPIO_Init(GPIOF, &GPIO_InitStruct);  /*3-时基配置*/TIM_TimeBaseStruct.TIM_Period = 1000-1;//自动重装载ARR值 TIM_TimeBaseStruct.TIM_Prescaler = 84-1;//预分频PSC值 10KHzTIM_TimeBaseStruct.TIM_ClockDivision=TIM_CKD_DIV1;//1分频TIM_TimeBaseStruct.TIM_CounterMode = TIM_CounterMode_Up;//向上计数模式TIM_TimeBaseInit(TIM14, &TIM_TimeBaseStruct);//完成初始化/*4-比较输出配置*/TIM_OCInitStruct.TIM_OCMode = TIM_OCMode_PWM2; //PWM模式1TIM_OCInitStruct.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能TIM_OCInitStruct.TIM_OCPolarity = TIM_OCPolarity_High; //输出极性高电平 TIM_OC1Init(TIM14, &TIM_OCInitStruct);  //初始化TIM5比较输出通道3/*5-使能预装载寄存器*/TIM_OC1PreloadConfig(TIM14, TIM_OCPreload_Enable);/*6-使能自动重装载寄存器*/TIM_ARRPreloadConfig(TIM14, ENABLE);/*7-使能定时器*/TIM_Cmd(TIM14,ENABLE);}

六、PWM应用之呼吸灯(开发板上的LED灯连接PF9,PWM通道1)

主函数

#include "stm32f4xx.h"void delay_ms(unsigned int n){SysTick->CTRL = 0// Disable SysTickSysTick->LOAD = n * 21 * 1000 - 1// Count from 255 to 0 (256 cycles),0算一次,所以要减1SysTick->VAL = 0// Clear current value as well as count fla// 0位控制使能,2位控制选择FCLK(1)还是STCLK(1),这里选择stclkSysTick->CTRL = 1// Enable SysTick timer with processor clockwhile ((SysTick->CTRL & 0x00010000)==0);// Wait until count flag is set,控制寄存器的第16位用于检测是否计数结束SysTick->CTRL = 0// Disable SysTick}uint16_t cnt = 0;int main(void{    TIM14_PWM_Init();    while (1) {       for(cnt = 0; cnt < 999; cnt++){       TIM_SetCompare1(TIM14, cnt);	delay_ms(5);       }    }}

效果

【声明】内容源于网络
0
0
Easy单片机
本公众号专注于单片机技术开发领域。用于记录和分享单片机开发相关技术知识,学习笔记,设计示例和基础教程。包括C/C++、51单片机、STM32单片机、PCB设计等。
内容 62
粉丝 0
Easy单片机 本公众号专注于单片机技术开发领域。用于记录和分享单片机开发相关技术知识,学习笔记,设计示例和基础教程。包括C/C++、51单片机、STM32单片机、PCB设计等。
总阅读3
粉丝0
内容62