MAX7651的扩展数学子程序集合

描述

本文给出了使用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处理器的这些例程。

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分