Continue to Site

Welcome to our site!

Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

  • Welcome to our site! Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

Learning 8051

Status
Not open for further replies.
DPTR is primarily used for accessing external RAM with the movx instruction. Also, it can be used for table reads with the movc instruction (movc a,@a + DPTR). In this case, DPTR would hold the 16-bit address that is the first line of the table while A would hold the line of the table that you wish to read.

Example...if DPTR contained address 0xCF51 and A contained 0x1F, the above instruction would read line 31 of a table that starts at code address 0xCF51.

R0 and R1 can be used as indirect pointers for internal RAM.

Code:
;write accumulator to external RAM

              movx          @DPTR,A

;write external RAM to accumulator

              movx          A,@DPTR
 
Thanks, Ian and Jon.

Now I know there are 3 ways to access the 128 bytes of upper RAM in 8052. I'll write a short program to do a block move from $80-$BF to $C0-$FF using the 8051 IDE and see how it works.

cheers.

Allen
 
The 128 upper bytes of internal RAM is accessed via indirect addressing -

Code:
                   mov         R0,#0x80
                   mov         A,@R0               ;write value stored in address that is stored in R0 to accumulator

or

                   mov          @R0,A              ;write value in accumulator to address stored in R0

The above code will access upper RAM region 0x80, or location 128. You can also write to the upper RAM region using the stack as well by moving the stack pointer to the location you wish to write to, then store a value in the accumulator, then push the accumulator onto the stack -

Code:
                 mov            SP,#0x7F
                 mov            A, <DATA>
                 push           ACC

The stack pointer is incremented before the value is written to the address in the stack pointer when a push instruction is executed. This means you would write the address - 1 to the stack pointer prior to pushing the stack. The above code will access location 0x80.

In most of my 8052 code, I use the upper 128 region as the stack. The first instruction in all of my code is mov SP,#0x7F. This gets the stack pointer out of the R register space. The upper 128 is then used as a 128 byte stack, while I use the lower 128 for all of my variable processing.
 
Code:
                 mov            SP,#0x7F
                 mov            A, <DATA>
                 push           ACC

The stack pointer is incremented before the value is written to the address in the stack pointer when a push instruction is executed. This means you would write the address - 1 to the stack pointer prior to pushing the stack. The above code will access location 0x80.

In most of my 8052 code, I use the upper 128 region as the stack. The first instruction in all of my code is mov SP,#0x7F. This gets the stack pointer out of the R register space. The upper 128 is then used as a 128 byte stack, while I use the lower 128 for all of my variable processing.

Normally I wouldn't mess around storing my data on the stack using it as a temporary storage. But I have seen subroutines using the stack for storing ASC messages for printing, something like this:

Code:
HELLO:
     call  prt_mess
     DB   'HELLO, WORLD',00
      ...................
prt_mess:
     pop   Acc
     mov  dpl, Acc          
     pop   Acc
     mov   dph, Acc
     print the message after CALL
     ..................
     mov  Acc, dph
     push  reg          ;restore return address
     mov  Acc, dpj
     push  reg
     ret

I would come to this later... And here's my block move program:

Code:
; Internal RAM testing using indirect addressing
; start date = 5.05.2013
;
; phase 1:  initial program.
; block move from $80-$bf to $c0-$ff
; first fill up $80-$bf with 00-3f
;
org 00h
jmp start
org 30h

source equ R0
dest equ R1
;
start:
; fill $80-$BF with numbers 00-#F
mov source, #0x80 ; source address
mov B,#0x40 ; 64 bytes to fill
mov A,#00 ; start number
next: mov @R0,A ; write Acc to source RAM
inc A ; A=A+1
inc source ; R0=R0+1
djnz B,next ; Do it 64 times
;Block move
mov source, #0x80 ; source address
mov dest, #0xC0 ; destination address
mov B,#0x40 ; number of bytes to move

repeat: mov A,@R0
mov @R1,A
inc source
inc dest
djnz b,repeat
here: jmp here ; stop here

end

Allen
 
Last edited:
I attach here with the circuit for doing multiplication.

The DIP switches on P2.0-P2.3 are used for the multiplier in binary format. The button at P2.7 is used to step the multiplicand from 2 - 16. For each push of the button, the multiplicand would increment by 1.

The result is displayed on the 7-segment LED. If the result is greater than 99, it would stop. I am not sure if a reset button is needed. I would know this once I complete the flowchart.
 

