Измеритель температуры и давления на AVR



Прежде чем непосредственно заняться этой относительно сложной конструкцией, нам придется углубиться в теорию и понять, как в восьмиразрядном контроллере производить арифметические действия с многобайтовыми числами, и к тому же получать результат в десятичной системе счисления. Без этого никакой измеритель с индикацией спроектировать невозможно, т. к. АЦП контроллера выдает абстрактные численные результаты, а нам нужны физические величины. Подгонять выходную шкалу с помощью регулирования соотношений опорного и измеряемого напряжения, как мы это делали в цифровом термометре из главы 17 , при наличии процессора – не просто глупое, но и крайне неудобное занятие: для термометра нужна одна шкала, для датчика давления – совсем другая (а если бы мы еще пару датчиков других величин придумали вставить?).

Поэтому для начала поучимся оперировать в контроллере большими числами и представлять их в десятичной форме. В следующей главе мы перейдем к Arduino , где таких проблем не существует вовсе, – любые арифметические действия программируются «прозрачно» для пользователя, а сопутствующие проблемы за вас уже решили создатели компилятора AVRGCC. Зато когда вы поглядите на объем получающегося кода, то оцените преимущества программирования на ассемблере. И дело даже не в самом объеме (аналогичная программа для Arduino просто не влезла бы в память mega8535), а в скорости исполнения: к этой программе мы спокойно можем добавить еще часы с будильником, запись в память, общение с компьютером, и все это будет спокойно выполняться на частоте 4 МГц с максимально возможной скоростью и без потерь.

 

Арифметика многобайтовых чисел в МК

Сложение и вычитание больших чисел в МК не представляет трудностей. Корректная операция сложения двух 16‑разрядных чисел будет занимать две команды:

Add RL1,RL2

Adc RH1,RH2

Здесь переменные RL1 и RL2 содержат младшие байты слагаемых, a RH1 и RH2 – старшие. Если при первой операции результат превысит 255, то перенос запишется во все тот же флаг переноса С и учтется при второй операции. Общий результат окажется в паре RH1:RL1. Совершенно аналогично выглядит операция вычитания. Примеры операций с большим числом слагаемых вы найдете в тексте программ далее.

А вот с умножением и делением несколько сложнее. Выполнение типовых операций на AVR для чисел с различной разрядностью, вообще говоря, приводится в фирменных руководствах по применению: «аппнотах» (Application Notes , в данном случае номер 200). Но эти процедуры для наших целей все равно придется творчески переработать. Поэтому мы не будем на них останавливаться, а сразу воспользуемся тем обстоятельством, что для контроллеров семейства Mega определены аппаратные операции умножения. Тогда и алгоритм сильно упрощается и легко модифицируется для любого размера операндов и результата. Вот так выглядит алгоритм для перемножения двух 16‑разрядных сомножителей с получением 24‑разрядного результата (в названиях исходных переменных отражен факт основного назначения такой процедуры – для умножения неких данных на некий коэффициент[37]):

 

 

Как видите, если нужно получить полный 32‑разрядный диапазон, просто добавьте еще один регистр для старшего разряда (temp3 , к примеру) и одну строку кода перед командой ret :

Adc temp3,r01

Естественно, можно просто обозвать r01 через temp3 , тогда и добавлять ничего не придется.

Деление – значительно более громоздкая процедура, чем умножение, требует больше регистров и занимает больше времени. Операции деления двух чисел (и 8‑ и 16‑разрядных) приведены в той же «аппноте» 200, но они не всегда удобны на практике: часто нам приходится делить результат какой‑то ранее проведенной операции умножения или сложения, а он нередко выходит за пределы двух байтов.

Здесь нам потребуется вычислять среднее значение для уточнения результата измерения по сумме отдельных измерений. Если даже само измерение укладывается в 16 разрядов, то сумма нескольких таких результатов уже должна занимать три байта. В то же время делитель – число измерений – будет относительно небольшим и укладывается в один байт. Но мы не будем здесь заниматься построением «настоящих» процедур деления (интересующихся отсылаю к моей книге [21]). Многие подобные задачи на деление удается решить значительно более простым и менее громоздким методом, если заранее подгадать так, чтобы делитель оказался кратным степени 2. Тогда все деление сводится, как мы знаем, к сдвигу разрядов вправо столько раз, какова степень двойки.

