0

I'm currently implementing a serial routine for an 8051 IC (specifically AT89C4051) and I don't have much stack space or memory left, and in order for me to achieve a decent baud rate on the serial port (38K or better), I need to make a high speed case construct since in my serial port interrupt routine, I'm building a packet and checking it for validity.

Assume we are in the serial interrupt and R0 is the address to the memory space in which data is to be received. Let's assume start address is 40h

So here we go with a bunch of compares:

Branching via many compares

serial:
    mov A,SBUF
    mov @R0,A
    mov A,R0
    anl A,#07h ;our packet is 8 bytes so get current packet # based on what we stored so far
    cjne A,#0h,nCheckMe ;this gets scanned if A=7... waste 2 clock cycles
        //We're working on first byte 
        ajmp theend
    nCheckMe:    
    cjne A,#1h,nCheckThem ;this gets scanned if A=7... waste 2 clock cycles
        //We're working on second byte 
        ajmp theend
    nCheckThem:    
    ...
    cjne A,#7h,nCheckEnd
        //We're working on last byte 
        ajmp theend
    nCheckEnd:    
    theend:
    inc R0
reti

The above code might be practical at first but as the current byte in the packet to work on increases, the routine runs 2 clock cycles slower each time because of the extra "cjne" instruction processing. For example, if we are on the 7th byte, then "cjne" would happen many times because it has to scan through each case which adds slowness.

Branching via jump

Now I thought of using just a jump but I can't figure out how to load DPTR at high speed because the interrupt can get called even when some other process is using the value of DPTR.

I thought of this code:

serial:
    mov A,SBUF
    mov @R0,A
    mov A,R0
    anl A,#07h ;our packet is 8 bytes so get current packet # based on what we stored so far
    swap A ;multiply A times 16 and
    rr A ;divide A by 2 so we get address times 8 since each block uses 8 bytes of code space.

    mov R3,DPH ;save DPTR high byte without breaking stack
    mov R6,DPL ;save DPTR low byte
    mov dptr,#table
    jmp @A+DPTR
    theend:
    mov DPL,R6 ;restore DPTR low byte
    mov DPH,R3 ;restore DPTR high byte
    inc R0     ;move on to next position
reti
table:
;insert 8 bytes worth of code for 1st case
;insert 8 bytes worth of code for 2nd case
;insert 8 bytes worth of code for 3rd case
...
;insert unlimited bytes worth of code for last case

In my code, R3 and R6 were free so I used them to store the old DPTR value but those mov instructions as well as loading the new DPTR value take 2 cycles each for 10 cycles total (including restoring old value).

Is there a faster way to process a case construct in 8051 assembly code so that my serial routine processes faster?

4

1 回答 1

1

如果可能,不要在 ISR 中运行逻辑。如果您坚持,您也许可以将 DPTR 分配给 ISR,并且只在非常短的正常代码中使用它,并且禁用中断。或者,可以使用 PUSH+RET 技巧。

这是一种链式方法,其中每个处理的字符只是设置下一步的地址。如果您可以确保这些步骤在同一个 256 字节块内,您只需要更新低字节。总开销为 8 个周期,但您还为算术节省了 4 个周期,因此赢得了 6 个周期。

.EQU PTR, 0x20  ; two bytes of SRAM

; Initialize PTR to address of step1 

serial:
    push PTR
    push PTR+1
    ret

step1:
    ; do stuff
    mov PTR, #low8(step2)
    reti

last_step:
    ; do stuff
    mov PTR, #low8(step1)
    reti
于 2018-03-29T23:41:47.890 回答