Writing a delay subroutine?

爷,独闯天下 提交于 2019-12-03 22:26:59

问题


I need to write a delay subroutine. It should delay about 1 second. It has to be for 8051 environment, DS89C430 micrcontroller ( 11.0592 MHz XTAL). How can I write this subroutine?

Delay1sec: ...
....
...
....
...

回答1:


To get an exact 1 second delay that also works during interrupts, you need to use a hardware timer, not a software timer. I would advise you to use one of the available on-board timers as suggested by Jerry Coffin.

Here's an approach involving a built-in timer and counting timer overflows. Since the running timer is updated every 12 clock cycles by default to maintain compatibility with the 8051, it will be updated 921,600 times per second. A bit of multiplication tells us that it takes 50 ms to count from 0 to 46080, which also tells us that we can start a 16-bit timer at 19456 and wait for it to overflow 20 times to delay for 1 second*.

The code could look something like this:

        CLR     T0M          ; set timer 0 to use a divide-by-12 of
                             ; the crystal frequency (default)

        MOV     R0,TMOD      ; set timer 0 to 16-bit mode without
        ORL     R0,#01h      ; affecting the setup of timer 1
        MOV     TMOD,R0

        LCALL   Delay1sec    ; call the delay subroutine

Delay1sec:
        MOV     R0,#20d      ; set loop count to 20

loop:   CLR     TR0          ; start each loop with the timer stopped
        CLR     TF0          ; and the overflow flag clear. setup
        MOV     TH0,#4Ch     ; timer 0 to overflow in 50 ms, start the
        MOV     TL0,#00h     ; timer, wait for overflow, then repeat
        SETB    TR0          ; until the loop count is exhausted
        JNB     TF0,$
        DJNZ    R0,loop
        RET   

Note: Overhead of instruction execution time excluded from example.
* How the math breaks down:
11059200 / 12 = 921600
0.05 * 921600 = 46080
65536 - 46080 = 19456 = 0x4C00


Software delay loops waste processor time and are disturbed by interrupts. That being said, you can do it the hardcoded way.

One approach involves knowing the number of clock cycles per machine cycle and the number of machine cycles various instructions take to execute. According to the data sheet, the DS89C430 usually uses one machine cycle for each instruction byte and requires one cycle for execution. The number of cycles for each instruction is provided in the Ultra-High-Speed Flash Microcontroller User's Guide.

Since your crystal frequency is 11.0592 MHz, your routine will need to delay for 11,059,200 clock cycles. This is often accomplished via nested loops of known length and then including any extra loop setup and possibly subroutine call and return instructions*.

The function could look something like this:

Delay1sec:                   ; <------------------------------+
;       LCALL   Delay1sec    ; 3 cycles                       |
        MOV     R2,#42d      ; 2 cycles                       |
        MOV     R1,#00d      ; 2 cycles                       |
        MOV     R0,#00d      ; 2 cycles                       |
loop:   DJNZ    R0,loop      ; 4 cycles <-- l1 <- l2 <- l3    Delay1sec
        DJNZ    R1,loop      ; 4 cycles <---------+     |     |
        DJNZ    R2,loop      ; 4 cycles <---------------+     |
        RET                  ; 3 cycles <---------------------+

Let's see how the math breaks down**:

l1 = 4 * 256 = 1024 cycles
l2 = (1024 + 4) * 256 = 263168 cycles
l3 = (263168 + 4) * 42 = 11053224 cycles
Delay1sec = 11072668 + 3 + 2 + 2 + 2 + 3 = 11053236 cycles

11053236 cycles * 1/11059200 seconds/cycle = 999.461 ms

* The subroutine call and return instructions may be omitted as necessary.
** I used Microsoft Excel to assist with calculations related to determining loop counters.




回答2:


This microcontroller has three on-board timers (see section 11 of the User's Manual) connected to the system clock (divided by 12), so it's a matter of programming them to generate an interrupt when the time expires. Since the divided input is just under 1 MHz, and the largest counter is 16 bits, you'll need to count 14 interrupts to reach one second (at least if I've done the math correctly).




回答3:


A) Reference a hardware timer.

B) Reference a CPU timer. Some processors have a very wide timer, i.e. 64-bit wide, that runs at the clock tick.

C) Software Loop. For best results, the code and all data should reside in internal memory that has predictable timing. Running from SDRAM can cause timing problems.

You don't have to count assembly cycles to do it. Instead you can "draw" a pulse on a pin (pull high before the loop, pull low after the loop), measure pulse width using a logic anlayzer, then change loop count to tweak your timings. For best results, you should compensate against the external CPU clock/crystal, by using a frequency counter to measure it, then compensate for being away from center frequency, because most cheap crystals aren't dead-on.

You could self-calibrate by using a timer to calculate the loop timing.



来源:https://stackoverflow.com/questions/5134836/writing-a-delay-subroutine

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!