Начав копаться с микроэлектроникой я начал с симуляторов. Но как правило это быстро надоедает и хочется попробовать тоже самое но в реальности, то есть чтобы устройство можно было пощупать, потрогать, посмотреть как оно на самом деле. В нашем городе в своё время смог нарыть только МК AtTiny2313 за 40 рублей (в других магазинах у нас же он стоит и 100 и 120 рублей), AtMega’у мне найти не удалось. Попробовав всякие «безделушки» на нём, вроде управления светодиодиками захотелось чего то большего, и вот лазяя по инету наткнулся на статью про вольтметр на сайте дихалта (изиелектроникс). Но! В AtTiny2313 нет АЦП… Но опять же «Но!» =) на всё том же сайте есть статья как сделать из аналогового компаратора и ШИМа этот самый АЦП. Сначала я подумал, а может легче купить атмегу? Но чета влом было искать их у нас, а по инету штучно не продает, или по крайней мере я об этом не знаю. И так, в итоге вольтметр получился, только вот числовые значения надо будем подгонять под пороговые. Так как у этого МК нет отдельного входа для определения границы измеряемого напряжения, оно равно напряжения питания МК. При создании вольтметра использовалось: AtTiny2313, экранчик от DVD-плеера и несколько самых обычных радиодеталей.
Схема самого устройства в Proteus:
Тут добавлен осциллограф чтобы отслеживать ШИМ. Вся проблема была более менее получить нормальный сглаженный выход с ШИМ, именно поэтому в схеме используется вторая RC-цепочка для сглаживания сигнала. Ниже будет показан выход с ШИМ и его сглаживание двумя RC-цепочками.
Структура файлов:
- voltmeter.asm
- - - init.asm
- - - interrupt.asm
- - - subprograms.asm
- - - macros.asm
Как видно из структуры файлов всё начинается с «voltmeter.asm», там происходит определение констант, именование регистров и код основной проги. По именам остальных файлов не сложно догадаться для чего они и всё же: инит — инициализация юарт, шим, таймера, аналогового компаратора и настройка портов; интеррапт — обработчики прерываний; макрос — тут ясно, мои макросы; субпрограмм — аналогично с макросами, мои подпрограммы.
Разберем основной файл. Там заложены основные идеи и всё самое интересное происходит там, не считая прерываний. Смысл такой, сначала объявляем нужные нам константы и переменные:
;///////////////// ;/// Константы /// ;///////////////// ;Настройки RS-232 .equ XTAL = 4000000 .equ BAUDRATE = 9600 .equ bauddivider = XTAL/(16*BAUDRATE)-1 ;Настройки измерений .equ zaderzhka_pwm = 0 ;задержка изменения выхода ШИМ .equ step_dec_pwm = 5 ;шаг изменения выхода ШИМ .equ volt_correct = 20 ;корректировка погрешности аналогового компаратора ;Частота обновления значения на экране .equ timeBinBcd_H = 0x00+0x01 .equ timeBinBcd_L = 0x0F ;////////////////// ;/// Переменные /// ;////////////////// ;Временные .def t1 = R16; .def t2 = R17; ;ШИМ .def lev_pwm = R18 ;уровень .def counter_zaderzhka = R19 ;длительность уровня(задержка изменения уровня) ;Значения напряжения на входе аналогово компаратора .def LED_bin_value = R20 ;в данный момент не используется так как ;потребовалось увеличить разрядность .def LED_bin_value_H = R25 ;двоичный формат .def LED_bin_value_L = R20 .def LED7segment_1 = R21 ;десятичный формат .def LED7segment_2 = R22 ;Частота обновления напряжения на сегментах экрана .def TimerBinBcd_H = R23 .def TimerBinBcd_L = R24
Затем как обычно начиная программу указываем нужные нам прерывания, а на остальные ставим заглушки. Далее мы попадаем на метку RESET, что соответствует только что включенному устройству. Самое главное первым делом не забыть инициализировать стек, иначе с устройством будет твориться что-то странное, причем даже в отладчике (я пользуюсь AVR Studio). Затем идет инициализация ШИМа, точнее совсем уж сначала =) я задал уровень и задержку, всё по нулям. Это означает что уровень ШИМа будет расти от нуля, и изменяться, то есть увеличиваться каждый раз когда приходит прерывание. Длительность и шаг ШИМа настраивается в константах, кури вставку кода выше. Там всё подробно написано. Далее инициализируются порты (входы\выходы), работа ШИМа и аналоговый компаратор. С этим всё, если посмотреть в код, можно увидеть закоменченные вызовы инициализации экрана и UART, не обращайте на них внимания. Я думал вообще подрубить его к компу и прогу накидать чтобы смотреть график, но не стал, и так нахимичился пока делал АЦП из ШИМа и UARTа, так что кому надо, можете посидеть над этим. Ну вот и всё с началом работы, далее разрешаются прерывания и записываются начальные значения для таймера по которому обновляется значение на экране.
;//////////////////////////////// ;/// Инициализация устройства /// ;//////////////////////////////// RESET: ldi t1, low(RAMEND); Main program start out SPL,t1 ;Set Stack Pointer to top of RAM LDI lev_pwm, 0 LDI counter_zaderzhka, 0 RCALL ports_init RCALL pwm_init RCALL analog_comp_init ;RCALL disp_init ;RCALL uart_init SEI ;Разрешение прерываний LDI TimerBinBcd_H, timeBinBcd_H LDI TimerBinBcd_L, timeBinBcd_L
Подобрались к выполнению основного цикла программы, тут я бы выделил 3 этапа: 1) обновление на экране старшего разряда (свечение циферки слева); 2) обновление на экране младшего разряда (свечение циферки справа); 3) изменение значения для высвечивания на экране.
На счет всех этих свечений и обновлений дело обстоит так. ШИМ постоянно меняет своё значение на выходе. Допустим у нас напряжение от 0 до 5 вольт, допустим оно изменяется за 0.3 секунды за 256 итераций(5 вольт делим на 256 получаем +0.019607843 вольта за каждую итерацию). Теперь главное не запутаться. Теперь перейдем к аналоговому компаратору, так как выход с ШИМа через RC-цепочки идет именно на него. Так вот, аналоговый компаратор, каждые 0.3 секунды выдает пару, тройку а может и десяток раз прерывание. В идеальном случае(читай в идеальных условиях) аналоговый компаратор должен выдать одно прерывание за 0.3 секунды, потому что входное нарпяжение должно один раз совпасть с напряжение на ШИМ. На ШИМе оно ведь всегда уменьшает(или всегда увеличивается) и по идее не равно предыдущему значению. Но! У нас не идеальный мир и есть погрешности, вот из-за этих погрешностей аналоговый компаратор несколько раз выдает прерывания из-за того что сигнал с ШИМа скачет. На картинках ниже это видно. Так, как работает аналоговый компаратор тоже разобрались. Еще раз(допустим у нас идеальный мир): ШИМ меняет значение от 0 до 5 вольт за 0.3 секунды в 255 интераций, аналоговый компаратор за эти 0.3 секунды вызывает один раз своё прерывание в том случае если напряжение с ШИМ и измеряемое напряжения равны.
В этом прерывании это значение записывается в память микроконтроллера (об этом ниже). Теперь возвращаясь в основную программу узнаем, что значения на экране НЕ МЕНЯЮТСЯ МГНОВЕННО с изменением напряжения на входе компаратора. А меняются тогда, когда это разрешит «искусственный» таймер. То есть, значение со входа компаратора меняется один раз в 0.3 секунды, как бы получается что и значение на экране должно меняться раз в 0.3 секунды, если напряжение конечно поменялось. НО! У нас, опять таки, не идеальный мир и за эти 0.3 секунды аналоговый компаратор может сказать что напряжение изменилось десяток раз. И что? Обновлять циферки на экране будем каждый раз, как нам об этом скажет аналоговый компаратор? А вот и нифига, представьте какой мерцание будет у вас на экранчике. Вы даже циферки разглядеть не сможете, представьте одновременно горят цифра 6 и 7, в итоге вы увидите 8 (восьмерку) на экране, потому что человеческий глазичек не успеет разглядеть эту разницу и сольет цифры 6 и 7 вместе. Ну может даже глаза тут не причем будут, а сам сегмент экранчика не успеет затушить цифру 6 и зажечь циферку 7. Так что вот так, для этого и сделан «искусственный» таймер, который меняет циферки (значения измеренного напряжения) на экране гораздо реже, чем делает это аналоговый компаратор с данными (опять же, со значениями измеренного напряжения) в памяти микроконтроллера.
Сначала думал привести код по частям с описанием, но получилось что описание я накатал не отрываясь от клавы. Теперь выкладываю код целиком с основным циклом программы, ибо нефиг тут уже чета по отдельности описывать, выше всё уже накатал. =)
;////////////////////////// ;/// Основная программа /// ;////////////////////////// beg: ;======================= ;=== Старший сегмент === ;======================= ;Выключение экрана CBI PORTB, 3 CBI PORTB, 4 ;Обновление цифры старшего сегмента ;LDI t1, 3 MOV t1, LED7segment_1 RCALL DecTo7LedPort ;Включение старшего сегмента экрана CBI PORTB, 3 SBI PORTB, 4 ;Горит старший сегмент экрана loop ;======================= ;=== Младший сегмент === ;======================= ;Выключение экрана CBI PORTB, 3 CBI PORTB, 4 ;Обновление цифры младшего сегмента ;LDI t1, 5 MOV t1, LED7segment_2 RCALL DecTo7LedPort ;Включение старшего сегмента экрана SBI PORTB, 3 CBI PORTB, 4 ;Горит младший сегмент экрана loop ;============================================ ;=== Получение нового значения для экрана === ;============================================ ;Насколько часто обновлять значение SUBI TimerBinBcd_L, 1 BRNE end SUBI TimerBinBcd_H, 1 BRNE not_nul_TimerBinBcd_H ;Забрать данные из аналогово компаратора и преобразовать их в двоично-десятичный код binbcd: cli LDI TimerBinBcd_H, timeBinBcd_H MOV t1, LED_bin_value_L MOV t2, LED_bin_value_H RCALL bin2bcd2REG sei not_nul_TimerBinBcd_H: LDI TimerBinBcd_L, timeBinBcd_L end: ;Возврат RJMP beg
Что же вышло. Вся отладка происходила в Proteus. После написание кода и постоянное тестирование его в proteus результат появился. Некоторые проблемы были с выводом циферок на индикаторы, сравнения напряжений на компаратора и пришлось подобрать номиналы резисторов и конденсаторов двух сглаживающих RC-цепочек. На первом скриншоте видно то о чем я писал выше, главная проблема которая была это множественное срабатывание аналогового компаратора на одном цикле ШИМа. Помните я приводил пример за 0.3 секунды 256 интераций? Так вот это оно самое, с ШИМа сигнал падает с максимального напряжения до нуля. За это время в идеальном случае аналоговый компаратор должен был сработать только один раз, а тут фигачет сколько ему захочется. Поясню что видим на экране виртуального осциллографа: желтым — выход ШИМ, голубым — первая RC-цепочка, красным — вторая RC-цепочка, зеленым — срабатывание аналогового компаратора. Первые два скриншота различают тем, что в первом я изменял входное напряжение. Это можно заметить по столбикам (срабатывает аналоговый компаратор) при разном напряжении идущим со сглаживающих RC-цепочек.
Собственно ради интереса сделал этих скриншота, ничего особенно нет, только на первом увеличен фрагмент с двумя срабатывании аналогового компаратора, а второй без. =) Второй показывает как сглаживается сигнал при уменьшении импульсов с ШИМа.
В заключение хотел бы немного уйти от темы и посоветовать всем программистам прочитать книгу «Совершенный код. Мастер-класс» С.Макконнелла, так как если бы я прочитал эту книгу до написания кода вольтметра, он был бы куда аккуратнее и понятнее. =) Так что исходники получайте такие какие есть, плюс кидаю видео как это всё работает.
Исходники в zip-архиве:
Проект AvrStudio плюс схема в Proteus
Видео первой попытки «вольтметра» на Attiny2313. Почему в ковычках? Потому что стоит переменный резистор, на который идет напряжение питания и это питание делиться на 5 пороговых точек. Вот и появляются цифры от 1 до 5. Причем к двум сегментам нет проводов, поэтому они не светятся =).
ну не плохо всё описал)) интересно))
вот только ума не приложу — как сам с программой разобрался. долго возился?
Да сам, сложно там немного, когда до этого разрабатал несколько хотя бы примитивных устройствах и сталкивался с передачей данных по проводам на таких устройствах =)
Спасибо за информацию!!!!!