本文给出了使用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处理器的这些例程。
全部0条评论
快来发表一下你的评论吧 !