Arduino Leonardo 可调频高速PWM的实现-Arduino中文社区 - Powered by Discuz!

Arduino中文社区

 找回密码
 立即注册

QQ登录

只需一步,快速开始

查看: 11699|回复: 9

Arduino Leonardo 可调频高速PWM的实现

[复制链接]
发表于 2016-11-9 23:41 | 显示全部楼层 |阅读模式
本帖最后由 希岩 于 2016-11-10 23:58 编辑

    Leonardo是一款很好用的开发板。用到了ATmel公司的ATMEGA32U4芯片,具有USB功能和高速PWM功能。
    查看网上讲的实现PWM过程也比较简单,可以说非常简单,如下所示:
#include "Arduino.h"
char pin1 = 9; // 3,5,6,9,10,11,13 均为PWM口
char pin2 = 5; //5


void setup()
{
    pinMode(pin1, OUTPUT);  
    pinMode(pin2, OUTPUT);  
    //tone(pin2,100000);  
    analogWrite(pin1, 50);      //占空比20/255  频率500HZ
    analogWrite(pin2, 90);      //占空比90/255  频率500HZ

}   

void loop()
{
  //none

}

图1 标准PWM

图1 标准PWM

其效果如图1所示,频率竟然只有500HZ,非常慢,可以调节占空比。但是觉得功能太差,用在我的Leonardo上 。看到参考中有tone这个函数,非常好,我就试了一下,程序如下,也非常简单;#include "Arduino.h"
char pin1 = 9; // 3,5,6,9,10,11,13 均为PWM口
char pin2 = 5; //5端口


void setup()
{
    pinMode(pin1, OUTPUT);  
    pinMode(pin2, OUTPUT);  
    tone(pin2,100000);            //频率100kHz
    analogWrite(pin1, 50);      //占空比20/255  频率500HZ
    //analogWrite(pin2, 90);      //占空比90/255  频率500HZ

}   

void loop()
{

  //none

}

图2 调频PWM

图2 调频PWM

调试结果如图2所示。1通道用的是数字9 ,2通道用的是数字5,可以看出,2通道频率到了10kHz。

图3 调频PWM

图3 调频PWM

但是再往上就有问题了,如图3所示,最高只能到34.33kHz,你在逗我吗?这是Leonardo的高速PWM吗?显然不是,因此我决定自己写一个函数来初始化高速的PWM通道。

图4 高速PWM流程

图4 高速PWM流程

根据图4所示数据手册给出的初始化PWM流程,对Atmega32u4 高速PWM进行初始化。本例采用初始化数字5端口和数字13引脚,这两个相位相反。我就给出了我的程序,对初学者来讲,不用管程序内容,只需要学会调用和给合适的参数。
程序完全给出,也非常简单:
[mw_shl_code=cpp,true]/*================================================================
* 晶振16Mhz
* 平台:Leonardo(ATMEGA32U4)
* 编译器:Arduino IDE1.6.8
*                                    ——设计:ChenYannan 2016/11/10
================================================================*/


#include "Arduino.h"
char pin2 = 5; //5端口
unsigned char u08temp;

/******************************************************************************
* 说明:Leonardo 已经打开了PLL,PLLCSR=19;CLKPR=74LL锁频到96MHZ,2分频后送到USB
* 此时我们需要打开PLL到定时器4
* 参数:Period 周期
* Duty 占空比,0-100
* 注意:周期是10位的数,0-1024.
******************************************************************************/
void Fast_PWM(unsigned short Period,unsigned char Duty,unsigned int upFrequency)
{   unsigned char temp;
    unsigned  short  PeriodHigh;
    SREG&=~0x80;                                                      //全局中断关
    Period&=0x3FF;                                                    //仅保留10位
    Duty&=0x7F;                                                       //保留7位
    PeriodHigh=(unsigned  short)((unsigned  long)Period*Duty/50);     //计算高电平时间
    switch(upFrequency)
    {
      case 64000:temp=1;break;                                        //64MHz  PWM
      case 32000:temp=2;break;
      case 16000:temp=3;break;
      case 8000: temp=4;break;
      case 4000: temp=5;break;
      case 2000: temp=6;break;
      case 1000: temp=7;break;                                        //1MHz  PWM
      case 500:  temp=8;break;
      case 250:  temp=9;break;
      case 125:  temp=10;break;
      case 62:   temp=11;break;
      case 31:   temp=12;break;
      case 15:   temp=13;break;
      case 8:    temp=14;break;            
      case 4:    temp=15;break;                                       //3.90625kHz
      default:   temp=7;                                              //默认1MHz
     }
    u08temp=temp;
    while (PLLCSR != 19);                    //等待PLL锁定
    PLLFRQ|=(1<<LLTM1);                     //将PLL送到TIMER4,1.5分频,速度最高64MHZ
    TCCR4A=0;
    TCCR4A=(1<<COM4A0)|(1<<WM4A);           //清除输出比较,OC4A PWM使能
    TCCR4B=(1<<WM4X)|(1<<CS40)|(1<<SR4);   //PWM反转,异步时钟1分频
    TCCR4B|=temp&0x0F;
    TCCR4D=0;                                //保护中断不开
    TCCR4E=(1<<ENHC4);                       //增强比较 10位模式

  
   
    TC4H = (Period>>8)&0x03;                 //高两位
    OCR4C  = Period&0xFF;                    //低8位
    TC4H = (PeriodHigh>>8)&0x03;             //高两位
    OCR4A = PeriodHigh&0xFF;                 //低8位

    SREG|=0x80;                              //中断开
  }
  
