The MAX7651 flash-programmable 12-bit integrated data acquisition system uses an 8-bit CPU core for all operations. There are cases where 8-bits are not sufficient resolution for data manipulation. An obvious example is when using the internal ADC, which has 12-bit resolution. Collecting several readings and then finding the maximum value requires math subroutines beyond the 8-bits in the CPU registers.
The solution is to use internal RAM registers in a group, and use the MAX7651's CPU to perform the math in 8-bit 'chunks'. Successive operations are performed until the desired result is obtained.
This application note presents several commonly used math subroutines that operate on data larger than 8-bits and is divided into four sections:
A subroutine for reserving internal RAM to hold variables A simple 8-bit ASCII character conversion subroutine which includes leading zero blanking Extended ASCII character conversion, which includes subroutines for 32-bit subtraction, 16x16 bit multiplication and 32-bit division An example explaining use of the aforementioned subroutines Reserving Internal MemoryThe following code tells the assembler to reserve internal memory to hold the variables used by the math subroutines. These memory locations can be anywhere in the memory map.
;
; Reserve internal RAM for use with the math subroutines
;
; A good starting memory location is at 30H, but the starting location
; can be anywhere in the memory map.
;
DIGIT2: | DS | 1 | ; 100's digit for ASCII routines | |
DIGIT1: | DS | 1 | ; 10's digit | |
DIGIT0: | DS | 1 | ; 1's digit | |
DATAHI: | DS | 1 | ; Upper byte of 16-bit register | |
DATAL | DS | 1 | ; Lower byte of 16-bit register | |
REMAIN: | DS | 1 | ; Remainder | |
OP3: | DS | 1 | ; OP3-OP0 are 4 8-bit registers. For 32-bit math | |
OP2: | DS | 1 | ||
OP1: | DS | 1 | ||
OP0: | DS | 1 | ; Least-significant byte of 32-bit 'operator' | |
TEMP3: | DS | 1 | ; TEMP3-TEMP0 includes the 32-bit temp register | |
TEMP2: | DS | 1 | ||
TEMP1: | DS | 1 | ||
TEMP0: | DS | 1 | ; Least-significant byte of temp register |
For example, let's assume you have data in a register that is a positive, 8-bit value from 00H to 0FFH. This corresponds to the binary numerical values ​​0 to 255. If you want to have the LCD show '127' on the screen , you need to send it three ASCII characters; one for each digit: the '100's digit [1], the' 10's digit [2] and the '1's digit [7].
Fortunately, the binary to ASCII conversion is straightforward. An ASCII numerical digit is simply the binary number added to 30H. To generate the three digits, the following subroutine successively divides the original binary data by 100, then subtracts this number from the original number ( 127/100 = 1 with a remainder of 27). It then takes the remainder and divides by 10 and retains the remainder (27/10 = 2 with a remainder of 7). Each value is then added to 30H to obtain the ASCII values , which are then stored.
In this subroutine, the 8-bit binary number to be converted is passed in the accumulator (register A). Since the MAX7651 uses the accumulator for all of its math functions, the internal register R0 is used to hold intermediate results. If your application needs to retain the value in R0, you simply use another register.
The subroutine uses the MAX7651's multiply instruction (MUL AB) to generate the '100's and' 10's digits to be subtracted out, and the ADD instruction to form the final ASCII values. The subroutine also performs 'leading zero blanking', so that if the number is 99 or less, the software will suppress any leading zeros and replace them with a blank space.
;
; Subroutine 2_ASCII
;
; Converts the 8-bit ACC into an ASCII digit
;
; ACC and RO are destroyed, previous value in DIGIT2-0 overwritten
;
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 |
From the above example, the algorithm is to multiply by the 'digit place' (ie, 100's, 10's), then subtract out that digit from the original number. Therefore, we need to write an extended subtraction subroutine and an extended multiply subroutine.
The subtraction subroutine is easy to do with the instruction SUBB, which automatically uses the borrow flag. It may seem strange at first glance, because the subroutine does not subtract in 'digits' as we are taught, but in blocks of 255 at a time (the full range of the accumulator). However, it does provide the correct answer.
The subroutine as written subtracts a 32-bit number (TEMP3: TEMP2: TEMP1: TEMP0) from another 32-bit number (OP3: OP2: OP1: OP0) and places the result back into OP. The accumulator is used to successively subtract the 8-bit 'chunks' from the original number.
;
; Subroutine SUB_32
;
; OP <OP-TEMP
;
; This routine overwrites the ACC and the carry flag (here used as a borrow flag)
; Note that the 2 numbers DO NOT have to be 32-bits
;
;
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 |
16x16 MultiplyThe next two subroutines are much more complicated. The first routine is a 16x16 multiply, with a 32-bit result. The routine assumes both numbers are positive (0000H to 0FFFFH). The result is placed into OP3: 0.
The subroutine first generates the first 8-bit "digit" using the internal MUL AB instruction. But after that, the routine must perform four separate operations for each "digit": two sets of a multiply / add instruction. This is because we are using binary arithmetic, not decimal arithmetic.
;
; Subroutine MUL_16
;
; Multiplies 16-bit number DATAHI: DATALO by 16-bit number OP3: 0 and places the result back into OP3: 0
; Uses the 32-bit TEMP3: 0 registers as well
;
;
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-Bit DivideNow that we can multiply two 16-bit numbers, we can also use this algorithm 'backwards' to divide. However, it requires four intermediate registers (R7, R6, R1, R0) to hold partial quotients. Since we are using binary arithmetic, we can divide by 2 with a simple shift right command. This can be extended by clever "shift and subtraction" to divide by 10's digits. This is called "Booth's Algorithm". The loop is run 32 times (once for each bit-position, which in turn is a power of 2).
;
; Subroutine DIV_16
;
; Divides OP3: 2: 1: 0 by DATAHI: DATALO and places results in 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 |
;
; Subroutine CONVERT3
;
; Converts a 16-bit value 000-999 in DATAHI: DATALO to ASCII
; Data stored into DIGIT2-DIGIT0
;
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 |
The ear-hook earphone refers to a kind of earphone that is added with an ear-hook on the side of the earphone for convenient use. Different from in-ear earphones, ear-hook earphones are a type of earphones and earbuds that are divided according to how they are worn. Design principle: the connection between the two units of the earphone and the wearing of the fixed beam can be canceled, with a beautiful hook to make it hang on the ear.
Advantages:
1: The way of ear hooks ensures the reliability of the user's wearing. In the design of earplugs, the unit is often enlarged to meet the needs of sound quality.
2: The design of the earhook fully demonstrates the design capabilities of the earphone manufacturer, from the design to the reliability of the wearing of the ergonomic design.
Earhook Earphones,Ear Hook Headphones,Earhook Headphones With Mic,Ear Hook Headphones With Mic
Shenzhen Linx Technology Co., Ltd. , https://www.linxheadphone.com