wiring, использование delayMicroseconds на частотах кварца, отличающихся от 8,16,20
- Войдите на сайт для отправки комментариев
Большинству из вас это не понадобится (раве что для общего развития), но, дабы не наступать на грабли повторно, я проанализирую эту ситуацию в разрезе функции http://arduino.ru/Reference/DelayMicroseconds.
Необходимо сразу сказать, что для миллисекундных задержек (функция delay) используется функция micros(), поэтому на любых частотах она будет работать корректно.
Цель данной статьи - найти решение для микросекундных задержек на нестандартных частотах кварца.
Итак, delayMicroseconds. В комментария функции (см. файл wiring.c) написано, что использование стандартной функции delay_us из avrlib не дает желаемых задержек на 1-2мкс, потому реализована своя функция.
Данная функция реализована на обычном цикле (loop) задержки, который выполняется за 4 такта (назовем его ЦЗ). Уже можно напрячься, потому что длительность одного такта напрямую зависит от частоты кварца, бинго! А перед выполнением ЦЗ производится расчет кол-ва выполняемых циклов задержки. Дополнительно выполняется оптимизация для задержек равных 1-2мкс.
Для начала рассчитаем, за какое время производится выполнение 4 тактов при разных значениях частот кварца:
Частота (МГц) | 8 | 12 | 16 | 20 |
1 такт | 0,125 | 0,0833333333 | 0,0625 | 0,05 |
4 такта | 0,5 | 0,3333333333 | 0,25 | 0,2 |
Главное, что нужно помнить, что в коде выделены (оптимизированы) три варианта:
>= 20 МГц,
>= 16MHz
прочие (подразумевается 8 MHz)
Выводы:
1. Вариант, тупой:
Чтобы понять, какую задержку нужно задавать на вход delayMicroseconds, необходимо:
1. Определить в какую область попадает частота нужного кварца. Например, для частоты 12МГц - это 8-16МГц.
2. Взять нижнюю частоту диапазона. Для 12МГц - это 8МГц.
3. Рассчитать относительное отклонение частоты. Для 12МГц это 12/8 = 1.5 раза.
4. При задании задержки, умножить её значение на полученную величину отклонения. Например, для 10мкс, необходимо задать 10 * 1.5 = 15. В этом случае мы получим задержку в 10мкс.
Практически, коэффициент можно задать так:
#if F_CPU >= 20000000L #define F_DELAY_MULT (F_CPU/20000000L) #elif F_CPU >= 16000000L #define F_DELAY_MULT (F_CPU/16000000L) #else #define F_DELAY_MULT (F_CPU/8000000L) #endif
Однако здесь получаются дробные коэффициенты и использовать такой метод втупую - не оптимально. Потому тупая переделка функции delayMicroseconds таким способом не даст эффективной замены и для каждой частоты кварца нужна отдельная оптимизация.
2. Вариант, более эффективен и даст более точные задержки:
Чтобы снизить нелинейность, добавить в delayMicroseconds свой вариант задержки, для конкретной частоты кварца. Этот вариант более простой, возможно это решение будет выложено позже и на основании него можно будет реализовать задержку по аналогии для других частот кварца.
Решение http://www.nongnu.org/avr-libc/user-manual/group__util__delay.html
Я не даром написал про комментарии из исходника wiring.c, что они не используют delay_us по причине того, что на малых задержках дает некорректные задержки. Цитирую: "В комментария функции (см. файл wiring.c) написано, что использование стандартной функции delay_us из avrlib не дает желаемых задержек на 1-2мкс, потому реализована своя функция."
В общем дело хозяйское, жедлающие могут вызвать delay_us.
Пока проверить не могу, но решение для 12МГц кварца достаточно простое, (добавлены строки 65-68):
В двух словах. Только для 12МГц ЦЗ увеличивается в полтора раза, с 4 циклов до 6 циклов, т.е. то же время 0.5мкс на ЦЗ, как и для 8МГц, т.е. всё должно работать нормально.
PS Исправлялся исходник ArduinoIDE v1.0.5
Не знаю кто это там написал, сам тоже не проверял, других источников об этой проблеме не нашел и в описании самой библиотеки об этом ничего нет . Может это справедливо только при вызове _delay_us() из тела функции
delayMicroseconds(
)
, но никто не мешает воспользоваться директивой #define.Дело совсем не в вызове, я смотрел исходники delay_us в avrlib, там неоптимизированная задержка и на малых значениях действительно будет совсем другая задержка.
Вот исходник, если кому интересно (из тулчейна ArduinoIDE v1.0.5)
Теперь не нужно калечить LiquidCrystal #15 для корректной работы на 12 мегагерц.
*проверил - всё работает.
Спасибо! А то пока не на чем проверить. ЛА подключать надо, по хорошему. Да прогнать по малым величинам.
Ребята, как решили проблему?...что-то я не вкурил)
У меня кварц внутренний т.е. на 8 МГц...проблема в дисплее - вылазят иногда кракозяблы, хотя до этого вроде не было проблем сним и все работало. Решит ли эту проблему ваше решение?
Для 8 не нужен патч и так должно работать, я фиксил для 12МГц.
Могу предположить, что на 8 никто не тестил. К сожалению мне проверить сейчас не на чем
на 8 тестировал, вроде правильно все
Да у меня тоже работало всё....видимо причина в другом. Буду искать
Как правило задержки виноваты, если проблемы при инициализации, иначе может и питание, хотя у меня проблем не было ни с атмега16, ни с стм32, на Ардуино не пробовал.