ШИМ произвольного разрешения на UNO/Nano (328)
- Войдите на сайт для отправки комментариев
Столкнулся с проблемой - не хватало разрешения стандартного 8-бит ШИМа. Изменение скважности на 1 давало слишком большой эффект. Потребовался ШИМ более высокого разрешения (второй параметр analogWrite нужен не 0-255, а 0-8192 в моём случае).
Написал три функции, которые позволяют сделать на пинах 9 и 10 ШИМ с разрешением от 2 бит (второй параметр analogWrite в пределах 0-3) до 16 бит (второй параметр analogWrite - 0-65 535).
Выкладываю просто на тот случай, если кому пригодится, чтобы время не терять.
Вот код.
inline void pwmInit(const int8_t bitRate = 16) { noInterrupts(); TCCR1A = bit(COM1A1) | bit(COM1B1) | bit(WGM11); TCCR1B = bit(WGM13) | bit(WGM12) | bit(CS10); TCCR1C = TCNT1 = OCR1A = OCR1B = TIMSK1 = TIFR1 = 0; ICR1 = (1u << bitRate) - 1u; interrupts(); } inline void pwmWrite9(const uint16_t v) { DDRB |= bit(PB1); OCR1A = v; } inline void pwmWrite10(const uint16_t v) { DDRB |= bit(PB2); OCR1B = v; }
Функцию pwmInit нужно вызвать один раз (например, в setup). Её параметр - разрядность ШИМ. Может быть от 2 до 16. Функция не проверяет корректность параметра - на Вашей совести.
После этого можно вызывать функции pwmWrite9 (для 9-го пина) и/или pwmWrite10 (для 10-го) пина и устанавливать скважность точно также, как мы вызываем analogWrite. Значение 0 - сигнала нет, максимальное значение - сигнал сплошной.
Допустимые максимальные значения (как и частота ШИМ) зависят от разрядности (параметра, который передавали функции pwmInit).
В таблице первый столбец - разрядность (параметр, который передаём pwmInit), второй столбец - максимальное значение, которое можно передавать pwmWrite9 и pwmWrite10 (то, при котором сигнал будет сплошным) в столбцах - частоты ШИМ при тех или иных тактовых частотах микроконтроллера. Для стандартной ардуины следует смотреть столбец 16МГц (второй справа).
Бит | Макс | Частота контроллера (МГц) | |||||
1 | 2 | 4 | 8 | 16 | 20 | ||
2 | 3 | 250 000,0 | 500 000,0 | 1 000 000,0 | 2 000 000,0 | 4 000 000,0 | 5 000 000,0 |
3 | 7 | 125 000,0 | 250 000,0 | 500 000,0 | 1 000 000,0 | 2 000 000,0 | 2 500 000,0 |
4 | 15 | 62 500,0 | 125 000,0 | 250 000,0 | 500 000,0 | 1 000 000,0 | 1 250 000,0 |
5 | 31 | 31 250,0 | 62 500,0 | 125 000,0 | 250 000,0 | 500 000,0 | 625 000,0 |
6 | 63 | 15 625,0 | 31 250,0 | 62 500,0 | 125 000,0 | 250 000,0 | 312 500,0 |
7 | 127 | 7 812,5 | 15 625,0 | 31 250,0 | 62 500,0 | 125 000,0 | 156 250,0 |
8 | 255 | 3 906,3 | 7 812,5 | 15 625,0 | 31 250,0 | 62 500,0 | 78 125,0 |
9 | 511 | 1 953,1 | 3 906,3 | 7 812,5 | 15 625,0 | 31 250,0 | 39 062,5 |
10 | 1 023 | 976,6 | 1 953,1 | 3 906,3 | 7 812,5 | 15 625,0 | 19 531,3 |
11 | 2 047 | 488,3 | 976,6 | 1 953,1 | 3 906,3 | 7 812,5 | 9 765,6 |
12 | 4 095 | 244,1 | 488,3 | 976,6 | 1 953,1 | 3 906,3 | 4 882,8 |
13 | 8 191 | 122,1 | 244,1 | 488,3 | 976,6 | 1 953,1 | 2 441,4 |
14 | 16 383 | 61,0 | 122,1 | 244,1 | 488,3 | 976,6 | 1 220,7 |
15 | 32 767 | 30,5 | 61,0 | 122,1 | 244,1 | 488,3 | 610,4 |
16 | 65 535 | 15,3 | 30,5 | 61,0 | 122,1 | 244,1 | 305,2 |
Используется режим Fast-PWM, если это кому-то важно. Кто не знает, что это такое - либо забейте, либо посмотрите в даташите.
Кому надо, пользуйтесь.
Финальное замечание: В принципе, функции pwmWrite9 и pwmWrite10 нафиг не нужны. Если вместо их использовать стандартные analogWrite(9, …) и analogWrite(10, …), всё будет работать ничуть не хуже. Можете попробовать. А выделил я их вот для чего. Дело в том, что после вызова pwmInit у Вас изменились разрешение и частота и разрешение ШИМ только на пинах 9 и 10. На остальных всё осталось как было раньше. Поэтому, если Вы, например, вызвали pwmInit(16); то с этого момента второй параметр analogWrite для пинов 9 и 10 имеет право быть от 0 до 65 535 в то время как для остальных пинов, он по прежнему должен быть в границах 0-255. Так вот, чтобы было поменьше путаницы, я и ввёл специальные функции для 9-го и 10-го пинов. Хотя, стандартные тоже нормально работают.
Блин, забыл написать - частота ШИМ в таблице дана в герцах.
Работающий пример можно?
Ну, не знаю, чего там примерять-то? Ну, ... вот ...
:) Интересный подход .. а каким макаром у Вас, Евгений, 16-и разрядное "v" передается в 8-и разрядный OCR1A? Там, вроде как по даташиту, требуется делать 2 пересылки в строго заданной последовательности И с запрещеннымии прерываниями .. макрос из io.h ( OCR1A=..) разве на такое способен?
:)
Вы что-то перепутали, OCR1A - 16-ти разрядный. См. п. 16.11.5 даташита (ссылка на даташит есть в исходном посте темы).
Там, похоже, ошибка другого рода имеется, как выловлю, выложу правку. При записи 0 (причём, когда там и так ноль был) иногда откуда-то высокий уровень проскакивает. Не часто и всплеск короткий - но непорядок. Ноль он должен и в Африке быть нулём. Сейчас пытаюсь отловить проблему и найти решение.
ЕвгенийП, ну проблемой то это трудно назвать, так и должно быть. Ноль в регистре сравнения -это не ничего, это когда TCNT1==0 :) В ардуиновской функции этот ноль специально отлавливается, и таймер просто останавливается.
Спасибо, что-то подобное я и предполагал, что в момент равенства должен на 1 такт вылетать импульс.
Другое дело, почему он не вылетает, когда я ничего туда не пишу? Вот сидит там себе 0 и никого не трогает и ничего не вылетает. ... Хотя, я уже начал слмневаться, не помню поставил ли я режим "пик детект" на осциллографе. А без него такой импульс нетрудно и пропустить.
В любом случае спасибо, в выходной займусь и как победю - выложу правку.
ЕвгенийП, пик должен быть периодическим, каждый раз при переходе через ноль. Можно позаимствовать метод "борьбы" из wiring_analog.c
Да, я уже проверил, спасибо. Действительно я просто не включал пик-детект и потому мне казалось, что эффект "плавающий". И когда собственно понял причину всё думал, но блин он должен быть всегда, почему он только иногда проявляется :))) Спасибо. Насчёт метода посмотрю - подумаю. В выходные выложу обновление. Там, на самом деле, если пожертвовать 16-битным режимом, можно просто вместо 0 зафигачивать туда "нечто большее, чем ICR1 - проблема уйдёт. Но 16-битный режим жалко, хотя прямо сейчас он мне и не нужен.
Добрый день, коллеги.
Как и обещал, в выходной вернулся к задаче и подробно исследовал проблему.
Значит, таки да, если параметр, отвечающий за скважность (впредь я и буду просто говорить «скважность») установить в 0 (или в максимальное значение), то это таки не LOW (HIHG) а просто минимальная (максимальная) длительность высокого уровня.
Ниже три осциллограммы, которые это показывают. При нуле (верхняя картинка) длительность импульса 62 наносекунды, при единице (средняя картинка) – 124 нс, а при двойке (нижняя картинка) – 186 нс.
pwmWrite9(0);
pwmWrite9(1);
pwmWrite9(2);
Таким образом, получается, что если делать так, как сделано в wrining_analog.c (т.е. при нуле и при максимальном значении просто нагло вставлять туда digitalWrite) то, мы получим скачок скважности между 0 и 1 вдвое больший, чем, между, скажем 1 и 2 или любыми другими соседними числами.
К сожалению, для моей задачи это неприемлемо и я от этого отказался.
Решил в целом оставить как есть, а если нужны чистые LOW и HIGH, то пользоваться аналогом digitalWrite напрямую.Написал для этих двух пинов digitalWrite9 и digitalWrite10.
Получилось несколько менее удобно, чем в среде Ардуино, т.к. нужно самому заботиться о том, чтобы явно вызвать digitalWrite9/10 для постоянного низкого или высокого уровней. Зато шаг между низким уровнем и 0 точно такой же, как между 0 и 1 и т.д., т.е. регулировка равномерная на всём диапазоне, а для меня это сейчас важно и стоит того удобства.
Оформил include’файлом (кому нравится, могут называть это библиотекой).
Вот пример. На экране осциллографа сигнал растягивается/стягивается как гармошка, а на чистых HIGH и LOW замирает на 5 секунд.
Вот код.
Функцию pwmInit нужно вызвать один раз (например, в setup). Её параметр - разрядность ШИМ. Может быть от 2 до 16. Функция не проверяет корректность параметра - на Вашей совести.
После этого можно вызывать функции pwmWrite9 (для 9-го пина) и/или pwmWrite10 (для 10-го) пина и устанавливать скважность точно также, как мы вызываем analogWrite. Значение 0 - сигнала нет, максимальное значение - сигнал сплошной.
Допустимые максимальные значения (как и частота ШИМ) зависят от разрядности (параметра, который передавали функции pwmInit).
Вопросы по коду программы:
- Входной параметр функции определен как константа, для чего это предусмотрено?
- Можно менять частоту ШИМ на меньшую, чем указано в таблице и как ?
- Какая расчетная формула для частот ШИМ в таблице?
- Что означает "1u" в строке 6 ?
- Входной параметр функции определен как константа, для чего это предусмотрено?
Указание компилятроу на то, что я не собираюсь изменять значение bitRate внутри функции. В большинстве случаев не влияет ни на что, но в редких случаях помогает ему (компилятору) в оптимизации кода. Заведите себе хорошую привычку: если объект реально константа - не скрывать это от компилятора. Нет-нет, да принесёт пользу.
- Можно менять частоту ШИМ на меньшую, чем указано в таблице и как ?
Можно. При помощи делителя частоты таймера. Но тогда, само собой, поползёт таблица - её нужно будет пересчитывать.
Делитель частоты задаётся битами CS1x регистра TCCR1B. У меня в строке 4 задано CS10 == 1, а остальные (CS11 и CS12) оставлены нулями. Это соответсвует делителю, равному 1. Если нужно делить частоту (до 1024), то надо задать правильную комбинацию битов (см. таблицу 16-5 в даташите).
- Какая расчетная формула для частот ШИМ в таблице?
Формула на стр. 125 даташита вверху при N=1 (это делитель частоты). В принципе, вот здесь можете взять готовый Excel, там есть эта таблица. Там же можно ввести другой делитель частоты и всё пересчитается.
- Что означает "1u" в строке 6 ?
u или U после числа (без пробела) означает, что число беззнаковое (unsigned).
Пытался переделать исходный текст программы под ATmega2560. Но не смог. "Ошибка компиляции для платы Arduino/Genuino Mega or Mega 2560."
Укажите, пожалуйста, на ошибки
Укажите, пожалуйста, на ошибки
Тебе компилятор указал на конкретную ошибку в конкретной строке, читай лог полностью.
Укажите, пожалуйста, на ошибки
Тебе компилятор указал на конкретную ошибку в конкретной строке, читай лог полностью.
collect2.exe: error: ld returned 1 exit status
Если это - что сие означает?
Ааа... Это проблема с линкером.
https://www.google.com/search?q=collect2.exe%3A+error%3A+ld+returned+1+exit+status+site%3Aarduino.ru
http://arduino.ru/forum/programmirovanie/oshibka-kompilyatsii-2#comment-...
http://arduino.ru/forum/programmirovanie/oshibka-kompilyatora-pomogite#c...
Ааа... Это проблема с линкером.
https://www.google.com/search?q=collect2.exe%3A+error%3A+ld+returned+1+exit+status+site%3Aarduino.ru
http://arduino.ru/forum/programmirovanie/oshibka-kompilyatsii-2#comment-...
http://arduino.ru/forum/programmirovanie/oshibka-kompilyatora-pomogite#c...
Может быть связь с тем, что на входе стоит CH340?
Другие программы норм работают; без проблем
Нет, это вообще не при чём. Почитайте внимательно по тем ссылкам, что я дал.
Нет, это вообще не при чём. Почитайте внимательно по тем ссылкам, что я дал.
Я прочитал. Я hardware; начну лазить и удалять всякие .lto - вообще завалю все)
Спасибо за оперативный консалт
Для начала попробуйте заменить ld.exe из другой версии Arduino IDE (более старой или более новой)
Интересные осциллограммы, однако я снимал прямую линию с этим кодом при 100% коэфф. заполнения (обозвал его яркостью по аналогии с либой pwm):
Ну, так не должно быть. Это противоречит и даташиту и здравому смыслу. Я тоже видел прямые линии пока не поставил редим peak detect на осциллографе.
Код Ваш не смотрел, надеюсь там всё нормально, но если что, могу запустить у себя.
Да там все нормально, кусок просто выдрал из скетча, инициализация в другом месте осталась)). Я делал 2х каналку - один счетчик включался по счету вверх выше значения регистра сравнения и отключался при счете вниз, другой - наоборот. Еще раз пролистал сабж и даташит - в сухом остатке мы имеем то, что при здании регистру сравнения топового значения, остаются щелчки на частоте, зависящей от опорной и количества попугаев в регистре TOP. Я их не видел, но раз Вы говорите что они есть и без peak-detect их не видно - то я их и не увижу, мой осцил старше меня. А если регистру сравнения здать нулевое значение, то при включении на счете вверх же ничего не должно щелкать, должен быть полный "0"? Почему задал вопрос - при изменении чстоты/скважности "на ходу", даже в последовательности обнулить регистры - подождать микросекунду - задть новые значения иногда бывают непредсказуемые "броски" с большой скважностью. Для большинства применений на это, конечно, можно было бы забить, но хорошие IGBT дороговты)
Добрый вечер. Читаю все Ваши коменты и решил задать вопрос о замере сигнала датчика давления 50гц и преобразовании в 25гц с темже процентом шима.
Заранее благодарю Николай
mail: nickavia@ukr.net Очень нужно.
Николай, ну, у меня нет Ваших датчиков, поэтому что я могу для Вас сделать? Теоретизировать как бы они могли работать? Так Вам не это надо.
Добрый вечер. Датчик давления кондиционера видаёт сигнал 12вольт 50 герц с шимом 5-95% в зависимости от давления. Нужно преобразоать сигнал в 25 герц с шимом 5-95%
Тоесть шим в % должен быть такой как в датчике давлания. С уважением Николай.
Добрый вечер. Датчик давления кондиционера видаёт сигнал 12вольт 50 герц с шимом 5-95% в зависимости от давления. Нужно преобразоать сигнал в 25 герц с шимом 5-95%
Тоесть шим в % должен быть такой как в датчике давлания. С уважением Николай.
скорее всего это можно сделать, если рассчитать частоту тактирования от обратного?!
(потеряв сериал)
Добрый вечер. Датчик давления кондиционера видаёт сигнал 12вольт 50 герц с шимом 5-95% в зависимости от давления. Нужно преобразоать сигнал в 25 герц с шимом 5-95%
Тоесть шим в % должен быть такой как в датчике давлания. С уважением Николай.
скорее всего это можно сделать, если рассчитать частоту тактирования от обратного?!
(потеряв сериал)
Добрый вечер. Датчик давления кондиционера видаёт сигнал 12вольт 50 герц с шимом 5-95% в зависимости от давления. Нужно преобразоать сигнал в 25 герц с шимом 5-95%
Тоесть шим в % должен быть такой как в датчике давлания. С уважением Николай.
скорее всего это можно сделать, если рассчитать частоту тактирования от обратного?!
(потеряв сериал)
не знаю, через pulseIn() + micros() ничего не делал, как-то всё напрямую )))
Не знаю, заглянут ли сюда специалисты, но спрошу...
В wiring_digital.c написано:
Вопрос: при выключении PWM через turnOffPWM() -> cbi(TCCR2A, COM2A1) связанный с таймером (юзаю #2) вывод всегда приходит в определённое положение или же в случайное, а затем оно фиксится последующей записью в порт?
Я тут пробую делать простой вкл/выкл PWM в режиме Phase corrected и, на осциллографе, при отключении вывода от таймера (сбросом COM2A1), он четко ложится в LOW.
Однако, я помню, что в режиме CTC, на таймере #1 при схожем способе отключения, нога оставалась в последнем установленном положении и приходилось дополнительно делать TCCR1C = (1 << FOC1A);
В даташите написано, что "The FOC2A bit is only active when the WGM bits specify a non-PWM mode", т.е. неприменим в данном случае, а про гарантированный переход выхода в заданное состояние при остановке генерации я, что-то, не вижу.
а про гарантированный переход выхода в заданное состояние при остановке генерации я, что-то, не вижу.
а мной тестовка вообще не воспринимается, исключительно, если разрисовать в жёсткой логике
Исходя из этого: "The extreme values for the OCR2A register represent special cases when generating a PWM waveform output in the phase correct PWM mode. If the OCR2A is set equal to BOTTOM, the output will be continuously low and if set equal to MAX the output will be continuously high for non-inverted PWM mode." - приостановка PWM с фиксацией вывода в состоянии LOW (при неинверсном режиме работы sbi(TCCR2A, COM2A1)) производится через OCR2A = 0x00.
Т.е. способ зафиксировать я нащупал, но академический вопрос всё же интересен - как становятся ноги при отключении от них генератора...
Исходя из этого: "The extreme values for the OCR2A register represent special cases when generating a PWM waveform output in the phase correct PWM mode. If the OCR2A is set equal to BOTTOM, the output will be continuously low and if set equal to MAX the output will be continuously high for non-inverted PWM mode." - приостановка PWM с фиксацией вывода в состоянии LOW (при неинверсном режиме работы sbi(TCCR2A, COM2A1)) производится через OCR2A = 0x00.
Т.е. способ зафиксировать я нащупал, но академический вопрос всё же интересен - как становятся ноги при отключении от них генератора...
ну если в Z состоянии, вход оборван, то практически обычно равнозначно что на этих пинах "1", а дальше как логика распорядилась, даже в 155 логике неиспользуемые пины рекомендовалось тянуть к + питания через резистор до 10 ног, но практически в обслуживаемых мной устройствах разработчик оставлял их свободно болтающимися, особо помехи не ловились...
надо проэмулировать, однозначно там будет однозначность )))
извиняюсь за мой французский
Ваш пример но с табличкой пояснения унутрях:
Вроде я с этим разобрался - выход перекидывается из состояния в другое состоянии при достижении переключающей точки (OCRn, TOP, BOTTOM - от режима зависит). При отключении от генератора остается в том состоянии, в котором был на тот момент, специально ничего МК не делает, если не понудить его через FOC или установку состояния вручную. Присвоение TCNT значения, приводящего его в переключающую точку, ничего не дает, ибо сравнение не срабатывает. Видимо оно только на тике генератора производится.