Невозможно установить рабочий цикл на 0% с помощью пользовательского PWM

Я пытаюсь управлять нагревательным элементом с помощью PWM с помощью Arduino Nano, но проблема в том, что даже если я установлю рабочий цикл на 0, он даст крошечный всплеск.

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

Вот код:

// Настройка быстрого ШИМ на Arduino Uno при 1 Гц на цифровом выводе D9
void setup() {

    // набор контактов ввода-вывода
    pinMode(LDR_INPUT, INPUT);      // A0
    pinMode(HEATER_OUTPUT, OUTPUT); // D9

    // Включить ШИМ-выход OC1A на цифровых выводах 9
    TCCR1A = _BV(COM1A1) | _BV(WGM11);
    // Установить быстрый ШИМ и прескалер 256 на таймере 1
    TCCR1B = _BV(WGM13) | _BV(WGM12) | _BV(CS12);
    // Установите частоту ШИМ на 1 Гц: 16 МГц/(256 * 1 Гц) - 1 = 62499
    ICR1 = 62499;

    // OCR1A = 32149; // это дает мне рабочий цикл 50%, работает нормально

    OCR1A = 0;       // но это не работает

    //разрешить задержку запуска ШИМ-таймеров
    delay(10);
}

Для тестирования я подключил светодиод к ШИМ - контакту (D9), и это шипы от считывания LDR с аналогового контакта A0 (у меня нет осциллографа).

Я получаю те же результаты с Nano и Uno. Моя версия IDE - 1.8.14.

Как я могу решить эту проблему?

Вот моя функция контроля температуры:

void Compute()
{
    unsigned long now = millis();
    int timeChange = (now - lastTime);

    if(timeChange>=SampleTime)
    {
        /*Вычислить все рабочие переменные ошибки*/
        double error = Setpoint - temprature;

        if (error <= 0)
        {
            Output = 0;
            Serial.println("level: 0");
        }
        else if (error <= 1)
        {
            Output = 5;
            Serial.println("level: 1");
        }
        else if (error <= 2)
        {
            Output = 30;
            Serial.println("level: 2");
        }
        else if (error <= 3)
        {
            Output = 50;
            Serial.println("level: 3");
        }
        else if (error <= 10)
        {
            Output = 60;
            Serial.println("level: 4");
        }
        else if (error <= 100)
        {
            Output = 200;
            Serial.println("level: 5");
        }
        else
        {
            Output = 255;
            Serial.println("level: 6");
        }

        analogWrite(HEATER_OUTPUT, Output);
        //OCR1A = map(Output, 0, 255, 0, 62499);
        delay(10);

        lastErr = error;
        lastTime = now;
    }
}

Проблема в том, что после того, как я переопределяю регистры PWM, analogWrite() кажется испорченным. Это не работает, и для всех значений выводарезультат почти одинаков.

ОБНОВЛЕНИЕ 22/06/2021

Я все еще пытаюсь исправить это, если я использую analogWrite() или digitalWrite (), чтобы отключить ШИМ-вывод, дальнейшие изменения в правилах OCR1A не действуют. какие бы значения я ни давал OCR1A, вывод остается низким, если я вручную делаю вывод высоким с помощью digitalWrite или analogWrite, вывод остается высоким навсегда, изменения в OCR1A не принимаются во внимание.

Я написал тестовую функцию

void testPWM3() {

    Serial.println("f: 1Hz | on 0s");
    digitalWrite(HEATER_OUTPUT, LOW); // выключите шим-вывод, чтобы получить полный (рабочий цикл 0%)
    delay(5000);
    /*
     После отключения вывода с помощью digitalWrite() дальнейшие изменения в OCR1A reg не принимаются в действие
    */
    digitalWrite(HEATER_OUTPUT, HIGH);
    Serial.println("f: 1Hz | on 125ms");
    OCR1A =  1953;
    delay(5000);

    Serial.println("f: 1Hz | on 250ms");
    OCR1A =  3906;
    delay(5000);

    Serial.println("f: 1Hz | on 500ms");
    OCR1A =  7812;
    delay(5000);

    Serial.println("f: 1Hz | on 1sec");
    digitalWrite(HEATER_OUTPUT, HIGH);  // это работает
    delay(5000);
}

Любые советы по этому поводу будут очень полезны, спасибо

, 👍2


2 ответа


7

Вот как это должно быть. В этом быстром режиме ШИМ рабочий цикл равен (OCR1A+1)/(ICR1+1). Если вы хотите, чтобы рабочий цикл был равен нулю, вы должны отключить ШИМ и повернуть вывод на низкийуровень.

Это то, что делает Arduino analogWrite(): он рассматривает значение 0 как особый случай. Обратите внимание, что 255 также рассматривается как особый случай, хотя это бесполезно.

Edit 1: о том, что analogWrite() испорчен.

analogWrite(9, value) устанавливает OCR1A в значение от 1 до 254 (0 и 255 обрабатываются по-разному). Рабочий цикл, который вы получаете, выглядит следующим образом: Я писал раньше (OCR1A+1)/(ICR1+1). Это не то, что ожидает ваша тестовая функция.

Я предлагаю вам отказаться от analogWrite() и вместо этого установить непосредственно OCR1A.

Edit 2: После того, как вы взяли под контроль ШИМ-канал на низком уровне, записав непосредственно в аппаратные регистры, вы не должны использовать функции Arduino digitalWrite() или analogWrite() на соответствующем выводе. Эти функции предполагают, что эти аппаратные регистры управляются исключительно ядром Arduino, и они будут возиться с ними таким образом, что, скорее всего, сломают вашу программу.

Чтобы установить рабочий цикл равным нулю, отключите режим ШИМ и установите вывод в положение НИЗКИЙ УРОВЕНЬ:

TCCR1A &= ~_BV(COM1A1);  // отключить ШИМ на выводе OC1A = PB1
PORTB  &= _BV(PB1);      // установить PB1 в НИЗКОЕ значение
,

Спасибо за ваш ответ, я пробовал это, но analogWrite не работает должным образом в моем случае, обновил вопрос., @Sumithran

Вы имеете в виду, что не работаете должным образом так, как *отличается* от того, как этот ответ прекрасно объясняет?, @timemage

@timemage, это отличный ответ, я получил то, что он говорит, с помощью analogWrite () я получаю чистый ШИМ, но с analogWrite(9,128) я должен получить рабочий цикл 50%, но то, что я получаю,-это действительно тонкий импульс.., @Sumithran

Re “_analogWrite(9,128) Я должен получить рабочий цикл 50%_”: Нет, это не так. Прочтите мой ответ внимательнее., @Edgar Bonet

о, черт меня побери, значит, чтобы получить 50%, я должен отдать половину ICR1, верно?.., @Sumithran

Привет, я все еще пытаюсь это исправить, если я использую analogWrite (), чтобы отключить вывод PWM, дальнейшие изменения в правилах OCR1A не будут действовать. какие бы значения я ни давал OCR1A, выход остается низким, если я вручную делаю вход высоким с помощью digitalWrite или analogWrite, контакт остается высоким навсегда, изменения в OCR1A не принимаются во внимание... Любые советы по этому поводу будут очень полезны, спасибо, @Sumithran


0

Быстрая ШИМ работает таким образом: при рабочем цикле 0% выходной сигнал устанавливается на высокий уровень на 1 цикл при TOP+1 для неинвертированного режима ШИМ и на низкий уровень для инвертированного режима. Если вы хотите иметь непрерывный низкий выходной сигнал, вам необходимо использовать фазовую коррекцию или ШИМ с коррекцией фазы и частоты.

,