极客工坊

 找回密码
 注册

QQ登录

只需一步,快速开始

查看: 105154|回复: 35

Arduino学习笔记A4 - Arduino软件模拟PWM以及提高软PWM效率

  [复制链接]
发表于 2011-9-25 16:02:50 | 显示全部楼层 |阅读模式
本帖最后由 Ansifa 于 2014-6-1 22:23 编辑

Arduino软件模拟PWM以及提高软PWM效率


注:
1.这篇文章断断续续写了很久,画图技术也不精,难免错漏,大家凑合看.有问题可以留言.
2.论坛排版把我的代码缩进全弄没了,大家将代码粘贴到arduino编译器,然后按ctrl+T重新格式化代码格式即可看的舒服.


一、什么是PWM

PWM即Pulse Wavelength Modulation脉宽调制波,通过调整输出信号占空比,从而达到改变输出平均电压的目的。相信Arduino的PWM大家都不陌生,在Arduino Duemilanove 2009中,有6个8位精度PWM引脚,分别是3, 5, 6, 9, 10, 11脚。我们可以使用analogWrite()控制PWM脚输出频率大概在500Hz的左右的PWM调制波。分辨率8位即2的8次方等于256级精度。但是有时候我们会觉得6个PWM引脚不够用。比如我们做一个10路灯调光,就需要有10个PWM脚。Arduino Duemilanove 2009有13个数字输出脚,如果它们都可以PWM的话,就能满足条件了。于是本文介绍用软件模拟PWM。

二、Arduino软件模拟PWM

Arduino PWM调压原理:PWM有好几种方法。而Arduino因为电源和实现难度限制,一般使用周期恒定,占空比变化的单极性PWM。
通过调整一个周期里面输出脚高/低电平的时间比(即是占空比)去获得给一个用电器不同的平均功率。

如图所示,假设PWM波形周期1ms(即1kHz),分辨率1000级。那么需要一个信号时间精度1ms/1000=1us的信号源,即1MHz。所以说,PWM的实现难点在于需要使用很高频的信号源,才能获得快速与高精度。下面先由一个简单的PWM程序开始:
[pre lang="arduino" line="1" file="PWM1.ino"]
const int PWMPin = 13;
int bright = 0;
void setup()
{
    pinMode(PWMPin, OUTPUT);
}
void loop()
{
    if((bright++) == 255) bright = 0;

    for(int i = 0; i < 255; i++)
    {
        if(i < bright)
        {
            digitalWrite(PWMPin, HIGH);
            delayMicroseconds(30);
        }
        else
        {
            digitalWrite(PWMPin, LOW);
            delayMicroseconds(30);
        }
    }
}[/code]
这是一个软件PWM控制Arduino D13引脚的例子。只需要一块Arduino即可测试此代码。

程序解析:由for循环可以看出,完成一个PWM周期,共循环255次。
假设bright=100时候,在第0~100次循环中,i等于1到99均小于bright,于是输出PWMPin高电平;
然后第100到255次循环里面,i等于100~255大于bright,于是输出PWMPin低电平。无论输出高低电平都保持30us。
那么说,如果bright=100的话,就有100次循环是高电平,155次循环是低电平。
如果忽略指令执行时间的话,这次的PWM波形占空比为100/255,如果调整bright的值,就能改变接在D13的LED的亮度。
这里设置了每次for循环之后,将bright加一,并且当bright加到255时归0。所以,我们看到的最终效果就是LED慢慢变亮,到顶之后然后突然暗回去重新变亮。
这是最基本的PWM方法,也应该是大家想的比较多的想法。

然后介绍一个简单一点的。思维风格完全不同。不过对于驱动一个LED来说,效果与上面的程序一样。

