Микроконтроллеры серии PIC16 компании Microchip могут выполнять простые арифметические команды с 8-разрядными операндами, поскольку их ядро само 8-разрядное. Но в некоторых проектах требуется больше вычислительных возможностей, поэтому в таких случаях будет полезна специальная библиотека арифметических операций.
Представленная библиотека позволяет осуществлять умножение, деление, сложение и вычитание 16-битных чисел, конвертировать числа в разные формы, проверять четность, возводить число в квадрат и еще несколько полезных мелочей.
UDATA
HIBYTE RES 1
LOBYTE RES 1
COUNTX RES 1
MULCND RES 1
MULPLR RES 1
BCD RES 2
ACCaLO res 1
ACCaHI res 1
ACCbLO res 1
ACCbHI res 1
ACCcLO res 1
ACCcHI res 1
ACCdLO res 1
ACCdHI res 1
R2 res 1
R1 res 1
R0 res 1
TEMPX res 1
L_temp res 1
H_temp res 1
w_save res 1
RandHi res 1
RandLo res 1
parity res 1
;*************************************************************************
; Умножение MULPLR(8 бит) x MULCND(8 бит) -->HIBYTE(msb),LOBYTE(lsb)
; a) Загружаем множитель в MULPLR
; b) Загружаем множимое в MULCND
; c) Вызываем Mpy8x8
; d) Msb (старший байт) будет находиться в HIBYTE
; e) Lsb (младший байт) будет находиться в LOBYTE
;*************************************************************************
Mpy8x8
clrf HIBYTE
clrf LOBYTE
clrf COUNTX
bsf COUNTX, 3
movf MULCND, W
LoopX
bcf STATUS, C
btfsc MULPLR, 0
addwf HIBYTE, f
rrf HIBYTE, f
rrf LOBYTE, f
bcf STATUS, C
rrf MULPLR, f
decfsz COUNTX, f
goto LoopX
return
;*******************************************************************
;Умножение: ACCb(16 бит)*ACCa(16 бит) -> ACCb,ACCc (32 бит)
;(a) Загружаем первый операнд в ACCaLO & ACCaHI (16 бит)
;(b) Загружаем второй операнд в ACCbLO & ACCbHI (16 бит)
;(c) Вызываем Mpy_16bit
;(d) 32-битный результат будет находиться в (ACCbHI,ACCbLO,ACCcHI,ACCcLO)
;*******************************************************************
Mpy_16bit
movlw .16 ; для сдвига на 16 разрядов
movwf temp
movf ACCbHI,W ; из ACCb в ACCd
movwf ACCdHI
movf ACCbLO,W
movwf ACCdLO
clrf ACCbHI
clrf ACCbLO
Mloop
rrf ACCdHI, F
rrf ACCdLO, F
btfsc STATUS,C ;нужно ли добавлять?
call Add_16bit
rrf ACCbHI, F
rrf ACCbLO, F
rrf ACCcHI, F
rrf ACCcLO, F
decfsz temp, F ;повторяем, пока не проверим все биты
goto Mloop
return
;******************************************************************
;Эта процедура конвертирует шестнадцатиричное значение,
;имеющееся в WREG, в десятичное
;и сохраняет результат в регистре: BCD и BCD+1
;******************************************************************
BinBCD
clrf BCD
clrf BCD+1
Again1
addlw 0x9C ;вычитаем 100 и проверяем на займ
btfss STATUS, C
goto add100
incf BCD+1, f
goto Again1
add100
addlw 0x64
Again
addlw 0xF6 ;вычитаем 10 и проверяем на займ
btfss STATUS, C
goto SwapBCD
incf BCD, f
goto Again
SwapBCD
addlw 0x0A
swapf BCD, f
iorwf BCD, f
return
;***************************************************************
;Эта процедура вычисляет квадрат числа, имеющегося в WREG
;Шестнадцатиричный результат сохраняется в WREG,
;а десятичный результат в регистрах общего назначения BCD and BCD+1
;***************************************************************
square
movwf COUNTX
movlw 0x01
movwf TEMPX
clrw
r_square
addwf TEMPX,W
incf TEMPX,F
incf TEMPX,F
decfsz COUNTX,F
goto r_square
movwf w_save
call BinBCD
movf w_save,W
return
;*******************************************************************
;Эта процедура вычисляет квадрат числа, имеющегося в WREG
;Результат сохраняется в WREG. Если число имеет не конечный квадратный корень,
;эта функция возвращает значение ошибки EE в WREG
;*******************************************************************
square_root
movwf w_save
movlw 0x01
movwf TEMPX
movwf COUNTX
loop
movf TEMPX,W
subwf w_save,f
btfsc STATUS,Z
goto zero
btfss STATUS,C
goto no_root
incf COUNTX,F
incf TEMPX,F
incf TEMPX,F
goto loop
zero
movf COUNTX,W
return
no_root
movlw 0XEE
return
;********************************************************************
;Процедура преобразования двоичного числа в двоично-десятичное (BCD)
;Она преобразует 16-разрядное двоичное число в пять разрядов BCD-числа
;16-разрядное двоичное число записывается в ACCaHI и
;ACCaLO, при этом старший байт ACCaHI.
;Пять разрядов BCD-числа возвращаются в R0, R1 and R2,
;где R0 хранит старший разряд
;********************************************************************
Hex_to_Dec
bcf STATUS, C
clrf COUNTX
bsf COUNTX, 4 ;установить счетчик до 16
clrf R0
clrf R1
clrf R2
Loop16a
rlf ACCaLO, f
rlf ACCaHI, f
rlf R2, f
rlf R1, f
rlf R0, f
decfsz COUNTX, f
goto Adjdec
return
Adjdec
movlw R2 ;загрузить косвенный указатель адреса
movwf FSR
call AdjBCD
incf FSR, f
call AdjBCD
incf FSR, f
call AdjBCD
goto Loop16a
AdjBCD
movf INDF, w
addlw 0x03
movwf TEMPX
btfsc TEMPX,3;проверка, если результат > 7
movwf INDF
movf INDF, w
addlw 0x30
movwf TEMPX
btfsc TEMPX, 7 ;проверка, если результат > 7
movwf INDF ;
return
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Деление : ACCb(16 бит) / ACCa(16 бит) -> ACCd(16 бит)
;Остаток в ACCc (16 бит)
; (a) Загружаем знаменатель в ACCaHI & ACCaLO ( 16 бит )
; (b) Загружаем числитель в ACCbHI & ACCbLO ( 16 бит )
; (c) Вызываем Division
; (d) 16-разрядный результат будет в ACCdHI & ACCdLO
; (e) 16-разрядный остаток будет в ACCcHI & ACCcLO
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
Division
clrf COUNTX
bsf COUNTX,4 ; установить счетчик = 16
clrf ACCcHI
clrf ACCcLO
clrf ACCdLO
clrf ACCdHI
divLoop
bcf STATUS,C
rlf ACCbLO,F
rlf ACCbHI,F
rlf ACCcLO,F
rlf ACCcHI,F
movf ACCaHI,W
subwf ACCcHI,W ; проверить, если a>c
btfss STATUS,Z
goto notz
movf ACCaLO,W
subwf ACCcLO,W
notz
btfss STATUS,C ; устанавливаем флаг переноса, если c>a
goto nosub ; если c < a
subca
movf ACCaLO,W ; c-a результат в c
subwf ACCcLO, F
movf ACCaHI,W
subwf ACCcHI, F
bsf STATUS,C ; сдвиг 1 в d (результат)
nosub
rlf ACCdLO,F
rlf ACCdHI,F
decfsz COUNTX,F
goto divLoop
return
;*******************************************************************
; Генератор случайных чисел
; Эта процедура реализует 16-битный генератор последовательности псевдослучайных чисел
; Он основан на регистре сдвига с линейной обратной связью.
; Последовательность генерируется посредством (Q15 xorwf Q14 xorwf Q12 xorwf Q3 )
; 16-битное случайное число будет находиться в RandHi(старший байт)
; & RandLo (младший байт)
; Перед вызовом этой процедуры нужно убедиться, что
; начальные значения RandHi & RandLo не являются нулем
;*******************************************************************
Random16
rlf RandHi,W
xorwf RandHi,W
movwf w_save
rlf w_save, F ; carry bit = xorwf(Q15,14)
swapf RandHi, F
swapf RandLo,W
movwf w_save
rlf w_save, F
xorwf RandHi,W ; LSB = xorwf(Q12,Q3)
swapf RandHi, F
andlw 0x01
rlf RandLo, F
xorwf RandLo, F
rlf RandHi, F
return
;**********************************************************************
; Процедура преобразования BCD-числа в двоичное
; Она преобразует пять разрядов BCD-числа в 16-разрядное двоичное число
; пять разрядов BCD-числа должны находиться в
; R0, R1 & R2, где R0 хранит старший разряд.
; 16-разрядное двоичное число будет находится в ACCaHI & ACCaLO
; ( старший & младший байт соответственно ).
; Метод, используемый для преобразования:
; Входное число X = abcde (пять разрядов BCD-числа)
; X = abcde = 10[10[10[10a+b]+c]+d]+e
;**********************************************************************
Dec_to_Hex
clrf ACCaHI
movf R0,W
andlw 0x0F
movwf ACCaLO
call mpy10a ; результат = 10a+b
swapf R1,W
call mpy10b ; результат = 10[10a+b]
movf R1,W
call mpy10b ; результат = 10[10[10a+b]+c]
swapf R2,W
call mpy10b ; результат = 10[10[10[10a+b]+c]+d]
movf R2,W
andlw 0x0F
addwf ACCaLO, F
btfsc STATUS,C
incf ACCaHI, F ; результат = 10[10[10[10a+b]+c]+d]+e
return ; преобразование выполнено
mpy10b
andlw 0x0F
addwf ACCaLO, F
btfsc STATUS,C
incf ACCaHI, F
mpy10a
bcf STATUS,C ; multiply by 2
rlf ACCaLO,W
movwf L_temp
rlf ACCaHI,W ; (H_temp,L_temp) = 2*N
movwf H_temp
bcf STATUS,C ; умножение на 2
rlf ACCaLO, F
rlf ACCaHI, F
bcf STATUS,C ; умножение на 2
rlf ACCaLO, F
rlf ACCaHI, F
bcf STATUS,C ; умножение на 2
rlf ACCaLO, F
rlf ACCaHI, F ; (H_byte,L_byte) = 8*N
movf L_temp,W
addwf ACCaLO, F
btfsc STATUS,C
incf ACCaHI, F
movf H_temp,W
addwf ACCaHI, F
return ; (H_byte,L_byte) = 10*N
;*********************************************************************************************
;Эта процедура используется для поиска бит четности(четн. или нечетн.) 8 разрядного числа, имеющегося в WREG.
;Бит четности сохраняется в младшем бите регистра четности. Чтобы выполнить проверку четности нужно
;определить EVEN_PARITY как TRUE. Чтобы выполнить проверку на нечетность нужно определить EVEN_PARITY как FALSE.
;*********************************************************************************************
find_parity
movwf TEMPX
swapf TEMPX,W
xorwf TEMPX,W
movwf parity
rrf parity, F
rrf parity, F
xorwf parity,W
andlw 0x03
addlw 0x01
movwf TEMPX
rrf TEMPX,F
rrf TEMPX,W
movwf parity
#if EVEN_PARITY
xorlw 0x01
movwf parity
#endif
return
;************************************************************************
; Вычитание : ACCb(16 бит) - ACCa(16 бит) -> ACCb(16 бит)
; (a) Загрузить 1-й операнд в ACCaLO & ACCaHI ( 16 бит )
; (b) Загрузить 2-й операнд в ACCbLO & ACCbHI ( 16 бит )
; (c) Вызвать Sub_16bit
; (d) Результат будет в ACCbLO & ACCbHI ( 16 бит )
;************************************************************************
Sub_16bit
call Neg_16bit
call Add_16bit
return
;************************************************************************
; Сложение : ACCb(16 бит) + ACCa(16 бит) -> ACCb(16 бит)
; (a) Загрузить 1-й операнд в ACCaLO & ACCaHI ( 16 бит )
; (b) Загрузить 2-й операнд в ACCbLO & ACCbHI ( 16 бит )
; (c) Вызвать Add_16bit
; (d) Результат будет в ACCbLO & ACCbHI ( 16 бит )
;************************************************************************
Add_16bit
movf ACCaLO,W
addwf ACCbLO, F
btfsc STATUS,C
incf ACCbHI, F
movf ACCaHI,W
addwf ACCbHI, F
return
;************************************************************************
; Инверсия ACCa ( -ACCa -> ACCa )
; (a) Загрузить операнд в ACCaLO & ACCaHI ( 16 бит )
; (b) Вызвать Neg_16bit
; (c) Результат будет в ACCaLO & ACCaHI ( 16 бит )
;************************************************************************
Neg_16bit
comf ACCaLO, F ;
incf ACCaLO, F
btfsc STATUS,Z
decf ACCaHI, F
comf ACCaHI, F
return
Перевод © digitrode.ru