我想用16MHz Arduino Uno(ATMEGA328P)设置自定义频率(12Hz)和占空比(20%)。
AVR计算器产量:
ICR1 = 20833
OCR1A = 4167
我已经阅读了大量的论坛和tuts,但由于某种原因,我无法让这个工作。
以下是我的代码:
void setup()
{
// PB1 is now an output (Pin9 Arduino UNO)
DDRB |= (1 << DDB1);
// PB2 is now an output (Pin10 Arduino UNO)
DDRB |= (1 << DDB2);
// Set PWM frequency/top value
ICR1 = 20833;
// Set PWM duty cycle
OCR1A = 4167;
// Set inverting mode (start low, go high)
TCCR1A |= (1 << COM1A1);
TCCR1A |= (1 << COM1B1);
TCCR1A |= (1 << COM1A0);
TCCR1A |= (1 << COM1B0);
// Set fast PWM Mode
TCCR1A |= (1 << WGM11);
TCCR1B |= (1 << WGM12);
TCCR1B |= (1 << WGM13);
// Set prescaler to 64 and starts PWM
TCCR1B |= (1 << CS10);
TCCR1B |= (1 << CS11);
}
void loop() {
// Refresh PWM frequency
OCR1A = 4167;
}
如果有人能提供帮助,那就太好了!
谢谢,
迪伦
好的,所以我似乎找到了这个问题。我没有为模式14中的快速PWM正确设置寄存器(ATMEGA328P有15个timer1模式)。经过大量的实验和进一步的阅读,下面是可变频率和占空比的正确设置。 ICR1表示TOP值(控制频率),OCR1A表示开关值(占空比)。
// ADJUSTABLE VARIABLES
// Strobe frequency
uint16_t timer1Prescaler = 64;
uint8_t strobeFreq = 20,
strobeDutyCycle = 20;
void setup
{
// Set PB1 to be an output (Pin9 Arduino UNO)
DDRB |= (1 << PB1);
// Clear Timer/Counter Control Registers
TCCR1A = 0;
TCCR1B = 0;
// Set non-inverting mode
TCCR1A |= (1 << COM1A1);
// Set fast PWM Mode 14
TCCR1A |= (1 << WGM11);
TCCR1B |= (1 << WGM12);
TCCR1B |= (1 << WGM13);
// Set prescaler to 64 and starts PWM
TCCR1B |= (1 << CS10);
TCCR1B |= (1 << CS11);
// Set PWM frequency/top value
ICR1 = (F_CPU / (timer1Prescaler*strobeFreq)) - 1;
OCR1A = ICR1 / (100 / strobeDutyCycle);
}
void loop()
{
// main loop code
}
注意:在使用Arduino IDE时清除T / C控制寄存器非常重要,因为它在执行setup()函数之前会在后台进行一些设置。
变量“strobeDutyCycle”设置为任何大于50的数字后,PWM停止工作。这是因为变量“strobeDutyCycle”被声明为uint8_t,这意味着如果我们有例如51,那么100/51将等于1,因为unsigned int(“uint8_t”)会削减小数部分。因此,任何大于50的数字都会获得相同的数字,从而导致OC1A输出引脚上的输出为0。该问题的解决方案是将变量“strobeDutyCycle”声明为float,然后在除法完成后将其转换为uint16_t。
float strobeDutyCycle = 60;
uint16_t timer1Prescaler = 1024;
uint16_t strobeFreq= 2;
...
float pwmFrequency = (F_CPU / (timer1Prescaler*strobeFreq)) - 1;
float dutyCycleDivisor = 100 / strobeDutyCycle;
float pwmValueWithDutyCycle = pwmFrequency / dutyCycleDivisor;
ICR1 = (uint16_t)pwmFrequency;
OCR1A = (uint16_t)pwmValueWithDutyCycle;