Юрий Ревич - Занимательная микроэлектроника
- Название:Занимательная микроэлектроника
- Автор:
- Жанр:
- Издательство:БХВ-Петербург
- Год:2007
- Город:Санкт-Петербург
- ISBN:978-5-9775-0080-7
- Рейтинг:
- Избранное:Добавить в избранное
-
Отзывы:
-
Ваша оценка:
Юрий Ревич - Занимательная микроэлектроника краткое содержание
Для широкого круга радиолюбителей
Занимательная микроэлектроника - читать онлайн бесплатно ознакомительный отрывок
Интервал:
Закладка:
Процедура перемножения двух таких величин в исходном 16-разрядном виде, с представлением результата в трехбайтовой форме может быть легко получена из исправленной нами процедуры MPY16U по «аппноте» 200, но я решил воспользоваться тем обстоятельством, что для контроллеров семейства Mega определены аппаратные операции умножения (в Приложении 4 я их не привожу). Тогда алгоритм сильно упрощается, причем он легко модифицируется как для 32-, так и для 24-разрядного результата. Таким образом, для Tuny и Classic по-прежнему следует пользоваться обычными процедурами из «аппноты» (исправленными), а алгоритм для Mega приведен в листинге 15.1 (в названиях исходных переменных отражен факт основного назначения такой процедуры — для умножения неких данных на некий коэффициент). Сокращения LSB и MSB, которые нам еще встретятся не раз, означают least (most) significant bit — младший (старший) значащий разряд, по-русски МЗР и СЗР соответственно.
Листинг 15.1
.def dataL = r4 ;multiplicand low byte
.def dataH = r5 ;multiplicand high byte
.def KoeffL = r2 ;multiplier low byte
.def koeffH = r3 ;multiplier high byte
.def temp = r16 ;result byte 0 (LSB — младший разряд)
.def temp2 = r17 ;result byte 1
.def temp3 = r18 ;result byte 2 (MSB — старший разряд)
…
;**********
;умножение двух 16-разрядных величин, только для Меда
;исходные величины dataH: dataL и KoeffH: KoeffL
;результат 3 байта temp2:temp1:temp;
;**********
Mu1616:
clr temp2 ;очистить старший
mul dataL,KoeffL ;умножаем младшие
mov temp,r0 ;в r0 младший результата операции mu1
mov tempi,r1 ;в r01 старший результата операции mu1
mul dataH,KoeffL ;умножаем старший на младший
add temp1,r1 ;в r0 младший результата операции mu1
adc temp2,r1 ;в r01 старший результата операции mu1
mul dataL,KoeffH ;умножаем младший на старший
add temp1,r0 ;в r0 младший результата операции mu1
adc temp2,r01 ;в r01 старший результата операции mu1
mul dataH,KoeffH ;умножаем старший на старший
add temp2,r0 ;4-й разряд нам тут не требуется, но он — в r01
ret
;**********
Как видите, эта процедура легко модифицируется под любую разрядность результата, если нужно получить полный 32-разрядный диапазон, просто добавьте еще один регистр для старшего разряда ( temp3, к примеру) и одну строку кода перед командой ret:
adc temp3,r01
Естественно, можно просто обозначить r01через temp3, тогда и добавлять ничего не придется.
Деление — значительно более громоздкая процедура, чем умножение, требует больше регистров и занимает больше времени (MPY16U из «аппноты» занимает 153 такта, по уверению разработчиков, а аналогичная операция деления двух 16-разрядных чисел — от 235 до 251 тактов). Операции деления двух чисел (и для 8-, и для 16-разрядных) приведены в той же «аппноте» 200, и на этот раз без ошибок, но они не всегда удобны на практике: часто нам приходится делить результат какой-то ранее проведенной операции умножения или сложения, а он нередко выходит за пределы двух байтов.
Потому пришлось разрабатывать свои операции. Например, часто встречается необходимость вычислить среднее значение для уточнения результата по сумме отдельных измерений. Если даже само измерение укладывается в 16 разрядов, то сумма нескольких таких результатов уже должна занимать 3 байта. В то же время делитель — число измерений — может быть и относительно небольшим, и укладываться в один байт. В листинге 15.2 я привожу процедуру деления 32-разрядных чисел (на всякий случай) на однобайтное число, которая представляет собой модификацию оригинальной процедуры из Application notes 200. Как и ранее, названия переменных отражают назначение процедуры — деление состояния некоего 4-байтового счетчика на число циклов счета (определения регистров-переменных не приводятся, комментарии сохранены из оригинального текста «аппноты», они соответствуют блок-схеме алгоритма, размещенной в pdf-файле).
Листинг 15.2
;********
;div32x8» — 32/8 деление беззнаковых чисел
;Делимое и результат в count_HH (старший), countTH,
;countTM, countTL (младший)
;делитель в cikle
;требуется четыре временных регистра dremL — dremHH
;из диапазона r16-r31
;для хранения остатка
;********
div32x8:
clr dremL ;clear remainder Low byte
clr dremM ;clear remainder
clr dremH ;clear remainder
sub dremHH,dremHH ;clear remainder High byte and carry
ldi cnt,33 ;init loop counter
d_1: rol countTL ;shift left dividend
rol countTM
rol countTH
rol count_HH
dec cnt ;decrement counter
brne d_2 ;if done
ret ;return
d_2: rol dremL ;shift dividend into remainder
rol dremM
rol dremH
rol dremHH
sub dremL,cikle ;remainder = remainder — divisor
sbci dremM,0
sbci dremH,0
sbci dremHH,0
brcc d_3 ;if result negative
add dremL,cikle ;restore remainder
clr temp
adc dremM,temp
adc dremH,temp
adc dremHH,temp
clc ;clear carry to be shifted into result
rjmp d_1 ;else
d_3: sec ;set carry to be shifted into result rjmp d_1
;******** конец 32x8
Многие подобные задачи на деление удается решить значительно более простым и менее громоздким методом, если заранее подгадать так, чтобы делитель оказался кратным степени двойки. Тогда все деление сводится, как мы знаем, к сдвигу разрядов вправо столько раз, какова степень двойки. Для примера предположим, что мы некую величину измерили 64 раза, и хотим узнать среднее. Пусть сумма укладывается в 2 байта, тогда вся процедура деления будет такой:
;деление на 64
clr count_data ;счетчик до 6
div64L:
lsr dataH ;сдвинули старший
ror dataL ;сдвинули младший с переносом
inc count_data
cpi count_data,6
brne div64L
He правда ли, гораздо изящнее и понятнее? Попробуем от радости решить задачку, которая на первый взгляд требует, по крайней мере, знания высшей алгебры — умножить некое число на дробный коэффициент (вещественное число с «плавающей запятой»). Теоретически для этого требуется представить исходные числа в виде «мантисса — порядок», сложить порядки и перемножить мантиссы (см. [10]). Нам же неохота возиться с этим представлением, т. к. мы не проектируем универсальный компьютер, и в подавляющем большинстве реальных задач все конечные результаты у нас представляют собой целые числа.
На самом деле эта задача решается очень просто, если ее свести к последовательному умножению и делению целых чисел, представив реальное число в виде целой дроби с оговоренной точностью. Например, число 0,48576 можно представить как 48 576/100 000. И если нам требуется на такой коэффициент умножить, к примеру, результат какого-то измерения, равный 976, то тогда можно действовать, не выходя за рамки диапазона целых чисел: сначала умножить 976 на 48 576 (получится заведомо целое число 47 410 176), а потом поделить результат на 105, чисто механически перенеся запятую на пять разрядов. Получится 474,10176 или, если отбросить дробную часть, 474. Большая точность нам и не требуется, т. к. и исходное число было трехразрядным.
Читать дальшеИнтервал:
Закладка: