главу 13), что заметно сократит программу. Кроме того, надо не забыть знак температуры, который удобно изготовить из отдельного плоского светодиода. В остальном принцип индикации точно такой же, как в часах, и мы остановимся на подробностях чуть далее, когда будем разбирать программу.
Не показан на схеме и программирующий разъем, который полностью одинаков для любой схемы на AVR и показан на рис. 13.4 и 15.2 (соответствующие выводы для ATmega8535 подписаны на схеме рис. 15.2). То, что вывод MOSI (вывод 6) совпадает с выводом индикации единиц давления, вас смущать уже не должно. Однако незадействованные в других функциях выводы программирования (в данном случае MISO и SLK, выводы 7 и 8) следует подсоединить к питанию +5 Вц «подтягивающими» резисторами номиналом от 1 до 10 кОм (на схеме не показаны), так же, как и вывод Reset, только, естественно, без каких-либо конденсаторов (на схеме для вывода Reset указан номинал резистора 5,1 кОм). Как и RC-цепочка для Reset, «подтягивающие» резисторы для выводов программирования в принципе не требуются, однако их следует устанавливать. В тех случаях, когда схема представляет собой временный макет, без этих деталей можно обойтись, однако в работающей схеме без них могут быть неприятности, о чем мы уже говорили в главе 12. Если разъем программирования вообще не предусматривается, то устанавливать резисторы к выводам программирования не нужно.
Схема источника питания показана на рис. 15.4.
Рис. 15.4.Схема источника питания для измерителя температуры и давления
Измеритель имеет четыре питания (+5 Вц, ±5 Ва и +12 В для индикации) и три «земли», причем обычным значком «» здесь обозначена аналоговая «земля» CNDa. Линия цифровой «земли» обозначена GNDц, кроме этого, имеется еще общий провод индикаторов GNDи. Все три «земли» соединяются только на плате источника питания. Отмечу, что готовый трансформатор с характеристиками, указанными на схеме, вы можете не найти. Поэтому смело выбирайте тороидальный трансформатор мощностью порядка 10–15 Вт на напряжение вторичной обмотки 10–14 В (для индикаторов), измерьте на нем число витков на вольт (как описано в главе 4), и домотайте три одинаковых обмотки на 7–8 В каждая поверх существующих, проводом не меньше, чем 0,3 мм в диаметре. Удобнее всего их мотать одновременно сложенным втрое проводом заранее рассчитанной длины.
Теперь немного разберемся с температурой. Сопротивление датчика составляет 760 Ом при 0 °C (~=610 Ом при -50°) и имеет крутизну примерно 3 Ом/° (о датчике см. главу 10). Величины резисторов в аналоговой части измерителя подогнаны так, чтобы обеспечить ток через датчик 1,3 мА. Таким образом напряжение на датчике в диапазоне температур от -50° до +50 °C будет меняться на 400 мВ, т. е. на выходе дифференциального усилителя (с учетом его коэффициента усиления около 12) диапазон напряжений составит примерно 4,9 В. Таким образом мы будем использовать весь диапазон АЦП (от 0 до Uon) в полной мере с некоторым запасом. Резистор R4 устанавливает нижнюю границу диапазона, и здесь его нужно выбирать равным не сопротивлению датчика при 0°, как в схеме по рис. 10.8, а его сопротивлению при минимальной требуемой температуре. При указанных на схеме номиналах нижняя граница диапазона температур будет около -47°, а верхняя — около 55 °C. Для медного датчика с другим сопротивлением следует пересчитать коэффициент усиления усилителя (соответствующая формула приведена в главе 6, см. рис. 6.8). Это можно делать приблизительно — окончательную калибровку под реальный датчик мы будем производить путем изменения коэффициентов пересчета в программе МК.
Программа
Чтобы перейти к обсуждению непосредственно программы измерителя, нам нужно решить еще один принципиальный вопрос. Передаточная характеристика любого измерителя температуры, показывающего ее в градусах Цельсия, должна «ломаться» в нуле — ниже и выше абсолютные значения показаний возрастают. Так как мы тут действуем в области положительных напряжений, то этот вопрос придется решать самостоятельно (в АЦП типа 572ПВ2, напомним, определение абсолютной величины и индикация знака производилась автоматически).
Это несложно сделать, если представить формулу пересчета значений температуры в виде уравнения:
N = К∙(х — Z),
где N — число на индикаторе, х — текущий код АЦП, Z — код АЦП, соответствующий нулю градусов Цельсия (при наших установках он должен соответствовать примерно середине диапазона).
Чтобы величина по данной формуле всегда получалась положительная, нам придется сначала определять, что больше — х или Z, и вычитать из большего меньшее. Заодно при этой операции сравнения мы определяем значение знака. Если мы предположим, что в регистрах AregH: AregL содержится значение текущего кода АЦП х, а в регистрах KoeffH: KoeffL значение коэффициента Z, то алгоритм будет выглядеть так, как иллюстрирует листинг 15.6.
Листинг 15.6
…
;вычисление знака:
ср AregL,KoeffL ;сравниваем х и Z
срс AregH,KoeffH
brsh Ь0
;если х меньше Z
sub KoeffL,AregL
sbc КоеffH,AregH
mov AregL,KoeffL ;меняем местами, чтобы температура
mov AregH,KoeffH ;оказалась опять в AregH: AregL
sbi PortD,7 ;знак -
rjmp m0
Ь0: ;если x больше Z
sub AregL,KoeffL
sbc AregH,KоеffH
cbi PortD,7 ;знак +
m0:
<умножение на коэффициент К>
…
Здесь разряд 7 порта D (вывод 21) управляет плоским светодиодом, который горит, если температура меньше нуля, и погашен в противном случае.
Давление занимает только положительную область значений, поэтому там такой сложной процедуры не понадобится. Если вы посмотрите на характеристику датчика в фирменном описании, то выясните, что он работает не с начала шкалы — нулевому напряжению на выходе (и, соответственно, нулевому коду АЦП) будет соответствовать некоторое значение давления. В результате можно ожидать, что в формуле пересчета значений давления, представленной в виде
N = К∙(х + Z),
все величины будут находиться в положительной области.
Физический смысл коэффициента К — крутизна характеристики датчиков в координатах «входной код АЦП — число на индикаторах». Умножение на К мы будем производить описанным методом — через представление его в виде двоичной дроби (за основу берется 210 = 1024, этого будет достаточно). Вычисление ориентировочных значений коэффициентов К и Z поясняется далее, при описании процедуры калибровки.
Теперь можно окинуть взглядом собственно программу, которая целиком приведена в Приложении 5 (раздел «Программа измерителя температуры и давления»). Как вы видите из таблицы прерываний, здесь используется всего один, самый простой Timer 0, который срабатывает с частотой около 2000 раз в секунду. В его обработчике по метке TIM0 и заключена большая часть функциональности.
В каждом цикле сначала проверяется счетчик cRazr, который отсчитывает разряды индикаторов (от 0 до 5). В соответствии с его значением происходит формирование кода индицируемого знака (по алгоритму вызова константы-маски знака, описанному в главе 13) и затем на нужный разряд подается питание.
Заметки на полях
Как видите, здесь формирование знака реализовано не очень красиво, и довольно громоздким способом — просто передачей управления на нужную процедуру в зависимости от значения счетчика. Сами же процедуры структурно одинаковы (меняются лишь адреса в памяти, из которых считываются значения разрядов индикатора и номера разрядов порта управления PortB). Программу в этой части можно слегка сократить (если просто вывести одинаковые операторы в отдельную процедуру, и задействовать локальные переменные), но я не стал этого делать, т. к. принципиально это ничего не изменит: места в памяти у нас достаточно, а программа, на мой взгляд, тем лучше читается, чем в ней меньше структурных блоков. (Упоминавшийся в главе 13 Дейкстра, несомненно, схватился бы за сердце, услышав такое, но тем не менее это чистая правда — весь алгоритм окинуть взглядом легче, когда он максимально структурирован, но каждый отдельный фрагмент его проще понять, если не приходится «рыскать» по всему листингу.)
Интерес же представляет другой момент во всем этом — а нельзя ли было бы кардинально решить проблему, учитывая тот факт, что разряды считаются подряд (от нулевого до пятого), в регистре PortB они также расположены подряд, и ячейки SRAM, содержащие значения цифр, также идут подряд (начиная с TdH, см. секцию констант и определений)? Очень хочется как-то «свернуть» все шесть повторяющихся фрагментов в один, т. к. все равно все увязано со значением счетчика cRazr. Отсчитывать адрес, где хранится текущая цифра, несложно, просто прибавляя к начальному адресу (TdH) значение счетчика. В основном же это естественное желание упирается в тот факт, что невозможно простым способом перевести двоичное значение некоего регистра (в данном случае cRazr) в номер устанавливаемого бита в регистре PortB. Чтобы заменить «ручное» задание нужного бита в регистре PortB (см. пару команд ldi temp, i << RazrPdL/ out portb, temp) на автоматическое в соответствии со значением cRazr, понадобится двоично-десятичный дешифратор, подобный по функциям микросхеме 561ИД1 (см. главу 8), программная реализация которого будет еще более громоздкой, чем данный алгоритм. Мы еще вернемся к этому вопросу в главе 17, когда нам понадобится управлять большим количеством индикаторов.