Увеличить разрядность PWM

Я хотел бы увеличить разрядность ШИМ Arduino Uno. На данный момент это 8-бит, что я считаю слишком низким. Возможно ли это без потери возможности прерываний и задержек?

Коэн

ИЗМЕНИТЬ Эта настройка дает 16-битный результат

void setupPWM16() {
    DDRB |= _BV(PB1) | _BV(PB2);        /* set pins as outputs */
    TCCR1A = _BV(COM1A1) | _BV(COM1B1)  /* non-inverting PWM */
        | _BV(WGM11);                   /* mode 14: fast PWM, TOP=ICR1 */
    TCCR1B = _BV(WGM13) | _BV(WGM12)
        | _BV(CS11);                    /* prescaler: clock / 8 */
    ICR1 = 0xffff;                      /* TOP counter value (freeing OCR1A*/
}
/* Comments about the setup
Changing ICR1 will effect the amount of bits of resolution.
ICR1 = 0xffff; (65535) 16-bit resolution
ICR1 = 0x7FFF; (32767) 15-bit resolution
ICR1 = 0x3FFF; (16383) 14-bit resolution etc....

Changing the prescaler will effect the frequency of the PWM signal.
Frequency[Hz}=CPU/(ICR1+1) where in this case CPU=16 MHz
16-bit PWM will be>>> (16000000/8)/(65535+1)=30.5175Hz
*/

/* 16-bit version of analogWrite(). Works only on pins 9 and 10. */
void analogWrite16(uint8_t pin, uint16_t val)
{
    switch (pin) {
        case  9: OCR1A = val; break;
        case 10: OCR1B = val; break;
    }
}

, 👍11


2 ответа


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

16

Arduino Uno основан на микроконтроллере ATmega382P. Этот чип имеет два 8-битных таймера, каждый из которых управляет двумя каналами ШИМ, и один 16-битный таймер, управление двумя последними каналами.

Вы не можете увеличить разрешение 8-битных таймеров. Ты можешь, однако поместите 16-битный таймер в 16-битный режим вместо 8-битного режима используется основной библиотекой Arduino. Это даст вам два 16-битных ШИМ. каналов, с пониженной частотой 244 Гц (максимум). Вы будете наверное придется таймер самому настраивать, а толку от этого не будет простая в использовании функция analogWrite(). Подробности смотрите в разделе на таймере 1 в ATmega328P техническое описание.

Обновление: вот реализация 16-битного analogWrite(). Это работает только на контактах 9 и 10, так как это единственные контакты, подключенные к 16-битный таймер.

/* Configure digital pins 9 and 10 as 16-bit PWM outputs. */
void setupPWM16() {
    DDRB |= _BV(PB1) | _BV(PB2);        /* set pins as outputs */
    TCCR1A = _BV(COM1A1) | _BV(COM1B1)  /* non-inverting PWM */
        | _BV(WGM11);                   /* mode 14: fast PWM, TOP=ICR1 */
    TCCR1B = _BV(WGM13) | _BV(WGM12)
        | _BV(CS10);                    /* no prescaling */
    ICR1 = 0xffff;                      /* TOP counter value */
}

/* 16-bit version of analogWrite(). Works only on pins 9 and 10. */
void analogWrite16(uint8_t pin, uint16_t val)
{
    switch (pin) {
        case  9: OCR1A = val; break;
        case 10: OCR1B = val; break;
    }
}

Вы можете заметить, что верхняя часть последовательности счетчиков настроена явно. Вы можете изменить это значение на меньшее, чтобы ШИМ быстрее за счет снижения разрешения.

А вот примерный скетч, иллюстрирующий его использование:

void setup() {
    setupPWM16();
}

/* Test: send very slow sawtooth waves. */
void loop() {
    static uint16_t i;
    analogWrite16(9, i);
    analogWrite16(10, 0xffff - i);
    i++;
    delay(1);
}
,

Вау, спасибо большое, это именно то, что мне нужно. Я хочу, чтобы мой результат ШИМ был таким же, как разрешение моего датчика. Если я изменю ваш код на <посмотрите мое редактирование>, будет ли это 13-битный результат? Если да, то какая будет частота? Я буду управлять двигателем постоянного тока, так что 244 Гц будет немного меньше, я думаю., @KoenR

@KoenR: Нет, прескалер не влияет на разрешение, его цель - замедлить подсчет. Установка предделителя на 8 даст вам частоту ШИМ 30,5 Гц. Если вы хотите 13-битное разрешение, установите ICR1 в 0x1fff, тогда ваша частота будет 1953 Гц (F\_CPU/(TOP+1)) с предварительным делителем на 1., @Edgar Bonet

Спасибо вам за разъяснение. Отредактировал мой вопрос, чтобы он покрывал эти ошибки. Таким образом, другие люди могут видеть это напрямую. Спасибо!, @KoenR

Могу ли я использовать это с экраном CAN-шины, CS которого находится на порту 9? Если да, то меняется ли только эта строка кода: DDRB |= _BV(PB1) | _БВ(ПБ2);, @KoenR

Удалите _BV(PB1) и _BV(COM1A1) из setupPWM16(), затем измените изменение analogWrite16() так, чтобы он работал _только_ на выводе 10., @Edgar Bonet

Фантастический ответ! Но будет ли это работать с ATmega32u4, как на Arduino Micro?, @Florin Andrei

