Почему после Servo или tone() перестал работать analogWrite?
Обновлено Редакция GAW
На ATmega328P всего три таймера, и каждый обслуживает два вывода ШИМ. Ядро Arduino при старте настраивает все три под analogWrite(). Библиотека, которой нужен точный отсчёт времени, перенастраивает таймер под себя, и ШИМ на его выводах пропадает — без сообщения об ошибке.
Кто какой таймер занимает на Uno
| Таймер | Выводы ШИМ | Кто ещё его использует | Что ломается |
|---|---|---|---|
| Timer0 | 5, 6 | millis(), micros(), delay() | менять делитель нельзя — поедет время |
| Timer1 | 9, 10 | библиотека Servo | analogWrite() на 9 и 10 после attach() |
| Timer2 | 3, 11 | tone() | analogWrite() на 3 и 11 во время звука |
Servo. В ServoTimers.h версии 1.2.2 для ATmega328P определён один таймер — Timer1. При первом attach() библиотека пишет TCCR1A = 0 (обычный режим счёта) и делитель 8, а импульсы на выводах серво формирует в прерывании по совпадению. Режим ШИМ Timer1 выключен, поэтому analogWrite() на 9 и 10 больше не выдаёт ШИМ, даже если серво висит на другом выводе.
tone(). Для ATmega328P в Tone.cpp указан Timer2. На время звука он работает в режиме CTC, и ШИМ на выводах 3 и 11 пропадает. noTone() возвращает Timer2 в режим ШИМ ядра, но выходы на выводах 3 и 11 остаются отключёнными до следующего analogWrite().
Как обойти
- Разнести функции по таймерам: Servo плюс ШИМ на выводах 3, 5, 6, 11; звук плюс ШИМ на 5, 6, 9, 10.
- Если выводов ШИМ не хватает, вынести ШИМ на отдельную микросхему — например, на PCA9685 по I²C.
- Для одного-двух серво можно обойтись без библиотеки: импульсы сформирует сам Timer1 в режиме быстрого ШИМ с TOP в ICR1 на выводах 9 и 10, без прерываний, а Timer2 останется свободным для
tone().
Как ядро настраивает таймеры для analogWrite(), разобрано в уроке «analogWrite изнутри», режимы таймеров ATmega — на странице «Таймеры AVR». Как устроен сервопривод и какой импульс ему нужен — в статье «Сервоприводы».
Похожие вопросы
Вернётся ли ШИМ на выводах 3 и 11 после noTone()?
Не сам по себе: noTone() на Uno возвращает Timer2 в режим ШИМ с правильной фазой и делителем 64, как его настраивает ядро. Но TCCR2A при этом записывается целиком, только с битом WGM20, — выходы сравнения обоих выводов отключаются, а OCR2A обнуляется. Поэтому analogWrite() нужно вызвать заново и на выводе 3, и на выводе 11.
Можно ли тогда управлять сервоприводом от вывода 9?
Можно: библиотека Servo формирует импульсы программно, по прерыванию таймера, и сигнал подаётся на любой цифровой вывод. Пропадает только аппаратный ШИМ analogWrite на 9 и 10 — даже если серво подключён к другому выводу.
Какие ещё библиотеки занимают таймеры?
Обычно те, что отмеряют точные интервалы, — для ИК-пультов, звука, программных UART и ШИМ. Какой таймер занят, надёжнее всего узнать в исходнике библиотеки — поиском TCCR1, TCCR2 или TIMER1_COMPA_vect.
Источники
- Arduino. Servo library 1.2.2: src/avr/ServoTimers.h, src/avr/Servo.cpp — ServoTimers.h: для микроконтроллеров, не перечисленных отдельно (в том числе ATmega328P), определён только _useTimer1; Servo.cpp, initISR: TCCR1A = 0 — обычный режим счёта, TCCR1B = _BV(CS11) — делитель 8, прерывание по совпадению OCR1A
- Arduino. ArduinoCore-avr 1.8.8: cores/arduino/Tone.cpp, variants/standard/pins_arduino.h — Tone.cpp: для ATmega328P AVAILABLE_TONE_PINS = 1, USE_TIMER2 — tone() использует Timer2 (TCCR2A = 0, TCCR2B = 0, затем CTC); disableTimer(2) в noTone() записывает TCCR2A = (1 << WGM20) — биты COM2A и COM2B сброшены, — делитель 64 (CS22) и OCR2A = 0, а для других таймеров, по комментарию в коде, ШИМ не восстанавливает; pins_arduino.h: таймеры выводов Uno — 3: TIMER2B, 5: TIMER0B, 6: TIMER0A, 9: TIMER1A, 10: TIMER1B, 11: TIMER2A
Нашли ошибку?
Смотрите также
- analogWrite и таймерыinit(), три таймера, карта выводов, 490,2 и 976,6 Гц, своя частота
- СервоприводыУстройство, импульс и угол, постоянное вращение, ток, питание, Arduino Servo
- Таймеры AVRTimer0/1/2, режимы WGM, предделители, формулы ШИМ, захват, примеры
- Частота ШИМ ArduinoЧастоты analogWrite по таймерам, выводы, смена делителя, millis() и tone()
- Почему сервопривод дёргается?Просадка питания, нет общей земли, импульс дрожит больше зоны нечувствительности