Где-то так, возможно я неправ. Правильно-работающие фрагменты кода с пояснениями. Масштабирование 16-разрядных данных АЦП в 16-разрядный результат.
В связи с увиденным множеством вариантов решения этой задачи,
хочу изложить свое понимание и решение.
Сразу напрашивается решение этой задачи целочисленным умножением
командами MULх 16-разрядных данных АЦП на 16-разрядный коэффициент и
использованием в качестве результата 16 старших бит 32-разрядного
произведения (эквивалентно делению произведения на 65536).
16 младших бит отбрасываются.
Это обеспечит вычисление результата с точностью, не хуже
одного младшего разряда ([0..1,0[).
Чтобы уменьшить максимальное абсолютное значение ошибки желательно
использовать правильное округление результата такого деления. Для этого
необходимо перед отбрасыванием прибавить к произведению 32768.
Это обеспечит вычисление результата с точностью, не хуже
половины младшего разряда ([-0,5..0,5[).
Если рассматривать 16-разрядные данные АЦП, 16-разрядный коэффициент и
32-разрядное произведение как действительные числа с фиксированной точкой,
то возможно множество вариантов расстановки этих точек. Для целых чисел
это форматы 16.0 и 32.0.
В файле AVRASM.chm при описании команд FMULх говорится:
"Let (N.Q) denote a fractional number with N binary digits left of the
radix point, and Q binary digits right of the radix point.
A multiplication between two numbers in the formats (N1.Q1) and (N2.Q2)
results in the format ((N1+N2).(Q1+Q2))."
Понятно, что места этих точек чисто условны и мы их там только
подразумеваем (хотя, например, для команд FMULx AVR_MC их необходимо
подразумевать в строго определенных позициях, формат 1.7 для входных
данных и 1.15 для выходных потому, что этими командами результат
сдвигается на один разряд влево и при этом старший бит переносится
в флаг "С-перенос", а младший всегда заполняется "0").
Если форматы данных и коэффициента - 16.0 и 0.16 или 0.16 и 16.0
соответственно, то формат произведения 16.16, а результом есть целая
часть произведения. В первом случае целые данные и дробный коэффициент,
во втором – дробные данные и целый коэффициент.
Если форматы данных и коэффициента 0.16 и 0.16 соответственно,
то формат произведения 0.32, а формат результата 0.16.
Во втором и третьем случае, если данные дробные (и юстированы влево),
то их значение может соответствовать отношению измеренного Uin
напряжения к образцовому Uref. Это отношение находится в диапазоне
[0,0..1,0[ для недифференциального включения и в диапазоне
[-0,5..0,5[ для дифференциального включения (в дополнительном коде).
Значением же коэффициента является то целое (или дробное для третьего
случая) значение 16-разрядного результата которое мы хотим получить
при значении Uin = Uref.
Неизбежно эта задача распадается на четыре знаковых варианта:
1. Беззнаковые данные, беззнаковый коэффициент и беззнаковый результат.
2. Знаковые данные, беззнаковый коэффициент и знаковый результат.
3. Беззнаковые данные, знаковый коэффициент и знаковый результат.
4. Знаковые данные, знаковый коэффициент и знаковый результат.
Учитывая то, что в четвертом варианте два старших бита произведения
всегда одинаковы (и соответствуют знаку) то для увеличения точности
результата и уменьшения количества отбрасываемых разрядов, перед
отбрасыванием младших 16 разрядов, произведение желательно здвинуть
на 1 разряд влево (вот тот случай, когда допустимо и целесообразно
воспользоваться командами FMULx, а не MULx). Это будет эквивалентно
представлению данных или коэффициента не в формате 0.16, а в формате 1.15.
Разговаривая здесь о точности имеется в виду только точность вычислений,
окончательная точность результата определяется еще и точностью
преобразования самого АЦП.
Учитывая то, что при использовании для умножения команд FMULx результат
автоматически сдвигается влево на один разряд, ее можно использовать
только для четвертого знакового варианта.
В первых же трех вариантах ее применение может привести к потере самого
старшего значащего (или знакового) разряда результата.
Исходя из всего выше изложенного, предлагаю следующие четыре варианта
таких масштабирований. За основу взяты примеры из справки по AVRASM:
; Это не подпрограммы а просто фрагменты кода.
; Для действительных диапазонов периферийные значения не включительно.
; Пусть для всех четырех знаковых вариантов
; коэффициент находится в R21:R20,
; данные АЦП находятся в R23:R22,
; результат находится в R19:R18.
; 1. Беззнаковые данные, беззнаковый коэффициент и беззнаковый результат.
; данные АЦП в диапазоне 0x0000..0xFFFF | 0.16-> 0,0..1,0 | 0.16-> 0,0..1,0
; коэффициент в диапазоне 0x0000..0xFFFF | 0.16-> 0,0..1,0 | 16.0-> 0..65535
; результат в диапазоне 0x0000..0xFFFF | 0.16-> 0,0..1,0 | 16.0-> 0..65535
; r19:r18:r17:r16 = r23:r22 * r21:r20
clr r2
mul r23, r21
movw r18, r0
mul r22, r20
mov r17, r1
mul r23, r20
add r17, r0
adc r18, r1
adc r19, r2
mul r21, r22
add r17, r0
adc r18, r1
adc r19, r2
; 2. Знаковые данные, беззнаковый коэффициент и знаковый результат.
; данные АЦП в диапазоне 0x8000..0x7FFF | 0.16-> -0,5..0,5 | 0.16-> -0,5..0,5
; коэффициент в диапазоне 0x0000..0xFFFF | 0.16-> 0,0..1,0 | 16.0-> 0..65535
; результат в диапазоне 0x8000..0x7FFF | 0.16-> -0,5..0,5 | 16.0-> -32767..32767
; r19:r18:r17:r16 = r23:r22 * r21:r20
clr r2
mulsu r23, r21
movw r18, r0
mul r22, r20
mov r17, r1
mulsu r23, r20
sbc r19, r2
add r17, r0
adc r18, r1
adc r19, r2
mul r21, r22
add r17, r0
adc r18, r1
adc r19, r2
; 3. Беззнаковые данные, знаковый коэффициент и знаковый результат.
; данные АЦП в диапазоне 0x0000..0xFFFF | 0.16-> 0,0..1,0 | 0.16-> 0,0..1,0
; коэффициент в диапазоне 0x8000..0x7FFF | 0.16-> -0,5..0,5 | 16.0-> -32767..32767
; результат в диапазоне 0x8000..0x7FFF | 0.16-> -0,5..0,5 | 16.0-> -32767..32767
; r19:r18:r17:r16 = r23:r22 * r21:r20
clr r2
mulsu r21, r23
movw r18, r0
mul r22, r20
mov r17, r1
mul r23, r20
add r17, r0
adc r18, r1
adc r19, r2
mulsu r21, r22
sbc r19, r2
add r17, r0
adc r18, r1
adc r19, r2
; 4. Знаковые данные, знаковый коэффициент и знаковый результат.
; данные АЦП в диапазоне 0x8000..0x7FFF | 0.16-> -0,5..0,5 | 1.15-> -1,0..1,0
; коэффициент в диапазоне 0x8000..0x7FFF | 1.15-> -1,0..1,0 | 0.16-> -0,5..0,5
; результат в диапазоне 0x8000..0x7FFF | 0.16-> -0,5..0,5 | 0.16-> -0,5..0,5
; данные АЦП в диапазоне 0x8000..0x7FFF | 1.15-> -1,0..1,0 | 0.16-> -0,5..0,5
; коэффициент в диапазоне 0x8000..0x7FFF | 16.0-> -32767..32767 | 17.0-> -65535..65535
; результат в диапазоне 0x8000..0x7FFF | 16.0-> -32767..32767 | 16.0-> -32767..32767
; Для формата 17.0 в R21:R20 записывать только 16 старших разрядов коэффициента
; r19:r18:r17:r16 = ( r23:r22 * r21:r20 ) << 1
clr r2
fmuls r23, r21
movw r18, r0
fmul r22, r20
adc r18, r2
mov r17, r0
fmulsu r23, r20
sbc r19, r2
add r17, r0
adc r18, r1
adc r19, r2
fmulsu r21, r22
sbc r19, r2
add r17, r0
adc r18, r1
adc r19, r2
; Если необходимо более точное округление перед отбрасыванием 16 младших разрядов
; произведения, то к всем приведенным фрагментам необходимо добавить код:
subi r17, low(-128)
sbci r18, high(-128)
sbci r19, byte3(-128)