Двоично-десетичните числа или по-познати в английската литература, като BCD (Binary Coded Decimal) представляват десетични числа, представени чрез двоичен код. Това са числа, при който всяка цифра от десетичната бройна система се кодирa чрез бинарен код, представен от нули и единици. Очевидно за да бъдат представени десетте цифри от десетичната бройна система са необходими минимум четири бита. В таблица 1 е представено нагледно как изглежда това кодиране.
Табл. 1
Тъй като основната градивна единица в микроконтролерите и микропроцесорите е байта с цел икономия на памет в един байт можем да представим две десетични цифри. Примерно десетичното число 24 може да бъде представено в един байт по следния начин: 0010 0100. Този начин на представяне се нарича пакетирано двоично-десетично число. От казаното до тук следва, че най-голямото пакетирано двоично-десетично число, което може да бъде представено чрез един байт е числото 99, отговарящо на двоично десетичен код 1001 1001.
Чрез двоично-десетичните числа лесно могат да бъдат изведени числени резултати върху различни видове дисплей. Още по-лесно е и извеждането на буквено цифрови дисплей, който използват ASCII символи. Естествено обаче пакетираните двоично-десетични числа трябва да бъдат представени чрез един байт за всяка десетична цифра преди да бъдат изпратени към дисплея.
В настоящата публикация ще разгледаме два алгоритъма за преобразуване на бинарни числа в двоично десетични числа. Единият от алгоритмите ще бъде приложен към PIC микроконтролери от серията 16F а другият към серията 18F.
Алгоритъм с ротация наляво и добавяне на числото 3.
Алгоритъма с ротация наляво и добавяне на числото три е широко разпространен. Този алгоритъм е сравнително прост с малко на брой инструкции, което го прави доста предпочитан сред програмистите. Самият алгоритъм се изпълнява в четири основни стъпки и те са следните:
- Преместваме бинарното число един бит наляво (чрез инструкция за ротация);
- Ако в някоя от колоните числото е по-голямо или равно на 5 добави 3;
- Ако сме направили осем ротации, числото е в колоните на единици десетици и стотици;
- Върни се в точка 1.
Таблица 2 - Нагледно представяне на алгоритъма с ротация наляво и добавяне на числото 3.
В таблица 2 нагледно е представено преобразуването на бинарното число 0xFF в двоично-десетично число. Там, където имаме налице условието единиците, десетиците или стотиците да са по-големи или равни на числото 5 е отбелязано с оранжев цвят в таблица 2. Със сив цвят е отбелязан резултата след добавяне на числото 3 към съответните четири бита на десетиците, единиците или стотиците. С зелен цвят в таблица 2 е подчертан крайният резултат .
В следващите редове ще обсъдим конкретен алгоритъм написан на асемблер за PIC микроконтролерите от серията 16F на фирмата Microchip. Преди да започнем обсъждането е необходимо да поясним, че разглеждания код се отнася за конвертирането на двубайтово число в двоично-десетично число. Или с други думи казано най-голямото бинарно число, което можем да преобразуваме е 0xFFFF, което отговаря на десетичното число 65535. За да представим това десетично число в пакетирано двоично-десетично такова е необходимо да заделим три байта, в който да се съдържа резултата. В конкретния пример тези байтове са кръстени BCDvalL, BCDvalM, BCDvalH. В променливите NumbLo и NumbHi се съдържа бинарното число, което искаме да преобразуваме. Тъй като програмата се реализира, като подпрограма е необходимо преди да я извикаме с инструкции call да поместим числото, което ще преобразуваме в променливите NumbLo, NumbHi. В разгледания пример се използват и още две променливи. Едната от тях e озаглавена MCount и се използва за преброяване на ротациите, в този случай те трябва да са шестнайсет. Последната променлива, която трябва да заделим в паметта на микроконтролера е Temp. Тя се ползва за временно съхранение на резултата от сбора с три.
В първите седем реда от програмата се нулират байтовете, в който ще се съдържа резултата. Също така на променливата MCount се присвоява десетичното число 16. След което се пристъпва към първата ротация (редовете от 8 до 13). След първата ротация променливата MCount се намалява с единица (ред 14). След което следва проверка за нулев резултат, тоест дали са се извършили всичките шестнайсет ротации, ако са се изпълнили следващата инструкция, която се изпълнява от програмата е return (ред 16), ако не са изпълнени всичките шестнайсет ротации инструкцията return на ред 16 се прескача и следва частта от програмата, която проверява дали в някои от четирите младши или четирите старши бита съдържат число по-голямо или равно на 5. Тук проверката се състои в добавяне на числото три и проверка на четвъртия бит дали се е установил в 1 (от 29 до 32 ред). Ако числото е 5 то неговата двоична стойност е b’101’ добавяйки три b’11’ се получава числото b’1000’. Виждаме, че четвъртия бит се е установил в 1. Това не би се получило, ако числото е по-малко от 5. За проверка на старшите четири бита се добавя числото b’00110000’. След сбора се проверява осмия бит на Temp (ред 37), ако е 1 то Temp се записва в BCDvalL. Действията от ред 8 до ред 27 се повтарят 16 пъти а действията от 28 до 39 ред се повтарят 3Х16 или общо 48 пъти. От този факт лесно можем да заключим, че на програмата са и необходими около 950 машинни цикъла за да преобразува едно шестнайсет битово число в BCD код.
Пример 1
;-----Необходими променливи------ NumbLo NumbHi BCDvalL BCDvalM BCDvalH MCount Temp 1. HexBCD: 2. movlw d'16' 3. movwf MCount 4. clrf BCDvalH 5. clrf BCDvalM 6. clrf BCDvalL 7. bcf STATUS,C 8. loop16: 9. rlf NumbLo,f 10. rlf NumbHi,f 11. rlf BCDvalL,f 12. rlf BCDvalM,f 13. rlf BCDvalH,f 14. decf MCount,F 15. btfsc STATUS,Z 16. return 17. adjDEC: 18. movlw BCDvalL 19. movwf FSR 20. call adjBCD 21. movlw BCDvalM 22. movwf FSR 23. call adjBCD 24. movlw BCDvalH 25. movwf FSR 26. call adjBCD 27. goto loop16 28. adjBCD: 29. movlw d'3' 30. addwf INDF,w 31. movwf Temp 32. btfsc Temp,3 33. movwf INDF 34. movlw 30h 35. addwf INDF,w 36. movwf Temp 37. btfsc Temp,7 38. movwf INDF 39. return
Алгоритъм със сума на BCD числа.
Първият алгоритъм, който разгледахме без проблем би могъл да се приложи и в микроконтролерите от серията 18F. Естествено необходимо е да заменим имената на регистрите за индиректна адресация INDF и FSR, защото при 18F тези регистри са по три на брой и са с имена INDF0, INDF1, INDF2. Съответно FSR0, FSR1 иFSR2. Има и още подробности относно непряката адресация, който нямат значение, тъй като алгоритъма, който предстой да разгледаме е приложим само за микроконтролерите 18F и е доста по-рационален от предния, който разгледахме. Или с други думи казано не необходимо да хабим усилия да прилагаме горния алгоритъм при PIC18F.
Както вече беше споменато този алгоритъм може да бъде приложен единствено върху микроконтролерите от фамилията 18F. Това е така, защото тези микроконтролери разполагат с инструкция за десетична корекция daw. Тази инструкция осъществява десетична корекция на числото, което се намира в работния регистър. Това се състой в проверка на битовете от 0 до 3 на работния регистър, ако числото, което се намира там е по-голямо от 9 или DC бита на STATUS регистъра е равен на 1 то към първите четири бита на работния регистър се добавя числото 6. Едновременно с това се проверяват и старшите четири бита на работния регистър, битовете от 4 до 7. Ако те са по големи от 9 или C бита на STATUS регистъра е равен на 1 то следва добавяне на числото 6. Всичките тези действия се изпълняват в рамките на един машинен цикъл. Повече за тази инструкция може да прочетете в [1] стр. 230.
Пример 2
;-----Необходими променливи------ BCDL BCDH BCDU COUNT BINL BINH 1. BinToBCD: 2. clrf BCDL 3. clrf BCDH 4. clrf BCDU 5. movlw d’16’ 6. movwf COUNT 7. ConvertBit: 8. rlcf BINL,F 9. rlcf BINH,F 10. movf BCDL,W 11. addwfc BCDL,W 12. daw 13. movwf BCDL 14. movf BCDH,W 15. addwfc BCDH,W 16. daw 17. movwf BCDH 18. rlcf BCDU 19. decfsz COUNT 20. bra ConvertBit 21. return
Примера, който ще разгледаме е представен в литературен източник [2] негов автор е BitWise. Кръстих алгоритъма сума на BCD числа, тъй като на практика чрез сумиране на BCD число се формира резултата. От булевата алгебра ни е ясно, че едно n битово двоично число се превръща в десетично с израза 1, където b е стойността на поредния бит (0 или 1).
D = bn*2n
+ bn-1*2n-1 + bn-2*2n-2…b1*21 (1)
Ако bn = 1 е необходимо да пресметнем 2n това може да стане, като умножим числото 2 n пъти само със себе си. Тъй като няма начин чрез, който да извършим умножение на BCD числа се налага операцията умножение да бъде представена като сума на резултата сам със себе си съответно n пъти.
В пример 2 е представена примерна програма следваща принципа, който разисквахме досега. Първото нещо, което ни прави впечатление е, че тя е поне два пъти по-кратка от първия пример, но освен това единствената логическа проверка, която прави е да провери дали цикъла се е изпълнил (ред 19). Тази програма конвертира шестнайсет битово число, поради този факт е необходимо редовете от 7 до 20 да се повторят шестнайсет пъти. Променливата чрез, която се задава това е COUNT. В променливите BCDL, BCDH и BCDU се съдържа резултата от програмата. В променливите BINL и BINH се съдържа числото което ще бъде преобразувано, което означава че преди да извикаме програмата чрез инструкция call е необходимо да заредим бинарното число в тези две променливи. В първите шест реда от програмата се нулират променливите за изходния резултат, след което в COUNT се зарежда числото d’16’. Следва основния цикъл редовете от 7 до 20. В ред 8 и 9 се извършва ротация през C бита на STATUS регистъра, така ако най-старшия бит на BINH е 1 следва при инструкцията от ред 11 той да се прибави в работния регистър. Преди това обаче е необходимо да заредим в работния регистър стойността на BCDL това става в ред 10. Естествено при първото изпълнение на цикъла стойността на BCDL е 0х00, но при следващите пъти неговата стойност ще нараства двойно плюс преноса от C бита. По този начин стойността на BCDL нараства два пъти за всеки цикъл. След сбора се извиква инструкцията за десетична корекция (ред 12), това автоматично коригира двете части от по четири бита на BCDL, в който стойността е над 9 и ако в старшите четири бита стойността е над 9 то C бита на STATUS регистъра също се установява в 1. След тези операции следва работния регистър да се запише обратно в променливата BCDL. Редовете от 14 до 17 работят по идентичен начин, като тези от 10 до 13. За най-последния байт (BCDU), в който се съдържа резултата е необходимо само да използваме инструкция за ротация (ред 18). Това е породено от факта, че най-голямото число, което е възможно да се съдържа в него е 6. Това прави ненужно сбора и десетичната корекция. Умножението по две се осъществява чрез ротацията наляво.
Редовете от 1 до 6 се изпълняват веднъж, което означава че отнемат 5 машинни цикъла, тъй като в тях не се съдържат инструкции, които отнемат два машинни цикъла. Редовете от 7 до 20 се изпълняват шестнайсет пъти, тъй като на инструкциите в тези редове са им необходими петнайсет машинни цикъла с изключения на последния цикъл, който отнема 14 машинни цикъла. Това е така, защото инструкцията decfsz отнема един машинен цикъл, когато прескача един ред (т.е. когато условието COUN=0 е изпълнено). Общо за редовете от 7 до 20 имаме (16*15)-1=239 машинни цикъла. Последния ред от програмата отнема два машинни цикъла. От казаното до тук следва, че на програмата са и необходими точно 246 машинни цикъла, което е около четири пъти по-малко от първия пример.
Преобразуване на пакетирани BCD числа в непакетирани.
В тази последна точка от публикацията ще обсъдим някой начини за разделянето на пакетираните BCD числа в еднобайтово BCD число. Това се налага, когато вече е необходимо да изведем резултата на дисплей и сегментни индикатори. За целта е необходимо да използваме няколко прости инструкции представени в пример 3.
Пример 3
1. swapf BCDL,W 2. andlw h’0F’ 3. addlw h’30’ 4. movwf TXREG 5. btfss TXSTA,TRMT 6. bra $-2 7. movf BCDL,W 8. andlw h’0F’ 9. addlw h’30’ 10. movwf TXREG 11. btfss TXSTA,TRMT 12. bra $-2
В пример 3 разглеждаме конкретен начин да разбием едно пакетирано BCD число. Поясняваме, че разглеждания пример представя разбиването на един байт от резултата получен чрез пример 2. В първия ред разменяме старшите четири бита с младшите четири бита и записваме резултата в работния регистър. На ред номер 2 извършваме нулиране на старшите четири бита на работния регистър, чрез инструкцията andlw h’0F’. След тази инструкция младшите четири бита остават непроменени. Така имаме вече числото на десетиците съдържащо се в един байт. За да изпратим ASCII символа на цифрата на десетиците е необходимо да добавим шестнайсетичното число 0x30. По този начин се получава съответния ASCII код на цифрата. Това действие се извършва на ред 3. В редове от 4 до 6 се извършва изпращане на ASCII кода по UART. В тези редове можем да извикаме и съответна подпрограма за изпращане на кода към LCD дисплей. На ред 7 преместваме пакетираното BCD число в работния регистър, без да разменяме старшите четири бита с младшите, като по този начин започваме да манипулираме с цифрата на единиците. Редовете от 8 до 12 са идентични, като тези от 2 до 6.
Литература:
- PIC18FXX2 Data Sheet – Microchip Technology Inc 2002
- http://www.microchip.com/forums/m322713.aspx