问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

单片机输出PWM的常见方法和注意事项

创作时间:
2025-01-22 03:47:40
作者:
@小白创作中心

单片机输出PWM的常见方法和注意事项

PWM(脉冲宽度调制)是一种常用的电子控制技术,广泛应用于电机控制、灯光调节、通信调制等领域。本文将详细介绍单片机中PWM的常见输出方法及其注意事项,特别以STM32F1为例,展示硬件PWM输出的具体实现过程。

什么是PWM?

PWM(Pulse Width Modulation)即脉冲宽度调制,是一种通过改变脉冲信号的占空比来实现信号调制的技术。如下图所示,PWM信号由一系列高低电平组成的脉冲信号构成,通过调整脉冲的频率和占空比,可以应用于各种不同的场合。

PWM常见输出方式

根据实现方式的不同,PWM输出可以分为以下几种级别:

1. 新手(菜鸟)级别

使用while循环和阻塞延时函数控制IO口的高低电平输出:

while(1)  
{  
  IO口高电平  
  Delay阻塞延时  
  IO口低电平  
  Delay阻塞延时  
}  

这种实现方式简单直观,但会占用大量CPU资源,且精度较低。

2. 入门(初级)级别

使用while循环和非阻塞延时函数控制IO口的高低电平输出:

while(1)  
{  
  IO口高电平  
  Delay非阻塞延时  
  IO口低电平  
  Delay非阻塞延时  
}  

非阻塞延时可以是定时器标识检测或RTOS延时,这种方式相比阻塞延时会更节省CPU资源。

3. 熟悉(中级)级别

使用定时器中断控制IO口的高低电平输出:

  1. 配置定时器中断
  2. 启动定时器
  3. 在中断服务程序中控制IO口的高低电平

这种方式相比前两种会更节省CPU资源,但当PWM频率较高时,仍会消耗较多CPU资源。

4. 熟练(中级+)级别

使用单片机自带的硬件PWM输出功能:

  1. 配置PWM对应的IO口和定时器
  2. 启动PWM自动输出

这种方式无需CPU干预,是最优的实现方式。

硬件输出PWM例子

以STM32F1为例,介绍硬件定时器输出PWM波形的具体实现。

PWM定时器相关宏定义

//定时器计数时钟(1M次/秒)  
#define PWM_COUNTER_CLOCK         1000000  
//预分频值(与系统时钟、计数值有关)  
#define PWM_PRESCALER_VALUE       (SystemCoreClock/PWM_COUNTER_CLOCK - 1)  

PWM配置

void PWM_TIM_Configuration(void)  
{  
  GPIO_InitTypeDef        GPIO_InitStructure;  
  TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;  
  TIM_OCInitTypeDef       TIM_OCInitStructure;  
  /* 时钟配置 */  
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);  
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);  
  /* 引脚配置 */  
  GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_0;  
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;  
  GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF_PP;  
  GPIO_Init(GPIOA, &GPIO_InitStructure);  
  /* 时基配置 */  
  TIM_TimeBaseStructure.TIM_Prescaler = PWM_PRESCALER_VALUE;         //预分频值  
  TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;        //向上计数  
  TIM_TimeBaseStructure.TIM_Period = 0xFFFF;                         //定时周期(暂定值)  
  TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;            //分频因子  
  TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);  
  /* PWM模式配置 */  
  TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;                  //输出PWM1模式  
  TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;      //使能输出  
  TIM_OCInitStructure.TIM_Pulse = 0;                                 //脉宽值(暂定值)  
  TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;          //输出极性(TIM_OC1对应通道1)  
  TIM_OC1Init(TIM2, &TIM_OCInitStructure);  
}  

PWM输出函数接口

void PWM_Output(uint32_t Frequency, uint32_t Dutycycle)  
{  
  uint32_t tim_period;  
  uint32_t tim_pulse;  
  tim_period = PWM_COUNTER_CLOCK/Frequency - 1;                      //计算出计数周期(决定输出的频率)  
  tim_pulse  = (tim_period + 1)*Dutycycle / 100;                     //计算出脉宽值(决定PWM占空比)  
  TIM_Cmd(TIM2, DISABLE);                                            //失能TIM  
  TIM_SetCounter(TIM2, 0);                                           //计数清零  
  TIM_SetAutoreload(TIM2, tim_period);                               //更改频率  
  TIM_SetCompare1(TIM2, tim_pulse);                                  //更改占空比(TIM_SetCompare1对应通道1)  
  TIM_Cmd(TIM2, ENABLE);                                             //使能TIM  
}  

初始化配置

void AppTask(void *p_arg)  
{  
  PWM_TIM_Configuration();  
  PWM_Output(1000, 20);  
  while(1)  
  {  
    //自己的应用代码  
  }  
}  

配置注意事项

  1. 引脚映射:如果使用的引脚需要映射,需要配置对应的参数。例如,STM32F1使用PB11时,需要增加对应的“映射”代码:

    //复用功能  
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);  
    //定时器(PWM)引脚映射  
    GPIO_PinRemapConfig(GPIO_FullRemap_TIM2, ENABLE);  
    
  2. 频率和占空比精度:使用32位定时器可以实现更宽的频率范围和更高的精度。如果使用16位定时器,需要注意参数不能超过16位(65535)。实际应用中建议增加参数判断,防止越界。

  3. 更多:不同系列的STM32在配置上可能略有差异,可以参考官方例程和手册。现在大部分单片机都带有硬件PWM输出功能,推荐使用硬件PWM以节省CPU资源。

© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号