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

希岩 发表于 2016-11-9 23:41

Arduino Leonardo 可调频高速PWM的实现

本帖最后由 希岩 于 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所示,频率竟然只有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所示。1通道用的是数字9 ,2通道用的是数字5,可以看出,2通道频率到了10kHz。

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

根据图4所示数据手册给出的初始化PWM流程,对Atmega32u4 高速PWM进行初始化。本例采用初始化数字5端口和数字13引脚,这两个相位相反。我就给出了我的程序,对初学者来讲,不用管程序内容,只需要学会调用和给合适的参数。
程序完全给出,也非常简单:
/*================================================================
* 晶振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=74;PLL锁频到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;
    unsignedshortPeriodHigh;
    SREG&=~0x80;                                                      //全局中断关
    Period&=0x3FF;                                                    //仅保留10位
    Duty&=0x7F;                                                       //保留7位
    PeriodHigh=(unsignedshort)((unsignedlong)Period*Duty/50);   //计算高电平时间
    switch(upFrequency)
    {
      case 64000:temp=1;break;                                        //64MHzPWM
      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;                                        //1MHzPWM
      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<<PLLTM1);                     //将PLL送到TIMER4,1.5分频,速度最高64MHZ
    TCCR4A=0;
    TCCR4A=(1<<COM4A0)|(1<<PWM4A);         //清除输出比较,OC4A PWM使能
    TCCR4B=(1<<PWM4X)|(1<<CS40)|(1<<PSR4);   //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
   
}

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



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






jackten 发表于 2016-11-10 09:40

赞赞赞               

jianwei569 发表于 2016-11-10 13:11

楼主,我觉得你要把你的代码贴在代码编辑框里面,不然有些会变成表情,不方便阅读

希岩 发表于 2016-11-10 23:03

本帖最后由 希岩 于 2016-11-10 23:59 编辑

jianwei569 发表于 2016-11-10 13:11
楼主,我觉得你要把你的代码贴在代码编辑框里面,不然有些会变成表情,不方便阅读 ...
是啊,才发现里面有表情。已修改,谢谢:lol

Tcwxc 发表于 2016-12-1 11:04

很强很强很强很强

希岩 发表于 2016-12-6 18:18

Tcwxc 发表于 2016-12-1 11:04
很强很强很强很强

lalala ==:P

单片机菜鸟 发表于 2016-12-15 08:38

可以可以谢谢分享

alxwxr 发表于 2016-12-15 18:26

:lol腻害,腻害

作业小斗士 发表于 2019-1-12 19:56

   TCCR4B
   CS43
   TCCR4D
   TC4H
这些都是什么呀

希岩 发表于 2019-1-13 15:59

作业小斗士 发表于 2019-1-12 19:56
TCCR4B
   CS43
   TCCR4D


AVR的寄存器
页: [1]
查看完整版本: Arduino Leonardo 可调频高速PWM的实现