[pre lang="arduino" line="1" file="PWM2.ino"]
const int PWMPin = 13;
int bright = 0;
void setup()
{
    pinMode(PWMPin, OUTPUT);
}
void loop()
{
    digitalWrite(PWMPin, HIGH);
    delayMicroseconds(bright * 30);
    digitalWrite(PWMPin, LOW);
    delayMicroseconds((255 - bright) * 30);
    if((bright++) == 255) bright = 0;
}[/code]
可以看出,这段代码少了一个For循环。它先输出一个高电平,然后维持(bright*30)us。然后输出一个低电平,维持时间((255-bright)*30)us。这样两次高低就能完成一个PWM周期。分辨率也是255。

三、多引脚PWM

Arduino本身已有PWM引脚并且运行起来不占CPU时间,所以软件模拟一个引脚的PWM完全没有实用意义。我们软件模拟的价值在于:他能将任意的数字IO口变成PWM引脚。当一片Arduino要同时控制多个PWM,并且没有其他重任务的时候,就要用软件PWM了。

多引脚PWM有一种下面的方式:

[pre lang="arduino" line="1" file="PWM3.ino"]int brights[14] = {0}; //定义14个引脚的初始亮度,可以随意设置
int StartPWMPin = 0, EndPWMPin = 13; //设置D0~D13为PWM引脚
int PWMResolution = 255; //设置PWM占空比分辨率

void setup()
{
    //定义所有IO端输出
    for(int i = StartPWMPin; i <= EndPWMPin; i++)
    {
        pinMode(i, OUTPUT);
        //随便定义个初始亮度,便于观察
        brights[ i ] = random(0, 255);
    }
}
void loop()
{
    //这for循环是为14盏灯做渐亮的。每次Arduino loop()循环,
    //brights自增一次。直到brights=255时候,将brights置零重新计数。
    for(int i = StartPWMPin; i <= EndPWMPin; i++)
    {
        if((brights++) == PWMResolution) brights = 0;
    }

    for(int i = 0; i <= PWMResolution; i++) //i是计数一个PWM周期
    {
        for(int j = StartPWMPin; j <= EndPWMPin; j++) //每个PWM周期均遍历所有引脚
        {
            if(i < brights[j])
            {
                digitalWrite(j, HIGH);
                delayMicroseconds(2);
            }
            else
            {
                digitalWrite(j, LOW);
                delayMicroseconds(2);
            }
        }
    }
}[/code]

这个程序比较简单,但是能演示基本的PWM功能。我们看loop(){}段,里面第一个for循环是做亮度渐增的,跟上面程序一样,每次循环自增,然后到255就置零重来。下面的for循环是外层循环组成一个PWM周期的,每个周期用255次循环完成。就是说,PWM精度255级。

看内层for循环,每个PWM周期都包含由StartPWMPin到EndPWMPin的遍历。就是说,按照brights数组里面的元素去设置每个引脚的PWM值。由于每个PWM周期都要遍历14个引脚,所以我们使用的delayMicroseconds); 延时要降低到2us左右。每个PWM周期就是2usx14只脚=28us左右,在加上代码执行时间误差。大概与原来的30us接近了。

四、提高PWM速度
由上面可以看出,多引脚PWM的周期大致为
每引脚PWM周期=每引脚判定后延时*要PWM的引脚数*每周期PWM判定次数(PWM精度)
上面的代码不包括指令执行时间,大概是2us x 14 x 255≈7ms=一个周期,频率142Hz。如果使用Arduino Mega 2560这样的大板,我们或者会用更多的引脚,比如32个。周期就变成2us x 32 x 255≈16ms一个周期,频率就是62Hz了。大概实验可以看到,如果周期超过12ms以上,驱动LED我们会看到明显的闪烁。所以必须降低三个值中的随便一个加快PWM速度。

所以我们要更改PWM周期的话,我们将精度(代码里面的变量:PWMResolution)降低就行,比如一般调整LED亮度的话,我们用64级精度就行。这样速度就是2x32x64=4ms。就不会闪了。



本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

评分

参与人数 2 +1 +5 收起 理由
幻生幻灭 + 1 原来我只能+威望,不能++贡献
弘毅 + 5 赞一个!

查看全部评分

回复

使用道具 举报

发表于 2011-11-28 23:41:54 | 显示全部楼层
  学习了··
