Как увеличить яркость или уменьшить яркость светодиода с помощью pwm atmega avr - PullRequest
0 голосов
/ 02 ноября 2018

Не знаю почему, но вместо увеличения яркости светодиодных импульсов период между каждым импульсом становится короче. Это скопированный код из учебника, в его видео он работал нормально, но для меня это не сработало даже в симуляторе. Как это может произойти?

Использование 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 Ответ

0 голосов
/ 04 ноября 2018

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 прямо внутри основного кода.

...