@FlorinAndrei: я не знаю 32u4, но, согласно [сводной таблице данных](http://www.atmel.com/Images/Atmel-7766-8-bit-AVR-ATmega16U4-32U4_Summary.pdf), он должен работать, так как имеет два 16-битных таймера и «Четыре ШИМ-канала с программируемым разрешением от 2 до 16 бит»., @Edgar Bonet

@Edgar Bonet Это здорово, но я не могу полностью выключить светодиод. Я использую ICR1 = 0x03FF, и при 0 я вижу крошечный импульс на осциллографе, достаточный для того, чтобы зажечь светодиод. Есть идеи?, @davivid

@davivid: Да, у вас не может быть нулевого рабочего цикла. analogWrite16(pin, val) дает рабочий цикл (val+1)/ICR1. В качестве обходного пути Arduino analogWrite() делает if (val == 0) digitalWrite(pin, LOW); иначе если (val == 255) digitalWrite(pin, HIGH);. Но тогда вы не можете получить рабочий цикл 1/ICR1..., @Edgar Bonet

@ Эдгар Бонет: Спасибо за объяснение - обходной путь работает для меня., @davivid

Я пытаюсь выяснить, можно ли увеличить разрешение ШИМ на Atmega168P на трех контактах. Насколько я понимаю, 16-битный таймер работает только с двумя выводами. Может быть, есть способ использовать два полных цикла 8-битного таймера, используя переменную, которая отслеживает, находится ли он на первом или втором цикле? Рабочий цикл 50% будет включен для одного 8-битного цикла и выключен для второго., @Dima

@Dima: Возможно, но это будет непросто., @Edgar Bonet


4

Проведя некоторую калибровку, вы можете суммировать выходные сигналы двух каналов ШИМ с разными весовыми резисторами. В крайнем случае вы можете условно использовать один выход для обеспечения 8-битного разрешения и масштабировать другой до 1/256 уровня и добавить их, чтобы 2-й канал покрывал один бит диапазона, и вы (опять же условно) получили 16-битное разрешение. Без огромной заботы и настройки все, что вы получите, будет беспорядком.
Однако, разделив 2-й канал на 16 или 32, вы можете добавить несколько дополнительных бит разрешения ШИМ. Просто добавив 2 канала PWM с аналоговыми фильтрами, вы добавляете дополнительный бит (поскольку потенциальный диапазон удваивается при неизменном уровне мВ/бит).
Условно (опять же) для каждого дополнительного деления на 2 вы получаете дополнительный бит разрешения, но это может быть выполнено только для, может быть, 4, или 5, или 6 дополнительных битов, с возрастающими требованиями к точности масштабирующих резисторов и более сложной калибровкой и склонностью к ошибкам. .

Краткий пример.
Если масштаб одного ШИМ уменьшить, чтобы получить, скажем, 0–255 мВ с шагом 1 мВ, то суммирование двух ШИМ с одинаковой амплитудой даст диапазон 0–510 мВ с шагом 1 мВ.
Если масштаб одного ШИМ уменьшить в 32 раза, то вместо добавления 255 мВ к начальному диапазону ШИМ он добавит только 8 мВ к верхнему пределу (0,256,32 = 8 мВ, но разрешение будет в 0,03125 (1/32-го числа). ) шаг мВ.

Хотя этого, возможно, можно было бы достичь только с помощью суммирования резисторов и RC-фильтрации, использование сумматора на операционном усилителе значительно улучшило бы результаты.

Кроме того, пульсации ШИМ можно отфильтровать с помощью простого RC-фильтра, но использование одного операционного усилителя в качестве буфера (или даже одного транзистора в качестве эмиттерного повторителя) даст вам 3 или 5 полюсов фильтрации нижних частот и гораздо больше шансов достижение дополнительного разрешения ШИМ. Я не проверял «фазовую когерентность» выходных сигналов ШИМ, но ожидаю, что они двигаются относительно синхронно, поэтому вы не получите преимущества сглаживания при добавлении двух некоррелированных сигналов.

При необходимости можно сделать дополнительные комментарии. Спросите, если интересно.

,

Это умно! Кажется, что [библиотека синтеза звука Mozzi](http://sensorium.github.io/Mozzi/) использует этот трюк для так называемого режима «HIFI»., @Edgar Bonet

Это большое преимущество ШИМ. Но не сгладит ли это форму волны? Я спрашиваю об этом, поскольку вы используете RC-фильтр. Не упомянул об этом в своем вопросе, но я управляю двигателем постоянного тока <мне стыдно>. Спасибо за отзыв!, @KoenR

@KoenR (fwiw: я не вижу ничего стыдного.) Я не знаю, какую частотную характеристику / скорость изменения вы хотите получить на выходе вашего АЦП. Или почему вам нужны N бит или насколько они велики. Двигатели обычно не управляются более чем 8 битами - зависит от того, насколько точное у вас приложение. Мотор действует как часть сглаживающего фильтра за счет индуктивности. Вам нужно сказать, что за двигатель и как привод. И принципиальная схема о существенном. Если двигатель не крошечный, у вас есть водитель. Коллекторный двигатель, питаемый ШИМ, должен иметь запирающий диод для пропуска тока двигателя, когда ШИМ выключен. Добавление двух..., @Russell McMahon

... ШИМ здесь вполне выполнимы, но необходимо знать детали схемы., @Russell McMahon

Остерегаться! В некоторых случаях сглаживание ШИМ низкочастотным RC нежелательно. Например, если вы подключите выход Arduino к затвору МОП-транзистора, МОП-транзистор будет оставаться холодным, пока он управляется чистым ШИМ. Но если его сгладить, МОП-транзистор начнет рассеивать гораздо больше тепла. Иногда это не очень хорошо., @Florin Andrei