Установить ШИМ по периоду? И он переменный?

В другом QA стека я спрашиваю о генерации импульсного сигнала с периодом от 63 мс. до 6,5 мс. Существует популярная библиотека ШИМ, однако она не компилируется, а также, поскольку 63 мс составляет 15,8 Гц, настройка целочисленной частоты в этой библиотеке бесполезна.

Есть ли определенный процесс, который не размахивает пучками благовоний по углам комнаты? В Интернете слишком много «как сделать», однако ни одно из них, похоже, не привело меня к решению.

У меня есть Mega и я использую TIMER5. Это «точно так же, как» TIMER1 в том смысле, что это 16-битный таймер.

, 👍2


1 ответ


2

Я наткнулся на решение методом проб и ошибок, наблюдая за результатами на о'скопе.

Лучшее руководство дал QA Stack Exchange. Настройка не особенно особенная, однако комбинация битов WGM здесь сработала, и я не стал исследовать дальше. Наибольшее значение имеют биты делителя предварительного делителя, три младших значащих бита TCCRxB. 001 означает отсутствие деления или предварительного масштабирования часов.

Все сводилось к 4 регистрам и вызову pinMode(). В итоге семь строк кода:

#define PULSE_PIN 45
void setup() {
    // ВАЖНО: очистить регистры управления таймером/счетчиком
    TCCR5A = 0;
    TCCR5B = 0;

    // Установка регистров управления таймером/счетчиком
    TCCR5A = B00101001; // Правильное изменение фазы и частоты ШИМ на OCRA
    TCCR5B = B00010010; // биты наименьшего знака устанавливают "несущую частоту" путем деления тактовой частоты процессора
    // Наблюдение:
    // TCCR5B = B00010010 и OCR5A = 16667 результатов за период 16,70 мс
    // означает, что OCR5A — это период в мкс, когда TCCR5B = B00010010
    // TCCR5B = B00010001 : OCR5A = 20000 результатов за период = 2,5 мс 399,3 Гц

    pinMode(PULSE_PIN, OUTPUT);
}

В скетче я наблюдаю за аналоговым выводом с потенциометром и сопоставляю его размах с эффективным диапазоном оборотов от 950 до 9000 об/мин. Отсюда произвольный диапазон периодов, которые я хочу обрабатывать. Когда loop() видит изменение АЦП, приведенный ниже код соответствующим образом настраивает ШИМ.

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

void setRpm(){
    // Долгий путь к периоду в мкс из оборотов в минуту
    // ответ в диапазоне типа данных:
    unsigned int rpm = map(pot_adc, 0, 1023, RPM_MIN, RPM_MAX);
    unsigned long period = rpm/50;
    period = 24000/period;
    period = period*50;
    // Триггер длится 26°, поэтому PW составляет 26/360 * период.
    dwell = period/36;
    dwell = dwell*26;
    dwell = dwell/10;

    // OCR5A - счетчик, до которого считает несущую частоту,
    // который устанавливает период/частоту вывода
    // означает, что OCR5A — это период в мкс, когда TCCR5B = B00010010
    OCR5A = period; // OCR5A — это период в мкс, когда TCCR5B = B00010010
    // OCR5B — ширина импульса. Если бы мы использовали float,
    // мы могли бы использовать рабочий цикл * период.
    OCR5B = dwell;
}

Очевидно, что этот код будет иметь ограничения на диапазон частот, которые он может обрабатывать, но в Интернете есть множество средств для более высокочастотной ШИМ, особенно когда желаемая ШИМ фиксирована и не нуждается в изменении.

Самый полезный способ увидеть регистры управления — это двоичные позиции, а не полагаться на макросы, маски и сдвиг битов. Этот блок комментариев очень помог:

 // WGM modes: 
 // TCCR1A
 // Bit              7      6      5      4      3     2     1     0
 // Bit Name         COM1A1 COM1A0 COM1B1 COM1B0 ----- ----- WGM11 WGM10
 // Initial Value    0      0      0      0      0     0     0     0
 // changed to       1      1      1      1      0     0     0     1

 // TCCR1B
 // Bit            7      6      5       4        3        2        1        0
 // Bit Name       ICNC1  ICES1  -----   WGM13    WGM12    CS12     CS11     CS10
 // Initial Value  0      0      0       0        0        0        0        0
 // changed to     0      0      0       0        1        0        0        1  
 // CS12,CS11,CS10 = (0,1,1) = prescaler divide by 64
,

Мне нравится сдвигать биты в группах. Например, TCCR1A = (0b11 <<COM1A0) | (0b11 <<COM1B0) | (0b01 << WGM10) ; и TCCR1B = (0b11 <<WGM12)| (0b001 <<CS10) -- тогда метод проб и ошибок переворачивания битов становится более понятным и самодокументируемым -- Если вы хотите попробовать прескалер /64, это 1-битное редактирование 0b011, @Dave X