本文給出了使用MAX32和MAX16等兼容16微控制器預留內部存儲器、簡單ASCII轉換、32位減法、8051x7651倍數和7652位分頻的匯編代碼示例。
MAX7651閃存可編程12位集成數據采集系統使用8位CPU內核進行所有操作。在某些情況下,8 位分辨率不足以進行數據操作。一個明顯的例子是使用具有12位分辨率的內部ADC。收集多個讀數,然后找到最大值需要 CPU 寄存器中 8 位以外的數學子程序。
解決方案是成組使用內部RAM寄存器,并使用MAX7651的CPU以8位“塊”執行數學運算。執行連續操作,直到獲得所需的結果。
本應用筆記介紹了幾種常用的數學子程序,這些子程序可處理大于8位的數據,分為四個部分:
用于保留內部 RAM 以保存變量的子例程
一個簡單的 8 位 ASCII 字符轉換子例程,包括前導零消隱
擴展的 ASCII 字符轉換,包括用于 32 位減法、16x16 位乘法和 32 位除法的子例程
說明使用上述子例程的示例
預留內部存儲器
下面的代碼告訴匯編程序保留內部存儲器來保存數學子例程使用的變量。這些內存位置可以位于內存映射中的任何位置。
;
;保留內部 RAM 以用于數學子例程
;
;一個好的起始內存位置是 30H,但起始位置
;可以位于內存映射中的任何位置。
;
數字2: | DS | 1 | ;ASCII 例程的 100 位數字 | |
數字1: | DS | 1 | ;10 的數字 | |
數字0: | DS | 1 | ;1 的數字 | |
數據嗨: | DS | 1 | ;16位寄存器的上字節 | |
達洛: | DS | 1 | ;16位寄存器的較低字節 | |
保持: | DS | 1 | ; Remainder | |
執行部分第3段: | DS | 1 | ;OP3-OP0是4個8位寄存器。對于 32 位數學運算 | |
執行部分第2段: | DS | 1 | ||
執行部分第1段: | DS | 1 | ||
執行部分第0段: | DS | 1 | ;32 位“運算符”的最低有效字節 | |
溫度3: | DS | 1 | ;TEMP3-TEMP0 包含 32 位溫度寄存器 | |
溫度2: | DS | 1 | ||
溫度1: | DS | 1 | ||
溫度0: | DS | 1 | ;臨時寄存器的最小有效字節 |
簡單的 ASCII 轉換
在許多MAX7651應用中,要求使用ASCII數據進行顯示。所述顯示類型可以是LCD、LED、真空熒光顯示器或其它技術。最常用的顯示器是單行或雙行LCD模塊。它們接受 ASCII 字符,因此軟件程序必須將二進制數據轉換為單獨的 ASCII 數字。ASCII(美國信息交換標準代碼的首字母縮寫)是一個七位數的二進制代碼,用于表示字母、數字和符號。
例如,假設寄存器中的數據是從 8H 到 00FFH 的正 0 位值。這對應于二進制數值 0 到 255。如果要讓LCD在屏幕上顯示“ 127”,則需要向其發送三個ASCII字符;每個數字一個:'100's 數字 [1]、'10's 數字 [2] 和 '1's 數字 [7]。
幸運的是,二進制到 ASCII 的轉換很簡單。ASCII 數字只是添加到 30H 的二進制數。為了生成三位數字,以下子例程將原始二進制數據連續除以 100,然后從原始數字中減去此數字(127/100 = 1,余數為 27)。然后它取余數并除以 10 并保留余數(27/10 = 2,余數為 7)。然后將每個值添加到 30H 以獲得 ASCII 值,然后存儲這些值。
在此子例程中,要轉換的 8 位二進制數在累加器(寄存器 A)中傳遞。由于MAX7651的所有數學功能均使用累加器,因此內部寄存器R0用于保存中間結果。如果應用程序需要保留 R0 中的值,則只需使用另一個寄存器即可。
子程序使用MAX7651的乘法指令(MUL AB)生成要減去的“100”和“10”數字,并使用ADD指令生成最終ASCII值。子例程還執行“前導零消隱”,因此,如果數字為 99 或更小,軟件將抑制任何前導零并用空格替換它們。
;
;子程序2_ASCII
;
;將 8 位 ACC 轉換為 ASCII 數字
;
;ACC 和 RO 被銷毀,DIGIT2-0 中的先前值被覆蓋
;
2ASCII: | MOV | RO,A | ||
MOV | B,#100 | ; Get 100's digit | ||
MOV | A,R0 | |||
DIV | AB | ; A has quotient, B has remainder | ||
MOV | DIGIT2,A | ; Save 100's digit | ||
MOV | B,#100 | |||
MUL | AB | ; Need to subtract out 100's digit | ||
XCH | A,R0 | |||
CLR | C | |||
SUBB | A,RO | |||
MOV | R0,A | |||
MOV | B,#10 | ; Get 10's digit | ||
DIV | AB | |||
MOV | DIGIT1,A | |||
MOV | DIGIT0,B | ; Remainder is the 1's digit | ||
; | ||||
; Now convert to ASCII | ||||
; | ||||
MOV | A,DIGIT0 | ; 1's digit | ||
ADD | A,#'0' | ; Offset from 30H | ||
MOV | DIGIT0,A | ; Write it back to memory | ||
MOV | A,DIGIT1 | ; 10's digit | ||
ADD | A,#'0' | ; Offset from 30H | ||
MOV | DIGIT1,A | ; Write it back | ||
MOV | A,DIGIT2 | ; 100's digit | ||
CJNE | A,#0,NOTZ | ; A non-zero value | ||
MOV | DIGIT2,#'' | ; Blank it | ||
; | ||||
; Blank again? | ||||
; | ||||
MOV | A,DIGIT1 | |||
CJNE | A,#'0',SKIPBL | ; Non-zero abort | ||
MOV | DIGIT1,#'' | |||
SKIPBL: | RET | |||
NOTZ: | ADD | A,#'0' | ; Offset from 30H | |
MOV | DIGIT2,A | ; Write it back | ||
RET |
擴展的 ASCII 轉換
32 位減法
僅當要轉換的數字為 255 或更小時,前面的子例程才有用。如果應用正在測量化學過程中的溫度,而我們希望顯示高達 999 度的溫度,該怎么辦?這需要使用一組擴展的數學子例程,將數據劃分為多個 8 位寄存器。
從上面的例子中,算法是乘以“數字位置”(即 100、10),然后從原始數字中減去該數字。因此,我們需要編寫一個擴展減法子例程和一個擴展乘法子例程。
減法子例程很容易使用指令 SUBB 完成,它會自動使用借用標志。乍一看似乎很奇怪,因為子程序不會像我們教的那樣減去“數字”,而是一次減去 255 個塊(累加器的整個范圍)。但是,它確實提供了正確的答案。
寫入的子例程從另一個 32 位數字 (OP3:OP2:OP1:OP0) 中減去一個 32 位數字 (TEMP3:TEMP2:TEMP1:TEMP0),并將結果放回 OP。累加器用于從原始數字中連續減去 8 位“塊”。
;
;子程序SUB_32
;
;OP < OP - TEMP
;
;此例程將覆蓋 ACC 和進位標志(此處用作借用 旗)
;請注意,這兩個數字不必是 2 位
;
;
SUB_32: | CLR | C | ||
MOV | A,OP0 | |||
SUBB | A,TEMP0 | |||
MOV | OP0,A | |||
MOV | A,OP1 | |||
SUBB | A,TEMP1 | |||
MOV | OP1,A | |||
MOV | A,OP2 | |||
SUBB | A,TEMP2 | |||
MOV | OP2,A | |||
MOV | A,OP3 | |||
SUBB | A,TEMP3 | |||
MOV | OP3,A | |||
RET |
16×16 乘法
接下來的兩個子例程要復雜得多。第一個例程是 16x16 乘法,結果為 32 位。該例程假定兩個數字都是正數(0000H 到 0FFFFH)。結果被放入OP3:0中。
子例程首先使用內部 MUL AB 指令生成第一個 8 位“數字”。但在此之后,例程必須為每個“數字”執行四個單獨的操作:兩組乘法/加法指令。這是因為我們使用的是二進制算術,而不是十進制算術。
;
;子程序MUL_16
;
;將 16 位數字 DATAHI:DATALO 乘以 16 位數字 OP3:0和地點 結果回到OP3:0
;使用 32 位 TEMP3:0也寄存器
;
;
MUL_16: | MOV | TEMP3,#0 | ||
MOV | TEMP2,#0 | ; Clear upper 16-bits | ||
; | ||||
; Generate lower byte of result | ||||
; | ||||
MOV | B,OP0 | |||
MOV | A,DATALO | |||
MUL | AB | |||
MOV | TEMP0,A | |||
MOV | TEMP1,B | ; 1st result | ||
; | ||||
; Byte 2 of result | ||||
; | ||||
MOV | B,OP1 | |||
MOV | A,DATALO | |||
MUL | AB | |||
ADD | A,TEMP1 | ; Lower nibble result | ||
MOV | TEMP1,A | |||
MOV | A,B | |||
ADCC | A,TEMP2 | |||
MOV | TEMP2,A | |||
JNC | MULOOP1 | |||
INC | TEMP3 | ; propogate carry | ||
MULOOP1: | MOV | B,OP0 | ||
MOV | A,DATAHI | |||
MUL | AB | |||
ADD | A,TEMP1 | |||
MOV | TEMP1,A | |||
MOV | A,B | |||
ADDC | A,TEMP2 | |||
MOV | TEMP2,A | |||
JNC | MULOOP2 | |||
INC | TEMP3 | ; byte 2 is done | ||
; | ||||
; Byte 3 | ||||
; | ||||
MULOOP2: | MOV | B,OP2 | ||
MOV | A,DATALO | |||
MUL | AB | |||
ADD | A,TEMP2 | |||
MOV | TEMP2,A | |||
MOV | A,B | |||
ADDC | A,TEMP3 | |||
MOV | TEMP3,A | |||
; | ||||
; Next nibble | ||||
; | ||||
MOV | B,OP1 | |||
MOV | A,DATAHI | |||
MUL | AB | |||
ADD | A,TEMP2 | |||
MOV | TEMP2,A | |||
MOV | A,B | |||
ADDC | A,TEMP3 | |||
MOV | TEMP3,A | |||
; | ||||
; Byte 4 | ||||
; | ||||
MOV | B,OP3 | |||
MOV | A,DATALO | |||
MUL | AB | |||
ADD | A,TEMP3 | |||
MOV | TEMP3,A | |||
MOV | B,OP2 | |||
MOV | A,DATAHI | |||
MUL | AB | |||
ADD | A,TEMP3 | |||
; | ||||
; Save results | ||||
; | ||||
MOV | OP3,A | |||
MOV | OP2,TEMP2 | |||
MOV | OP1,TEMP1 | |||
MOV | OP0,TEMP0 | |||
RET |
32 位除法
現在我們可以將兩個 16 位數字相乘,我們也可以使用這個算法“向后”除法。但是,它需要四個中間寄存器(R7、R6、R1、R0)來保存部分商。由于我們使用二進制算術,因此我們可以通過簡單的右移命令除以 2。這可以通過巧妙的“移減”來擴展,以除以 10 的數字。這被稱為“布斯算法”。循環運行 32 次(每個位位置運行一次,反過來是 2 的冪)。
;
;子程序DIV_16
;
;將 OP3:2:1:0 除以 DATAHI:DATALO,并將結果放入 OP3:0
;
;
DIV_16: | MOV | R7,#0 | ||
MOV | R6,#0 | ; Zero partial remainder | ||
MOV | TEMP0,#0 | |||
MOV | TEMP1,#0 | |||
MOV | TEMP2,#0 | |||
MOV | TEMP3,#0 | |||
MOV | R1,DATAHI | ; Load the divisor | ||
MOV | R0,DATALO | ; Bit counter | ||
MOV | R5,#32 | ; Shift dividend and msb>carry | ||
DIV_LOOP: | CALL | SHIFT_D | ||
MOV | A,R6 | |||
RLC | A | |||
MOV | R6,A | |||
MOV | A,R7 | |||
RLC | A | |||
MOV | R7,A | |||
; | ||||
; Now test to see if R7:R6 =>R1:R0 | ||||
; | ||||
CLR | C | |||
MOV | A,R7 | |||
SUBB | A,R1 | ; see if R7 < R1 | ||
JC | CANT_SUB | ; yes | ||
; | ||||
; At this point R7>R1 or R7=R1 | ||||
; | ||||
JNZ | CAN_SUB | ; R7 is > R1 | ||
; | ||||
; If R7=R1, test for R6=>R0 | ||||
; | ||||
CLR | C | |||
MOV | A,R6 | |||
SUBB | A,R0 | ; Carry set if R6 < R0 | ||
JC | CANT_SUB | |||
CAN_SUB: | CLR | C | ||
; | ||||
; Subtract divisor from partial remainder | ||||
; | ||||
MOV | A,R6 | |||
SUBB | A,R0 | |||
MOV | R6,A | |||
MOV | A,R7 | |||
SUBB | A,R1 | ; A=R7 - R1 - borrow bit | ||
MOV | R7,A | |||
SETB | C | ; Shift 1 into quotient | ||
SJMP | QUOT | |||
CANT_SUB: | CLR | C | ; Shift 0 into quotient | |
QUOT: | CALL | SHIFT_Q | ; Shift carry into quotient | |
DJNZ | R5,DIV_LOOP | ; Did it 32 times? | ||
; | ||||
; All done! | ||||
; | ||||
MOV | OP0,TEMP0 | |||
MOV | OP1,TEMP1 | |||
MOV | OP2,TEMP2 | |||
MOV | OP3,TEMP3 | |||
DIV_DONE: | RET | |||
; | ||||
; Shift the dividend one bit to the left and return msb in carry bit | ||||
; | ||||
SHIFT_D: | CLR | C | ||
MOV | A,OP0 | |||
RLC | A | |||
MOV | OP0,A | |||
MOV | A,OP1 | |||
RLC | A | |||
MOV | OP1,A | |||
MOV | A,OP2 | |||
RLC | A | |||
MOV | OP2,A | |||
MOV | A,OP3 | |||
RLC | A | |||
MOV | OP3,A | |||
RET | ||||
; | ||||
; Shift the quotient one bit to the left and shift carry bit into lsb | ||||
; | ||||
SHIFT_Q: | MOV | A,TEMP0 | ||
RLC | A | |||
MOV | TEMP0,A | |||
MOV | A,TEMP1 | |||
RLC | A | |||
MOV | TEMP1,A | |||
MOV | A,TEMP2 | |||
RLC | A | |||
MOV | TEMP2,A | |||
MOV | A,TEMP3 | |||
RLC | A | |||
MOV | TEMP3,A | |||
RET |
將一切整合在一起
現在,我們有了擴展 ASCII 轉換所需的所有子例程。最后一個例程將 0 到 999 范圍內的數字(存儲在 DATAHI:DATALO 中)轉換為 3 個 ASCII 數字。該算法與早期的簡單轉換例程相同,只是現在我們使用三個擴展的數學例程對 16 位寄存器進行操作。
;
;子程序轉換3
;
;將 DATAHI:DATALO 中的 16 位值 000-999 轉換為 ASCII
;數據存儲在數字 2 - 數字 0 中
;
CONVERT3: | MOV | OP0,DATALO | ||
MOV | OP1,DATAHI | |||
MOV | OP2,#00 | |||
MOV | OP3,#00 | |||
MOV | TEMP8,DATALO | |||
MOV | TEMP9,DATAHI | ; Save original for remainder | ||
MOV | DATALO,#100 | |||
MOV | DATAHI,#00 | |||
CALL | DIV_16 | ; Divide number by 100 | ||
MOV | A,OP0 | ; Answer is 2-9 + remainder | ||
ADD | A,#30H | ; Convert to ASCII | ||
MOV | DIGIT2,A | ; Save it | ||
MOV | DATALO,#100 | ; Convert the remainder | ||
MOV | DATAHI,#0 | |||
CALL | MUL_16 | |||
MOV | TEMP0,OP0 | |||
MOV | TEMP1,OP1 | |||
MOV | TEMP2,OP2 | |||
MOV | TEMP3,OP3 | |||
MOV | OP0,TEMP8 | |||
MOV | OP1,TEMP9 | |||
CALL | SUB_32 | ; Subtract 100's digit | ||
MOV | A,OP0 | |||
MOV | B,#10 | ; 10's digit calculation | ||
DIV | AB | |||
ADD | A,#30H | |||
MOV | DIGIT1,A | ; Get the 10's digit | ||
MOV | A,B | |||
ADD | A,#30H | |||
MOV | DIGIT0,A | ; Get the 1's digit | ||
; | ||||
; Check for zero blanking | ||||
; | ||||
MOV | A,DIGIT2 | |||
CJNE | A,#'0',BK_DONE | |||
; | ||||
; Blank 100's digit | ||||
; | ||||
MOV | DIGIT2,#'' | |||
; | ||||
; Now check 10's digit | ||||
; | ||||
MOV | A,DIGIT1 | |||
CJNE | A,#'0',BK_DONE | |||
; | ||||
; Blank 10's digit | ||||
; | ||||
MOV | DIGIT1,#'' | |||
BK_DONE: | RET |
結論
這些例程將MAX7651的數學運算能力擴展至16位。您也可以修改這些子例程以處理 32 位數據。MAX7651的四時鐘周期CPU大大加快了標準8051處理器的這些例程。
-
微控制器
+關注
關注
48文章
7496瀏覽量
151086 -
寄存器
+關注
關注
31文章
5325瀏覽量
120054 -
存儲器
+關注
關注
38文章
7455瀏覽量
163624
發布評論請先 登錄
相關推薦
評論