what is the best way to write a delay code in assembly

Discussion in 'Embedded Systems and Microcontrollers' started by walid el masry, Oct 3, 2009.

  1. walid el masry

    Thread Starter Active Member

    Mar 31, 2009
    132
    0
    i want help to write a delay code which don't interrupt the current process so it it will be accurate in it's timing and as an example i wrote a program to show the time LCD and every character i write on LCD i wait for a delay 5msec till it not busy and after writing all characters i wait for the 1 sec and recalculate or increment the time and i found that the 5msec and the time taken by increment code make delay in the whole process and the time showed on the LCD is delayed by 5mins every 4 or 5 hours

    i want a 1 sec delay code which works stand alone of the main code and when it reaches the 1 sec it makes interrupt and recalculate the time

    what is the best way to do that ?

    i write for microchip assembly for pic16f876
     
  2. mik3

    Senior Member

    Feb 4, 2008
    4,846
    63
    You can't make a delay by code and not affect the whole program. You can use an external timer of 1 second which will interrupt the uP externally.
     
  3. GetDeviceInfo

    Senior Member

    Jun 7, 2009
    1,571
    230
    set up a timer interrupt which vectors to an adding routine which upon a certain accumulated count, calls your update routine.

    If you have an RTC in circuit, it can perform the timing function for you.
     
  4. walid el masry

    Thread Starter Active Member

    Mar 31, 2009
    132
    0
    to mik3
    in this case i will cost my self unneeded cost and i think i can do it with the mcu as i will replay to
    GetDeviceInfo next

    to GetDeviceInfo
    in case i use 4 mhz crystal then 1 million instructions can be executed in 1sec so after make the prescaler calculation
    and assume that it is 1:256 it will be like this
    10^6 pulse = 1.0 sec
    256*x pulse = 1.0 sec

    x pulses = 10^6/256 = 3906.25 and after ignoring the fraction then 3906 pulses must counted to indicate a 1sec

    what did you mean with timer interrupt? do you mean the interrupt of the over flow or what ?
    and dose this applies the calculation above?
     
  5. walid el masry

    Thread Starter Active Member

    Mar 31, 2009
    132
    0
    i know RTC, a friend just told me about it today
     
  6. Tahmid

    Active Member

    Jul 2, 2008
    344
    25
    Hi,
    why you don't make a subroutine for 1Sec delay including call and return? You can make with timer or using variable counter and then verify the time with Stopwatch in MPLAB.
    Thanks.
     
  7. walid el masry

    Thread Starter Active Member

    Mar 31, 2009
    132
    0
    no i want the timer do work stand alone with the main code so that i can execute instruction in the same time the timer counts cause if i make a subroutine make the controller wait for the 1 sec and then continue the main program it will makes delay in the program timing cause i waited the 1 sec + the time of the execution of the main program
     
  8. mik3

    Senior Member

    Feb 4, 2008
    4,846
    63


    I am sorry I thought you you were using a microprocessor. In your case you can setup an interrupt which fires every second.
     
  9. GetDeviceInfo

    Senior Member

    Jun 7, 2009
    1,571
    230
    yes, interrupt on overflow. I'm not familiar with the pics so I can't tell you if they have timer prescale, but if any of the timers don't give you the full second count, you will have a few steps inbetween.

    If the time of day is of concern, go with the RTC
     
  10. THE_RB

    AAC Fanatic!

    Feb 11, 2008
    5,435
    1,305
    Have a look on this page, there are some PIC assembler routines that you can use to make accurate 1second timed period AND still use the micro for things as the same time;
    www.RomanBlack.com/one_sec.htm
     
  11. Tahmid

    Active Member

    Jul 2, 2008
    344
    25
    Hi,
    If you want to count for 1second while the program counter is running and you are doing some other work simultaneously, you would need to use one of the internal timer modules. Then you can wait for interrupt (if interrupt is enabled), else you can wait for the interrupt flag to be set high by "polling".
    E.g. If you are using a 4MHz clock, and using 16 bit timer1. Each instruction takes [1/(4MHz/4)] = 1uS. So you would need 1000*1000, ie, 1million instruction time for 1second. Set timer prescaler to 16. So, your timer would overflow every 65536*16 = 1048576 instructions. So you have a 1.048576 second delay. You can enable the interrupt bit and go to the interrupt vector and carry out your required tasks each 1.048576 seconds. (Make sure you clear the interrupt flag). If you need more accuracy, set TMR1H:TMR1L for 3036, resulting in (65536-3036)*16=62500*16 = 1 sec. Set timer value to 3036 every interrupt.
    Hope this helps.
     
  12. walid el masry

    Thread Starter Active Member

    Mar 31, 2009
    132
    0
    thnx THE_RB for the link but i'm afraid iam not 100% understand the sequence due to some syntax i don't know like

    tstf
    skpnz
    skpnc

    but i tried to write a code and that's it

    Code ( (Unknown Language)):
    1.  
    2.     LIST P=16F876
    3.     #INCLUDE<P16F876.INC>
    4.     __CONFIG _CP_OFF&_LVP_OFF&_BODEN_OFF&_PWRTE_ON&_WDT_OFF&_XT_OSC
    5.     CBLOCK 0x20
    6. TMR0_LOW    ;0X40
    7. TMR0_MID    ;0X42
    8. TMR0_HIGH    ;0X0F
    9.     ENDC
    10.     ORG 0X00
    11.     GOTO initialize
    12.     ORG 0X04
    13. ;interrupt_subrutine
    14.     BCF INTCON,GIE
    15. ;****************************************************************************************
    16. ;test 1sec delay code
    17. ;decrement code
    18.     DECF TMR0_LOW,1
    19.     MOVLW 0XFF
    20.     XORWF TMR0_LOW
    21.     BTFSS STATUS,Z    
    22.     GOTO out_interrupt;NO
    23.     DECF TMR0_MID,1;YES
    24.     MOVLW 0XFF
    25.     XORWF TMR0_MID
    26.     BTFSS STATUS,Z
    27.     GOTO out_interrupt;NO
    28.     MOVLW 0XFF;YES
    29.     XORWF TMR0_LOW
    30.     BTFSS STATUS,Z
    31.     GOTO out_interrupt;NO
    32.     DECF TMR0_MID,1;YES
    33. ;zeros test code
    34.     MOVLW 0X00
    35.     XORWF TMR0_LOW,W
    36.     BTFSS STATUS,Z
    37.     GOTO out_interrupt;NO
    38.     MOVLW 0X00;YES
    39.     XORWF TMR0_MID,W
    40.     BTFSS STATUS,Z
    41.     GOTO out_interrupt;NO
    42.     MOVLW 0X00;YES
    43.     XORWF TMR0_HIGH,W
    44.     BTFSS STATUS,Z
    45.     GOTO out_interrupt;NO
    46.     MOVLW 0X40;YES
    47.     MOVWF TMR0_LOW
    48.     MOVLW 0X42
    49.     MOVWF TMR0_MID
    50.     MOVLW 0X0F
    51.     MOVWF TMR0_HIGH
    52. ;****************************************************************************************
    53. ;code to be executed every 1sec
    54. ;bla
    55. ;bla
    56. ;bla    
    57. ;****************************************************************************************
    58. out_interrupt
    59.     BCF INTCON,T0IF
    60.     BSF INTCON,GIE
    61.     RETFIE
    62. initialize
    63. ;****************************************************************************************
    64. ;initialize_variables
    65.     MOVLW 0X40
    66.     MOVWF TMR0_LOW
    67.     MOVLW 0X42
    68.     MOVWF TMR0_MID
    69.     MOVLW 0X0F
    70.     MOVWF TMR0_HIGH
    71. ;****************************************************************************************
    72. ;initialize_INTCON
    73. ;GIE: Global Interrupt Enable bit                = 1 = Enables all unmasked interrupts
    74. ;PEIE: Peripheral Interrupt Enable bit            = 0 = Disables all peripheral interrupts
    75. ;TMR0IE: TMR0 Overflow Interrupt Enable bit    = 1 = Enables the TMR0 interrupt
    76. ;INTE: RB0/INT External Interrupt Enable bit    = 0 = Disables the RB0/INT external interrupt
    77. ;RBIE: RB Port Change Interrupt Enable bit    = 0 = Disables the RB port change interrupt
    78. ;TMR0IF: TMR0 Overflow Interrupt Flag bit    = 0 = TMR0 register did not overflow
    79. ;INTF: RB0/INT External Interrupt Flag bit        = 0 = The RB0/INT external interrupt did not occur
    80. ;RBIF: RB Port Change Interrupt Flag bit        = 0 = None of the RB7:RB4 pins have changed state
    81.     BANKSEL INTCON    ;goto OPTION_REG bank
    82.     MOVLW B'10100000'
    83.     MOVWF INTCON        ;load data into OPTION_REG
    84.     BANKSEL 0            ;back to normal bank 0
    85. ;****************************************************************************************
    86. ;initialize_OPTION_REG
    87. ;RBPU:        PORTB Pull-up Enable bit            =   1   = PORTB pull-ups are disabled
    88. ;INTEDG:    Interrupt Edge Select bit         =   0   = Interrupt on falling edge of RB0/INT pin
    89. ;T0CS:         TMR0 Clock Source Select bit    =   0   = Internal instruction cycle clock (CLKOUT)
    90. ;T0SE:        TMR0 Source Edge Select bit        =   0   = Increment on low-to-high transition on RA4/T0CKI pin
    91. ;PSA:        Prescaler Assignment bit        =   1   = Prescaler is assigned to the WDT
    92. ;PS2:PS0:    Prescaler Rate Select bits        = 000 = 1 : 128 for WDT Rate
    93.     BANKSEL OPTION_REG    ;goto OPTION_REG bank
    94.     MOVLW B'10001000'
    95.     MOVWF OPTION_REG    ; load data into OPTION_REG
    96.     BANKSEL 0                ; back to normal bank 0
    97. ;****************************************************************************************
    98. ;initialize_PORTB&TMR0
    99.     CLRF PORTB
    100.     MOVLW 0X00
    101.     MOVWF TRISB
    102.     MOVWF PORTB
    103.     MOVWF TMR0
    104. ;****************************************************************************************
    105. main
    106. ;bla
    107. ;bla
    108. ;bla
    109.     GOTO main
    110.     END
    111.  
    and for you Tahmid i think you are interested so let's study my code and i will study your idea also NOTE that used crystal in this example is 4mhz
     
  13. walid el masry

    Thread Starter Active Member

    Mar 31, 2009
    132
    0
    any help here?
     
  14. Markd77

    Senior Member

    Sep 7, 2009
    2,803
    594
    You will need to put this code in your interrupt routine which is in section 12 of the datasheet.
    It is needed because the interrupt routine can happen at any point in your main code and whatever is in the interrupt routine will change these registers which would give you very unpredictable results.
    Hope this gets you a bit closer.
    You can also write to the timer in the interrupt if you need to get closer to 1 second.

    code:
    MOVWF W_TEMP ;Copy W to TEMP register
    SWAPF STATUS,W ;Swap status to be saved into W
    CLRF STATUS ;bank 0, regardless of current bank, Clears IRP,RP1,RP0
    MOVWF STATUS_TEMP ;Save status to bank zero STATUS_TEMP register
    MOVF PCLATH, W ;Only required if using pages 1, 2 and/or 3
    MOVWF PCLATH_TEMP ;Save PCLATH into W
    CLRF PCLATH ;Page zero, regardless of current page
    :
    :(ISR) ;(Insert user code here)
    :
    MOVF PCLATH_TEMP, W ;Restore PCLATH
    MOVWF PCLATH ;Move W into PCLATH
    SWAPF STATUS_TEMP,W ;Swap STATUS_TEMP register into W
    ;(sets bank to original state)
    MOVWF STATUS ;Move W into STATUS register
    SWAPF W_TEMP,F ;Swap W_TEMP
    SWAPF W_TEMP,W ;Swap W_TEMP into W
    </code>
     
  15. Markd77

    Senior Member

    Sep 7, 2009
    2,803
    594
    If you used timer1 instead and set the timer1 prescaler to 8 you would get about 2 interrupts per second. If you set the high byte of timer 1 to 0Bh and the low byte to DB in the interrupt I think it would be very close to 2 interrupts per second. Still use the register saving code.
     
  16. walid el masry

    Thread Starter Active Member

    Mar 31, 2009
    132
    0
    good thinking Markd77 and i missed it :D

    really thnx guys and that what i wrote after deep thinking and it is working
    Code ( (Unknown Language)):
    1.  
    2.     list    p=16F84A
    3.     #include p16F84a.inc
    4.     ERRORLEVEL -302
    5.     __CONFIG   _CP_OFF & _WDT_OFF & _PWRTE_OFF & _XT_OSC
    6. ;************declare variables************
    7.     CBLOCK 0X0C
    8. status_temp:1
    9. w_temp:1
    10. ;-------------------------------------------
    11. cpt:1
    12.     ENDC
    13. ;************Reset vector************
    14.     ORG 0X00
    15.     GOTO INIT    
    16. ;*****************Interrupt vector**************************
    17.     ORG 0X04
    18. ;******************Interrup subrutine*****************
    19.     movwf   w_temp; save W register value
    20.     swapf    STATUS,w    
    21.     movwf    status_temp; save STATUS register value
    22.     BTFSS   INTCON,T0IF; is the interrupt accured is for TIMER0 or not ?
    23.     GOTO    ENDI;NO
    24.     BCF     INTCON,T0IF;YES
    25.     DECF    cpt,f; decrement the counter
    26.     btfss   STATUS,Z; test if  counter reaches the end - 1 second have just passed
    27.     GOTO    ENDI;NO
    28.     MOVLW   D'61';YES
    29.     MOVWF   cpt; re-intiate counter value
    30. ;***********************Code Executed every second*************************
    31. ;bla
    32. ;bla
    33. ;bla
    34. ;***********************every time when leaving interrupt do this*************************
    35. ENDI
    36.     swapf    status_temp,w; get the old value of the STATUS register    
    37.     movwf   STATUS
    38.     swapf       w_temp,f
    39.     swapf       w_temp,w; get the old value of the W register    
    40.     RETFIE; end of interrupt subrutine
    41. ;*****************Chip initialize**************************
    42. INIT
    43. ; initialize ports
    44.     BSF     STATUS,RP0; goto bank1
    45.     MOVLW   B'00000000'
    46.     MOVWF   TRISB      ; portb is output
    47. ;-------------------------------------------
    48. ; initialize special function registers
    49.     movlw    0x85  ; interruption sur timer0  
    50.     movwf    OPTION_REG
    51. ;RBPU        =1=     PORTB pull-ups are disabled
    52. ;INTEDG    =0=     Interrupt on falling edge of RB0/INT pin
    53. ;T0CS        =0=     Internal instruction cycle clock (CLKOUT)
    54. ;T0SE        =0=     Increment on low-to-high transition on RA4/T0CKI pin
    55. ;PSA        =0=     Prescaler is assigned to the Timer0 module
    56. ;PS2:PS0    =101=    1 : 64 for TMR0 Rate
    57. ;-------------------------------------------
    58.     movlw    0xA0
    59.     movwf    INTCON
    60. ;GIE    =1=     Enables all unmasked interrupts
    61. ;PEIE    =0=     Disables all peripheral interrupts
    62. ;TMR0IE    =1=     Enables the TMR0 interrupt
    63. ;INTE    =0=     Disables the RB0/INT external interrupt
    64. ;RBIE    =0=     Disables the RB port change interrupt
    65. ;TMR0IF    =1=     TMR0 register has overflowed (must be cleared in software)
    66. ;INTF    =0=     The RB0/INT external interrupt did not occur
    67. ;RBIF    =0=     None of the RB7:RB4 pins have changed state
    68. ;-------------------------------------------
    69. ;give ports and vaiables initiale values
    70.     BCF     STATUS,RP0; return to bank0
    71.     CLRF    PORTB
    72. ;-------------------------------------------
    73.     MOVLW   D'61'
    74.     MOVWF   cpt
    75. ;************************main program********************          
    76. START  
    77. ;bla
    78.  ;bla
    79.  ;bla
    80.     goto START
    81.     END
    82.  
    and it accepts this calculation
    256 x 64 = 16384 usec
    and
    1 sec = 1000000 usec
    and 1000000/16384 = 61.03515625 = 61 => D'61'

    almost equal 1 sec

    plz tell me if there any comments

     
Loading...