Быстрый ШИМ, работающий с int main(void), но не с void setup()

плата: Arduino Mega (ATMEGA2560)

Я хочу генерировать импульсы, используя таймер 1, настроенный как быстрый ШИМ. Вот мой код, который работает отлично:

#include <avr/io.h>

int main(void)
//void setup()
{

  DDRB |= (1 << DDB5) | (1 << DDB6); // PWM outputs


  ICR1 = 0x7FFF;  // TOP
  OCR1A = 0x3FFF; // 50% of ICR1
  OCR1B = 0x3FFF; // 50% of ICR1

  TCCR1A |= (1 << COM1A1) | (1 << COM1B1);
  TCCR1A |= (1 << WGM11);
  TCCR1B |= (1 << WGM12) | (1 << WGM13);
  TCCR1B |= (1 << CS10);

}

void loop() {

}

Не понимаю, почему не работает void setup() вместо int main(void). Я работаю с Arduino IDE 1.8.2 и всегда использовал void setup(). В данном случае я использовал int main(void), потому что он используется в некоторых обучающих материалах, которые я нашёл, например, в этом: https://www.youtube.com/watch?v=O_Yqf_cugwE

РЕДАКТИРОВАНИЕ: Когда я говорю, что это не работает, на самом деле вместо сигналов ШИМ у меня просто постоянный выход 5 В на выводах OC1A и OC1B

, 👍1

Обсуждение

Попробуйте использовать = вместо |=., @Edgar Bonet

Используя = вместо |=, я теперь получаю выходное напряжение 0 В вместо 5 В. Сначала я попробовал инициализировать TCCR1A = 0; и TCCR1B = 0;., @Mr. C


1 ответ


Лучший ответ:

1

Если вы не определите свой собственный main(), ваша программа будет связана с main(), предоставляемый основной библиотекой Arduino, который выглядит примерно так: вот так (я немного упрощаю):

int main(void)
{
    init();      // Инициализация ядра Arduino
    setup();     // инициализация пользователя
    for (;;)
        loop();  // основной цикл пользователя
}

Функция ядра init() настраивает таймер 1 для 8-битной фазы правильный ШИМ на частоте 490 Гц, что означает, что к тому времени, как ваш setup() запускается, регистры управления таймером не находятся в состоянии по умолчанию больше нет, и некоторые из их битов уже установлены в 1. Если вы затем обновите эти регистры с помощью оператора |=, в результате вы получите смесь конфигурация ядра и ваша собственная.

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

Следующее работает независимо от того, используете ли вы базовую библиотеку Arduino:

#ifdef NOCORE
int main(void)
#else
void setup()
#endif
{
    DDRB   = _BV(PB5) | _BV(PB6);
    TCCR1A = _BV(COM1A1)
           | _BV(COM1B1)
           | _BV(WGM11);
    TCCR1B = _BV(WGM12)
           | _BV(WGM13)
           | _BV(CS10);
    ICR1   = 0x7fff;  // TOP
    OCR1A  = 0x3fff;  // 50% of ICR1
    OCR1B  = 0x3fff;  // 50% of ICR1
}

#ifndef NOCORE
void loop(){}
#endif

Обратите внимание:

  1. Управляющие регистры устанавливаются (=), а не просто изменяются с помощью |=
  2. Режим устанавливается до регистров сравнения выходных данных. Это связано с тем, что Настройка выходных регистров сравнения не работает должным образом, когда таймер находится в 8-битном режиме.
,

Спасибо, что указали на то, что порядок установки таймера может как-то влиять. Я понятия не имел. С вашим кодом у меня всё равно не работает. У вас была возможность протестировать?, @Mr. C

@Mr.C: Я проверил это (и всё работает) на Uno. Мне пришлось изменить DDRB на _BV(PB1) | _BV(PB2), чтобы соответствовать распиновке Uno. Обратите внимание, что в предыдущей версии ответа я по ошибке оставил настройку DDRB соответствующей Uno., @Edgar Bonet

После замены DDRB на _BV(PB1) | _BV(PB2) на соответствующие пины в MEga DDRB на _BV(PB5) | _BV(PB6) всё работает отлично, я этого не замечал. Большое спасибо @Edgar Bonet! После твоего объяснения функции init ядра мне стало понятнее, почему не работало void setup()., @Mr. C