单片机_PWM输出原理详解

2023-05-16

单片机_PWM输出原理详解

理论篇
  博主自己的经历告诉我,PWM波的理解和应用确实还是挺重要的,这里专门花一期详细介绍一下

  • 什么是PWM?

  PWM,英文名Pulse Width Modulation,是脉冲宽度调制缩写,它是通过对一系列脉冲的宽度进行调制,等效出所需要的波形(包含形状以及幅值),对模拟信号电平进行数字编码。
  通俗的说,就是控制在一个周期内,控制高电平多长时间,低电平多长时间(前面文章种有说过IO口就只有两种状态,0和1,对应就是0和5V或者0和3.3V)。也就是说通过调节高低电平时间的变化来调节信号、能量等的变化。
图为周期4毫秒的PWM波形
  图为周期4毫秒的PWM波形

  • 两个重要的概念,频率、占空比

  频率是指每秒钟信号从高电平到低电平再回到高电平的次数,为一个PWM波周期的倒数。上图中频率=1/(0.003+0.001)=250 HZ
  占空比是指高电平持续时间比一个周期持续的时间。上图中占空比=1/(1+3)=25%,所以可以通过控制占空比,来控制输出的等效电压。
  所以对于方波的话,频率和占空比就确定了一个波。

  • 怎么能产生一个PWM波?

  方法1利用芯片内部模块输出PWM信号,STM32 的定时器除了 TIM6 和 7。其他的定时器都可以用来产生 PWM 输出。其中高级定时器 TIM1 和 TIM8 可以同时产生多达 7 路的 PWM 输出。而通用定时器也能同时产生多达 4路的 PWM 输出,这样, STM32 最多可以同时产生 30 路 PWM 输出! 但是!!!同一个定时器TIM只能产生一个频率的PWM波,你只能改变占空比。 具体例程见一下实战篇STM32部分。
  方法2利用IO口高低电平转变输出PWM信号,比如上图中先把电平置1,维持1ms,然后将电平拉低,维持3ms,再将电平置高,如此循环往复下去,就可以产生一个周期4毫秒占空比为25%的PWM波了。具体方法就是给IO口加一个定时器,用定时器中断来实现及时切换高低电平。 具体历程见以下51单片机部分。

  • 定时器

  要想使用51单片机来产生一路PWM,根据上述的方法2,首先你应该知道什么是定时器?定时器是怎么工作的?

  定时器:和计数器说的是一个东西,因为它既能计时也能计数。定时器的实质是,由机器频率向一个16位寄存器累加,累加满溢出时触发中断。为了产生一个我们想要的时间间隔。比如说1s,所以我们要在这个寄存器里设定一个初值,以至于让它在这个初值上累加可以产生一个1s的倍数。这样我们就得到了稳定的时间间隔。
  这个寄存器分为TH(高八位)和TL(低八位)。所以我们需要把计算好的初值分成两部分分别放入TH和TL。

  过程
  首先,我们通过单片机的晶振频率得知其时钟周期,再尤其乘以12得到机器周期。每一个机器周期在寄存器内+1,直到加满溢出产生中断。

  举例说明
  若单片机频率为12Mhz,其时钟周期就是1/12μs,机器周期为1μs,也就是每1μs寄存器+1。16位的寄存器加到溢出最多需要(2^16)-1=65535μs,溢出也需要一个机器周期,所以总共要65536μs。但这个值太别扭,和我们要的1s没什么关系。我们最好让它记50000μs产生一次中断,所以其初值就设为65536-50000=15536。但我们还要将这个值分别放在高八位和低八位,所以要将这个十进制数,转换为4位十六进制数再分开赋值。十进制计算法:TH = 15536/256; TL = 15536%256;,进制计算问题这里不细讨论。这样的话,每50ms就会产生一次中断。我们只要用程序判断其中断20次就记1s。
  定时器部分摘自:https://www.jianshu.com/p/90ea43a7b4fd

  • PWM的应用

1 输出模拟电压(通过电压的高低来控制如LED的亮度,直流电机的速度等)
  PWM对模拟信号电平进行数字编码的方法,计算机只能输出0或5V的数字电压值而不能输出模拟电压,而我们如果想获得一个模拟电压值(介于0 - 5V的电压值),则需通过使用高分辨率计数器,改变方波的占空比来对一个模拟信号的电平进行编码。电压是以一种连接(1)或断开(0)的重复脉冲序列被夹到模拟负载上去的,连接即是直流供电输出,断开即是直流供电断开。通过对连接和断开时间的控制,只要带宽足够,可以输出任意不大于最大电压值的模拟电压。

  输出电压=(接通时间/脉冲时间)*最大电压值12