void setup()
{

    pinMode(pin2, OUTPUT);  
    Fast_PWM(100,50,1000);        //10kHz,50%占空比

   
}   

void loop()
{
  //none
   
}[/mw_shl_code]

    本设计程序根据Atmega32u4的数据手册,根据图4所示初始化流程进行初始化。注意,由于定时器4用到了异步时钟,即PLL时钟,这个时钟是给USB用的,不要轻易动,否则串口通信该出问题了。
    由于Leonardo本已初始化了PLL锁相环,因此我只需将锁相环时钟给路由到我的Timer4即可。另外设置定时器4的运行模式,这里重要的是OCR4C寄存器和OCR4A 寄存器。前者定义了占空比,后者定义了频率。两个都配置成了10位的,也就是说,可以有0-1024个步长,PWM的分辨率大大改善。
    同时,通过函数的第三个参数进行PWM模块的频率设置,也就是设置了计数器的单次计数时间。该高速PWM计数时钟最高可达64MHz真的非常高了,技术手册中告诉我们,这个计数器4是用来驱动BLDC(直流无刷电机)的。设置PWM默认计数频率为1MHz。

图5 高速PWM

图5 高速PWM

  
       图5 显示了调试结果,设置了10kHz PWM频率,50%占空比,1通道显示的为5引脚的电平变化,可以看出,频率为10kHz,占空比为50%。13引脚为5引脚的反相。通过调整第一个参数用来调频,调整第二个参数用来调整占空比,调整第三个参数也用来调频,不过这调频就不是连续的了。结论就是改程序是验证正确的
     美中不足的是未设置其余几个高速PWM。另外,本程序思路完全可以应用在Arduino Uno上,自己写一个可调度高的PWM,Uno可是有三个定时器,6个pWM通道的。






发表于 2016-11-10 09:40 | 显示全部楼层
赞赞赞                 
发表于 2016-11-10 13:11 | 显示全部楼层
楼主,我觉得你要把你的代码贴在代码编辑框里面,不然有些会变成表情,不方便阅读
 楼主| 发表于 2016-11-10 23:03 | 显示全部楼层
本帖最后由 希岩 于 2016-11-10 23:59 编辑
jianwei569 发表于 2016-11-10 13:11
楼主,我觉得你要把你的代码贴在代码编辑框里面,不然有些会变成表情,不方便阅读 ...

是啊,才发现里面有表情。已修改,谢谢
发表于 2016-12-1 11:04 | 显示全部楼层

回帖奖励 +1 金币

很强很强很强很强
 楼主| 发表于 2016-12-6 18:18 | 显示全部楼层
Tcwxc 发表于 2016-12-1 11:04
很强很强很强很强

lalala ==  
发表于 2016-12-15 08:38 | 显示全部楼层

回帖奖励 +1 金币

可以可以  谢谢分享
发表于 2019-1-12 19:56 | 显示全部楼层
   TCCR4B
   CS43
   TCCR4D
   TC4H
这些都是什么呀
 楼主| 发表于 2019-1-13 15:59 | 显示全部楼层
作业小斗士 发表于 2019-1-12 19:56
TCCR4B
   CS43
   TCCR4D

AVR的寄存器
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|Archiver|手机版|Arduino中文社区

GMT+8, 2024-11-28 09:24 , Processed in 0.099609 second(s), 19 queries .

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

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