Attachments

  • 8051 multiplication.PNG
    8051 multiplication.PNG
    200.1 KB · Views: 476
Last edited:
Comparing Numbers

I have problems comparing numbers....

Let's say Acc = #90, and I am comparing Acc with #X. When Acc <> #X, a CJNE instruction would be able to test that.

But how do I test if 1) Acc >= #X, 2) Acc > #X, 3) Acc =< #X , 4) Acc < #X ?

There is no Zero flag on the PSW and what's the use of OV flag and P flag? Do I need to do a SUBB A,#X before comparison?

Do I need to clear the Carry flag before doing a subtraction?

Thanks

Allen
 
I have some codes from one of my books:

Code:
FCTL: SETB KYF ;SET KEY FLG
CJNE A,#20H,$+3 ;A<20H ?
JC FCTL1 ;YES
CJNE A,#7FH,$+3 ;A<7FH ?
JNC FCTL1 ;NO
CLR KYF
FCTL1: RET

Does it mean that after failing the CJNE test, Acc can be "=20H" or <20H ?
The JC is to make sure that it is actually "<20H", right?

Allen
 
Last edited:
No...cjne means -

"Compare,Jump if Not Equal"

The values within the two registers being compared do not change. What happens is that if the values within the two registers are not equal, the program counter will jump to the address that is present in the 3rd operand (compare, jump if NOT equal). However, if they are equal, the jump will not happen and the program counter continues on as if the jump never existed.
 
You're absolutely right about the CJNE instruction. I simulated the program I attached on the IDE with all sort of values for "Acc" and now I have a clearer picture of how the instruction works. Thanks.

Still struggling with my multiplication program due to lack of time this week.

cheers.

Allen
 
Do you not know about the "mul" instruction?

You place the two values to be multiplied in the A (accumulator, or ACC) and B registers. Then you execute "mul AB". Once this instruction executes, the A register contains the low order byte of the 16-bit result while the B register contains the high order byte of the 16-bit result.

Example, if the result of a mul AB instruction is 0xC5D9, the A register will contain the value of 0xD9 while the B register will contain the value of 0xC5.

Hope this helps.
 
Thanks for the info. The "MUL" instruction is the easy part. As I discard the the Product High and only use the product Low in Acc. The difficulty is that I need to convert the Binary in Acc to BCD and then to 7 segments form for displaying. I was almost there and I need a little more time for the final tunning. Looks like the easiest way to do BIN2BCD conversion is to use the "DIV" instruction.

I'll list it here ASAP when I have it ready.

Next I would include a 32x8 SRAM interface to the 8051 P0 & P2 using 74HC573. My 89S52 just arrived yesterday and I am busy working on the PCB.

Allen
 
Last edited:
I'm working up a code example for you right now. Is your segment display common cathode or common anode?
 
Thanks for helping. They are common cathode display as in the schematic attached.

Allen
 

Attachments

  • 73366d1367730307-learning-8051-8051-multiplication.png
    73366d1367730307-learning-8051-8051-multiplication.png
    200.1 KB · Views: 457
OK...this code does not contain anything that will drive the display yet. But what it does do is convert a value that is in the accumulator (ACC) to BCD, stores the 3 BCD bytes in registers HUND, TEN and ONE for the hundreds, tens and ones place digits, then converts them to LED display segment data and stores the segment pattern data for each digit in registers HUND, TEN and ONE. You will see that a look up table for the segment data was used to convert BCD to segment data, while the BCD values were used to call the appropriate table line for each digit's segment pattern data -

Code:
HUND		DATA		0x30			;hundreds place digit register
TEN		DATA		0x31			;tens place digit register
ONE		DATA		0x32			;ones place digit register

		org		0x0000			;reset vector
		ajmp		START			;jump to start of main code
		
		
;this space reserved for IRQ space


		org		0x0100			;main code starts past IRQ space
START:		mov		A,#0xFF			;place value to convert to BCD in A
		acall		BIN2BCD			;convert to BCD using this routine
		acall		LEDSegFetch		;convert to LED segment data using this routine
		ajmp		$			;replace this line with rest of your code
		
		
;*************************************************************************************************
;*************************************************************************************************
;**												**
;**				LED Segment Pattern Fetch					**
;**												**
;*************************************************************************************************
;*************************************************************************************************

;The following function takes the BCD values in registers HUND, TEN and ONE, and replaces
;them with table values that will light up the LED segments in the correct digit pattern
;that matches the replaced BCD value. After this code execution completes, the segment patterns
;will be stored in registers HUND, TEN and ONE -

;HUND - Contains segment data for hundreds place digit
;TEN - Contains segment data for tens place digit
;ONE - Contains segment data for ones place digit
		
LEDSegFetch:	mov		DPTR,#CCDisplay		;DPTR to first line of table
		mov		A,HUND			;fetch BCD value from HUND, place in accumulator
		movc		A,@A+DPTR		;fetch LED pattern from table line number in A
		mov		HUND,A			;store in HUND, replacing original BCD value
		mov		A,TEN			;fetch BCD value from TEN, place in accumulator
		movc		A,@A+DPTR		;fetch LED pattern from table line number in A
		mov		TEN,A			;store in TEN, replacing original BCD value
		mov		A,ONE			;fetch BCD value from ONE, place in accumulator
		movc		A,@A+DPTR		;fetch LED pattern from table line number in A
		mov		ONE,A			;store in ONE, replacing original BCD value
		ret					;done
				
;*************************************************************************************************
;*************************************************************************************************
;**												**
;**				8-bit Binary to BCD Converter					**
;**												**
;*************************************************************************************************
;*************************************************************************************************

;The following subroutine converts a single byte into 3 binary coded decimal bytes, and
;stores them into buffer registers in RAM.
		
BIN2BCD:	mov		B,#0x64			;divisor = 100
		div		AB			;divide value in accumulator by 100
		mov		HUND,A			;store integer in HUND register
		mov		A,B			;transfer remainder to accumulator
		mov		B,#0x0A			;divisor = 10
		div		AB			;divide value in accumulator by 10
		mov		TEN,A			;store integer in TEN register
		mov		ONE,B			;store remainder in ONE register
		ret					;done
		
;*************************************************************************************************
;*************************************************************************************************
;**												**
;**			Common Cathode 7-Segment LED Display Patterns				**
;**												**
;*************************************************************************************************
;*************************************************************************************************

;The following look up table holds the binary patterns to light up the segments of a
;7 segment LED display in a pattern which resembles digits.
		
CCDisplay:	db		00111111b		;0
		db		00000110b		;1
		db		01011011b		;2
		db		01001111b		;3
		db		01100110b		;4
		db		01101101b		;5
		db		01111101b		;6
		db		00000111b		;7
		db		01111111b		;8
		db		01101111b		;9

		end

Read through all of my comments and all should become clear. You can then transfer the data in registers HUND, TEN and ONE out to the port that drives the segment display to display the digits.
 
Last edited:
Wow, that was really quick. I inserted your codes for BIN2BCD into the IDE and assembled and tested it in ISIS and it worked well.

Thanks so much for the code and that was a very good style of writing programs which I should learn how to do it in future....

Anyway, the complete code is attached as below:

Code:
; Multiplication Table x2 to x9
; Max output is Decimal 99
;
; start date = 5.05.2013
; phase 1:  Make use of the 7 seg display routine
; phase 2:  initial trial 6.05.2013
; phase 3:  tested with John's "MULT codes" on 10.05.2013
;
org 00h
jmp start
org 1bh
jmp T1_isr
org 100h

sa equ 01h
sb equ 02h
sc equ 04h
sd equ 08h
se equ 10h
sf equ 20h
sg equ 40h
sdp equ 80h
;
d_flag bit 20h.0 ; disp flag
ovf bit 20h.2 ; overflow flag
button bit p2.7 ; button
sav_tl1 equ 30h ; temp for T1L
sav_th1 equ 31h ; temp for T1H
unit_dig equ 32h ; unit digit disp pattern
ten_dig equ 33h ; tenth digit disp pattern
m_cand equ 34h ; multiplicand
product equ 35h

unit equ R2 ; unit digit
tenth equ R3 ; tenth digit
hund equ R4
;
start:
mov SP,#0x80 ; move stack to upper RAM 0x80
call init
call display ; display them
nxt_num:
mov M_cand,#01 ; init multiplicand
but_loop:
call delay_200ms ; debounce button
jb button,but_loop ; button not pressed, loop
call mult ; do multiplication
call chk_product ; check if product out of range
jb ovf,nxt_num ; O.O.R. goto next number
call bin2BCD ; convert binary to BCD
call display ; convert to 7S pattern
call delay_200ms
call delay_200ms
jmp but_loop ; wait for next button press