PWM输出等效电压
   PWM输出等效电压
解释部分引自 http://www.eeworld.com.cn/mcu/article_2018061939827.html

2 控制舵机
  大一大二期间做项目经常用到的一个元件就是舵机,而舵机的控制就是通过一个固定周期但是不同占空比来控制舵机摆角的位置的。
  舵机的控制一般需要一个20ms左右的时基脉冲(频率为50HZ),该脉冲的高电平部分一般为0.5ms-2.5ms范围内的角度控制脉冲部分,总间隔为2ms。以180度角度伺服为例,那么对应的控制关系是这样的:
0.5ms--------------0度;
1.0ms------------45度;
1.5ms------------90度;
2.0ms-----------135度;
2.5ms-----------180度;
在这里插入图片描述
此图可以表现脉冲宽度(也可以转换成占空比)和舵机摆臂的位置图
3 控制步进电机
  之前在做项目的过程中,一般涉及到精确控制位移的时候,这个时候往复式驱动原件(舵机)就不适合了,所以就会经常用到步进电机。
  步进电机把电脉冲信号变换成角位移以控制转子转动的微特电机。在自动控制装置中作为执行元件。每输入一个脉冲信号,步进电动机前进一步,故又称脉冲电动机。 !!!这里注意一点,直接控制单片机的话是脉冲控制,就是进来一个脉冲信号,步进电机转动一个步进角(一般为1.8°)。所以控制步进电机速度的方式就是通过控制 频率 (占空比一般都是50%)但是!!!现在可以通过接入步进电机驱动板的方式(比如博主之前使用的一款步进电机驱动板Tb6560)细分步进角。比如细分为2,一个脉冲步进电机就转动半个脉冲(0.9°)

PS:这里由于篇幅原因,舵机、步进电机的控制代码就不上传了,网上一搜一大堆,也可以联系博主私法给你喽!


实战篇

  这里使用51和STM32实现呼吸灯的功能,同样原理也可以控制直流电机,舵机是频率一定的情况下控制占空比来控制摆臂的方向,而步进电机是通过控制频率的方式来控制速度。

51部分
  例程使用51单片机将P1.0接一个二极管。运用PWM输出等效模拟电压完成呼吸灯功能。引

unsigned char PWM_COUNT;  //计数
unsigned int  HUXI_COUNT;    //占空比更新时间
unsigned char PWM_VLAUE;    //占空比比对值
bit direc_flag;             //占空比更新方向

void timer0_init()
{
    TMOD=0x02;          //模式设置,00010000,定时器0,工作于模式2(M1=1,M0=0)
    TH0=0x47;               //定时器溢出值设置,每隔200us发起一次中断。
    TL0=0X47;
    TR0=1;                  //定时器0开始计时
    ET0=1;                  //开定时器0中断
    EA=1;                       //开总中断
    PWM_COUNT =0;
}

void time0() interrupt 1
{   
    PWM_COUNT++;
    HUXI_COUNT++;
    if(PWM_COUNT == PWM_VLAUE)      //判断是否到了点亮LED的时候
        LED = 1;                    //点亮LED
    if(PWM_COUNT == 10)             //当前周期结束
    {
        LED = 0;                    //熄灭LED
        PWM_COUNT = 0;              //重新计时
    }

    if((HUXI_COUNT == 600) && (direc_flag == 0))
    {                               //占空比增加10%
        HUXI_COUNT = 0;
        PWM_VLAUE++;
        if(PWM_VLAUE == 9)          //占空比更改方向
            direc_flag = 1; 
    }

    if((HUXI_COUNT == 600) && (direc_flag == 1))
    {                               //占空比减少10%
        HUXI_COUNT = 0;
        PWM_VLAUE--;
        if(PWM_VLAUE == 1)          //占空比更改方向
            direc_flag = 0; 
    }   
}
void main()
{
    HUXI_COUNT = 0;
    PWM_COUNT = 0;
    PWM_VLAUE = 5;
    direc_flag = 0;
    LED = 1;            //默认LED熄灭   
    timer0_init();      //定时器0初始化
    while(1);
}

  例程部分引自 http://www.eeworld.com.cn/mcu/article_2018061939827.html 有删改