回复 支持 反对

使用道具 举报

发表于 2012-3-23 14:59:34 | 显示全部楼层
实际上应该慢好多。

因为 digitalWrite 非常耗时,这是我对Arduino不爽的地方。
牺牲 运行速度 换取 编写速度  ,arduino的IO操作特点。
回复 支持 反对

使用道具 举报

发表于 2012-3-23 16:17:49 | 显示全部楼层
要好好學習
THX
回复 支持 反对

使用道具 举报

发表于 2012-7-3 13:20:37 | 显示全部楼层
有辨法透過軟體pwm控制多個伺服電機麻?
我想在在一塊arduino 上控制超過8個servo
回复 支持 反对

使用道具 举报

发表于 2012-7-4 04:28:13 | 显示全部楼层
软PWM+74HC595=屌爆了!
回复 支持 反对

使用道具 举报

发表于 2012-9-19 22:53:08 | 显示全部楼层
重新复习了一次PWN波,谢谢楼主分享
回复 支持 反对

使用道具 举报

发表于 2012-10-25 22:35:35 | 显示全部楼层
请教各位大师,用Arduino自带的PWM接口,使用analogWrite输出,有办法降低LED闪烁吗?
回复 支持 反对

使用道具 举报

 楼主| 发表于 2012-10-25 22:38:17 | 显示全部楼层
ewx_boy 发表于 2012-10-25 22:35
请教各位大师,用Arduino自带的PWM接口,使用analogWrite输出,有办法降低LED闪烁吗?

降低delay值,或者改成delayMicroseconds
用PORTD=0x01之类的直接驱动语句代替digitalWrite
降低PWM分辨率,比如原来255级分辨率会闪烁,改成64就好了
回复 支持 反对

使用道具 举报

发表于 2012-10-25 22:40:57 | 显示全部楼层
Ansifa 发表于 2012-10-25 22:38
降低delay值,或者改成delayMicroseconds
用PORTD=0x01之类的直接驱动语句代替digitalWrite
降低PWM分辨 ...

谢谢版主回复,
在下刚开始接触,小白中,多问问,
1、PORTD=0x01有没有详细的讲解,或看哪方面知识?
2、降低分辨率到64好像简单些,具体是哪个语句?
回复 支持 反对

使用道具 举报

发表于 2012-10-27 12:27:56 | 显示全部楼层
我试了这个程序,为什么灯是一闪一闪的?
回复 支持 反对

使用道具 举报

发表于 2012-12-3 14:26:20 | 显示全部楼层
zhangsiyan12134 发表于 2012-7-4 04:28
软PWM+74HC595=屌爆了!

为啥呀?难道软PWM还能透过595到输出脚上去吗?
回复 支持 反对

使用道具 举报

发表于 2012-12-3 15:00:21 | 显示全部楼层
为什么要delay?计算好指令运行的时间,提高扫描的频度岂不是更好?delay不是让控制器白白的浪费吗?没看懂,求讲解。。。。
回复 支持 反对

使用道具 举报

发表于 2012-12-6 06:45:22 | 显示全部楼层
长长牙齿 发表于 2012-12-3 14:26
为啥呀?难道软PWM还能透过595到输出脚上去吗?

可以的哦!具體參見ShiftPWM庫
我做的8*8RGB LED矩陣就是用的這個原理http://v.youku.com/v_show/id_XNDE2MjM0Nzc2.html
回复 支持 反对

使用道具 举报

发表于 2013-5-17 14:58:31 | 显示全部楼层
ewx_boy 发表于 2012-10-25 22:40
谢谢版主回复,
在下刚开始接触,小白中,多问问,
1、PORTD=0x01有没有详细的讲解,或看哪方面知识?
...

同求怎么配置10k频率的PWM波?????????????
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则 需要先绑定手机号

Archiver|联系我们|极客工坊

GMT+8, 2022-12-10 02:39 , Processed in 0.051256 second(s), 32 queries .

Powered by Discuz! X3.4 Licensed

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表