mult: mov a,p2 ; get multiplier
anl a,#0x0f ; mask of upper nibble
mov b,m_cand ; get multiplicand
mul ab ; do the multiplication
mov product,a ; discard MSB byte
inc m_cand ; multiplicand +1
ret ; Acc is LSB of product
chk_product:
clr ovf ; clear overflow flag
mov a,product
cjne a,#99,$+3 ; Is product <100?
jc chk_exit ; yes
setb ovf ; set over flow flag
chk_exit: ret

BIN2BCD:
mov B,#0x64 ;divisor = 100
div AB ;divide value in accumulator by 100
mov HUND,A ;store integer in HUND register
mov A,B ;transfer remainder to accumulator
mov B,#0x0A ;divisor = 10
div AB ;divide value in accumulator by 10
mov TENTH,A ;store integer in TEN register
mov UNIT,B ;store remainder in ONE register
ret ;done

display:
mov a,r2
movc a,@a+dptr ; get number patter for display
mov unit_dig,a
mov a,r3
movc a,@a+dptr ; get number patter for display
mov ten_dig,a
ret

init:
mov DPTR,#TABLE ; dptr = 7 segment table address
mov unit, #0 ; unit digit
mov tenth, #0 ; tenth digit
mov TMOD,#0x10 ; set up timer1 for 10mS
mov TL1,#0xf0 ; Mode=1
mov TH1,#0xd8 ; 0xD8F0 is 10ms counting
mov sav_tl1,#0xf0
mov sav_th1,#0xd8
setb tr1 ; start timer1
setb et1 ; enable timer1 interrupt
setb ea ; enable global interrupt
ret

T1_isr:
PUSH ACC
PUSH PSW
PUSH DPH
PUSH DPL
clr TR1 ;stop timer1
mov TL1,sav_tl1 ;restore t1
mov TH1,sav_th1
setb TR1 ;start timer1
cpl d_flag ;toggle d_flag
jb d_flag,upper ;1=upper 0=lower
lower: clr p3.0 ;switch off both digits
clr p3.1
mov p1,unit_dig ;get new value
setb p3.0 ;turn on lower digit
sjmp isr_exit
upper: clr p3.0 ;switch off both digits
clr p3.1
mov p1,ten_dig ;get new value
setb p3.1 ;turn on upper digit
isr_exit:
clr TF1 ;clear timer1 flag
POP DPL
POP DPH
POP PSW
POP ACC
reti
;
; 7 segment pattern table
;
table: db sa+sb+sc+sd+se+sf ; 0
db sb+sc ; 1
db sa+sb+sg+se+sd ; 2
db sa+sb+sc+sd+sg ; 3
db sb+sc+sf+sg ; 4
db sa+sc+sd+sf+sg ; 5
db sa+sc+sd+se+sf+sg ; 6
db sa+sb+sc ; 7
db sa+sb+sc+sd+se+sf+sg ; 8
db sa+sb+sc+sd+sf+sg ; 9

delay_200ms:
mov r4,#0ffh
sjmp delay
delay_20ms:
mov r4,#1
loop0: djnz r4,loop0
ret
delay:
loop2: mov r5,#04Fh
loop1: djnz r5,loop1
djnz r4,loop1
ret

end

Allen
 

Attachments

  • mult_8952.PNG
    mult_8952.PNG
    61.9 KB · Views: 566
Last edited:
Attached is my 8051 development board with 32Kx8 SRAM 61256.

Where do the 3 wires on the SRAM ie. /OE, /WR & /CE go to?

Can I leave the /EA & /PSEN not connected? I know I need to put 10K pullup resistors on port 0. The schematic is done in a rush.

Allen
 

Attachments

  • my 8051 dev board.PNG
    my 8051 dev board.PNG
    73.4 KB · Views: 506
  • DSC00331_halved.JPG
    DSC00331_halved.JPG
    312.4 KB · Views: 565
Last edited:
Here's the bin2bcd subroutine I wrote:

Code:
conv2BCD:
;divider in Acc
mov b,#100 ; put divisor in B
div ab ; A/B  ans. in A, rem. in B
jz bcd2 ; If quotient = 0
nop ; discard 100's digit
bcd2: mov a,#10 ; put in new divisor
xch a,b ; divisor in B and remainder in Acc
div ab ; Acc/B
mov tenth,a ; get 10th digit
xch a,b ; Put B in ACc
mov unit,a ; get unit digit
ret

Allen
 
Last edited:
Status
Not open for further replies.

Latest threads

New Articles From Microcontroller Tips

Back
Top