c - 如何使用 pwm atmega avr 增加亮度或调暗 LED

标签 c timer interrupt avr

我不知道为什么,LED 脉冲并没有增加亮度,而是每个脉冲之间的周期越来越短。这是从教程中复制的代码,在他的视频中效果很好,但对我来说效果不佳,即使在模拟器中也是如此。怎么会发生这种事?

使用 avr 328p。

#define F_CPU   20000000

#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>

double dutyCycle = 0;

int main(void)
{   
    DDRD = (1 << PORTD6);
    TCCR0A = (1 << COM0A1) | (1 << WGM00) | (1 << WGM01);
    TIMSK0 = (1 << TOIE0);
    OCR0A = (dutyCycle/100.0)*255.0;
    sei();

    TCCR0B = (1 << CS00) | (1 << CS02);
    while(1)
    {
        _delay_ms(100);
        dutyCycle += 10;
        if(dutyCycle > 100){
            dutyCycle = 0;
        }                       
    }
}

ISR(TIMER0_OVF_vect){ OCR0A = (dutyCycle/100.0)*255;}

最佳答案

1) 如果某个变量在主代码和中断中同时使用,则必须将其标记为 volatile 。然后对它的每次读或写都会被编译为对相应存储单元的读/写。否则,编译器可以优化,最小化内存访问。因此,在主程序内部写入变量在中断中将不可见。

2) 为什么使用double?除非非常必要,否则不要使用浮点。 AVR没有对浮点运算的硬件支持,因此每个浮点运算将被表示为多个运算。在您的示例中,没有什么可以阻止使用从 0 到 255 变化的整数变量。即使您想使用 0-100 范围变量,您也可以使用整数算术重新计算它。

3) 请注意更新长度超过 1 个字节的变量。 AVR是8位架构。这意味着,更新内存中超过 8 位宽的变量需要一系列多个操作。 double 是 8 个字节长,需要太多这样的操作。中断可能在该系列中间的任何时刻触发,这意味着 ISR 内获得的变量值将仅部分更新,从而导致不可预测的结果。在主代码中,在 cli() - sei() 中关闭 ISR 内部使用的且宽度超过 1 个字节的变量的任何更新。

3) 避免在 ISR 中进行硬计算。根据经验:任何 ISR 都应该尽快完成,所有密集计算都应该放在 ISR 之外。

4) 在此示例中,您根本不需要 ISR!您可以在主代码中编写 OCR0A。

关于c - 如何使用 pwm atmega avr 增加亮度或调暗 LED,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53110480/

相关文章:

c - 为什么这个c程序输出这样?

java - Activity 关闭并重新启动后计时器不会停止

c# - Windows Phone 8.1 运行时 HttpClient 异步,结果相同

arduino - 关注 Arduino 程序中 micros() 值的漂移

interrupt - 如何在Windows或Linux ubuntu 14.04中观察中断

c - OpenGL程序不会停止接受输入并且不显示输出

c - getc(fp): strange character : ÿ ( at the very bottom )

c++ - 如何在多线程中安全地使用 boost deadline timer?

operating-system - 处于管理模式时不会触发外部中断

C 文件中的行数 (UNIX)