Увеличьте битовое разрешение ШИМ

9

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

Koen

РЕДАКТИРОВАТЬ Эта установка обеспечивает 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;
    }
}
KoenR
источник

Ответы:

15

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.
Эдгар Бонет
Спасибо за объяснение. Отредактировал мой вопрос так, чтобы он покрывал эти ошибки. Так что другие люди могут видеть это напрямую. Спасибо!
KoenR
1
@ Эдгар Бонет: Это здорово, но я не могу полностью выключить светодиод. Я использую, ICR1 = 0x03FFи в 0 я вижу крошечный пульс на прицеле, достаточный, чтобы зажечь светодиод. Любые идеи?
Давид
1
@davivid: Да, у вас не может быть нулевого рабочего цикла. analogWrite16(pin, val)дает рабочий цикл (val + 1) / ICR1. Как обходной путь, Arduino analogWrite()делает if (val == 0) digitalWrite(pin, LOW); else if (val == 255) digitalWrite(pin, HIGH);. Но тогда вы не можете получить рабочий цикл 1 / ICR1 ...
Эдгар Бонет
3

С помощью некоторой калибровки вы можете суммировать выходы двух каналов ШИМ с различными весовыми резисторами. В крайнем случае вы можете условно использовать один выход для обеспечения 8-битного разрешения, а другой масштабировать до 1/255-го уровня и добавлять их так, чтобы 2-й канал покрывал один бит диапазона, и вы (опять же условно) получили 16-битное разрешение. Без огромной заботы и настройки все, что вы получите, будет беспорядок.
Однако, разделив 2-й канал на 16 или 32, вы можете добавить несколько дополнительных битов PWM-разрешения. Просто добавив 2 канала ШИМ аналоговых фильтрованных выходов, вы добавляете дополнительный бит (так как потенциальный диапазон удваивается для неизменных мВ / бит).
Условно (опять же) для каждого дополнительного деления на 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 использует этот трюк для так называемого режима «HIFI».
Эдгар Бонет
Это отличный нам ШИМ. Но разве это не сгладит форму волны? Я спрашиваю об этом, так как вы используете фильтр RC. Я не упомянул об этом в моем вопросе, но я за рулем мотора постоянного тока, мне <стыдно>. Спасибо за вклад!
KoenR
@KoenR (fwiw: я не вижу ничего постыдного.) Я не знаю, какую частотную характеристику / скорость изменения вы хотите в своем выходе АЦП. Или почему вы хотите N бит или насколько большой достаточно. Двигатели обычно не будут эффективно управляться более чем 8 битами - это зависит от того, насколько точна ваша прикладная программа. Мотор действует как часть сглаживающего фильтра благодаря индуктивности. Нужно сказать, что за мотор и как водить. И принципиальная схема имеет важное значение. Если мотор не маленький, у вас есть водитель. ШИМ с питанием от щеточного двигателя должен иметь защелкивающийся диод для пропускания тока двигателя, когда ШИМ выключен. Добавление двух ...
Рассел МакМэхон
... ШИМ здесь вполне выполнимо, но детали схемы должны быть известны.
Рассел МакМахон
Осторожно! В некоторых случаях сглаживание ШИМ с RC нижних частот нежелательно. Например, если вы подключите выход Arduino к воротам MOSFET, MOSFET будет оставаться холодным, пока он управляется чистым ШИМ. Но если вы сгладите его, MOSFET начнет рассеивать гораздо больше тепла. Иногда это не очень хорошая вещь.
Флорин Андрей