21

Светомузыка с Arduino. Сезон 2 Часть 3

Часть 1 https://pikabu.ru/story/svetomuzyika_s_arduino_sezon_2_chast...

Часть 2 https://pikabu.ru/story/svetomuzyika_s_arduino_sezon_2_chast...


Продолжаем ковырять прерывания и таймеры.

Поговорим о делителях частоты и зачем они нужны. Делители - тупо уменьшают системную частоту, зачем - да фиг его знает, но зачем то нужны. В прошлом посте я пришёл к тому, что цикл прерывания работает, и не мешают работе ШИМ, но вот чот не так быстро как хотелось бы. Напомню, что время между тактами составляло порядка 2 миллисекунд, а надо микросекунды. Будем отключать делитель.

У адруины нано есть вот такие варианты:

1. таймер/Счетчик выключен;

2. Тактовый генератор;

3. Делитель на 8;

4. Делитель на 64;

5. Делитель на 256;

6. Делитель на 1024.

По умолчанию, в частности на таймере 2, делитель установлен на 64. Посчитаем:

Ардуина работает на 16 МГц, следовательно 16 000 000 / 64 = 250 000, т.е. 250 кГц, т.е. 250 000 раз в секунду.

Далее посмотрим, сколько времени требуется на такт: 1 / 250 000 = 0, 000004 сек, или 4 микросекунды. Счетчик таймеры 2 считает до 255, а значит прерывание срабатывает 1 раз за 256 тактов (0 - 255), значит прерывание срабатывает каждые 4 * 256 = 1024 мкс, или 1,024 миллисекунды,...


Отступление: многие мануалы, которые я прочитал,пытаются заставить работать прерывание со сбросом при совпадении, и настраивают его на работу в 1 миллисекунду, но при выборе режима работы допускают маленькую ошибку, и получают время 1,024 - на практике это особо не заметно, и их светодиоды работают вроде бы как надо, но отличить глазом разницу в 24 мкс - не реально, и типа ок. А когда я стал с этим разбираться, тайминги не совпадали, и ничего не работало как надо.


Ок, вернемся к нашим баранам.

1,024 миллисекунды - слишком медленно,... будем выключать делители. Посмотрим как это делать: (картинка с видео)

Как видим, какие-то мутные регистры, но ничего сложного:

Все настройки делаются в блоке setup!

CS00, CS01, CS02 - Разбираемся: CS - Регистры, просто запоминаем. первая цифра - номер таймера, Нам нужен 2. Вторая цифра - управляющий бит. Итак, нам надо тактовый генератор на канале B таймера 2, а значит надо установить регистры CS20 = 1, CS21 = 0, CS22 = 0 в регистр таймера 2 канал B

В программном виде это выглядит так:

TCCR2B |= 1<<CS20;

TCCR2B &= ~((1<<CS22)|(1<<CS21));

, где

TCCR2B - Регистр таймера 2, канал B (Соответственно может быть TCCR0А, TCCR0B, TCCR1A, TCCR1B, TCCR2A, TCCR2B - Думаю понятно что тут и где).

Первой строчкой через оператор OR устанавливаю 1 на регистр CS20, второй строчкой устанавливаю нули в регистры CS21 и CS22 используя операторы AND и Инвертирование от OR.

Для тех кто не знает:

| - это оператор OR (логиеское или). Принцип работы: имеем биты А и Б, Про себя проговариваем: Если А или Б = 1, то на выходе 1. т.е. если хотя бы один из битов = 1, то на выходе 1. Соответственно, если оба = 0, то на выходе 0.

& - это оператор AND (логическое и). Принцип работы: имеем биты А и Б, Про себя проговариваем: Если А и Б = 1, то на выходе 1. т.е. если хотя бы один из битов = 0, то на выходе 0. Соответственно, если оба = 0, то на выходе тоже 0. Чуишь разницу?

~ - это оператор NOT (логическое не). Принцип работы: Имеем бит А. Про себя проговариваем: Если А = 0, то на выходе не 0, и наоборот. Грубо говоря, просто инвертируем значение.

<< - Операция побитового смещения влево, То есть, было у нас 3 бита = 001, смещаем их влево на 1 знак, получаем 010, Еще разок сместим - 100, еще разок сместим 000. ну и вправо смещать тоже можно аналогично.


Как работает вся эта конструкция в целом - да не особо важно, если будет ты туда залез, и тебе это нужно - разобраться будет не сложно.