32部分
  转自正点原子库函数手册PWM部分教程
  这里用到了 TIM3 的部分重映射功能(重映射:可以理解成把管脚的外设功能映射到另一个管脚,具体哪个引脚可以映射见参考手册), 例程把 TIM3_CH2 直接映射到了 PB5 上。

//TIM3 PWM 部分初始化
//PWM 输出初始化
//arr:自动重装值
//psc:时钟预分频数
void TIM3_PWM_Init(u16 arr,u16 psc)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
    TIM_OCInitTypeDef TIM_OCInitStructure;
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //①使能定时器 3 时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|
    RCC_APB2Periph_AFIO, ENABLE); //①使能 GPIO 和 AFIO 复用功能时钟
    GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3, ENABLE); //②重映射 TIM3_CH2->PB5
    //设置该引脚为复用输出功能,输出 TIM3 CH2 的 PWM 脉冲波形 GPIOB.5
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; //TIM_CH2
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB, &GPIO_InitStructure); //①初始化 GPIO
    //初始化 TIM3
    TIM_TimeBaseStructure.TIM_Period = arr; //设置在自动重装载周期值
    TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置预分频值
    TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM 向上计数模式
    TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //③初始化 TIMx
    //初始化 TIM3 Channel2 PWM 模式
    TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; //选择 PWM 模式 2
    TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
    TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //输出极性高
    TIM_OC2Init(TIM3, &TIM_OCInitStructure); //④初始化外设 TIM3 OC2
    TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable); //使能预装载寄存器
    TIM_Cmd(TIM3, ENABLE); //⑤使能 TIM3
}


int main(void)
{
    u16 led0pwmval=0;
    u8 dir=1;
    delay_init(); //延时函数初始化
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置 NVIC 中断分组 2
    uart_init(115200); //串口初始化波特率为 115200
    LED_Init(); //LED 端口初始化
    TIM3_PWM_Init(899,0); //不分频,PWM 频率=72000/900=80Khz
    while(1)
    {
	    delay_ms(10);
	    if(dir)led0pwmval++;
	    else led0pwmval--;
	    if(led0pwmval>300)dir=0;
	    if(led0pwmval==0)dir=1;
	    TIM_SetCompare2(TIM3,led0pwmval);
    }
}
//实验现象:我们将看 DS0 不停的由暗变到亮,然后又从亮变到暗。每个过程持续时间大概为 3 秒钟左右。

皮一下,欢迎交流啊! 共同学习,共同进步。
在这里插入图片描述

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

单片机_PWM输出原理详解 的相关文章

