STM32F103ZET6应用笔记(一)---PWM生成
一、PWM简介
PWM(Pulse Width Modulation)是一种调制技术,用于控制电子设备中的信号的占空比。它通过改变信号的脉冲宽度来控制输出信号的平均功率。
在PWM中,信号由一系列脉冲组成,每个脉冲的宽度可以调整。脉冲的宽度决定了信号的占空比,即脉冲高电平的时间与脉冲周期的比值。占空比越大,脉冲高电平时间越长,平均功率也就越大。
PWM的应用非常广泛,特别是在电子设备中的电机控制、调光、音频放大器等方面。通过改变PWM信号的占空比,可以实现对电机转速、LED灯亮度、音频音量等的精确控制。
PWM信号的频率和占空比可以根据具体应用的需求进行调整。较高的PWM频率可以提供更精细的控制,但也会增加功耗。而较高的占空比可以提供更大的输出功率,但也会增加系统的热量。
总的来说,PWM是一种通过改变信号的脉冲宽度来控制输出信号的平均功率的调制技术。它在电子设备中的控制和调节中起着重要的作用。
二、STM32F103ZET6有哪些定时器可以生成PWM
以下是该型号的单片机常用的定时器用于PWM生成的配置:
1. 定时器2 (TIM2):包含4个通道,可以生成多个独立的PWM信号。
2. 定时器3 (TIM3):包含4个通道,可以生成多个独立的PWM信号。
3. 定时器4 (TIM4):包含4个通道,可以生成多个独立的PWM信号。
4. 定时器1 (TIM1):包含4个通道,可以生成多个独立的PWM信号,同时具有更高的分辨率和更多的功能。
这些定时器在STM32F103ZET6上都可以配置为PWM模式,并用于生成PWM信号。具体使用哪个定时器来生成PWM,取决于您的需求和资源分配。
请注意,在使用定时器生成PWM信号之前,还需根据具体的需求进行相关的配置,如设置定时器的频率、占空比等参数,以实现所需的PWM波形输出。
总结:STM32F103ZET6微控制器上常用的定时器用于生成PWM的有TIM2、TIM3、TIM4和TIM1,具体使用哪个定时器取决于您的需求和资源分配。
三、使用IAR软件和STM32官方的LL库编写PWM程序
说明:生成12路占空比和周期可调的PWM。
//r_pwm.h
#ifndef __R_PWM_H__
#define __R_PWM_H__
#include "common.h"
#define TIM1_CH1 LL_TIM_CHANNEL_CH1 //PA8
#define TIM1_CH2 LL_TIM_CHANNEL_CH2 //PA9
#define TIM1_CH3 LL_TIM_CHANNEL_CH3 //PA10
#define TIM1_CH4 LL_TIM_CHANNEL_CH4 //PA11
#define TIM2_CH1 LL_TIM_CHANNEL_CH1 //PA0测试成功
#define TIM2_CH2 LL_TIM_CHANNEL_CH2 //PA1测试成功
#define TIM2_CH3 LL_TIM_CHANNEL_CH3 //PA2测试成功
#define TIM2_CH4 LL_TIM_CHANNEL_CH4 //PA3测试成功
#define TIM3_CH1 LL_TIM_CHANNEL_CH1 //PA6测试成功
#define TIM3_CH2 LL_TIM_CHANNEL_CH2 //PA7测试成功
#define TIM3_CH3 LL_TIM_CHANNEL_CH3 //PB0测试成功
#define TIM3_CH4 LL_TIM_CHANNEL_CH4 //PB1测试成功
#define TIM4_CH1 LL_TIM_CHANNEL_CH1 //PB6测试成功
#define TIM4_CH2 LL_TIM_CHANNEL_CH2 //PB7测试成功
#define TIM4_CH3 LL_TIM_CHANNEL_CH3 //PB8测试成功
#define TIM4_CH4 LL_TIM_CHANNEL_CH4 //PB9测试成功
//TIM5、TIM8未编写
class PWM
{
public:
PWM(TIM_TypeDef *TIMx, uint32_t Channelx);
void Start();
void Stop();
void SetDutyCycle(uint8_t dutyCycle); // DutyCycle范围0~100
void SetPeriod(uint32_t period_us); // Period单位为微秒(us)
private:
TIM_TypeDef * TIMx;
uint32_t Channelx;
};
#endif
//r_pwm.cpp
#include "r_pwm.h"
PWM::PWM(TIM_TypeDef *TIMx, uint32_t Channelx) : TIMx(TIMx), Channelx(Channelx)
{
// 这部分需要根据你的硬件引脚和定时器进行参数的配置
LL_GPIO_InitTypeDef GPIO_InitStruct = {0};
if (TIMx == TIM1)
{
LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_GPIOA);
LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_TIM1);
if (Channelx == TIM1_CH1)//TIM1_CH1:PA8
{
//把引脚设置为PWM模式
GPIO_InitStruct.Pin = LL_GPIO_PIN_8;
LL_GPIO_SetPinMode(GPIOA, LL_GPIO_PIN_8, LL_GPIO_MODE_ALTERNATE); // 配置引脚PA8为复用功能
LL_GPIO_AF_EnableRemap_TIM1(); // 使能TIM3重映射功能
}
else if (Channelx == TIM1_CH2)//TIM1_CH2:PA9
{
GPIO_InitStruct.Pin = LL_GPIO_PIN_9;
}
else if (Channelx == TIM1_CH3)//TIM1_CH3:PA10
{
GPIO_InitStruct.Pin = LL_GPIO_PIN_10;
}
else if (Channelx == TIM1_CH4)//TIM1_CH4:PA11
{
GPIO_InitStruct.Pin = LL_GPIO_PIN_11;
}
// 根据上面选择好的针脚对IO口进行相关设置
GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_HIGH;
GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
LL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}
else if (TIMx == TIM2)
{
// 启动GPIOA的电源
LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_GPIOA);
LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_TIM2);
//LL_GPIO_AF_EnableRemap_TIM2(); // 使能TIM3重映射功能
if (Channelx == TIM2_CH1)//TIM2_CH1:PA0
{
//把引脚设置为PWM模式
GPIO_InitStruct.Pin = LL_GPIO_PIN_0;
}
else if (Channelx == TIM2_CH2)//TIM2_CH2:PA1
{
GPIO_InitStruct.Pin = LL_GPIO_PIN_1;
}
else if (Channelx == TIM2_CH3)//TIM2_CH3:PA2
{
GPIO_InitStruct.Pin = LL_GPIO_PIN_2;
}
else if (Channelx == TIM2_CH4)//TIM2_CH4:PA3
{
GPIO_InitStruct.Pin = LL_GPIO_PIN_3;
}
// 根据上面选择好的针脚对IO口进行相关设置
GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_HIGH;
GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
LL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}
else if (TIMx == TIM3)
{
// 启动GPIOA的电源
LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_TIM3);
if (Channelx == TIM3_CH1)//TIM3_CH1:PA6
{
LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_GPIOA);
GPIO_InitStruct.Pin = LL_GPIO_PIN_6;
}
else if (Channelx == TIM3_CH2)//TIM3_CH2:PA7
{
LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_GPIOA);
GPIO_InitStruct.Pin = LL_GPIO_PIN_7;
}
else if (Channelx == TIM3_CH3)//TIM3_CH3:PB0
{
LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_GPIOB);
GPIO_InitStruct.Pin = LL_GPIO_PIN_0;
}
else if (Channelx == TIM3_CH4)//TIM3_CH4:PB1
{
LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_GPIOB);
GPIO_InitStruct.Pin = LL_GPIO_PIN_1;
}
// 根据上面选择好的针脚对IO口进行相关设置
GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_HIGH;
GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
LL_GPIO_Init(GPIOA, &GPIO_InitStruct);
LL_GPIO_Init(GPIOB, &GPIO_InitStruct);
}
else if (TIMx == TIM4)
{
// 启动GPIOB的电源
LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_TIM4);
LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_GPIOB);
if (Channelx == TIM4_CH1)//TIM4_CH1:PB6
{
//把引脚设置为PWM模式
GPIO_InitStruct.Pin = LL_GPIO_PIN_6;
}
else if (Channelx == TIM4_CH2) //TIM4_CH2:PB7
{
GPIO_InitStruct.Pin = LL_GPIO_PIN_7;
}
else if (Channelx == TIM4_CH3)//TIM4_CH3:PB8
{
GPIO_InitStruct.Pin = LL_GPIO_PIN_8;
}
else if (Channelx == TIM4_CH4)//TIM4_CH4:PB9
{
GPIO_InitStruct.Pin = LL_GPIO_PIN_9;
}
// 根据上面选择好的针脚对IO口进行相关设置
GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_HIGH;
GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
LL_GPIO_Init(GPIOB, &GPIO_InitStruct);
}
LL_TIM_InitTypeDef TIM_InitStruct;
// Setting up required TIMER
TIM_InitStruct.Prescaler = 71;//预分频器
TIM_InitStruct.CounterMode = LL_TIM_COUNTERMODE_UP;//向上计数模式
TIM_InitStruct.Autoreload = 65535;
TIM_InitStruct.ClockDivision = LL_TIM_CLOCKDIVISION_DIV1;//设置时钟分割因子
LL_TIM_Init(TIMx, &TIM_InitStruct);
LL_TIM_DisableARRPreload(TIMx);
LL_TIM_SetClockSource(TIMx, LL_TIM_CLOCKSOURCE_INTERNAL);
LL_TIM_OC_InitTypeDef TIM_OC_InitStruct;
/*
1. LL_TIM_OCMODE_PWM1模式:
在每个周期中,PWM 信号的占空比从 0% 变化到设定的比例,然后保持高电平直到下一个周期开始。
适用于大多数常见的 PWM 应用,如驱动电机、控制 LED 亮度等。
2. LL_TIM_OCMODE_PWM2模式:
在每个周期中,PWM 信号的占空比从 0% 变化到设定的比例,然后在中间值和设定值之间交替切换。
适用于一些特殊的应用,例如功率端的谐波抑制、升压/降压转换器等。
*/
TIM_OC_InitStruct.OCMode = LL_TIM_OCMODE_PWM1;
TIM_OC_InitStruct.OCState = LL_TIM_OCSTATE_DISABLE;
TIM_OC_InitStruct.OCNState = LL_TIM_OCSTATE_DISABLE;
TIM_OC_InitStruct.CompareValue = 0;
TIM_OC_InitStruct.OCPolarity = LL_TIM_OCPOLARITY_HIGH;
LL_TIM_OC_Init(TIMx, Channelx, &TIM_OC_InitStruct);
LL_TIM_OC_DisableFast(TIMx, Channelx);
LL_TIM_SetTriggerOutput(TIMx, LL_TIM_TRGO_RESET);
LL_TIM_DisableMasterSlaveMode(TIMx);
}
void PWM::Start()
{
LL_TIM_EnableCounter(TIMx);
LL_TIM_CC_EnableChannel(TIMx, Channelx);
/*
LL_TIM_EnableAllOutputs(TIMx); // 使能所有的输出通道
LL_TIM_EnableCounter(TIMx); // 使能TIM1计数器
LL_TIM_EnableAllOutputs(TIMx); // 使能TIM1的所有输出
*/
}
void PWM::Stop()
{
LL_TIM_DisableCounter(TIMx);
LL_TIM_CC_DisableChannel(TIMx, Channelx);
}
void PWM::SetDutyCycle(uint8_t dutyCycle)
{
if (dutyCycle >= 100)
{
dutyCycle = 100;
}
uint32_t compareVal = (dutyCycle * (LL_TIM_GetAutoReload(TIMx) + 1)) / 100;
if(Channelx ==LL_TIM_CHANNEL_CH1)//通道选择
LL_TIM_OC_SetCompareCH1(TIMx, compareVal);
else if(Channelx ==LL_TIM_CHANNEL_CH2)
LL_TIM_OC_SetCompareCH2(TIMx, compareVal);
else if(Channelx ==LL_TIM_CHANNEL_CH3)
LL_TIM_OC_SetCompareCH3(TIMx, compareVal);
else if(Channelx ==LL_TIM_CHANNEL_CH4)
LL_TIM_OC_SetCompareCH4(TIMx, compareVal);
}
void PWM::SetPeriod(uint32_t period_us)
{
uint32_t TimerClk = SystemCoreClock;
uint32_t prescaler = (TimerClk/1000000*period_us+65535)/65536;
uint32_t ARR = (TimerClk/1000000*period_us)/prescaler;
LL_TIM_SetPrescaler(TIMx, prescaler - 1);
LL_TIM_SetAutoReload(TIMx, ARR - 1);
}
//main.cpp
#include "common.h"
#include "r_pwm.h"
#include "r_gpio.h"
#include "r_systick.h"
r_systick timer;
r_gpio led;
u8 Cycle=1;
u16 DutyCycle=40;
void TIM_PWM();
int main()
{
InitSystem();
timer.Init1usTick();
PWM myPWM(TIM1, TIM1_CH1);//PB6
myPWM.SetPeriod(900);//9Khz
myPWM.SetDutyCycle(50);
myPWM.Start();
//led.Init(GPIOB,Pin9,led.C_Mode_Output,led.C_Speed_High,led.C_OutputType_PP,led.C_Pull_Up);
while(1)
{
timer.delayms(30);
if(Cycle)
DutyCycle++;
else
DutyCycle--;
if(DutyCycle>100)
Cycle=0;
if(DutyCycle==0)
Cycle=1;
myPWM.SetDutyCycle(DutyCycle); //修改比较值(占空比)
}
}
四、代码运行效果
将相应代码烧进正点原子的精英板,由于正点原子的LED灯连接的是PB5,所以将相应的PWM通道对应的引脚使用杜邦线连接到PB5,就可以看到呼吸等的效果。
这里测试仅用了PA0(TIM2的CH1),其他经测试,效果都实现了,附图如下。
PWM视频