Итак. Делители выключили, посчитаем: 1 секунда / 16 000 000 Гц = 0,0625 мкс, * 256 = 16 мкс. Во, уже просто супер! Теперь можно хреначить цикл работы с микрухой анализатора.

Изменение частоты делителя, никак не влияет на работу ШИМ, - так как подняв частоту ШИМ, мы не меняем скважность сигнала (процентное соотношение времени вкл и выкл) - это как частоту на мониторе поднять, просто мерцать меньше будет.


В общем и целом,что можно сделать на ардуине:

7 типа параллельных потоков:

один цикл - Основной LOOP, - очень шустрый, менее 1 мкс на круг.

еще два цикла - на таймере 2 каналы A и B - довольно шустрые по 16 мкс на круг.

еще два цикла - на таймере 1 каналы A и B - помедленнее, так как они до 1024 считаю, т.е. 64 мкс на круг.

и еще два - на таймере 0, но тут частоту менять не надо, то есть 1024 мкс на круг.


Далее, если нам не надо ШИМ или всякие Delay, micros, millis, и прочие (в зависимости от таймеров и каналов), можно поставить в прерывании по таймеру сброс счетчика, и в регистр сравнения записать 1, таким образом, прерывание будет вызываться в каждый такт: т.е. получим скорость как у основного цикла LOOP, = 0,0625 мкс.

Посмотрим как это делать:

Видим, что надо задавать регистры WGM, где первая цифра - это номер таймера, вторая - задающий бит (У таймера 1 немного иначе - имейте ввиду). То есть надо сделать так:

TCCR2B = 1<<WGM21;

По умолчанию установлен режим Нормальный, то есть биты стоят в 0. поэтому дополнительных манипуляций не требуется, просто берем и устанавливаем единицу в нужный регистр.

Обращаю внимание что оператор | (OR) перед знаком равенства не используется! Я фиг знает почему так, - логично что надо бы, но если его поставить, то нифига не работает. Ну и соответственно, эту строчку надо бы размещать перед другими настройками этого бита (TCCR2B в данном случае).

Если надо установить в оба бита, то делаем так: TCCR2B = ((1<<WGM21)|(1<<WGM20));

Итак: в конечном виде это выглядит так:

TCCR2B = 1<<WGM21;  Режим Сброс при совпадении

TCCR2B |= 1<<CS20;  Режим работы по генератору 16 МГц

TCCR2B &= ~((1<<CS22)|(1<<CS21));  Делители выключаем.

OCR2B = 1;  Число для сравнения с счетчиком, пущай каждый такт прерывание фигачит

TIMSK2 |= (1 << OCIE2B);  Разрешаем прерывание при совпадении

В коде функции прерывания, напишем немного кода, что бы посчитать время. И на мониторе порта посмотрим результат: 30 мкс... Сабака, так быстро тикает, что пропускает такты, или micros не успевает считать. Ок,.. поставим Счетчик подальше, OCR2B = 127 и... 15 мкс

Вывод: Без делителей, смысла использовать режим Сброса при совпадении - особо нет. Да и ШИМ перестает работать - нахер эту ерунду. Оставляем режим в "нормальном", вырубаем делители, и получаем и ШИМ и 16 мкс на круг. Всё, можно писать прошивку для Светомузыки с  микросхемой анализатора.


Продолжение следует.... со скоростью китайско-русской почты. Жду посылку с микрухой.

Arduino & Pi

1.5K поста20.9K подписчика

Правила сообщества

В нашем сообществе запрещается:

• Добавлять посты не относящиеся к тематике сообщества, либо не несущие какой-либо полезной нагрузки (флуд)

• Задавать очевидные вопросы в виде постов, не воспользовавшись перед этим поиском

• Выкладывать код прямо в посте - используйте для этого сервисы ideone.com, gist.github.com или схожие ресурсы (pastebin запрещен)

• Рассуждать на темы политики

• Нарушать установленные правила Пикабу

0
Автор поста оценил этот комментарий

Оу, а вот это интересно, вот что значит полагаться на свой глаз при тестировании :) Надо добавить эту оговорку на ютюбе, спасибо!

раскрыть ветку (1)
0
Автор поста оценил этот комментарий

Только перед этим, проверте это на действительность. А то я сам не совсем понял, почему OR там не используется.

0
Автор поста оценил этот комментарий

