16bit P&F correct PWM 如何设置Timer1的频率和占空比

How to set frequency and duty cycle of Timer1 in 16bit P&F correct PWM

我正在使用 328P(内部 8mhz 上的运行)生成大约 111K hz 或 120K hz 的方波,并具有可调节的占空比。

我对这种级别的硬件操作还很陌生,所以请原谅我的无知,我花了 3 天时间在网上和数据表中查找,但恐怕我对首字母缩写词和缩写词的含义的理解仍然太过有限

到目前为止,我在网上和数据表中进行研究后得出了以下代码。但我觉得我遗漏了一些东西,在(数据表 20.12.5)相位和频率校正 PWM 模式中,什么寄存器操纵频率以及什么寄存器操纵占空比?

void setup(){
    DDRB |= (1 << DDB1); //Set PB1 as output
    OCR1A = (Unsure of what TOP should be here);
    TCCR1A = (1 << WGM10) | (1 << COM1B0) | (1 << COM1A0);
    TCCR1B = (1 << CS10) | (1 << WGM13);
}
void loop(){
    //manipulate duty cycle with other code here
}

我错过了什么或者我应该在这里做什么不同的事情? 我尝试使用在线 AVR Timer Calc 来帮助我获得该频率所需的时钟滴答。它说没有预分频器和 16 位定时器的总共 72 个定时器滴答将产生 111Khz(近似)方波。跳到 73 或 71 会使频率超出所需范围太多。有什么方法可以让 AVR 更接近?

在标题中,您问的是"ATMega328P How to set TIMER1 PWM Frequency"。您可以通过将 TOP 值设置为您想要的值来调整频率。 TOP 值越小意味着计数器到达 TOP 越早,因此频率越高。用于定时器的预分频器和时钟源决定了它的计数速度,因此这也会影响频率、可能的频率范围以及设置频率的分辨率。您的 TOP 值还决定了您在设置占空比时有多少分辨率,因为占空比应该是一个小于或等于 TOP 的整数。

在 post 的底部附近,你问 "is there any way to get that closer on an AVR?" 因为你的 AVR 在 8 MHz 时是 运行,你可以用它的定时器生成的每个频率都是以下形式( 8 MHz)/N,其中 N 是某个整数。那是因为定时器在您提供给它们的时钟源的滴答声之间不能做任何事情或改变任何事情。定时器 1 每微秒将获得 8 次时钟滴答,每次时钟滴答发生时它都可以增加其计数 and/or 控制 PWM 引脚 and/or 触发中断。我想您已经使用在线 AVR 计时器计算器解决了这个问题。以下是您可以在 8 MHz 的 AVR 运行 上获得的一些可实现的 PWM 频率:

  • 8000 kHz / 73 = 109.6 kHz
  • 8000 kHz / 72 = 111.1 kHz
  • 8000 kHz / 71 = 112.7 kHz

如果您需要更精确的频率调谐,您需要以某种方式在您的系统中获得更快的时钟源。您可以获得更快的微控制器,或者弄清楚如何提供更快的时钟作为微控制器上定时器的输入。

经过与我的一位朋友(也是一名 EE 并使用嵌入式系统)进行大量研究后,我们找到了从 328p 中获得所需内容的最佳解决方案。我将 post 下面的解决方案提供给任何有兴趣并需要类似东西的人。

void setup() {
  // put your setup code here, to run once:
  //Set Timer1 for around 109Khz signal
  cli();//stop interrupts
  TCCR1A = 0;// set entire TCCR1A register to 0
  TCCR1B = 0;// same for TCCR1B
  TCNT1  = 0;//initialize counter value to 0
  //Set register bits 
  TCCR1A = _BV(COM1A1) | _BV(WGM11);
  TCCR1B = _BV(CS10) | _BV(WGM12) | _BV(WGM13);
  ICR1 = 73; // frequency = 8000000/73 = 109.589 kHz
  OCR1A = 0; //0% Duty Cycle or Full Off.
  sei();//allow interrupts
}

void loop() {
  // put your main code here, to run repeatedly:
  OCR1A = 36; //50% Duty Cycle 73/2 = 36.5 Can be changed as needed.
}

此代码设置定时器 1 的寄存器,因此它不应干扰 Arduino 库中的毫秒或其他计时功能。无论如何我都需要操作一个特定的引脚,PB1 (OC1A)(或 Arduino 数字引脚 9)是这将振荡的引脚。

您可以根据一些简单的数学运算将 ICR1 更改为您需要的任何值, 您的时钟频率除以计数器的值等于产生的近似频率。 OCR1A 设置信号的占空比。

你的确切频率是有限的,但对于我的需要来说,这是可行的。 我仍然能够用它来驱动我正在使用的换能器。

这是对最初问题的快速回答,并且允许我更改占空比作为奖励。我不记得我们设置的寄存器的确切信息,当我有时间时,我会用数据 sheet 中的相关信息更新这个答案。