Как плавно регулировать яркость одной лампы я так и не понимаю, хотя уже множество попыток сделал.
1) Добавлял переменную B в прерывании, делал так:
Код:
if (b < bright1) {зажигать} else {не зажигать}
полная фигня, лампа моргает, засветы и прочие артефакты
2) Пробовал выполнять переключения ламп в прерывании, а зажигать сегменты в основной программе, она же типа быстрее. Получалась тоже фигня с засветами и мерцаниями.
Очень прошу помощи, уже не помню сколько раз возвращался к вопросу и так и не осилил (
Если вы не можете запрячь под это дело DMA, то можете добавить просто прерывание по сравнению таймера и по прерыванию по переполнению показываете новую цифру, а по прерыванию сравнения - старую.
Когда я делал такую штуку на светодиодах, там заметил, что чтобы яркость спадала/возрастала линейно, ШИМ должен изменяться по экспоненциальному закону. Но если разом гасить и зажигать другую цифру - ничего не получится, поэтому ШИМ меняем просто линейно.
С тех пор, как мне захотелось совместить регулировку яркости с плавной сменой цифр, я стал делать так:
Каждый разряд при динамической индикации обрабатывается не 1 раз, а 16 (частота, соответственно, также увеличена в 16 раз). Выводятся цифры, хранящиеся в двумерном массиве - по 16 элементов массива на разряд. Чтобы получить плавную смену цифр, например, поменять 5 на 6, нужно с некоторым интервалом заполнять массив (в данном разряде) следующими наборами значений: 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 6 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 6 6 5 5 5 5 5 5 5 5 5 5 5 5 5 5 6 6 6 5 5 5 5 5 5 5 5 5 5 5 5 5 ... 6 6 6 6 6 6 6 6 6 6 6 6 6 6 5 5 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 5 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6
Заполнение я делаю не в прерывании, а в main(), при помощи программного таймера, отсчитывающего интервалы времени для шагов плавной смены.
Такой алгоритм позволяет очень легко реализовать и другие эффекты, например, не "перетекание" одной цифры в другую, а плавное погасание старой цифры и резкое зажигание новой (это делается записью в массив специального кода, соответствующего пустому разряду).
P.S. Эх, пока я это всё писал, uldemir меня опередил. Но, к слову, я использую описанный алгоритм на прерываниях в AVR без всяких DMA. И всё прекрасно работает, поскольку код в прерываниях сделан максимально компактным - просто выводится цифра из массива.
Добавлено after 6 minutes 17 seconds: Re: Программное изменение яркости вакуумно-люминисцентных ламп
uldemir писал(а):
Если вы не можете запрячь под это дело DMA, то можете добавить просто прерывание по сравнению таймера и по прерыванию по переполнению показываете новую цифру, а по прерыванию сравнения - старую
Такой вариант плохо совмещается с регулировкой яркости, к сожалению. Я по началу так и делал, а потом захотелось регулировать яркость, и пришлось от такого способа отказаться.
_________________ Этот пост оказался полезен? Не поленись, нажми слева! Куплю индикаторы ИТС-1А, ИТС-1Б, ИГВ1-8х5Л, ИГПС1-222/7, ИГПС1-111/7 и подобные.
Такой вариант плохо совмещается с регулировкой яркости, к сожалению. Я по началу так и делал, а потом захотелось регулировать яркость, и пришлось от такого способа отказаться.
А если использовать два канала сравнения? один для яркости, другой для эффекта? Или у AVR нет таймеров с несколькими каналами захвата/сравнения?
Такие таймеры в AVR есть. Тут дело в другом. Пусть есть, скажем, 8-разрядный таймер. На полной яркости прерывание, где гасятся все разряды, возникает, например, при значении 240 в счётном регистре (чтобы сформировать паузу между разрядами). А на минимальной яркости пусть будет где-нибудь 80. Тогда при максимальной яркости менять значение в регистре сравнения того канала, где меняется старая цифра на новую, нужно в диапазоне 0...239, а на минимальной - в диапазоне 0...79. Если будет, например, 16 шагов при смене цифр, то придётся рассчитывать значение регистра сравнения для канала, где переключается цифра, исходя из текущего значения яркости. На полной яркости будет один набор значений, на минимальной - другой, где-то посередине - ещё один. На мой взгляд, это неудобно. Лишние расчёты. И ещё нужно следить за тем, чтобы значение в регистре сравнения для гашения изменялось не абы когда, а только в определённые моменты времени, чтобы прерывания не поменялись местами.
Ну и гибкость у такого решения невелика. Вариант с 16 "проходами" одного разряда позволяет очень легко реализовать и всякие другие эффекты.
_________________ Этот пост оказался полезен? Не поленись, нажми слева! Куплю индикаторы ИТС-1А, ИТС-1Б, ИГВ1-8х5Л, ИГПС1-222/7, ИГПС1-111/7 и подобные.
К сожалению пока я не понимаю... DMA в атмеге8 вряд ли найдется, может вам не сложно будет кусок кода набросать? Что проще, перетекание одной цифры в другую или плавное гашение одной и розжиг другой? Допустим для начала всего одну цифру секунд плавно гасим, затем зажигаем новую.
Подойдет и на каком-нибудь условном языке.
Идею с массивом я понял, но никак не дойдет, как это будет в виде алгоритма выглядеть, хватит ли 8 МГц на это? Среда в принципе любая, но codevision с его PORTx.y = z понятнее была бы ) ---
Усиленно перечитываю, вроде потихоньку что то доходит, но все равно пока еще не очень, буду благодарен за пример кода
Вот как я представил себе это. Есть сомнения на счет t, что там будет происходить, если общая яркость понижена, что то мне подсказывает что где то фигня, поправьте пожалуйста )
Тут нет определения некоторых переменных и структур, а также присутствует много макросов типа SET_PORT(). Также тут нет кода, определяющего факт смены цифр, устанавливающего биты в display_state.change_effect_flags и запускающего цикл смены. Надеюсь, без этого всего принцип всё равно будет понятен.
_________________ Этот пост оказался полезен? Не поленись, нажми слева! Куплю индикаторы ИТС-1А, ИТС-1Б, ИГВ1-8х5Л, ИГПС1-222/7, ИГПС1-111/7 и подобные.
// Прерывание таймера 1 по совпадению, с чем? Он 16 битный... Можно ли использовать как у меня, таймер 2 по совпадению и гасить по переполнению? ISR(TIMER1_COMPA_vect){ uint8_t symbol;
if(bright_step_counter >= BRIGHT_STEPS) // число градаций яркости? Если пробежали весь массив, то уходим из прерывания, почему так? Что будет если не уйти? return;
symbol = display_buffer_raw[digit_counter][bright_step_counter]; // выбираем цифру из массива массивов цифр, так? Получается двумерный массив, в строках идут цифры, 16 значений, которые пишем в лампу? id1_output(symbol); // цифру в порт, так?
if(symbol < 10){ // Если цифра от 0 до 10, то зажечь порт, если нет, не зажигать порт, верно? switch(digit_counter){ case 0: SET_PORT(ANODE_0); break; case 1: SET_PORT(ANODE_1); break; case 2: SET_PORT(ANODE_2); break; case 3: SET_PORT(ANODE_3); break; } } }
// Совпадение таймера со вторым значением? ISR(TIMER1_COMPB_vect){
// зачем это? Не пойму что делает этот кусочек, if(++bright_step_counter == BRIGHT_STEPS_WITH_SAFE_PAUSE){ bright_step_counter = 0; digit_mask <<= 1; if(++digit_counter == DIGITS){ digit_counter = 0; digit_mask = (1 << 0); } }
// гасим все цифры? Но почему 4, в часах 4 лампы? CLR_PORT(ANODE_0); CLR_PORT(ANODE_1); CLR_PORT(ANODE_2); CLR_PORT(ANODE_3); }
Вытянет ли 8 МГц все это? Проверять в железе смогу только завтра начать...
Где у вас происходит изменение bright_step_counter ?
Таймер 16-битный, но используется в режиме Fast PWM, TOP = OCR1A. Это сделано для того, чтобы можно было произвольно менять частоту динамической индикации. По некоторым причинам в тех часах это было нужно. Можно использовать 8-битный, но тоже в режиме Fast PWM (выходы OC** при этом включать не нужно). Этот режим хорош тем, что значения в регистрах сравнения буферизуются - новое значение загружается при переполнении таймера, поэтому не получится так, что в регистр сравнения записывается значение, меньшее, чем текущее значение в счётном регистре, и прерывания по совпадению не пропускаются. Первое прерывание в коде заменяется на прерывание по переполнению (тут зажигаем разряд), второе - по совпадению (тут всё гасим). Можно, конечно, и наоборот, но тогда при увеличении OCR яркость будет снижаться.
служит для создания дополнительной паузы при переключении разрядов. Так пришлось сделать для подавления паразитной засветки. Для ВЛИ, думаю, это не актуально. BRIGHT_STEPS_WITH_SAFE_PAUSE = BRIGHT_STEPS + 1. Поэтому в конце отображения каждого разряда, когда bright_step_counter == BRIGHT_STEPS, одно прерывание, где должен был бы зажечься разряд, пропускается (именно поэтому там return). Можно убрать первый кусочек и заменить во втором BRIGHT_STEPS_WITH_SAFE_PAUSE на BRIGHT_STEPS, тогда будет обычный алгоритм, который я описывал ранее.
Во втором кусочке инкрементируется текущая градация яркости, а когда все градации пройдены - номер разряда. На digit_mask не обращайте внимания, надо было мне её из кода убрать. Это переменная с "бегущей единицой", которая использовалась для десятичных точек и мигания разрядов.
Дальше Вы всё написали верно - выбираем цифру из массива, выводим в порт (в данном случае - на К155ИД1), зажигаем нужный разряд. Потом гасим. 4 цифры - для сокращения числа строк в примере. Можно и больше сделать.
На 8 МГц всё это прекрасно работает. Число градаций яркости было даже не 16, а 20. Частота переполнений таймера была 135 * 20 * 4 Гц. Это соответствует обычной динамической индикации, где частота импульсов на каждый разряд равна 135 Гц.
_________________ Этот пост оказался полезен? Не поленись, нажми слева! Куплю индикаторы ИТС-1А, ИТС-1Б, ИГВ1-8х5Л, ИГПС1-222/7, ИГПС1-111/7 и подобные.
Почти правильно. Нужно подправить инкремент a и t:
Код:
t++; if (t > 15){ t = 0; a++; if (a == 6) a = 0; }
print(), кстати, можно вынести за switch, используя a как индекс массива. Можно оставить, как сейчас, это, вроде бы, должно даже выполняться быстрее.
И учтите, что при OCR2 = 250 и предделителе таймера 8 на выполнение прерывания по совпадению есть всего 6 * 8 = 48 тактов.
Изменять OCR в режиме Fast PWM можно как угодно, но нужно следить, чтобы не было слишком больших и слишком маленьких значений. Иначе код в прерываниях не успеет выполниться, и следующее прерывание будет сдвинуто во времени.
_________________ Этот пост оказался полезен? Не поленись, нажми слева! Куплю индикаторы ИТС-1А, ИТС-1Б, ИГВ1-8х5Л, ИГПС1-222/7, ИГПС1-111/7 и подобные.
Последний раз редактировалось *Trigger* Ср авг 11, 2021 20:29:04, всего редактировалось 1 раз.
Если так сделать, мне кажется будет довольно сильно моргать с делителем 8... Вы таймер настраиваете без делителя? Или лучше действительно делать на таймере0 с двумя совпадениями и подбирать эти совпадения так, чтобы не слишком лагало и мерцало? Без делителя мне кажется, контроллер только и будет висеть в прерываниях постоянно )
А если так не делать, то получается что-то странное вместо нормального алгоритма. Хотя, можете попробовать. Даже интересно, что получится.
В том варианте, который предложил я, частота импульсов на каждый разряд (в терминах обычной динамической индикации) будет 8 000 000 / 8 / 256 / 6 / 16 = 40,7 Гц. Я мерцание с такой частотой вижу, но это всё индивидуально. В телевидении 25 Гц, и никто не жалуется.
Собственно, это была одна из причин, по которой я использовал таймер с двумя каналами совпадения.
Без делителя будет трудно уложить прерывания в ограниченное время выполнения.
_________________ Этот пост оказался полезен? Не поленись, нажми слева! Куплю индикаторы ИТС-1А, ИТС-1Б, ИГВ1-8х5Л, ИГПС1-222/7, ИГПС1-111/7 и подобные.
Что то похожее я уже делал и тут не то чтобы мерцало как телевизор... Там скорее 5-6 Гц частота была )) что весьма печалило, однако это уже кое что новое, чем делал я.
Большое спасибо за помощь и подсказки, в ближайшее время доберусь до собранных часов и попробую, а то уже очень много времени этот процесс с места не двигается, а все мои личные попытки провальны ( ---
Проверил в железе... и все работает, сколько возился с этим, даже не предполагал что все на столько просто, капец... ---
К сожалению на частоте 8 МГц при делителе 1000 8 ощутимо мерцает, без делителя все адски лагает, так как постоянно висит в прерывании. Буду либо таймером0 делать, либо ставить кварц )
Я делал просто - использовал BAM-модуляцию. Хранится текущая цифра и следующая. Дальше при выводе на единичный бит значения счётчика выводится одна цифра, а на нолик другая. Когда счётчик досчитал, одна цифра заменяется на другую.
volatile uint8_t Digit[4]={0,0,0,0};//текущие отображаемые цифры volatile uint8_t NewDigit[4]={0,0,0,0};//новые отображаемые цифры volatile uint8_t ChangeDigitCounter[4]={0,0,0,0};//счётчик перехода от старых к новым цифрам
FreshMan, простите за долгий ответ, был в отпуске. По алгоритму триггера получилось вроде, но вот прямо конкретно реализовать алгоритм полностью с заполнением массива еще не делал, сегодня завтра начну. В целом, на 8 МГц мерцание заметно, если вблизи находиться, но если чуть отойти, то уже не особо.
Сам алгоритм вроде понял, а вот БАМ я когда то пытался осилить для управления ргб светодиодами, теми, что не ws2812, раз сто статьи по ней прочитал, так и не дошло (
for (uint8_t n = 0; n < 4; n++){ if (ChangeDigitCounter[n]== 0) Digit[n]= NewDigit[n];//цифра сменилась digind[n]=(ChangeDigitCounter[n]& mask)? Digit[n]: NewDigit[n]; }
Kotto, предлагаю вам реализовать более легкий , на мой взгляд, метод реализации регулировки яркости индикаторов на 2-м таймере атмеги8 с кварцем на 16МГц (не пожалейте на него денег ) алгоритм следующий: - в прерыванию по сравнению мы зажигаем ту или иную лампу - в преывании по переполнению гасятся все лампы
если данная информация вас заинтересовала, прошу лайкнуть и мы не спеша продолжим дальше наше увлекательное путешествие в удивительный мир знаний
Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 16
Вы не можете начинать темы Вы не можете отвечать на сообщения Вы не можете редактировать свои сообщения Вы не можете удалять свои сообщения Вы не можете добавлять вложения