随机推荐

  • 在 word 中 mathType 菜单灰色,无法使用

    问题 xff1a 在 word 中安装的 mathType 菜单是灰色的 xff0c 不能使用 解决方法 xff1a 把 C Program Files x86 Common Files microsoft shared VBA VBA6
  • 基本遗传算法(GA)的算法原理、步骤、及Matlab实现

    算法原理 遗传算法可以用来求函数的极值 xff08 1 xff09 用二进制编码来离散自变量 xff0c 码长根据离散精度来确定 码长为 l o g 2 xff08 m a x m i n 精 度 43 1 xff08 2 xff09 交叉
  • 机器人工具箱 V9.10(Robotics Toolbook) (1):建立机器人模型

    机器人学工具箱 xff08 Robotics Toolbook for Matlab 是matlab中专门用于机器人仿真的工具箱 xff0c 在机器人建模 轨迹规划 控制 可视化方面使用非常方便 创建机器人的两个最重要的函数是 xff1a
  • 机器人动力学方程的性质

    一个 n 连杆的机器人的动力学方程含有很多项 特别是全部是转动关节的机械臂 让人看着害怕 但是 机器人动力学方程含有一些有助于开发控制算法的重要性质 其中最重要的是反对称性 无源性 有界性和参数的线性性 反对称性 skew aymmetry
  • LaTEX、 Aurora、 markdown常用数学符号

    在用 LaTEX 的时候 xff0c 公式排版中经常用到各种数学符号 xff0c 平时在编写 word 文档时 xff0c 应用 Aurora 编辑公式时也经常用到 xff0c 就是在CSDN 中用markdwon 编辑公式的时候也要用到
  • 机器人(机械臂)动力学建模方法(Newton-Euler equation)

    牛顿 欧拉公式 xff08 Newton Euler equation xff09 根据中间连杆上的力 力矩平衡关系上推断出来的 它的解具有递归的形式 xff0c 前向递归用于连杆的速度 加速度的传递 xff0c 后向递归用于力的传递 参数
  • 基于重力补偿的 PD 控制

    PD 控制是常规的控制方法 xff0c 设计简单 xff0c 用李雅普诺夫方法证明简单 xff0c 不需要系统的模型 xff0c 是无模型控制中的基本方法 令 q T q T 为系统的状态向量 xff0c 其中 xff1a q 61 q d
  • 矩阵的 Jordan 标准型

    如果把矩阵化成对角矩阵 xff0c 关于矩阵的函数计算问题就会大大简化 但一般的矩阵未必与对角矩阵相似 矩阵的标准型有多重 xff0c Jordan xff08 约当 xff09 标准型是最接近对角矩阵的形式 xff0c 在控制理论中经常用
  • 滑膜控制的基本原理

    滑动模态的定义 人为设定一经过平衡点的相轨迹 xff0c 通过适当设计 xff0c 系统状态点沿着此相轨迹渐近稳定到平衡点 xff0c 或形象地称为滑向平衡点的一种运动 xff0c 滑动模态的 滑动 二字即来源于此 滑模控制的优点 xff1
  • 基于策略的设计 vs 多继承

    基于策略的设计是对多继承的超越 基于策略的设计 xff08 Policy Based Design xff09 包含两个重要的部分 xff1a 策略类 xff08 Policy Classes xff09 和一个具有极大张力的核心 许 多人
  • linux 如何以树形结构显示文件目录结构

    linux 如何以树形结构显示文件目录结构 1 linux 如何显示文件信息 一般可用 ls 命令来查看文件的信息 xff1a ls OPTION FILE 如 xff1a ls 显示所有文件 ls 1 显示所有文件 xff08 按行显示
  • ROS学习(五):package.xml 文件

    package xml 文件 和 manifest 文件类似 xff0c 描述功能包的属性 xff0c 包括功能包的名字 版本号 作者 维护者 通行证 以及所以来的功能包 http wiki ros org catkin package x
  • ROS Gazebo(一):安装与使用

    gazebo 可以主要用来进行机器人动力学的仿真 一 安装和开始 完整安装时已经安装好 xff08 ros kinetic desktop full ros jade desktop full xff0c ros indigo deskto
  • 如何在window系统VS中设置boost编程环境

    在windows系统中设置boost编程非常简单 xff1a 1 下载boost软件包 网址 xff1a http www boost org 最新版 xff1a http www boost org users history versi
  • 个人信用报告机构查询中有:深圳前海微众银行股份有限公司,为什么???

    最近 xff0c 网上各路专家纷纷现身解读新版征信 xff0c 我就查询了一下自己的 xff0c 发现一条被 深圳前海微众银行股份有限公司 查询的记录 xff0c 不理解 xff0c 惊讶带恐慌 随后查询了一下相关的知识 查询报告流程 ht
  • 各种平板显示技术比较

    各种平板显示技术简介 CRT发展历史 CRT xff08 Cathode Ray Tube xff09 即阴极射线管 xff0c 作为成像器件 xff0c 它是实现最早 应用最为广泛的一种显示技术 阴极射线管 xff08 CRT xff09
  • k8s网络与本地开发环境网络互通方案(一)

    现状 k8s集群内是有一套完整网络环境 我们不能直接通过IP访问到k8s集群内的pod 或者service 只能通过nodeport 或者ingress 才能访问到服务 痛点 开发人员进行微服务开发的时候需要通过服务发现进行Pod级服务的直
  • Opencv 3.4 中P3P位姿估计算法解析

    先上图 xff0c Opencv3 4中用两种算法实现P3P位姿估计问题 一种是基于距离P3P算法问题 xff08 算法1 xff1a P3P xff09 xff0c 一种是基于矩阵P3P算法问题 xff08 算法2 xff1a aP3P
  • 套接字

    套接字 socket 套接字 socket 是一个抽象层 应用程序可以通过它发送或接收数据 可对其进行像对文件一样的打开 读写和关闭等操作 套接字允许应用程序将I O插入到网络中 并与网络中的其他应用程序进行通信 网络套接字是IP地址与端口
  • 单片机_PWM输出原理详解

    单片机 PWM输出原理详解 理论篇 博主自己的经历告诉我 xff0c PWM波的理解和应用确实还是挺重要的 xff0c 这里专门花一期详细介绍一下 什么是PWM xff1f PWM xff0c 英文名Pulse Width Modulati