Широтно-импульсная модуляция (ШИМ) в AVR Studio

Я пытаюсь использовать ШИМ для светодиода на ATmega8, любой контакт порта B. Настройка таймеров меня раздражает, и я не знаю, что делать с моим OCR1A. Вот мой код, и я хотел бы получить отзывы.

Я просто пытаюсь понять, как использовать ШИМ. Я знаю концепцию, и OCR1A должен быть частью всего времени счетчика, в течение которого я хочу, чтобы импульс был включен.

#define F_CPU 1000000  // 1 MHz

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

int main(void){

    TCCR1A |= (1 << CS10) | (1 << CS12) | (1 << CS11);
    OCR1A = 0x0000;
    TCCR1A |= ( 0 << WGM11 ) | ( 1 << WGM10 ) | (WGM12 << 1) | (WGM13 << 0);
    TCCR1A |= ( 1 << COM1A0 ) | ( 0 << COM1A1 );
    TIMSK |= (1 << TOIE1); // Enable timer interrupt
    DDRB = 0xFF;
    sei(); // Enable global interrupts
    PORTB = 0b00000000;

    while(1)
    {
        OCR1A = 0x00FF; //I'm trying to get the timer to alternate being on for 100% of the time,
        _delay_ms(200);
        OCR1A = 0x0066; // Then 50%
        _delay_ms(200);
        OCR1A = 0x0000; // Then 0%
        _delay_ms(200);
    }
}

ISR (TIMER1_COMA_vect)  // timer0 overflow interrupt
{
    PORTB =~ PORTB;
}

person michaeljiz    schedule 28.02.2012    source источник


Ответы (2)


Нет, это не то, как вы должны использовать ШИМ. Например, как с его помощью установить скорость ШИМ, например, 42%? Кроме того, размер кода большой, это можно сделать гораздо эффективнее. Кроме того, вы тратите 16-битный таймер на выполнение 8-битных операций. У вас есть 2x 8-битных таймера (таймер/счетчик 0 и 2) и один 16-битный таймер, Timer/Counter 1.

Также плохой идеей является установка неиспользуемых контактов портов на вывод. Все портпины, которые ни к чему не подключены, следует оставить входными.

В ATmega8 есть встроенный ШИМ-генератор на таймерах 1 и 2, программно моделировать его не нужно. Вам даже не нужно устанавливать свои порты вручную (вам нужно только установить соответствующий вывод порта для вывода)

Вам даже не нужно никакого прерывания.

#define fillrate OCR2A 


 //...

 // main()

PORTB=0x00;
DDRB=0x08;  //We use PORTB.3 as output, for OC2A, see the atmega8 reference manual

// Mode: Phase correct PWM top=0xFF
// OC2A output: Non-Inverted PWM
TCCR2A=0x81;
// Set the speed here, it will depend on your clock rate.
TCCR2B=0x02;

// for example, this will alternate between 75% and 42% PWM
while(1)
{
    fillrate = 191; // ca. 75% PWM
    delay_ms(2000);

    fillrate = 107; // ca. 42% PWM
    delay_ms(2000);
}

Обратите внимание, что вы можете использовать другой светодиод с другим ШИМ, используя тот же таймер и установив OCR2B вместо OCR2A. Не забудьте установить TCCR2A, чтобы включить OCR2B в качестве выхода для вашего PWM, так как в этом примере разрешен только OCR2A.

person vsz    schedule 23.03.2012
comment
Конечно, если вам абсолютно необходимо использовать Timer1, потому что ваше оборудование уже готово, вы можете сделать то же самое с TCCR1 и OCR1 вместо 2. Конечно, я жестко запрограммировал значения для инициализации таймера, чтобы показать, какие биты установлены, вместо этого вы можете использовать причудливые макросы. - person vsz; 23.03.2012
comment
Где найти регистр TCCR2A? В даташите нашел только TCCR2. Кроме того, хотелось бы упомянуть, что гораздо более читабельный способ - сдвинуть влево биты, которые вы хотите установить, следующим образом: TCCR2 = (1 << FOC2) | (1 << CS20); это то же самое, что и TCCR2 = 0x81;, но теперь вы можете фактически прочитать, какие биты установлены - person chwi; 18.06.2012
comment
Ваш пост заставил меня понять, что у меня должно быть OCR1B больше 0, чтобы увидеть нетривиальный вывод ШИМ. Спасибо. - person Roman Susi; 27.10.2013

Вам нужно инициализировать OCR1A двумя строками:

TCCR1A = (1 << WGM10) | (1 << COM1A1);
TCCR1B = (1 << CS10) | (1 << WGM12);

А затем используйте это:

OCR1A = in

И знайте, что диапазон 0-255. Подсчитайте свои проценты, и вот они!

#define F_CPU 1000000  // 1 MHz
#include <avr/io.h>
#include <avr/delay.h>
#include <avr/interrupt.h>

int main(void){
    TCCR1A = (1 << WGM10) | (1 << COM1A1);
    TCCR1B = (1 << CS10) | (1 << WGM12);
    DDRB = 0xFF;
    sei(); // Enable global interrupts
    PORTB = 0b00000000;

    while(1)
    {
        OCR1A = 255;
        _delay_ms(200);
        OCR1A = 125;
        _delay_ms(200);
        OCR1A = 0;
        _delay_ms(200);
    }
}
person michaeljiz    schedule 28.02.2012
comment
Я бы предпочел опросить значение таймера или настроить прерывание для изменения значения регистра сравнения. - person chwi; 18.06.2012