Да, настраивали вы его верно, но при установке режима, у вас выставлено с использованием |=, о чем я написал в посте и тем самым режим сброса при совпадении не устанавливается. И получается что таймер продолжает считать до 255, и на выходе 1024 мкс, а не 1000. А глазом эту разницу - не заметить. Попробуйте в вашем исходнике настроить счетчик не на 250, а на 10 - разницы не будет. Возможно конечно этот фокус только на ардуине нано, точно не знаю, нет возможности проверить, но так или иначе, у меня по вашему видео ситуация именно такая. Однако спустя пару дней мучений и поиска - нашёл эту проблемку на какой-ть ветке stackoverflow, а каком-то ответе.

раскрыть ветку (1)
0
Автор поста оценил этот комментарий

Дополню:

Таймер пролоджает не выполняя сброс. - т.е. у вас получается, что таймер досчитал до 250, вызвал прерывание, и продолжил считать до 255, после чего обнуляется и опять считает до 250 - прерывание - 255 - сброс... т.е. количество счетов между прерываниями получается 255.

Если установить регистр сравнения в 10, то будет

0 - 10(прерывание) - 255 - сброс - 10(прерывание) - 255. Т.е. опять счет между прерываниями до 255,

Тут можно, конечно, в функции прерывания руками обнулить счетчик - но нам надо именно режим сброса.

показать ответы
0
Автор поста оценил этот комментарий

Не понимаю, в чем ошибка, если прерывание по совпадению я настраивал не на 256 (иначе было бы, как вы правильно заметили, 1024), а на 250, ставя в регистр сравнения соответствующее шестнадцатеричное число, что равняется как раз 1000..  Посмотрите исходники.

раскрыть ветку (1)
0
Автор поста оценил этот комментарий

Да, настраивали вы его верно, но при установке режима, у вас выставлено с использованием |=, о чем я написал в посте и тем самым режим сброса при совпадении не устанавливается. И получается что таймер продолжает считать до 255, и на выходе 1024 мкс, а не 1000. А глазом эту разницу - не заметить. Попробуйте в вашем исходнике настроить счетчик не на 250, а на 10 - разницы не будет. Возможно конечно этот фокус только на ардуине нано, точно не знаю, нет возможности проверить, но так или иначе, у меня по вашему видео ситуация именно такая. Однако спустя пару дней мучений и поиска - нашёл эту проблемку на какой-ть ветке stackoverflow, а каком-то ответе.

показать ответы
0
Автор поста оценил этот комментарий

Знакомые картинки :)

раскрыть ветку (1)
0
Автор поста оценил этот комментарий

Ага, хорошие картинки. А вас попрошу в этом видео (с которого картинки) найти свою ошибку =)

За видео, кстати, большое спасибо, если бы не эта распространенная ошибка, было бы вообще супер.

показать ответы
0
Автор поста оценил этот комментарий

Вот это я понимаю, брать ардуинку и фигачить чистейший C и вместо задержек использовать прерывания:)
Хотя я перебрался на голые контроллеры типа attiny разных версий, а ардуину юзаю как программатор, выходит дешевле и прикольней. Единственное подтягивающие резисторы можно "включить" только паяльником, но это не большая беда.

раскрыть ветку (1)
0
Автор поста оценил этот комментарий

=) Меня вот еще STM32 манит. А вот голые контроллеры - и да и нет... с одной стороны прикольно когда МК на плате припаян, с другой стороны - самому его обвязывать, программировать, да и цена то копеешьная. Физ знает. Может тоже приду к голым МК... все-таки люблю простоту и минимализм

показать ответы

Темы

Политика

Теги

Популярные авторы

Сообщества

18+

Теги

Популярные авторы

Сообщества

Игры

Теги

Популярные авторы

Сообщества

Юмор

Теги

Популярные авторы

Сообщества

Отношения

Теги

Популярные авторы

Сообщества

Здоровье

Теги

Популярные авторы

Сообщества

Путешествия

Теги

Популярные авторы

Сообщества

Спорт

Теги

Популярные авторы

Сообщества

Хобби

Теги

Популярные авторы

Сообщества

Сервис

Теги

Популярные авторы

Сообщества

Природа

Теги

Популярные авторы

Сообщества

Бизнес

Теги

Популярные авторы

Сообщества

Транспорт

Теги

Популярные авторы

Сообщества

Общение

Теги

Популярные авторы

Сообщества

Юриспруденция

Теги

Популярные авторы

Сообщества

Наука

Теги

Популярные авторы

Сообщества

IT

Теги

Популярные авторы

Сообщества

Животные

Теги

Популярные авторы

Сообщества

Кино и сериалы

Теги

Популярные авторы

Сообщества

Экономика

Теги

Популярные авторы

Сообщества

Кулинария

Теги

Популярные авторы

Сообщества

История

Теги

Популярные авторы

Сообщества