I'd like comments on this simple delay routine. Instead of loops I've used Timer1 with an 8MHz crystal and a 1:8 prescaler. It should increment the LSB every 4us and the TMR1H register every 1.024ms. My math could be off (and my thinking too ) Comments?
Code:
TMRDLY ;*** using TMR1 as a delay for LCD setup call with W
;*** 1.024ms per TMR1H, Delay in ms = 255 - (W * 1.024)
bcf STATUS, RP0 ;B0
clrf TMR1L ;B0 added as per eblc1388 suggestion.
movwf TMR1H ;B0 TMR1H = W (976us)
bcf PIR1, TMR1IF ;B0 clear the interrupt flag
btfss PIR1, TMR1IF ;B0 test TMR1IF for overflow
goto $-1 ; wait for TMR1IF (overflow)
return
I haven't checked your math and I assume you've intialized Timer1 first, but I've used this method before. It allows for accurate timing when you have an ISR running in the background.
Timer1 trips its flag when the whole 16-bit word overflows pass 0xFFFF, not just the high side byte TMR1H. You have to load TMR1L and TMR1H with the correct values which intales performing subtraction for the CALL, RETURN, loading TMR1L TMR1H and general code delays. When you finally find the correct values to plug into the timers, you will have to do it all over again if you want a delay other than this fixed time. To load them with any value other than 1 to indicate 1 millisecond goes against logic.
TMR1H= 1, TMR1L= 2 : so the delay = 257 milliseconds is logical.
TMR1H= 153, TMR1L= 231: so the delay = desired time not representative of the variables is illogical .
The LCD does not have to be precise down to a few microseconds. Using a general millisecond delay routine will work for this and all your other code. Now though the timer is used here, this is still a looping routine. Detecting if a register hits zero or if a flag bit gets set never the less is still a loop once you use Goto $ - 1. Using a timer as a true interrupt is what will qualify the delay as non looping.
Timers are too precious for grunt work like general delays.
I agree, Timers are far too few to be used this way. The exception is using it to initialize delays on a cold start. Once the LCD is setup I'll switch to reading the busy flag instead.
What PIC are you using? Unless you get lucky with the pre and postscaler settings on a timer that will allow for properly timed periodic interrupts without preloading the timer registers, you are pretty much stuck using a timer with a period register. For most PICs, Timer 2 is the one with that functionality. The dsPIC and PIC24 line have period registers on every timer.
Is Timer 2 available in your application or is it tied up with other resources?
Seemed pretty straight forward, no variables required, easy to solve the timing loop, irq friendly (if you want to do it that way). I call it four times to get the 4 bit mode setup.
Got a decent decfsz version? Here's the LCD init routine
Code:
LCD_INIT ;send 0x03 three times to enter 4 bit mode
bcf STATUS, RP0 ;B0
bsf LCD_E ;B0 enable bit high
movlw 0x03 ;B0 send 03 to PORTA
movwf PORTA ;B0
movlw .240 ; 15ms delay
bcf LCD_E ;B0 latch data to LCD PORTA
call TMRDLY
bsf LCD_E ;B0 enable bit high
movlw 0x03 ;B0 send 03 to PORTA
movwf PORTA ;B0
movlw .254 ; .5ms delay
bcf LCD_E ;B0 latch data to LCD PORTA
call TMRDLY
bsf LCD_E ;B0 enable bit high
movlw 0x03 ;B0 send 03 to PORTA
movwf PORTA ;B0
movlw .250 ; 5ms delay
bcf LCD_E ;B0 latch data to LCD PORTA
call TMRDLY
bsf LCD_E ;B0 enable bit high
movlw 0x02 ;B0 send 02 (4 bit mode set)
movwf PORTA ;B0
movlw .254 ; .5 ms delay
bcf LCD_E ;B0 latch data to LCD PORTA
call TMRDLY
return
Yes, use the delay code generator on the PICList, its nice and easy, it's used in my tutorials. You might also consider looking at my tutorial LCD code, here's the initialisation section for that.
Those routines reset the controller in software. If the electrical requirements are met, the inizialization is performed by the built-in reset circuitry.
Those routines reset the controller in software. If the electrical requirements are met, the inizialization is performed by the built-in reset circuitry.
LCD_INIT ;send 0x03 three times to enter 4 bit mode
movlw .220 ; 15ms (255 - 15/0.512) delay (before LCD is ready)
call TMRDLY ; program will halt till TMR1 overflows
movlw 0x03 ;B0 W = 3
movwf count ;B0 count = 3
movwf PORTA ;B0 PORTA = 3 (RS = 0)
LCD_loop
bsf LCD_E ;B0 enable bit high
movlw .245 ; 5ms delay
bcf LCD_E ;B0 latch data to LCD PORTA
call TMRDLY
decfsz count
goto LCD_loop
return
It appears the 3x 0x3 gets the LCD setup. I usually send a 0x28 (4bit 2line) using a more generic byte to nibble routine (working on the BF code now) and no longer use the delay code.
It's called "Initialization by instructions" in the datasheet. You can always use these instructions to initialize the LCD, but they're not required if the power supply conditions are met.