Для примера предположим, что мы некую величину измерили 64 раза и хотим узнать среднее. Пусть сумма укладывается в 2 байта, тогда вся процедура деления будет такой:

 

 

He правда ли, гораздо изящнее и понятнее? Попробуем от радости решить задачку, которая на первый взгляд требует по крайней мере знания высшей алгебры – умножить некое число на дробный коэффициент (вещественное число с «плавающей запятой»). Теоретически для этого требуется представить исходные числа в виде мантисса‑порядок, сложить порядки и перемножить мантиссы. Нам же неохота возиться с этим представлением, т. к. мы не проектируем универсальный компьютер, и в подавляющем большинстве реальных задач все конечные результаты у нас есть целые числа.

На самом деле эта задача решается очень просто, если ее свести к последовательному умножению и делению целых чисел, представив реальное число в виде целой дроби с оговоренной точностью. В десятичной форме это выглядит так: представим число 0,48576 как 48 576/100 000. И если нам требуется на такой коэффициент умножить, к примеру, число 976, то можно действовать, не выходя за рамки диапазона целых чисел: сначала умножить 976 на 48 576 (получится заведомо целое число 47 410 176), а потом поделить результат на 105, чисто механически перенеся запятую на пять разрядов. Получится 474,10176 или, если отбросить дробную часть, 474. Большая точность нам и не требуется, т. к. и исходное число было трехразрядным.

Улавливаете, к чему я клоню? Наше ноу‑хау будет состоять в том, что мы для того, чтобы «вогнать» дробное число в целый диапазон в микроконтроллере, будем использовать не десятичную дробь, а двоичную – деление тогда сведется к той же самой механической процедуре сдвига разрядов вправо, аналогичной переносу запятой в десятичном виде.

Итак, чтобы умножить 976 на коэффициент 0,48576, следует сначала последний вручную умножить, например, на 216 (65 536), и тем самым получить числитель соответствующей двоичной дроби (у которой знаменатель равен 65 536) – он будет равен 31 834,76736, или, с округлением до целого, 31 835. Такой точности хватит, если исходные числа не выходят, как у нас, за пределы 3–4 десятичных разрядов. Теперь мы в контроллере должны умножить исходную величину 976 на константу 31 835 (см. процедуру перемножения ранее) и полученное число 31 070 960 (оно оказывается 4‑байтовым – $01DA1AF0, потому нашу процедуру Mui1x16 придется чуть модифицировать, как сказано при ее описании) сдвинуть на 16 разрядов вправо:

 

 

В результате, как вы можете легко проверить, старшие байты окажутся нулевыми, а в ddM: ddL окажется число 474 – тот же самый результат. Но и это еще не все – такая процедура приведена скорее для иллюстрации общего принципа. Ее можно еще больше упростить, если обратить внимание на то, что сдвиг на восемь разрядов есть просто перенос значения одного байта в соседний (в старший, если сдвиг влево, и в младший – если вправо). Итого получится, что для сдвига на 16 разрядов вправо нам надо всего‑навсего отбросить два младших байта и взять из исходного числа два старших ddHH: ddH – это и будет результат. Проверьте – $01DA и есть 474. Никаких других действий вообще не требуется!

Если степень знаменателя дроби, как в данном случае, кратна 8, то действительно никакого деления, даже в виде сдвига, не требуется, но чаще всего это не так. Однако и тогда приведенный принцип может помочь – например, при делении на 215 вместо пятнадцатикратного сдвига вправо результат можно сдвинуть на один разряд влево (умножив число на два), а потом уже выделить из него старшие два байта. В программе далее мы будем делить на 210 = 1024, отбрасывая младший байт (деление на 8) и еще дважды сдвигая результат вправо. Вот такая специальная арифметика в МК.

 

 


Дата добавления: 2019-02-12; просмотров: 207; Мы поможем в написании вашей работы!

Поделиться с друзьями:






Мы поможем в написании ваших работ!