15.2 Пример разработки: пляжный экспонометр (V)
==1054
Этот незаменимый спутник отдыхающих первый раз встретился в Части _4 [* §4.8.4 ] , где разбиралась его чисто аналоговая версия, а второй раз в Части 13 [* §13.9.1 ] , где использовалось интегрирующее АЦП. Упомянутые версии хорошо раскрывали методы разработки с использованием аналоговых и цифровых компонентов. В этой части история завершится микроконтроллерной реализацией. Сейчас будет задана базовая конструкция, а позднее она будет слегка доработана.
Напомним, что задача состоит в сообщении пользователю, что пора завершать солнечные ванны. Она решается подсчётом набранной дозы солнечного излучения с фотодиодом в качестве первичного датчика ( его ток пропорционален освещённости ). Любитель загара устанавливает требуемую дозу «полного солнечного эквивалента» ( FSE ) с помощью потенциометра в пределах 0...90 FSE, нажимает кнопку «START» и начинает цикл выпекания. Об окончании набора дозы сообщает пьезо-зуммер.
15.2.1 Реализация на микроконтроллере
==1055
Классическое, но скучное, решение - преобразование тока в напряжение на резисторе обратной связи операционного усилителя, оцифровка выхода ОУ с помощью внутреннего АЦП с последующим интегрированием в цифровой форме. Но есть путь проще и лучше, вполне доступный микроконтроллеру ценой $0.50 ( рис. 15.2 ). Идея состоит в том, чтобы заряжать с помощью фототока конденсатор, затем, используя встроенный в µC компаратор, активировать короткий разрядный импульс через вывод порта, находящегося до того в третьем состоянии _5 . Такая схема генерирует пилообразный сигнал, чья частота пропорциональна освещённости. Микроконтроллер просто считает циклы заряд-разряд и включает сирену, когда нужный уровень достигнут ( устанавливается потенциометром «доза» ). Цифровой входной порт фиксирует нажатие на кнопку «START», а цифровой выходной порт управляет пьезо-зуммером.
Рис. 15.2 С помощью микроконтроллера можно легко реализовать уже знакомый пляжный экспонометр
Интегрирующий метод не только проще, чем банальное преобразование I-V-АЦП, но и точнее. Преобразование тока в напряжение требует хорошего динамического диапазона ( т.е. очень низких напряжений смещения ), чтобы сохранять точность при низких токах, а генератор пилы сохраняет отличную линейность даже в этом случае. Предложенное решение сильно отличается от обычной инженерной практики и показывает широту возможностей, которую получают адепты микроконтроллеров.
Схема 15.3 - самая простая из пяти представленных в книге вариантов детектора, но требует написания и загрузки в кристалл кода, о чём будет разговор далее. Перед тем, как переходить к программе, рассмотрим подробнее все достоинства и особенности нового варианта: номиналы компонентов, выбор контроллера, фотодетектора и т.п. Здесь есть чему поучиться. Собственно, выбор компонентов, а также компромиссы, на которые приходится идти, и есть суть разработки электроники.
Рис. 15.3 Полная схема пляжного экспонометра. См рис. 4.93 , где приводятся данные сенсора G5842
На рис. 15.3 показана итоговая схема, построенная на компонентах, доступных в момент написания книги. Давайте разберём её.
15.2.1.A Выбор микроконтроллера
К этому элементу каких-то особенных требований нет. Скорость не важна, а компаратор и АЦП есть сейчас почти во всех таких микросхемах. Зато хочется малого потребления и работы прямо от батареи без промежуточного регулятора. Данная модель выбрана как самая младшая в линейке “picoPower” AVR фирмы Atmel. Экземпляр на схеме имеет режим микропотребления, даже когда работает от +5V _6 . Диапазон питания 1.8—5.5 V , а ток 35...170μA ( по всему диапазону питания ) при работе от внутреннего генератора на 128 kHz .
15.2.1.B Схема разряда
От использования выхода с третьим состоянием для разряда времязадающего конденсатора ( рис. 15.2 ) придётся отказаться, т.к. параметры утечки не позволяют рассчитывать на успешное завершение проекта. Максимальный ток утечки составляет ±1μA , что сравнимо с током фотодиода на свету. Можно выбрать датчик с бОльшими размерами и, соответственно, бОльшим током, но внешний ключ на n-МОП транзисторе легко решит задачу _7 . Здесь используется компактный BSS123: уровень переключения подходит для схемы, а ток стока достигает 0.5 A при 2.5 V на затворе. Утечка для наихудшего случая 10 nA при \(V_{DS}\)=20 V . Его легко купить, а цена всего $0.05 в количествах _8 .
15.2.1.C Потенциометр установки дозы
В упрощённой схеме потенциометр поставлен между землёй и питанием, но правильная разработка требует, чтобы в этом случае ток холостого хода был существенно выше, чем утечка выводов порта, т.е. требует существенных расходов мощности. Правильнее будет подавать на потенциометр питание, только когда оно и в самом деле требуется, т.е. при считывании положения движка. Это просто: надо подключить верхний вывод вместо питания на порт микроконтроллера. Когда программа выдаст на выход ВЫСОКИЙ уровень, внутренний p-канальный ключ подтянет вывод к Vcc .
==1056
15.2.1.D Фильтрация и развязка
Схема 15.3 украшена развязочными конденсаторами, возможно, даже с некоторым перебором. Шина аналогового питания AVcc отвязана от шумной цифровой Vcc с помощью LC фильтра, рекомендованного в справочных данных на контроллер. Есть развязка и на выходной линии потенциометра, потому что напряжение на него подаётся с той же шумной цифровой шины. Наконец, клемма источника питания развязана параллельно включёнными конденсаторами, чтобы сохранить низкий импеданс на всех частотах. Последовательная индуктивность ESL и сопротивление ESR электролитического конденсатора снижает его эффективность на высоких частотах, где эстафету перенимает небольшой керамический собрат, см. Часть X1 [* ##§X1.3 ] . В предложенной схеме можно было бы снизить требования, т.к. точности 6 разрядов [* потенциометру ] хватит за глаза, а все перечисленные выше рекомендации относятся к режиму максимальной точности АЦП. Но лучше сразу привыкать проектировать схемы должным образом.
15.2.1.E Генератор
А генератора-то нет! Выбранный контроллер, подобно множеству других, имеет несколько внутренних источников тактирования, а именно два: 8 MHz и 128 kHz удовлетворительной точности ( ±10% ).
15.2.2 Код для микроконтроллера
Встроенное программное обеспечение - микропрограмма - должна решать несколько задач.
- Провести инициализацию при включении питания ( установить режим работы портов ввода-вывода, АЦП, компаратора и установить выходы в рабочее состояние ).
- Подать питание на потенциометр на 25 ms , считать выходной уровень и снять питание.
- Подсчитать на основе полученного с потенциометра напряжения величину дозы в полных циклах заряд-разряд пилообразного генератора.
- Использовать выход компаратора для подсчёта числа циклов полного заряда-разряда и формирования разрядного импульса.
- По достижении нужного числа циклов активировать сирену.
Кроме того, есть ещё несколько рабочих параметров ( например, частота генератора ), на которые программа повлиять не может, и которые должны быть установлены в момент загрузки кода в кристалл. Эти параметры, они называются «перемычками» ( «fuse» ), будут разобраны чуть позже.
15.2.2.A Псевдокод
Все перечисленные задачи переложены в текст на псевдокоде 15.1 . Псевдокод - неформальная текстовая замена для структурной схемы .
Псевдокод 15.1 Алгоритм экспонометра
Setup Variables: Define 32-bit integers count and termcount Low Power: Disable unused peripherals Ports: Set as outputs PC1 (potbias), PD0 (cap discharge) andPD1 (buzzer); set as analog inputs PC0 (pot readout) and PD7 (cap voltage); initialize PD1 LOW (buzzer off), and PD0 HIGH (cap held at gnd) Analog modes: Set comparator mode & bandgap ref; set ADC ref to Vcc, ADC mode to left-adjusted, and MUX to channel 0 Read “bake” setting Setbits PC1 (power to pot) and ADEN (ADC enable); wait 100 ms Start ADC conversion (setbit ADSC) Wait while ADSC=1 (busy), then read unsigned 8-bit result (“bake”) Clearbits PC1 and ADEN, and disable ADC to save power Compute termcount=360000 x bake/256 Count Cycles Wait while comparator is HIGH (Vcap < Vref), then: setbit PD0, then clearbit PD0 (software discharge pulse) increment count if count<termcount, repeat Count Cycles otherwise set bit PD1 (buzzer), clear 10 sec later
Краткие пояснения. Код начинается с настроечных задач ( «Setup» ). Они требуются для периферийных устройств, которыми забит кристалл, настройки рабочих режимов и функций ввода-вывода _9 . Гибкость - это и удобство, и проблема. Удобства очевидны, поговорим о проблемах. Требуется изрядная внимательность, чтобы выставить все опции правильно. Делается это изменением состояния отдельных битов в массиве периферийных регистров микросхемы. У выбранного процессора таких регистров 256 , из которых около 40 отвечают за опции и настройки. Устанавливать их все нет нужды, потому что по умолчанию они получают вполне разумные значения. Потребуется менять только настройки компаратора, АЦП и портов ввода-вывода.
Код, стартующий после включения, должен выполнить именно эту задачу. Каждая линия порта может быть индивидуально настроена на вход или на выход: для этого надо выставить нужным образом биты в регистре направления передачи, а для входов можно включить или выключить внутренние подтяжки. Дальше выход пьезо-пищалки переводится в состояние НИЗКИЙ, а выход активации разряда - в состояние ВЫСОКИЙ. Завершается настройка выбором источника опорного напряжения для компаратора и АЦП, установкой масштабного коэффициента и источника аналогового сигнала. Наконец, ещё несколько бит отвечают за источник тактирования ( внешний/внутренний ), частоту и делитель. В данном микроконтроллере биты тактового сигнала входят в число «перемычек» , устанавливаются при загрузке кода в кристалл и исполняемой программе недоступны 10 .
Дальше идёт инициализация. Выходные линий зуммера и разряда устанавливаются в НИЗКОЕ состояние, заводится и обнуляется регистровая переменная «count», где будет накапливаться число циклов генератора пилы.
Читаем ( «Read “bake” setting» ) состояние потенциометра, задающего дозу. Для этого надо выдать на вывод «PC1» уровень Vcc , подождать 25 ms , запустить АЦП, подождать флага готовности данных, прочитать старший байт 10-разрядного результата ( в этой задаче точность лучше 0.5% не нужна ), снять питание с потенциометра и рассчитать требуемое число циклов интегратора. Последняя цифра предполагает нормальную пилу частотой 100 Hz с интегратора ( фототок 1 μA ) и полный цикл 90 минут.
==1058
Всё перечисленное занимает менее секунды. Дальше надо считать ( «Count Cycles» ) циклы пилы, длительность которых зависит от реального фототока, а спадающий фронт формируется программным импульсом ( ВЫСОКИЙ, затем НИЗКИЙ ) на затворе МОП транзистора. Делать процессору больше нечего, поэтому здесь просто опрашивается выход компаратора, хотя красивее было бы использовать прерывание ( в предложенных настройках прерывания запрещены ). Когда счётчик циклов досчитает до целевого значения, программа включит зуммер и войдёт в «бесконечный» цикл, т.е. перезапуск программы возможен только через выключатель питания.
15.2.2.B Развёрнутый код на Си
В листинге 15.1 приведён исходный код на Си. Программисты, знакомые с ним, легко разберутся. Отметим несколько особенностей, относящихся к микроконтроллерам. Файл «io.h» и «fuse.h» хранят определения для регистров, функций и т.д., учитывающих особенности внутреннего устройства конкретного контроллера ( фрагментация адресов, битовые переменные ). Непосредственное общение с битами в регистрах и портах - совершенно обычное дело для работы с микроконтроллерами. Выбор бита производится наложением маски. Сначала выбирается позиция бита в регистре ( по соответствующему «define» в заголовочном файле ), после чего создаётся маска для операции «ИЛИ» ( «1<<n» ) либо «И» ( «~(1<<n)» ). Скажем, для имени «PORTC1» в файле «io.h» указана позиция 1 [* счёт идёт с 0 ] , поэтому выражение « #define POT ( 1<<PORTC1 )» присваивает имени «POT» значение 0x02 [* т.е. отмечается второй справа бит ] . Здесь есть хитрая ловушка . В программах, подобных этой, значения считываются из регистров периферии ( компаратор, АЦП ), отображённых в память. Соответствующие переменные требуется отмечать модификатором «volatile», чтобы исключить «оптимизацию» операций с ними, при которой обращение к регистру может быть исключено компилятором. Именно для таких целей используется файл «io.h», где перечислены адреса памяти регистров периферии, но туда можно добавить и дополнительные переменные с долгим сроком жизни.
Программа 15.1
#include <avr/io.h> #include <avr/fuse.h> #include <util/delay.h> #define DISCHARGE (1<<PORTD0) #define BUZZER (1<<PORTD1) #define POT (1<<PORTC1) int main() { long termcount, count; // Total timer counts and running counter // Power saving measures PRR = ~(1<<PRADC) & ~(1<<PRSPI); // Shut off peripherals except ADC & SPI DIDR0 = 0x3f; // Disable digital input buffers on analog pins // Setup the pins DDRD = DISCHARGE | BUZZER; // Set two pins to output, rest to input DDRC = POT; // Set the POT pin to output, rest to input DIDR0 |= (1<<ADC0D); // Use PC0 as ADC0 -- the ADC input DIDR1 |= (1<<AIN1D); // Use PD7 as AIN1 -- the comparator input PORTD = BUZZER; // Hold cap low, and start with buzzer off // Comparator Setup ACSR = (1<<ACBG); // Set the reference to the band gap (needs 70 us) // Read the desired exposure duration PORTC |= POT; // Turn on the top of the resistor divider. _delay_ms(25); // Delay 25 ms for 10RC settle ADMUX = (1<<REFS0) | (1<<ADLAR); // Use Vcc ref; left-adjusted result ADCSRA = (1<<ADEN) | (1<<ADSC); // Enable, and start ADC /*** Wait until ADC conversion is done. ***/ while ( ADCSRA & (1<<ADSC) ) { } termcount = (360000L * ADCH) >> 8; // Convert ADC result to timer count PORTC &= ~POT; // Turn off the top of the resistor divider ADCSRA &= ~(1<<ADEN); // Disable ADC PRR |= (1<<PRADC); // Enable power-reduction for ADC /*** Wait until desired sunlight exposure ***/ for (count = 0; count < termcount; count++) { // Wait for cap to charge, then comparator output goes low while(ACSR & (1<<ACO)) { } PORTD |= DISCHARGE; // Discharge the capacitor PORTD &= "DISCHARGE; // And release it to recharge } // Buzz for 10 seconds PORTD |= BUZZER; // Power the buzzer _delay_ms(10*1000); // Delay 10 seconds PORTD = BUZZER; // Turn off buzzer // Loop forever while (1) { } }
15.2.2.C Некоторые комментарии
- Для задания начального момента в устройстве используется включение питания. Другой способ - использовать кнопку, которая замыкает вывод порта на землю. Состояние данного вывода можно опрашивать программно или использовать прерывание «по уровню». Второй вариант красивее и позволяет переводить процессор в спящий режим ( <1 μA ) по завершении работы. А будить его будет прерывание по НИЗКОМУ уровню на выводе. В такой схеме можно вообще убрать переключатель питания, но делать этого не стоит, потому что при «слёте» программы единственным способом восстановить нормальную работу будет переподсоединение батареи. В конце концов, все мы знакомы с потребительской электроникой ( автоответчиками, камерами и т.п. ), которые внезапно зависают в каком-то положении, заставляя проводить «холодную перезагрузку»: вытаскивая шнур из розетки или вынимая батарейку. [* С другой стороны, ожидать, что в давно отлаженном устройстве с конкретной ограниченной функциональностью программа внезапно сойдёт с ума, наверно, не стоит. Здесь выбор за разработчиком ] .
==1057
==1059
- Выход компаратора позволяет узнать, перешло ли напряжение линейно возрастающего фронта пилы заданный уровень, и опрашивается программно. Вместо программного опроса можно использовать прерывание. Делать в программе больше нечего, поэтому заметных преимуществ такая модификация не даёт. В прерывании надо разряжать конденсатор и увеличивать счётчик, а основной цикл должен просто проверять значение счётчика.
- Микроконтроллер - устройство с большими возможностями. Приложив некоторые усилия можно добавить в экспонометр дополнительные опции: ЖК-индикатор с линейной шкалой, показывающий степень «прожарки» и цифры текущей освещённости, проигрыватель MP3, игру... [* и взлететь потом со всем этим ] .
15.2.2.D Загрузка кода
Последним шагом будет «прошивка» кристалла, т.е. загрузка бинарного файла, выданного компилятором, и установка «перемычек» . Вот как это делается.
- Необходимо подключить программный адаптер к нужным выводам микроконтроллера ( через предусмотренный для этого разъём внутрисхемного программирования ) и порту USB управляющего компьютера.
- Подать питание на целевое устройство.
- Запустить тест, проверяющий корректность подключения и определяющий модель контроллера.
- Выбрать нужное сочетание «перемычек» 11 и загрузить его.
- Выбрать требуемый файл прошивки на управляющем компьютере и загрузить его в программную память контроллера. Выбрать файл с содержимым EEPROM, если требуется, и загрузить его в память данных.
- Завершив загрузку, утилита программирования проведёт сброс целевого кристалла, после чего последний приступит к выполнению программы. Теперь адаптер можно отключить. Программа будет запускаться вновь вместе с очередным циклом включения питания.
15.2.2.E Замечания по интерфейсу пользователя
Коллега авторов Джим МакАртур ( Jim MacArthur ) предложил следующий комментарий.
Если бы, в книге разбирался бы интерфейс пользователя ( HI ), предложенный вариант был бы учебным заданием по исправлению отклонений от спецификации. Задание звучит проще некуда: повторите функциональность аналогового решения. И ваш тестировщик интерфейса, несомненно, скажет, что данный вариант отличается от аналогового в одной важной детали: если изменить положение потенциометра в середине экспозиции, аналоговая схема это учтёт, а цифровая - нет 12 . Дальше проверяющий не без занудства сообщит, что 47% пользователей действуют строго определённым образом: сначала включают питание, а затем крутят регулятор. 13% этих людей возвратят устройство, не сделав ни одной попытки разобраться, в чём дело, компания потеряет миллионы, а вы лишитесь премии. Мораль:
- Никогда вообще не отдавайте пользователям изделия, не получившие одобрения от тестировщика интерфейса ( кстати, в случае провала будет на кого всё свалить ).
- Аналоговые схемы не так легко воспроизводить в цифровой форме, как кажется на первый взгляд.
15.2.2.F Ревизия разработки
После отрезвляющего общения со специалистом по интерфейсам было решено провести ревизию всей разработки. Кто-то обратил внимание, что вещь, лежащая на раскалённом солнцем песке, сильно нагревается. «Скажите пользователю, чтобы клал экспонометр в тень под шезлонгом». Это предложение вызвало полный ступор. Для проверки сомнений было проведено моделирование при 85°C . Это достаточно много и ток утечки BSS123, удваивающийся каждые 10°C , достиг совершенно неприемлемого уровня 3.2 μA ( т.е. в 64 раза больше, чем 50 nA при 25°C ). Опа! Интересно, что параметры компаратора при этом не изменились, потому что его входной ток 50 pA нормируется при 85°C . Кроме того, увеличился темновой ток фотодетектора: с 50 pA при комнатной температуре до 3 nA в тепле. Но это изменение не выходит за допустимые границы.
Упражнение 15.1 Найдите замену для ключа на BSS123. Одним из вариантов будет адаптация метода с рис. 13.49 для микроконтроллера. Подсказка Замените \(U_2\), \(U_3\) и \(U_4\) микроконтроллером. Учтите, что напряжение в точке «X» не имеет значения. В схеме 13.49 используется операционный усилитель LMC6842, для которого указывается входной ток 10 pA max при температуре 85°C , что очень хорошо, а плохо то, что ток потребления у него 1 mA . ОУ станет основным потребителем. Попробуйте, может, найдёте решение получше.
==1059
5 Функцию обнаружения порога можно динамически заменять функцией разряда конденсатора, переключая управляющие биты в конфигурационных регистрах режима порта ввода-вывода. <-
6 Многие микроконтроллеры используют режим пониженного потребления при низком напряжении питания, например, 1.8 V , но не включают его при уровнях 3...5V . <-
7 Зато можно быть вполне уверенным, что типовой ток утечки, скорее всего, ниже 10 nA , т.к. специфицируется при полном промышленном диапазоне температур –40°...+85°C . Обычно производители кремния бывают очень консервативны при указании значений утечек, и в данном случае беспокоиться не о чем: по спецификации максимальное значение входного тока компаратора 50 nA . <-
8 BSS123 является аналогом 2N7000 и 2N7002, см. табл. 3.4a . <-
9 Например, на 28-ой вывод корпуса заведён бит 5 ( из 8 ) порта «C». Кроме того, тот же вывод может использоваться как аналоговый вход АЦП ( через внутренний мультиплексор на 8 входов ), как тактовый вывод «SCL» и как «источник прерывания по изменению состояния». <-
10 У микроконтроллеров с большим числом выводов корпуса часто можно увидеть несколько ножек, отведённых на выбор режима начальной загрузки, состояние которых влияет на поведение стартового кода. <-
11 Для AVR в их число входят: напряжение срабатывания внутреннего детектора питания, разрешение/запрет отладки, источник тактового сигнала ( внутренний, внешнее тактирование, внешний кристалл ), разрешение JTAG, SPI и сторожевого таймера. <-
12 Но программу можно подправить, чтобы цифровая система тоже «учитывала» изменения. <-