首页 > 解决方案 > 如何使用 pwm atmega avr 增加亮度或调暗 LED

问题描述

我不知道为什么,但不是增加亮度,而是 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;}

标签: ctimerinterruptavr

解决方案


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。


推荐阅读