цифровая электроника
вычислительная техника
встраиваемые системы

 



Библиотека арифметических операций для PIC16

Автор: Mike(admin) от 3-06-2014, 15:33

Микроконтроллеры серии 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


<Источник>




Уважаемый посетитель, Вы зашли на сайт как незарегистрированный пользователь.
Мы рекомендуем Вам зарегистрироваться либо войти на сайт под своим именем.

Комментарии:

Оставить комментарий
  • vladras1

  • 21 сентября 2018 11:14
  • Группа: Посетители
  • ICQ:
  • Регистрация: 21.09.2018
  • Статус: Пользователь offline
  • Комментариев: 1
  • Публикаций: 0
^
Добрый день!
С помощью ПП Division выполнил в MPLAB деление для PIC16F877
5535/1000 (159F/03E8) и получил 6 с остатком 559(022F),
а должен был получить 5 с остатком 535.
Есть какие-то тонкости применения этой подпрограммы?
Прошу сообщить.
С уважением.