Глава 17 «Зеленые» микросхемы
Поскольку значительное количество энергии тратится на разговоры, прекращение этого вида деятельности может оказаться весьма благотворным.
Виктор Санчес «Практическое использование техник Карлоса Кастанеды»
Глупо тратить энергию только на поддержание работоспособности МК: как и ПК, основное время современные контроллеры тратят на ожидание прерывания или внешних команд. Даже самая громоздкая процедура (не считая медленных по самому принципу действия алгоритмов передачи данных к внешним устройствам и записи в EEPROM), состоящая, к примеру, из тысячи команд процессора, будет выполнена в МК с весьма невысокой тактовой частотой 4 МГц примерно за треть миллисекунды, а то и быстрее. Остальное время контроллер будет простаивать, но потреблять практически столько же, сколько в рабочем режиме. Хочется придумать механизм, который бы позволял «будить» процессор только тогда, когда это требуется. Оказывается, это очень непросто.
Потребляет типичный микроконтроллерный прибор немного: даже с самосветящимися LED-индикаторами наш измеритель температуры и давления тратит не более нескольких ватт, из которых на долю контроллера приходятся лишь доли ватта. Оставшееся расходуется на индикацию и при желании это потребление можно предельно снизить, если использовать ЖК-индикаторы. Потому к таким устройствам не придерется даже самый упертый «зеленый» (в отличие от процессоров для ПК с их гигагерцевыми частотами, часто совсем неоправданными).
И на практике режимы энергосбережения в большинстве случаев целесообразны только тогда, когда ваша схема работает от автономного источника. Посчитайте: типичный AVR семейства Mega потребляет около 15 мА при напряжении питания 5 В. Таким образом, щелочные батарейки самого распространенного для таких устройств типа АА (см. Приложение 2) при емкости примерно 2000 мА ч проработают всего 133 часа или чуть больше 5,5 суток — это не считая потребления внешних устройств.
При использовании режима энергосбережения следует очень тщательно продумывать и схему, и саму процедуру перехода в этот режим. Может случиться, что какие-то внешние устройства у вас потребляют больше, чем сам контроллер, тогда все ваши усилия пойдут насмарку. Например, в схеме часов из главы 14 (там энергосбережение нецелесообразно, но я использую его схему, как наглядный пример) разряды порта D и В управляют транзисторными ключами. Если мы введем МК в режим энергосбережения, то процессор остановится, но состояния портов останутся такими, какими они были к моменту остановки. И если вдруг они случайно оказались в состоянии высокого уровня, то на каждый вывод придется немного менее чем по 1 мА вытекающего тока. При всех включенных выводах это около 10 мА, что сравнимо с потреблением самого контроллера. Какое уж тут энергосбережение…
О режимах энергосбережения AVR
В контроллерах AVR доступны несколько разных режимов энергосбережения (до пяти). Все они вызываются единой командой sleep, а результат ее выполнения различается в зависимости от предварительных установок. Интересно, что фирменное описание рекомендует производить установки и разрешать режим «сна» непосредственно перед подачей команды sleep — иначе МК, по словам разработчиков, может уйти в «сон» самопроизвольно. Это неудобно, но придется такой рекомендации следовать.
Что касается самих режимов, то принципиально отличаются от остальных лишь режим Idle (ждущий) и ADC Noise reduction. В первом из них отключение касается лишь центрального процессора, а все остальные устройства продолжают функционировать. При этом потребление микросхемы уменьшается всего лишь на треть, и на фоне общего расхода энергии всей схемы с учетом внешних устройств такая экономия теряется. Режим Idle удобен тем, что «просыпание» происходит мгновенно по любому прерыванию. В режиме ADC Noise reduction (подавления шумов АЦП — мы его не задействовали), кроме отключения процессорного ядра, отключаются также порты ввода/ вывода, в остальном он аналогичен Idle. Его используют только по прямому назначению, т. к. питание также практически не экономится, а выводить МК из этого режима сложнее, чем в случае Idle.
С точки зрения энергосбережения более интересны остальные режимы, которые не очень сильно отличаются друг от друга. Во всех моделях AVR без исключения имеется наиболее универсальный режим Power Down, при котором отключаются все внутренние тактовые сигналы. Потребление МК при этом снижается до нескольких микроампер. Вывести МК из такого состояния можно лишь внешним прерыванием (и то не всяким, о чем далее) или сбросом, т. е. выключением/включением питания, подачей внешнего сигнала Reset или сигналом от сторожевого таймера (последним мы займемся в конце этой главы). Вывод из состояния «сна» в режиме Power Down занимает довольно много времени, и это время еще требуется контролировать с помощью конфигурационных битов.
Потому мы здесь будем рассматривать его модификацию под названием Standbye. В этом режиме выключается все, кроме тактового генератора, что несколько повышает потребление (до десятков микроампер), зато вывод из «сна» занимает всего 6 машинных тактов. Так как Standbye имеется не во всех AVR семейства Mega, а в семействах Classic и Tuny его нет вообще, то для этих МК можно без переделок задавать обычный режим Power Down, не забывая только, что пробуждение тогда может длиться до более чем 10 мс (это важно, например, если МК управляется случайными сигналами и промежуток между ними может быть короче времени «пробуждения» — сигнал попросту потеряется).
Внешние прерывания, которыми можно вывести МК из режима Standbye, — это прерывания INTO, INTI и INT2 (если последнее имеется в данной модели). Однако сначала требуется тщательно изучить их режимы, т. к. они делятся на синхронные и асинхронные. Мы уже знаем из главы 12, что синхронные прерывания — это те, которые обнаруживаются лишь в момент прихода фронта тактового импульса. Их преимущество в практически мгновенном обнаружении сигнала на прерывание, но в режиме «сна», когда тактовые сигналы отключены, они, естественно, не могут быть обнаружены. К синхронным относятся прерывания по фронту и по спаду INTO и INT1. Режим прерывания по низкому уровню асинхронный, потому он пригоден для «пробуждения» МК. Кроме этого, очень удобно, если МК поддерживает прерывание INT2, поскольку оно имеет режимы только по фронту или по спаду, которые обнаруживаются асинхронно, в отличие от INTO и INT1 (из основных представителей семейства Mega INT2 нет только у ATmega8).
Очень важный момент здесь — ответ на вопрос: а что, собственно, будет делать МК, если его «разбудить» по прерыванию? Если это происходит в результате сброса (от любого источника), то выполняется, естественно, процедура начальной загрузки, как при включении питания. А «разбуженный» внешним прерыванием контроллер сделает совсем другое: в первую очередь он выполнит процедуру прерывания, которое его разбудило, а после этого перейдет к выполнению команды, следующей после sleep, на которой выполнение программы было прервано уходом в состояние «сна». Такая определенность очень важна. Хочу обратить также внимание, что команда sleep игнорируется, если она расположена внутри обработчика прерывания, она допустима лишь в тексте основной программы. Этот момент в фирменных руководствах явно не прописан, потому обратите на него особое внимание.
Измеритель давления и температуры в автономном режиме
Покажем возможную схему действий на примере нашего измерителя температуры и давления из главы 15 (без часов и записи в память). Схема его была приведена на рис. 15.2, и мы помним, что там не показана индикация (она такая же, как у часов на рис. 14.2, только используются другие разряды портов). В принципе можно было бы использовать ЖК-индикаторы, и тогда срок службы батарей сильно увеличивается (и их количество можно уменьшить, см. далее), но это потребует заметного изменения системы управления индикаторами (на ЖК, напомню, надо подавать переменное напряжение). К тому же подобрать удобный и эстетичный ЖК-индикатор для нашей конкретной цели — тоже отдельная и непростая задача. Чтобы не уходить в сторону от темы, я решил не останавливаться на этом вопросе: оставим LED-индикаторы, как более яркую иллюстрацию особенностей программирования режима энергосбережения.
Не забудем, что аналоговая часть потребляет сама по себе не менее 3 мА, потому ее придется отключать принудительно. Вариант батарейного источника питания для измерителя в автономном режиме, сделанный с учетом этого обстоятельства, показан на рис. 17.1. Один набор батарей служит здесь для обеспечения всех напряжений, причем напряжение аналоговой части (положительное и отрицательное) формируется с помощью инвертора, входящего в состав микросхемы P6BU-0505Z фирмы PEAK Electronic. Входное напряжение для этого DC/DC-преобразователя берется от стабилизатора цифровой части схемы.
Рис. 17.1. Батарейный источник питания для измерителя температуры и давления
Микросхема P6BU-0505Z может быть заменена на аналогичные изделия других фирм, только следите за характеристиками: так, преобразователи TMR-3 фирмы TRACO имеют встроенную возможность отключения, могут работать от нестабилизированного входного напряжения (т. е. прямо от батарей), но если присмотреться внимательно, то окажется, что они плохо работают при малых токах потребления (менее 20 % от номинала) и даже в режиме StandBye потребляют до 10 мА, что, конечно, неприемлемо в нашем случае.
Выбранная микросхема потребляет на холостом ходу гораздо меньше (порядка 1 мА), но встроенной возможности отключения не имеет, потому приходится вводить отдельный ключ (электронное реле КР293КП5В с контактами на замыкание), отключающий аналоговую часть при переходе в режим «сна». Для включения/отключения используем разряд 6 порта D (вывод 20 микросхемы ATmega8535), который придется устанавливать на выход и с нулевым уровнем, иначе схема не заработает.
Если пренебречь разделением аналогового и цифрового положительного питания, то P6BU-0505Z можно заменить на доступный и дешевый (порядка 1 долл.) преобразователь напряжения +5 В в одно напряжение -5 В под названием ICL7660 (он же МАХ1044, он же 1168ЕП1), который включается примерно по той же схеме, но требует дополнительно еще двух конденсаторов. Главный недостаток этой микросхемы — невысокая стабильность выходного напряжения, из-за чего точность измерений заметно снизится.
Индикаторы питаются нестабилизированным напряжением от 6 элементов питания, т. к. минимальное напряжение, при котором индикаторы еще горят, равно приблизительно 6 В. Среднее же напряжение во время эксплуатации будет около 8 В, соответственно ограничительные резисторы тока сегментов (R27—R34 на рис. 14.2), которые в сетевой схеме были равны 470 Ом, следует уменьшить как минимум вдвое — примерно до 200–220 Ом.
Для щелочных батареек типа АА общее время работы до исчерпания их ресурса составит около 10 часов (потребление тока шестью одновременно включенными индикаторами по схеме рис. 14.2, с учетом уменьшения сопротивлений составит максимум 200 мА), что для наших целей приемлемо. Если хотите увеличить время работы, и габариты позволяют, то следует выбрать более емкие батареи — так, со щелочными батареями типа D (особенно модели Duracell Ultra) наш прибор проработает около недели.
Задачу сформулируем так: пусть по первому нажатию внешней кнопки контроллер «просыпается», а по второму — «засыпает». Кроме этого, введем режим автоматического «засыпания» по истечении некоторого промежутка времени. Чтобы пользоваться прибором было удобно, этот промежуток должен быть достаточно большим: не менее минуты, так что нам придется потрудиться (встроенный таймер, как нам известно, обеспечит лишь интервал порядка 16 с). Разумеется, при выключении контроллера должна отключаться и индикация.
Заметки на полях
Одно замечание: никогда не проектируйте устройств, в которых режим энергосбережения не выключается отдельной кнопкой! Посмотрите, как неудобно пользоваться мобильными телефонами, в которых для включения подсветки экрана надо обязательно совершить какое-то действие. В идеале устройство должно содержать возможность отключения энергосбережения вообще, хотя в простейших случаях это не всегда удобно, и внешней кнопки включения/отключения достаточно, но если устройство управляется через экранное меню или от компьютера, то оно обязательно должно иметь команду полного отключения режима энергосбережения (или, по крайней мере, установки достаточно большого — более 10 минут — интервала отключения). Если подобных возможностей нет, это сразу говорит о неряшливости разработчиков.
Использование режима энергосбережения
В случае использования прерывания по низкому уровню, как мы уже знаем из главы 12, при первом же возникновении его следует запретить, иначе оно будет происходить непрерывно, пока действует низкий уровень. Это не очень удобно, когда прерывания управляются от внешних сигналов в форме меандра (как от часов из главы 16), потому что возникает вопрос — а когда разрешать его снова? Если перед уходом в «сон», то, в силу длительности состояния низкого уровня на выводе прерывания, оно тут же произойдет опять, и МК вообще никогда (в течение текущего полупериода) не «заснет». И управление «засыпанием» и прерываниями очень сильно усложняется. В таких случаях лучше будет задействовать прерывание INT2, которое не нужно запрещать (дребезг в таких случаях исключен), и соответствующую модель контроллера.
Но для нашей ситуации с управлением от кнопки это безразлично, поскольку дребезг при нажатии все равно приводит к необходимости запрета даже в прерываниях по фронту, а проблемы разрешения заново не возникает: непосредственно перед «засыпанием» или по таймеру. Если в момент разрешения кнопка окажется все еще нажата, то МК просто тут же проснется (или заснет) заново, ничего страшного. И мы, не мудрствуя лукаво, используем здесь универсальное для всех моделей AVR прерывание INTO по низкому уровню.
Кнопка (без фиксации) подсоединяется к выводу INTO (PD2, вывод 16 для ATmega8535). Если кнопка имеет перекидной контакт (т. е. три вывода), она подсоединяется к «земле» и питанию так, как показано на рис. 8.3, б (вместо элемента «исключающее ИЛИ» выступает, естественно, наш МК). Чаще встречаются кнопки с двумя выводами, тогда их подсоединяют так, как показано на рис. 17.2. В обоих случаях при ненажатой кнопке на выводе должен быть потенциал питания, т. е. высокий уровень, а при нажимании вывод коммутируется на «землю».
Рис. 17.2. Подсоединение кнопки с двумя выводами к МК АТтеда8535
Заметки на полях
Еще раз напомню (см. главу 12), что в принципе от резистора на схеме рис. 17.2 можно отказаться, поскольку специально для таких случаев в AVR предусмотрено подключение внутреннего «подтягивающего» резистора. Однако для надежности ставить его все же следует (так же, как и по выводам программирования, см. описание схемы к рис. 14.2 в главе 14), т. к. номинал встроенного «подтягивающего» резистора достаточно велик (минимум 35 кОм, согласно руководству), то на нем могут возникать наводки, которые приведут к ложным срабатываниям кнопки. Вы можете попробовать исключить резистор из схемы и убедиться в этом сами — ложные срабатывания появятся обязательно. И уж наверняка ложное прерывание будет возникать в ситуации, описанной в главе 14 для кнопки Кн1, когда питание внезапно переключается с сети на батареи. Так как мы тут такой режим не используем, то конденсатор параллельно кнопке (см. схему подключения Кн1 на рис. 14.2) ставить необязательно.
Доработка программы
Доработку программы измерителя (напоминаю, что за основу берем листинг П5.2, приведенный в Приложении 5, в разделе «Программа измерителя температуры и давления») начнем с того, что, во-первых, включим питание аналоговой части. Для этого в разделе начальной загрузки, там, где устанавливаются порты («установка портов вход/выход»), вместо команды ldi temp, 0b10000000 (перед out DDRD, temp) запишем ldi temp, 0b11000000. Теперь вывод 20 сконфигурирован на выход и для надежности следует добавить еще оператор cbi PortD, 6 (т. к. включается ключ низким уровнем).
Во-вторых, инициализируем таймер (Timer 1). В пробной программе записи во flash из главы 16 мы его настраивали ровно на 15 с, а здесь выберем вдвое меньший интервал — 7,5 с. В загрузочную секцию вместо строк инициализации Timer 0 (ldi temp, (i<<TOIE0) и out TIMSK, temp) добавляем строки листинга 17.1.
Листинг 17.1
;++++++++Set Timer 1
ldi temp,high(292 97)
out OCR1AH,temp
ldi temp,low(2 9297)
out OCR1AL,temp
ldi temp,0b00001101
out TCCR1B,temp ;1/1024; очистить после совпадения
ldi temp,(1<<TOIE0)|(1<<OCIE1A) ;разреш. прерывания
;по совпадению для Timer 1 и переполнению Timer 0
out TIMSK,temp
Переключающий режим для вывода PDS-OC1A здесь мы не используем. Кроме этого, введем переменную count_min, с помощью которой будем считать интервалы в 7,5 с. В секции объявления переменных добавим:
def count_min = r23 ;счетчик 7,5-секундных интервалов
А в секции начальных установок его не забудем обнулить:
clr count_min
Далее введем специальный флаг sleep (пусть будет бит 7 в регистре Flag), который будет сигнализировать о режиме. Если этот бит установлен — пора «спать», если обнулен — работаем, как ни в чем не бывало. По умолчанию он обнулен (см. секцию «начальная установка переменных») и нам ничего не грозит, если мы вставим в основной цикл запуск режима энергосбережения по схеме, согласно листингу 17.2.
Листинг 17.2
Gcykle:
sbrs Flag,7 ;если бит 7 установлен, то засыпаем
гjmp Gcykle ;иначе бесконечный цикл
cli ;на всякий случай запрещаем прерывания; все порты на вход, и нули в разряды, кроме PortD,6
clr temp
out DDRB,temp
out DDRC,temp
out DDRD,temp
out PortB,temp
out PortC,temp
ldi temp,0b01000000 ;выключение питания на всякий случай
out PortD,temp
ldi temp,0b11100000 ;разрешение Sleep, режим Standby прерывание по уровню
out MCUCR,temp
ldi temp,(1<<INT0) ;разрешение INTO
out GICR,temp
sei ;разрешаем прерывания
Sleep ;наконец, спим
cbr Flag, $80 ;по выходу из сна сбрасываем флаг sleep
clr count_min ;отсчет времени
;сначала установка портов вход-выход обратно
cli ;на всякий случай запрещаем прерывания
ldi temp,0b00111111 ; разряды out DDRB,temp
ldi temp,0b01111111 ; сегменты out DDRC,temp
ldi temp,0b11000000 ; знак минус и питание
out DDRD,temp
clr temp
out PortD,temp ;включить аналоговое питание
out MCUCR,temp ;запрещаем режим Sleep
out TCNT1H,temp ;очищаем счетные регистры таймера
out TCNT1L,temp
ldi temp,0b00001101 ; запускаем таймер
out TCCR1B,temp ;1/1024 очистить после совпадения
sei ;разрешаем прерывания
rjmp Gcykle ;бесконечный цикл
Теперь самое сложное: разобраться с прерываниями и установкой флага sleep. Не забудьте заменить reti на rjmp TIM1_COMPA в таблице прерываний, в строке для прерывания Timer1 Compare А (шестое сверху, не считая RESET). Прерывание таймера иллюстрирует листинг 17.3.
Листинг 17.3
ТIМ1_СОМРА:
inc count_min
cpi count_min,1 ;через 7,5 с разрешаем INTO
brne schet_time
ldi temp,(1<<INT0) ;разрешение INTO
out GICR,temp ;GIMSK и GIGR — синонимы
schet_time: ;здесь отсчет времени
sbrs count_min,3 ;если бит 3=1, то прошло 8 интервалов =1 мин
reti ;иначе выходим
clr count_min ;в след, раз — сначала
sbr Flag,$80 ;устанавливаем бит sleep
clr temp ;останавливаем таймер
out TCCR1B,temp
reti ;выходим
Если бы мы не связывались с кнопкой, то на этом можно было бы закончить — через 1 минуту после включения у нас измеритель уходит в «сон», из которого его можно вывести только выключением-включением питания или подачей сигнала Reset. Но мы хотим все делать грамотно, потому используем кнопку. Прерывание INTO тогда будет выглядеть так, как показано в листинге 17.4 (также не забудьте заменить в таблице прерываний во второй строке reti на rjmp EXT_INT0).
Листинг 17.4
EXT_INT0:
clr temp
out GICR,temp ;запрещаем внешние прерывания
sbrs Flag,7 ;если были во сне, то больше ничего не делаем
reti
clr temp ;иначе готовимся ко сну
out TCCR1B,temp ;останавливаем таймер
clr temp ;чистим счетные регистры таймера
out TCNT1H,temp
out TCNT1L,temp
ldi temp,0b00001101 ;заново запускаем
out TCCR1B,temp
ldi count_min,7 ;на интервал 7,5 с
reti
Теперь у нас измеритель будет работать как задумывали: после включения через 1 мин. он уходит в режим энергосбережения, когда индикаторы гаснут, и потребление минимизируется. Если нажать на кнопку, то МК «проснется» и выполнит все процедуры после команды Sleep. Если ничего не делать, то через минуту измеритель опять «заснет». Если нажать до истечения этого срока на кнопку (но не ранее, чем через 7,5 с), то он «заснет» через 7,5 с после нажатия. В принципе таймер можно было бы и не останавливать перед засыпанием, но так мы более уверены, что он начнет отсчет с самого начала.
Время задержки вы легко можете регулировать, просто меняя число, которое загружается в регистры OCR1AH/L. Оно рассчитывается исходя из формулы: время задержки в секундах равно частоте кварца в герцах, деленному на коэффициент предварительного деления (1024) и на это число. Например, если вместо 29 297 загрузить 11 719, то пауза до засыпания по нажатию кнопки станет равной 3 с, а время работы сократится менее чем до полуминуты. Чтобы увеличить время «бодрствования» вдвое, команду sbrs count_min,3 в прерывании таймера нужно заменить на sbrs count_min,4.
Заметки на полях
В прерывании кнопки для исключения случайного попадания в конец отсчитываемого интервала времени используется очистка регистров таймера. Тогда таймер начнет считать сначала, пока не достигнет заданного числа. Если вам потребуется задать отсчитываемый интервал очень точно, то следует почистить также счетчик предделителя (в семействе Classic такой возможности не было). Для этого в регистре SFIOR нужно записать единицу (не ноль!) в бит, соответствующий таймеру: для Timer 1 (а также Timer 0, так как у них предделитель общий) это будет бит 0 под названием PSR10. Этого не нужно делать при установленном счете напрямую (с коэффициентом деления тактовой частоты 1/1), в остальных случаях при запуске таймера в произвольный момент времени счетчик предделителя начнет со случайного числа (за исключением момента начального запуска при включении питания). Чем меньше коэффициент деления и чем больше число, отсчитываемое таймером, тем меньше относительная погрешность, но абсолютная ошибка всегда может достигнуть величины интервала между отчетами таймера (при коэффициенте 1/1 она попросту неустранима, только и всего).
Вторая особенность таймеров семейства Меда — наличие асинхронного режима работы для одного из таймеров (в большинстве моделей это Timer 2). Тогда его можно завести от независимого источника импульсов (от внешней частоты или низкочастотного кварца, в том числе часового 32 768 Гц), и он может считать независимо от работы всей остальной схемы. Теоретически эту функцию можно использовать как RTC, но на практике это неудобно (нет ни счета времени, ни календаря), зато ее очень удобно применять для вывода МК из «сна» по времени. В режиме энергосбережения под названием Power Save МК будет «просыпаться» каждое прерывание от Timer 2, и подсчетом этих прерываний его можно окончательно «разбудить» автоматически через нужный промежуток времени.
Использование сторожевого таймера
Сторожевой (watchdog) таймер — одно из самых полезных устройств в составе микроконтроллеров AVR. Причем это неочевидно: в нормальном режиме работы, когда все настроено идеально, он вовсе не нужен. Но представьте себе такую ситуацию: МК настроен на прием данных от компьютера, причем по простейшей схеме из главы 16, с непрерывным опросом бита UDRE. К примеру, он ожидает шесть байтов, как в нашей программе, но на пятом байте ПК внезапно ломается (кто-то прошел и ногой выдернул провод из COM-порта). Что будет с контроллером? Естественно, он повиснет в ожидании байта, и из этого состояния его не удастся вывести никаким способом, кроме полного сброса. Еще более опасны в этом отношении наши процедуры опроса линии по интерфейсу I2С — при программировании для ПК нам бы «голову оторвали» за такую организацию процесса.
И тем не менее здесь это нормальный способ программирования — нецелесообразно усложнять программу на порядки только для того, чтобы исключить все возможные ситуации, которые, может быть, за время «жизни» прибора вообще ни разу не произойдут. В ПК все иначе: во-первых, там есть удобные инструменты для таких случаев, во-вторых, там от одной программы могут зависеть и другие. Вот если бы мы на МК сооружали устройство управления космическим кораблем — тогда другое дело…
Но все же, как быть в таких случаях — ставить специальную кнопку Reset (как в компьютере) или писать в инструкции «если прибор не реагирует, то выключите и включите питание»? Вот тут-то на помощь и приходит сторожевой таймер, который, будучи включен, выполняет одну-единственную операцию: считает импульсы от собственного генератора (абсолютно независимо от всей остальной схемы МК), и когда досчитает до заданного их числа, не обращая внимания ни на что, попросту сбрасывает процессор, как будто был подан сигнал Reset. Самая длительная выдержка, которую можно получить от сторожевого таймера, составляет примерно 2 с (с большим разбросом, т. к. задающий генератор простейшего RC-типа).
Ну и что с этим делать, спросите вы? Нельзя же сбрасывать МК каждые две секунды «на всякий случай» и начинать работу заново, правда? Но этого и не требуется: достаточно завести сторожевой таймер, а потом сбрасывать его в исходное состояние, не дожидаясь, пока он сбросит контроллер. Это можно делать специально по таймеру или в любой другой периодически протекающей процедуре, которая должна выполняться раньше, чем таймер успеет сбросить процессор. Тогда, если МК завис по любой причине (даже просто из-за ошибки в программе), таймер сработает и приведет все в начальное состояние. В том числе, кстати, «разбудит» МК, даже если тот находится в самом глубоком «сне» (в режиме Power Down).
Для примера разберем такой случай возможного использования сторожевого таймера. В измерителе с часами, который был описан в главе 16, опасная ситуация может возникнуть, если по каким-то причинам не «придет» прерывание от часов. Это может произойти, например, если сами часы «повисли» (у них ведь тоже достаточно сложный алгоритм) или просто «потерялся» фронт очередного импульса. А так как мы настраиваемся на круглогодичную работу этого прибора, то подобную ситуацию следует рассматривать как вероятную. В конце концов, космическая частица может прилететь и все нарушить — согласно некоторым исследованиям, сбои такого рода неизбежны с вероятностью примерно 1 сбой на 1000 часов работы (правда, автор этих строк лично ничего такого не наблюдал, но и специально вопрос не исследовал). В этом случае неплохо выполнить процедуру включения прибора заново: в начале программы мы в том числе инициализируем и часы, а уж если они совсем сломались, тут ничего не поделаешь.
Для этого перед началом основного цикла инициализируем сторожевой таймер:
;запускаем WDT на 2 сек:
wdr ;команда на сброс — так рекомендуется
ldi temp,(1<<WDCE)|(1<<WDE)
out WDTCR,temp
ldi temp,(1<<WDP0)|(1<<WDP1)|(1<<WDP2)|(0<<WDCE)|(1<<WDE)
out WDTCR,temp
Теперь осталось по каждому прерыванию от часов (INTO) просто сбрасывать сторожевой таймер:
EXT_INT0:
wdr ;сброс сторожевого таймера
…
Так как прерывание должно возникать каждую секунду, то мы сбрасываем таймер заведомо раньше, чем он сработает, и он начнет отсчет выдержки сначала. Если же что-то (часы или программа) «повиснет», то произойдет общий сброс МК, и он начнет работать опять. Причем после чтения данных из flash мы сможем это обнаружить: если помните, мы в кадр времени записывали байт сбоев, в котором установленный бит 3 означал, что сброс произошел именно от сторожевого таймера.
В этом деле есть единственный «тонкий» момент: если у нас где-то в программе имеется процедура с запрещением прерываний, выполняющаяся дольше, чем 2 с, то сторожевой таймер на время ее выполнения, естественно, следует выключить (не просто сбросить, а вообще запретить его работу). У нас такая процедура есть — это чтение данных из памяти, при котором даже индикация выключается. Вызов этой процедуры придется переписать так, как показано в листинге 17.5.
Листинг 17.5
proc_F2: ;F2 читать flash
cli ;запрещаем прерывания выключить WD:
wdr ;Reset WDT
in temp, WDTCR
ori temp,(1<<WDCE)|(1<<WDE)
out WDTCR, temp
ldi temp,(0<<WDE) ;выключить WDT
out WDTCR, temp
rcall ReadFullFlash ;читаем данные
;запускаем WDT обратно, 2 с
wdr ;команда на сброс
ldi temp,(1<<WDCE)|(1«WDE) out WDTCR,temp
ldi temp,(1«WDP0)|(1«WDP1)|(1<<WDP2)|(0<<WDCE)|(1«WDE)
out WDTCR,temp
sei ;разрешаем прерывания — необязательно, уже есть в ReadFullFlash
rjmp Gcykle
Заметьте, что после такой длительной процедуры можно прерывания и не разрешать (для этого придется убрать разрешение и из самой процедуры ReadFullFlash) — а вдруг мы чего-то нарушили в работе? Тогда контроллер просто перезапустится с нуля, и работа восстановится.
Более 800 000 книг и аудиокниг! 📚
Получи 2 месяца Литрес Подписки в подарок и наслаждайся неограниченным чтением
ПОЛУЧИТЬ ПОДАРОК