16 位定时器 PWM LED 调光器
16-bit Timer PWM LED Dimmer
我是 AVR C 编程的新手,我正在使用 Atmega328p Counter/Timers 上的 16 位定时器测试一个简单的 PWM,它应该充当 LED 的调光器。
我的代码:
#define F_CPU 16000000UL
void initTimer();
int x = 1;
int n = 1000;
int main(void)
{
initTimer();
DDRB |= (1 << PB1)| (1 << PB2);
while(1)
{
x++;
if(x > 65) {
x = 1;
}
}
}
void initTimer() {
TCCR1A = (1 << COM1A1) | (1 << COM1B1) | (1 << WGM11);
TCCR1B = (1 << WGM13) | (1 << WGM12) | (1 << CS11);
// used for TOP, makes for 50 Hz PWM
ICR1 = 40000;
OCR1A = n * x;
}
ISR(TIMER1_OVF_vect)
{
OCR1A = n * x;
}
问题是它不显示调光效果,无论我最初为 OCR1A (PB1) 输出引脚设置什么值,LED 的亮度都保持不变,它应该随着中断发生而改变值,但它只是不这样做,这应该是简单的测试我做错了什么?
更新:
作为建议,我使用 TIMSK1 寄存器和 SEI() 启用了中断,但是 LED 亮度仍然是相同的问题,无论在 initTimer() 中指定的 OCR1A 的原始值如何。
int main(void)
{
initTimer();
DDRB |= (1 << PB1)| (1 << PB2);
while(1)
{
x++;
if(x > 65) {
x = 1;
}
}
}
void initTimer() {
ICR1 = 40000;
OCR1A = n * x;
TCCR1A = (1 << COM1A1) | (1 << COM1B1) | (1 << WGM11);
TIMSK1 |= (1 << ICIE1);
TCCR1B = (1 << WGM13) | (1 << WGM12) | (1 << CS11);
sei();
}
ISR (TIMER1_COMPA_vect)
{
OCR1A = n * x;
}
虽然我尝试了另一种方法并且它给了我调光效果:
int main(void)
{
initTimer();
DDRB |= (1 << PB1)| (1 << PB2);
while(1)
{
_delay_ms(20);
OCR1A = n * 4;
_delay_ms(20);
OCR1A = n * 8;
_delay_ms(20);
OCR1A = n * 15;
_delay_ms(20);
OCR1A = n * 25;
_delay_ms(20);
OCR1A = n * 1;
}
}
void initTimer() {
ICR1 = 40000;
OCR1A = n * x;
TCCR1A = (1 << COM1A1) | (1 << COM1B1) | (1 << WGM11);
TCCR1B = (1 << WGM13) | (1 << WGM12) | (1 << CS11);
}
看来问题出在中断上,因为 PWM 影响有效,但它只是无法与中断处理程序一起工作。
我首先想到的是 x 和 n 应该是易变的。您还应该在 TIMSK0 寄存器中启用中断。还可以通过调用 sei.
来启用中断
如果我是你,我会从一些已知的好示例代码开始。我提到的页面有一个每 4 毫秒触发一次中断的示例。获取该代码并打开和关闭 LED。
另一个问题是您在更改 x 时没有考虑是否调用了 isr。所以实际上你每次在 isr 中都会得到一个随机的 x。这段代码很简单,可能会卡在一个简单的模式中。而是将 x 的设置移至您的 isr。
这里有一个很好的 avr 定时器介绍:https://sites.google.com/site/qeewiki/books/avr-guide/timers-on-the-atmega328
ICR1 = 40000;
OCR1A = n * x;
TCCR1A = (1 << COM1A1) | (1 << COM1B1) | (1 << WGM11);
TCCR1B = (1 << WGM13) | (1 << WGM12) | (1 << CS11);
这是错误的,您需要在初始化ICR1和OCR1A之前配置TCCR1A和TCCR1B。有关详细信息,请参阅 this answer。
我是 AVR C 编程的新手,我正在使用 Atmega328p Counter/Timers 上的 16 位定时器测试一个简单的 PWM,它应该充当 LED 的调光器。
我的代码:
#define F_CPU 16000000UL
void initTimer();
int x = 1;
int n = 1000;
int main(void)
{
initTimer();
DDRB |= (1 << PB1)| (1 << PB2);
while(1)
{
x++;
if(x > 65) {
x = 1;
}
}
}
void initTimer() {
TCCR1A = (1 << COM1A1) | (1 << COM1B1) | (1 << WGM11);
TCCR1B = (1 << WGM13) | (1 << WGM12) | (1 << CS11);
// used for TOP, makes for 50 Hz PWM
ICR1 = 40000;
OCR1A = n * x;
}
ISR(TIMER1_OVF_vect)
{
OCR1A = n * x;
}
问题是它不显示调光效果,无论我最初为 OCR1A (PB1) 输出引脚设置什么值,LED 的亮度都保持不变,它应该随着中断发生而改变值,但它只是不这样做,这应该是简单的测试我做错了什么?
更新:
作为建议,我使用 TIMSK1 寄存器和 SEI() 启用了中断,但是 LED 亮度仍然是相同的问题,无论在 initTimer() 中指定的 OCR1A 的原始值如何。
int main(void)
{
initTimer();
DDRB |= (1 << PB1)| (1 << PB2);
while(1)
{
x++;
if(x > 65) {
x = 1;
}
}
}
void initTimer() {
ICR1 = 40000;
OCR1A = n * x;
TCCR1A = (1 << COM1A1) | (1 << COM1B1) | (1 << WGM11);
TIMSK1 |= (1 << ICIE1);
TCCR1B = (1 << WGM13) | (1 << WGM12) | (1 << CS11);
sei();
}
ISR (TIMER1_COMPA_vect)
{
OCR1A = n * x;
}
虽然我尝试了另一种方法并且它给了我调光效果:
int main(void)
{
initTimer();
DDRB |= (1 << PB1)| (1 << PB2);
while(1)
{
_delay_ms(20);
OCR1A = n * 4;
_delay_ms(20);
OCR1A = n * 8;
_delay_ms(20);
OCR1A = n * 15;
_delay_ms(20);
OCR1A = n * 25;
_delay_ms(20);
OCR1A = n * 1;
}
}
void initTimer() {
ICR1 = 40000;
OCR1A = n * x;
TCCR1A = (1 << COM1A1) | (1 << COM1B1) | (1 << WGM11);
TCCR1B = (1 << WGM13) | (1 << WGM12) | (1 << CS11);
}
看来问题出在中断上,因为 PWM 影响有效,但它只是无法与中断处理程序一起工作。
我首先想到的是 x 和 n 应该是易变的。您还应该在 TIMSK0 寄存器中启用中断。还可以通过调用 sei.
来启用中断如果我是你,我会从一些已知的好示例代码开始。我提到的页面有一个每 4 毫秒触发一次中断的示例。获取该代码并打开和关闭 LED。
另一个问题是您在更改 x 时没有考虑是否调用了 isr。所以实际上你每次在 isr 中都会得到一个随机的 x。这段代码很简单,可能会卡在一个简单的模式中。而是将 x 的设置移至您的 isr。
这里有一个很好的 avr 定时器介绍:https://sites.google.com/site/qeewiki/books/avr-guide/timers-on-the-atmega328
ICR1 = 40000;
OCR1A = n * x;
TCCR1A = (1 << COM1A1) | (1 << COM1B1) | (1 << WGM11);
TCCR1B = (1 << WGM13) | (1 << WGM12) | (1 << CS11);
这是错误的,您需要在初始化ICR1和OCR1A之前配置TCCR1A和TCCR1B。有关详细信息,请参阅 this answer。