;--------------------------------------------------------------- ; Program for displaying battery voltage, current, and amp-hours ; in an ebike application. LCD Module is attached to PORTC, with ; the following connections: ; RC5 - EN of LCD ; RC4 - RS of LCD ; RC3:0 - D7:4 of LCD ; RA0 - Battery Voltage, seen through a 20:1 resistor divider ; RA1 - Positive Battery Current, 0.1 Volt / Amp ; RA2 - Negative Battery Current, -0.111 Volt / Amp ; RA3 - Active Low Reset Button (clears Ah registers) ; ; Assumes an 8MHz crystal ; ; March 15th 2005 Justin Lemire-Elmore ;--------------------------------------------------------------- list p=16f684 ; list directive to define processor #include ; processor specific variable definitions __CONFIG _BOD_ON & _PWRTE_ON & _WDT_OFF & _HS_OSC & _MCLRE_OFF & _FCMEN_OFF & _IESO_OFF cblock 0x20 AcqDel ; Delay for AD charge holding capacitor NumH,NumL ; 16 bit Number to convert to to BCD TenK,Thou,Hund,Tens,Ones ; BCD Result LCDAdd ; Curser Address of LCD Screen LCDByte ; Full Byte to transmit to LCD Display nibToSend ; Nibble for 4-bit LCD Transmission hiByte ; Used by LCD Routine loByte ; Used by LCD Routine d1, d2 ; Delays during LDC output a2,a1,b2,b1 ; Multiply Arguments res4,res3,res2,res1 ; 16x16 Multiply Result Ah4,Ah3,Ah2,Ah1,Ah0 ; Binary format of accumulated amp-hours Tmp2,Tmp1,Tmp0 ; Temporary registers Flags ; Flags for whatever, like positive or negative currents endc #define ADVolts b'10000001' ; ADCON0 for AN0, Batt Voltage, Vdd #define ADPAmps b'10000101' ; ADCON0 for AN1, Current Sense, Vdd #define ADNAmps b'10001001' ; ADCON0 for AN2, Negative Current Sense, Vdd #define LCDEnable PORTC,5 ; The LCD enable pin #define LCDRS PORTC,4 ; The LCD R/S pin #define ResetPin PORTA,3 ; Active Low Push-Button for resetting Ah data #define NegAmpsFlag Flags,0 org 0 Init clrf PORTA ; clear port I/O clrf PORTC movlw b'00000111' ; Set RC<4,1:0> to movwf CMCON0 ; digital I/O bsf STATUS,RP0 ; Bank1 clrf TRISC ; All Outputs for LCD module movlw b'00000111' ; Sets AN0-2 as analog inputs movwf ANSEL movlw b'00100000' ; AD converter on Tosc/32 movwf ADCON1 bcf STATUS,RP0 ; Bank0 movlw b'00110001' ; Timer1 on internal clk with 1:8 prescale movwf T1CON call LONGDLY call LONGDLY ; Set Up the LCD Screen call LCDFUN ; sets the LCD's function to 4 bit, 2 line, 5x7 font call LONGDLY call LCDDISP ; Turns on the display and cursor call LONGDLY call LCDENT ; sets auto increment right after write (like a typewriter) call LCDCLR call LONGDLY call LONGDLY movlw "V" ; Prepare Line1 for heading Data call LCDCHAR movlw "O" call LCDCHAR movlw "L" call LCDCHAR movlw "T" call LCDCHAR movlw "S" call LCDCHAR movlw d'6' movwf LCDAdd call LCDADD movlw "A" call LCDCHAR movlw "M" call LCDCHAR movlw "P" call LCDCHAR movlw "S" call LCDCHAR movlw d'12' movwf LCDAdd call LCDADD movlw "A" call LCDCHAR movlw "-" call LCDCHAR movlw "H" call LCDCHAR movlw "r" call LCDCHAR bsf STATUS,RP0 ; Restore the Ah data to the values clrf EEADR ; burnt in eeprom on last powerdown bsf EECON1,RD ; EE Read movf EEDAT,w ; bcf STATUS,RP0 movwf Ah0 ;Restore Ah0 bsf STATUS,RP0 incf EEADR,f bsf EECON1,RD ;EE Read movf EEDAT,w bcf STATUS,RP0 movwf Ah1 ;Restore Ah1 bsf STATUS,RP0 incf EEADR,f bsf EECON1,RD ;EE Read movf EEDAT,w bcf STATUS,RP0 movwf Ah2 ;Restore Ah2 bsf STATUS,RP0 incf EEADR,f bsf EECON1,RD ;EE Read movf EEDAT,w bcf STATUS,RP0 movwf Ah3 ;Restore Ah3 bsf STATUS,RP0 incf EEADR,f bsf EECON1,RD ;EE Read movf EEDAT,w bcf STATUS,RP0 movwf Ah4 ;Restore Ah4 goto Main ;----------------------------------------------------------- ; Main program. Sends voltage, current, and temperature data ; twice a second. Uses TMR1 to set the 0.5 second time base. ; Each line goes "VAT HHH HHH HHH ; where each H is a hex value from the conversion ;----------------------------------------------------------- Main movlw h'DD' movwf TMR1L ; 0.5 Sec = 62500 (500000/8) TMR1 clocks movlw h'B' ; 65536 -62500 = h'BDC'(+1 = BDD) movwf TMR1H bcf PIR1,TMR1IF ; Clear the overflow flag call LCDLINE2 ; Set LCD Curser to begining of line2 ; -------- Get and Display battery voltage, power down if necessary ----------- call ReadVolts ; Sample the battery pack voltage call CheckPower ; If volts <12, save Ah to eeprom before loosing pwr call BinToBCD ; Convert result to binary coded decimal movlw d'48' ; ASCII for "0" subwf Hund,w ; If Z set, we have leading zero to suppress movlw " " ; Space for suppression skpz movf Hund,w ; If not Z, then display thou's digit call LCDCHAR ; Display 10's of voltage or space on LCD movf Tens,w call LCDCHAR ; Display Units of voltage on LCD movlw "." ; call LCDCHAR ; Display Decimal on LCD movf Ones,w call LCDCHAR ; 1/10th's of voltage on LCD movlw " " ; Space on LCD Screen call LCDCHAR ; -------- Get and Display battery current ----------- call ReadAmps ; Sample the battery pack current call BinToBCD ; Convert scaled result to binary coded decimal movlw "-" btfss NegAmpsFlag ; Place minus sign if amps is negative movlw " " ; blank space if positive call LCDCHAR ; Display "-" or " " movlw d'48' ; ASCII for "0" subwf Thou,w ; If Z set, we have leading zero to suppress movlw " " ; Space for suppression skpz movf Thou,w call LCDCHAR ; Display 10's of amps or space on LCD movf Hund,w call LCDCHAR ; Display Units of amperage on LCD movlw "." ; call LCDCHAR ; Display Decimal on LCD movf Tens,w call LCDCHAR ; 1/10th's of amps on LCD movlw " " ; Space on LCD Screen call LCDCHAR ; -------- Compute and Display Total Amp-Hours ----------- call CalcAh ; CalcAh assumes NumH:NumL still contains 1/100th of amp call BinToBCD ; Convert into decimal format for display movlw d'48' ; ASCII for "0" subwf Thou,w ; If Z set, we have leading zero to suppress movlw " " ; Space for suppression skpz movf Thou,w call LCDCHAR ; Displays 10's of A movf Hund,w call LCDCHAR ; Display 1's of Ah movlw "." call LCDCHAR movf Tens,w ; Display Decimal Pt call LCDCHAR ; Display 1/10th's of an Ah movf Ones,w call LCDCHAR ; Display 1/100th's of an Ah ; -------- Check if Reset Button is being pressed ----------- btfss ResetPin call ResetAh WaitLoop clrwdt btfss PIR1,TMR1IF ; Wait until 0.25s passed goto WaitLoop goto Main ;--------------------------------------------------------- ; -Hex2Ascii- Converts the lower 4 bits of the working ; register into the 8 bit ASCII representation, 0..1,A..F ; Result is returned in the working register. ;--------------------------------------------------------- Hex2Ascii andlw b'00001111' ; Mask lower 4 bits addwf PCL,f ; Jump to appropriate result retlw d'48' ; ASCII '0' retlw d'49' ; ASCII '1' retlw d'50' ; '' retlw d'51' ; '' retlw d'52' ; '' retlw d'53' ; '' retlw d'54' ; '' retlw d'55' ; '' retlw d'56' ; '' retlw d'57' ; ASCII '9' retlw d'65' ; ASCII 'A' retlw d'66' ; '' retlw d'67' ; '' retlw d'68' ; '' retlw d'69' ; '' retlw d'70' ; ASCII 'F' ;--------------------------------------------------------- ; -AcqDelay- Gives a ~60uS delay to allow the sample and hold ; capacitor to fully charge before an AtoD conversion ;--------------------------------------------------------- AcqDelay movlw d'40' movwf AcqDel AcqLoop decfsz AcqDel,f goto AcqLoop return ;--------------------------------------------------------- ; -ReadVolts- Reads the to battery voltage and scales it ; assuming that there is a 1:20 voltage divider and a 0.6V ; diode drop. So full scale = 5V*(20+1)= 105V ;--------------------------------------------------------- ReadVolts movlw ADVolts movwf ADCON0 call AcqDelay ; Acquisition delay to charge internal capacitance bsf ADCON0,GO ; Start AtoD conversion of battery voltage ADLoop1 btfsc ADCON0,GO goto ADLoop1 movf ADRESH,w ; movwf a2 bsf STATUS,RP0 ; movf ADRESL,w ; Copy ADResult into a2:a1 bcf STATUS,RP0 ; Bank0 addlw d'6' ; Add diode drop, 6 ADC units ~ 0.6V movwf a1 skpnc incf a2,f ; movlw 0x01 movwf b2 movlw 0x07 ; 0x0107 = 263 movwf b1 call Multiply ; 263 * 1023 = 269049 movf res3,w ; movwf NumH ; 269049 / 256 = 1050 or 105 V movf res2,w movwf NumL return ;--------------------------------------------------------- ; -CheckPower- Checks the battery voltage to see if the ; system power was removed. If V<15V, this is assumed the ; case. Power supply capacitor must be large enough that ; PIC stays powered up for at least 50 mS, so 50-100uF needed ; Assumes NumH:NumL contain voltage in 1/10th's of a volt. ; If power <15V, data is saved to eeprom and goodby msg ; displayed. ;--------------------------------------------------------- CheckPower movf NumH,f skpz return ; If NumH>0, then volts > 25.6 and we're good movlw d'150' ; 15 amps subwf NumL,w skpnc return ; If NumL > 150 and NumH =0, we're also good PowerDown movf Ah0,w bsf STATUS,RP0 ; Bank1 movwf EEDAT clrf EEADR bcf STATUS,RP0 ; Bank0 call EEWrite ; Save Byte0 of Amp-Hours to EEPROM movf Ah1,w bsf STATUS,RP0 ; Bank1 movwf EEDAT incf EEADR,f bcf STATUS,RP0 ; Bank0 call EEWrite ; Save Byte1 of Amp-Hours to EEPROM movf Ah2,w bsf STATUS,RP0 ; Bank1 movwf EEDAT incf EEADR,f bcf STATUS,RP0 ; Bank0 call EEWrite ; Save Byte2 of Amp-Hours to EEPROM movf Ah3,w bsf STATUS,RP0 ; Bank1 movwf EEDAT incf EEADR,f bcf STATUS,RP0 ; Bank0 call EEWrite ; Save Byte3 of Amp-Hours to EEPROM movf Ah4,w bsf STATUS,RP0 ; Bank1 movwf EEDAT incf EEADR,f bcf STATUS,RP0 ; Bank0 call EEWrite ; Save Byte4 of Amp-Hours to EEPROM call LCDLINE2 ; Set LCD Curser to begining of line2 movlw "P" call LCDCHAR movlw "O" call LCDCHAR movlw "W" call LCDCHAR movlw "E" call LCDCHAR movlw "R" call LCDCHAR movlw " " call LCDCHAR movlw "D" call LCDCHAR movlw "O" call LCDCHAR movlw "W" call LCDCHAR movlw "N" call LCDCHAR KillTime goto KillTime ; Wait in futile loop until Power Supply ; drops to the point the pic can't run. ;--------------------------------------------------------- ; -EEWrite- Burns the data in EEDAT to address EEADR in ; the EEPROM. ;--------------------------------------------------------- EEWrite bsf STATUS,RP0 ;Bank 1 bsf EECON1,WREN ;Enable write movlw 0x55 ;Unlock write movwf EECON2 ; movlw 0xAA ; movwf EECON2 ; bsf EECON1,WR ;Start the write EELOOP btfsc EECON1,WR goto EELOOP ; Poll until write is complete bcf EECON1,WREN ; Disable write bcf STATUS,RP0 ; Bank0 return ; Burning of eeprom is done ;--------------------------------------------------------- ; -ReadAmps- Reads the to battery current and scales the ; result so that the values NumH:NumL are in 100th's of ; an amp. Assumes 50 amps = full scale of ADC for positive ; and 45.45 amps = full scale for negative (gains of 10 and ; 11 respectively). Uses the larger of negative or positive ; amps. ;--------------------------------------------------------- ReadAmps nop ; Not sure if current will be positive or negative ReadPosAmps movlw ADPAmps ; Check for positive current first movwf ADCON0 call AcqDelay ; Acquisition delay to charge internal capacitance bsf ADCON0,GO ; Start AtoD conversion of battery current ADLoop2 btfsc ADCON0,GO goto ADLoop2 movf ADRESH,w ; movwf a2 ; Argument for Multiply Routine bsf STATUS,RP0 ; Bank1 movf ADRESL,w ; Copy the lower 8 bits of conversion bcf STATUS,RP0 ; Bank0 movwf a1 ; Lower Arg of Multiply Routine movf a2,f skpz ; If high byte >0, amps is clearly positive goto PosAmps ReadNegAmps movlw ADNAmps ; Measure the negative current, movwf ADCON0 call AcqDelay ; Acquisition delay to charge internal capacitance bsf ADCON0,GO ; Start AtoD temperature conversion ADLoop3 btfsc ADCON0,GO goto ADLoop3 movf ADRESH,w ; skpz ; If high byte >0, no need to compare low byte goto NegCont bsf STATUS,RP0 ; Bank1 movf ADRESL,w ; Copy the lower 8 bits of conversion bcf STATUS,RP0 ; Bank0 subwf a1,w ; = lower byte of [PosAmps - Neg Amps] skpnc ; goto PosAmps NegCont movf ADRESH,w movwf a2 ; ADRESH is already in 'w' bsf STATUS,RP0 ; Bank1 movf ADRESL,w ; Copy the lower 8 bits of conversion bcf STATUS,RP0 ; Bank0 movwf a1 ; Move to lower arg of multiply routine NegAmps movlw 0x04 movwf b2 movlw 0xA7 ; 0x04a7 = 1191 movwf b1 call Multiply ; 1191 * 1023 (full scale ADC) = 1218393 movf res3,w ; Move only the middle two bytes, ignorning movwf NumH ; lower byte equivalent to divide by 256 movf res2,w movwf NumL ; 1218393/256 = 4759 which will display as -47.5 amps bsf NegAmpsFlag ; Indicate negative currents flag return PosAmps movlw 0x04 movwf b2 movlw 0xE4 ; 0x04E4 = 1252 movwf b1 call Multiply ; 1252 * 1023 (full scale ADC) = 1280796 movf res3,w ; Move only the middle two bytes, ignorning movwf NumH ; lower byte equivalent to divide by 256 movf res2,w movwf NumL ; 1280796/256 = 5003 which will display as 50.0 amps bcf NegAmpsFlag ; Clear flag to indicate + currents return ;--------------------------------------------------------- ; -CalcAh- Adds the value in NumH:NumL directly to the amp- ; hour counter Ah4:Ah0. Assumes that NumH:L is in 1/100th ; of an amp, ie. 1 amp = 100, sampling is 4 times a second, so ; 1 Ah will accumulate to (3600*4*100) = 1440000 = 15F900 ; Scales results and places them in NumH:NumL such that ; the value in NumH:NumL is in 1/100th's of an amp-hour ;--------------------------------------------------------- CalcAh btfss NegAmpsFlag goto AddAmps goto SubAmps AddAmps movf NumL,w ; Add the results in Num to Ah addwf Ah0,f ; Ah0 = Ah0 +NumL movf NumH,w btfsc STATUS,C incfsz NumH,w addwf Ah1,f ; Ah1 = Ah1 +NumH + C movlw d'0' btfsc STATUS,C movlw d'1' addwf Ah2,f movlw d'0' btfsc STATUS,C movlw d'1' addwf Ah3,f movlw d'0' btfsc STATUS,C movlw d'1' addwf Ah4,f goto ScaleAh SubAmps movf NumL,w ; Subtracts NumH:L from Ah4:0 subwf Ah0,f movf NumH,w btfss STATUS,C incfsz NumH,w subwf Ah1,f ; Ah1:0 = Ah1:0 - NumH:L with valid carry movlw d'0' btfss STATUS,C movlw d'1' subwf Ah2,f movlw d'0' btfss STATUS,C movlw d'1' subwf Ah3,f movlw d'0' btfss STATUS,C movlw d'1' subwf Ah4,f ; Ah4:0 = Ah4:0 - NumH:L with valid carry btfsc STATUS,C goto ScaleAh clrf Ah4 ; If we're here, it means we're in clrf Ah3 ; negative amp-hour territory. Just clrf Ah2 ; reset it to 0 amp-hours in this generation clrf Ah1 ; of code. Later should upgrade to display clrf Ah0 ; negative amp-hours goto ScaleAh ScaleAh movf Ah3,w ; Need to roll Ah3:1 data movwf Tmp0 ; for extracting optimum 16 bits movf Ah2,w ; of the accumulated amp-hours movwf a2 ; to use in the multiply routine movf Ah1,w movwf a1 movlw d'4' ; Roll Ah3:1 data 4 times for /16 movwf Tmp1 ; Tmp1 as loop counter RollLoop rrf Tmp0,f ; After this division, a1:a0 contains rrf a2,f ; the amp-hours such that 1Ah corresponds to rrf a1,f ; 1440000/256/16 = 351.5625 decfsz Tmp1,f goto RollLoop movlw 0x48 ; 0x48D1 = d'18641' movwf b2 movlw 0xD1 movwf b1 call Multiply ; 18641 * 351.5625 = 6553476 in Res3:Res0 movf res4,w movwf NumH ; Using upper 2 bytes like dividing by 2^16 movf res3,w ; 6553476 / 2^16 = 100, which displays as 1.00 Ah movwf NumL return ;--------------------------------------------------------- ; -ResetAh- Checks that the user is indeed holding the ; reset button. If so, Ah data in RAM is reset to zero ;--------------------------------------------------------- ResetAh movlw d'15' movwf Tmp2 ResetLoop call LONGDLY decfsz Tmp2 goto ResetLoop btfsc ResetPin ; If user not holding button after return ; this duration, don't reset clrf Ah0 clrf Ah1 clrf Ah2 clrf Ah3 clrf Ah4 call LCDLINE2 ; Set LCD Curser to begining of line2 movlw " " ; Then indicate data has been reset call LCDCHAR movlw " " call LCDCHAR movlw " " call LCDCHAR movlw " " call LCDCHAR movlw " " call LCDCHAR movlw "R" call LCDCHAR movlw "E" call LCDCHAR movlw "S" call LCDCHAR movlw "E" call LCDCHAR movlw "T" call LCDCHAR movlw " " call LCDCHAR movlw " " call LCDCHAR movlw " " call LCDCHAR movlw " " call LCDCHAR movlw " " call LCDCHAR movlw " " call LCDCHAR ResetLoop2 btfss ResetPin ; Hold "RESET" on screen until button released goto ResetLoop2 return ;--------------------------------------------------------- ; BinToBCD. Code copied from sample by John Payson. ; http://www.dattalo.com/technical/software/pic/bcd.txt ; Enter with 16-bit binary number in NumH:NumL. ; Exits with BCD ASCII equivalent in TenK:Thou:Hund:Tens:Ones. ;--------------------------------------------------------- BinToBCD ; Takes number in NumH:NumL ; Returns decimal in ; TenK:Thou:Hund:Tens:Ones swapf NumH,w andlw 0x0F ;*** PERSONALLY, I'D REPLACE THESE 2 addlw 0xF0 ;*** LINES WITH "IORLW 11110000B" -AW movwf Thou addwf Thou,f addlw 0xE2 movwf Hund addlw 0x32 movwf Ones movf NumH,w andlw 0x0F addwf Hund,f addwf Hund,f addwf Ones,f addlw 0xE9 movwf Tens addwf Tens,f addwf Tens,f swapf NumL,w andlw 0x0F addwf Tens,f addwf Ones,f rlf Tens,f rlf Ones,f comf Ones,f rlf Ones,f movf NumL,w andlw 0x0F addwf Ones,f rlf Thou,f movlw 0x07 movwf TenK ; At this point, the original number is ; equal to TenK*10000+Thou*1000+Hund*100+Tens*10+Ones ; if those entities are regarded as two's compliment ; binary. To be precise, all of them are negative ; except TenK. Now the number needs to be normal- ; ized, but this can all be done with simple byte ; arithmetic. movlw 0x0A ; Ten Lb1: addwf Ones,f decf Tens,f btfss 3,0 goto Lb1 Lb2: addwf Tens,f decf Hund,f btfss 3,0 goto Lb2 Lb3: addwf Hund,f decf Thou,f btfss 3,0 goto Lb3 Lb4: addwf Thou,f decf TenK,f btfss 3,0 goto Lb4 ; -- Convert BCD Result into ASCII format ----- movf TenK,w call Hex2Ascii movwf TenK movf Thou,w call Hex2Ascii movwf Thou movf Hund,w call Hex2Ascii movwf Hund movf Tens,w call Hex2Ascii movwf Tens movf Ones,w call Hex2Ascii movwf Ones return ;********************************************************************** ; -Multiply- Computes the unsigned product of a2:a1 with b2:b1 leaving ; the result in res4:res3:res2:res1 ; Pulled from PICLIST webiste ; http://www.piclist.com/techref/microchip/math/mul/16x16umalin.htm ;********************************************************************** Multiply clrf res4 clrf res3 clrf res2 movlw 0x80 movwf res1 nextbit rrf a2,f rrf a1,f btfss STATUS,C goto nobit_l movf b1,w addwf res2,f movf b2, w btfsc STATUS,C incfsz b2, w addwf res3, f btfsc STATUS,C incf res4, f bcf STATUS,C nobit_l btfss a1, 7 goto nobit_h movf b1,w addwf res3,f movf b2, w btfsc STATUS,C incfsz b2, w addwf res4, f nobit_h rrf res4,f rrf res3,f rrf res2,f rrf res1,f btfss STATUS,C goto nextbit return ;********************************************************************** ; LCD Routine, modified and optimized from code found on the NET ; Useful functions to call include LCDCHAR to output a character stored ; in LCDByte to the module ; LCDADD sets the curser to the position in LCDAdd ;********************************************************************** LCDADD ; makes LCDAdd the current LCD Address bcf LCDRS movf LCDAdd,w movwf LCDByte bsf LCDByte, 7 call LCDBYTE bsf LCDRS return LCDENT ; Entry Mode set - currently set to move cursor to right after each write bcf LCDRS movlw b'00000110' movwf LCDByte call LCDBYTE bsf LCDRS return LCDDISP ; turns on display and cursor bcf LCDRS movlw b'00001110' movwf LCDByte call LCDBYTE bsf LCDRS return LCDFUN ; sets up the LCD function byte, for 4 bit control, 2 lines, standard font movlw d'2' movwf nibToSend bcf LCDRS ; we're sending a command, so R/S must be lo call SENDNIB ; due to 4 bit operation, we have to resend the first nibble call SHORTDLY movlw b'00101000' movwf LCDByte call LCDBYTE bsf LCDRS return LCDCLR ; clears the entire display clrf LCDAdd movlw d'1' movwf LCDByte bcf LCDRS ; 'cause we are doing a command, set the R/S line lo call LCDBYTE ; writes LCDByte to the LCD call LONGDLY ; Clearing the LCD takes ages, so a larger delay is needed bsf LCDRS ; set the R/S line, ready for characters return LCDCHAR ;Updates curser address it seems to do word wrapping. movwf LCDByte movf LCDAdd,w call LCDBYTE incf LCDAdd,f ; check if we have hit the end of the top line movlw d'16' subwf LCDAdd,w skpnz goto LCDLINE2 ; must go to new line return LCDLINE2 movlw b'01000000' movwf LCDAdd call LCDADD return LCDBYTE; sends a byte to the LCD, in 4bit fashion ; responsible for breaking up the byte to send into high and low nibbles ; and dumps them to LCD ;NOT responsible for the status of the LCDRS line, nor the major delays ; IN: LCDByte - destructive OUT: hiByte, loByte movlw b'00001111' andwf LCDByte,w ; Mask out upper for bits to generate movwf loByte ; lower nibble swapf LCDByte,w andlw b'00001111' ; Same for high nibble movwf hiByte movf hiByte,w ; Transmit both the Hi and Low Nibbles movwf nibToSend call SENDNIB movf loByte,w movwf nibToSend call SENDNIB call SHORTDLY ; blocks until most instructions are done return SENDNIB; responsible for sending nibble commands to the LCD. ;IN: nibToSend - non-distructive movf PORTC,w andlw b'110000' ; Preserve RS amd E lines iorwf nibToSend,w ; movwf PORTC ; Send data out to PORTC nop nop bsf LCDEnable; pulse the LCD Enable high nop nop nop nop bcf LCDEnable ; lower LCD Enable nop nop RETURN SHORTDLY ; 217 cycles movlw 0x48 movwf d1 SHORTDLY_0 decfsz d1, f goto SHORTDLY_0 ;4 cycles (including call) return LONGDLY ; 92153 cycles movlw 0xFE movwf d1 movlw 0x48 movwf d2 LONGDLY_0 decfsz d1, f goto $+2 decfsz d2, f goto LONGDLY_0 ;3 cycles goto $+1 nop ;4 cycles (including call) return end