jsobell 0 Report post Posted July 3, 2005 Well, after struggling for two days with bizzare issues with a simple LCD display, I finally found the problem. I'm not sure if this is common to all PICs, and it's been many years since I did any amount of PIC programming, but I had extreme problems interfacing to the display due to the way the template is coded for reading data back in 4bit mode. If you choose to use a single port to write to a display, it makes sense to set all of the ports to one port, in my case PortD. The problem arises that if you use a function such as: data |= d & 0xF0; then the existing value is read from the port, then written back. Unfortunately, if there are output pins on that port then the data read does not reflect the existing values, so your existing outputs get stomped on. Using the latch register (latd) in place of the port (portd) fixes the problem, but the latch can't be used to read the values when the port is reversed for status information, so two different variables are required to specify the port for reading (portd) and writing (latd). I've modified the template to reflect this change, and would appreciate someone checking to see if this makes sense or if I've completely missed the point and was misusing the template in the first place (Dave?). I can't test it with the other pin setups (8 bit mode and 4bit lo nibble), but I assume those sections are now corrected too. Ah well, at last my display is alive... Now to find something to use it for. Cheers, Jason p.s. A file attach option on the forum might be nice... ------------Code follows----------- //////////////////////////////////////////////////////////////////////////// // LCD with HD44780 drive chip //////////////////////////////////////////////////////////////////////////// // Author(s): David Hobday, Pavel Baranov // Date 15 November 2004 // // Copyright (c) 2004-2005 Pavel Baranov // Copyright (c) 2004-2005 David Hobday // Copyright (c) 2004 Andrew Smallridge // // // How to use - by David Hobday // ============================ // These must be define before template header file is included // Remember when using LCD in 4 bit mode you must connect to the LCDs DB4-DB7 pins // // *** For a list of functions, see the END of this file. // // The time delays used in the code should mean that it will work on PIC18 with // 40MHz clock without any changes. // // The example below (which would work on a PIC16F84) operates the display // in 4bit mode and requires the following connections: // RS to RA3 // R/W to RA2 // E to RA1 // DB0 to None // DB1 to None // DB2 to None // DB3 to None // DB4 to RB4 // DB5 to RB5 // DB6 to RB6 // DB7 to RB7 // // // Add the following after #include <system.h> in you source file: // //#define LCD_ARGS 2, /* Interface type: mode 0 = 8bit, 1 = 4bit(low nibble), 2 = 4bit(upper nibble) */ \ // 1, /* Use busy signal: 1 = use busy, 0 = use time delays */\ // LATD, PORTD, TRISD, /* Write Data port, Read Data port, and data port tris register */ \ // LATD, TRISD, /* Control port and control port tris register */ \ // 3, /* Bit number of control port is connected to RS */ \ // 2, /* Bit number of control port is connected to RW */ \ // 1 /* Bit number of control port is connected to Enable */ // //#include <lcd_driver.h> // include the LCD template code // // // Revisions // ========= // // V1.10 David Hobday 03/03/2005 // ============================= // 1) Improved documentation in file // 2) Changed delays to delay_10us for usage on target with clock <= 4MHz // 3) Changed template to make more friendly and obvious // // V1.11 18/03/2005 // David Hobday // 1) Fixed operation with PIC18 - template arguments of incorrect type // 2) Tested with PicDem2Plus board (4MHz PIC16F877/PIC18F452) // 3) Added lcd_gotoxy function. // 4) Added option to use display busy bit or time delays // 5) Added overloaded lprintf function for ROM string // 6) Added overloaded lprintf function to output numbers supported formats: // "%d" - decimal // "%X" - hex // "%b" - binart // example: display binary number six digits, '0' as file character. // lprintf( "val:%06b", numb ); // 7) Other improvements/cleanup. // // // V1.11 David Hobday 25/03/2005 // ============================= // 1) Fixed bug with lprintf( "test:%d", 0 ); not printing a 0. // 2) Added a few more comments in lprintf( const char*, int ) code // // // V1.12 Jason Sobell 03/07/2005 // ============================= // 1) Fixed bug when data and control ports are the same (introduced ReadDataPort) // //////////////////////////////////////////////////////////////////////////// char writeDelayType; //////////////////////////////////////////////////////////////////////////// // LCD Commands ( Refer to LCD Data Sheet ) //////////////////////////////////////////////////////////////////////////// #define clear_lcd 0x01 // Clear Display #define return_home 0x02 // Cursor to Home position #define entry_mode 0x06 // Normal entry mode #define entry_mode_rev 0x04 // Normal entry mode -reverse direction #define entry_mode_scroll 0x07 // - with shift #define entry_mode_scroll_rev 0x05 // reverse direction #define system_set_8_bit 0x38 // 8 bit data mode 2 line ( 5x7 font ) #define system_set_4_bit 0x28 // 4 bit data mode 2 line ( 5x7 font ) #define system_set_reset 0x30 // Reset code #define display_on 0x0C // Display ON - 2 line mode #define display_off 0x08 // Display off #define set_dd_line1 0x80 // Line 1 position 1 #define set_dd_line2 0xC0 // Line 2 position 1 #define set_dd_ram 0x80 // Line 1 position 1 #define write_data 0x00 // With RS = 1 #define cursor_on 0x0E // Switch Cursor ON #define cursor_off 0x0C // Switch Cursor OFF #define cursor_blink_on 0x0F // Cursor plus blink #define cursor_shift_right 0x14 // Move cursor right #define cursor_shift_left 0x10 // Move cursor left #define display_shift_right 0x1C // Scroll display right #define display_shift_left 0x18 // Scroll display left #define WriteNoDelay 1 #define WriteControlled 0 // Interface type #define LCD_8_BIT_MODE 0 #define LCD_4_BIT_LO_NIB_MODE 1 #define LCD_4_BIT_HI_NIB_MODE 2 // These macros make susequent code more readable, but can seem a little confusing #define _LCD_RawWriteNibble LCD_RawWriteNibble <InterfaceType, UseBusy, DataPort, ReadDataPort, Data_PortTris, CtrlPort, Ctrl_PortTris, RS, RW, E> #define _LCD_RawWriteNibbleInline LCD_RawWriteNibbleInline <InterfaceType, UseBusy, DataPort, ReadDataPort, Data_PortTris, CtrlPort, Ctrl_PortTris, RS, RW, E> #define _LCD_Read LCD_Read <InterfaceType, UseBusy, DataPort, ReadDataPort, Data_PortTris, CtrlPort, Ctrl_PortTris, RS, RW, E> #define _LCD_WaitForNotBusy LCD_WaitForNotBusy <InterfaceType, UseBusy, DataPort, ReadDataPort, Data_PortTris, CtrlPort, Ctrl_PortTris, RS, RW, E> #define _LCD_Write LCD_Write <InterfaceType, UseBusy, DataPort, ReadDataPort, Data_PortTris, CtrlPort, Ctrl_PortTris, RS, RW, E> #define _LCD_FunctionMode LCD_FunctionMode <InterfaceType, UseBusy, DataPort, ReadDataPort, Data_PortTris, CtrlPort, Ctrl_PortTris, RS, RW, E> #define _LCD_DataMode LCD_DataMode <InterfaceType, UseBusy, DataPort, ReadDataPort, Data_PortTris, CtrlPort, Ctrl_PortTris, RS, RW, E> #define _LCD_RawWrite LCD_RawWrite <InterfaceType, UseBusy, DataPort, ReadDataPort, Data_PortTris, CtrlPort, Ctrl_PortTris, RS, RW, E> #define _LCD_ClockOut LCD_ClockOut <InterfaceType, UseBusy, DataPort, ReadDataPort, Data_PortTris, CtrlPort, Ctrl_PortTris, RS, RW, E> #define _LCD_TEMPL template < unsigned char InterfaceType,\ unsigned char UseBusy,\ unsigned int DataPort, unsigned int ReadDataPort, unsigned int Data_PortTris,\ unsigned int CtrlPort, unsigned int Ctrl_PortTris,\ unsigned char RS, unsigned char RW, unsigned char E> _LCD_TEMPL inline void LCD_FunctionMode( void ) { volatile bit rs@CtrlPort.RS = 0; } _LCD_TEMPL inline void LCD_DataMode( void ) { volatile bit rs@CtrlPort.RS = 1; } inline void LCD_CycleMakeupDelay() { // Enable cycle time must be > 1000ns total for both reading and writing // LCD_SetupDelay + LCD_EnablePulseDelay + LCD_HoldupDelay + LCD_CycleMakeupDelay >= 1000ns // 200 + 500 + 100 + 200 >= 1000ns // This delay is required to meet the Sharp data sheet total cycle time of > 1000ns // @40MHz this is 2 instructions asm nop asm nop } inline void LCD_EnablePulseDelay() { // PWEH > 460ns on Sharp data sheet // @40MHz this is 5 instructions asm nop asm nop asm nop asm nop asm nop } inline void LCD_SetupDelay() { // tAS > 140ns min on Sharp data sheet // @40MHz this is 2 instructions asm nop asm nop } inline void LCD_HoldupDelay() { // tAS > 10ns min on Sharp data sheet // @40MHz this is 1 instructions asm nop } _LCD_TEMPL char LCD_Read() { char d; volatile unsigned char data@DataPort, tris@Data_PortTris, readdata@ReadDataPort; volatile bit rw@CtrlPort.RW, e@CtrlPort.E; if( InterfaceType == LCD_4_BIT_HI_NIB_MODE ) { // upper nibble input tris |= 0xF0; rw = 1; // set reading mode // first high nibble LCD_SetupDelay(); e = 1; LCD_EnablePulseDelay(); // d = data & 0xF0; // read data d = readdata & 0xF0; // read data e = 0; LCD_HoldupDelay(); LCD_CycleMakeupDelay(); // then low nibble LCD_SetupDelay(); e = 1; LCD_EnablePulseDelay(); //d |= data >> 4; d |= readdata >> 4; e = 0; LCD_HoldupDelay(); LCD_CycleMakeupDelay(); } if( InterfaceType == LCD_4_BIT_LO_NIB_MODE ) { // lower nibble input tris |= 0x0F; rw = 1; // set reading mode // first high nibble LCD_SetupDelay(); e = 1; LCD_EnablePulseDelay(); d = readdata << 4; e = 0; LCD_HoldupDelay(); LCD_CycleMakeupDelay(); // then low nibble LCD_SetupDelay(); e = 1; LCD_EnablePulseDelay(); d |= readdata & 0x0F; e = 0; LCD_HoldupDelay(); LCD_CycleMakeupDelay(); } if( InterfaceType == LCD_8_BIT_MODE ) { // port input tris = 0xFF; rw = 1; // set reading mode LCD_SetupDelay(); e = 1; LCD_EnablePulseDelay(); d = readdata; e = 0; LCD_HoldupDelay(); LCD_CycleMakeupDelay(); } return d; } _LCD_TEMPL void LCD_RawWriteNibble( char d ) { // Note: this function is duplicate below, but declared inline. // this is to reduce stack depth usage // Note: this function is above, but declared inline. // this is to reduce stack depth usage //volatile unsigned char data@DataPort, tris@Data_PortTris; volatile unsigned char data@DataPort, tris@Data_PortTris; volatile bit rw@CtrlPort.RW, e@CtrlPort.E; if( InterfaceType == LCD_4_BIT_HI_NIB_MODE ) { // port upper nibble output rw = 0; // set writing mode LCD_SetupDelay(); tris &= 0x0F; data &= 0x0F; data |= d & 0xF0; e = 1; LCD_EnablePulseDelay(); e = 0; LCD_HoldupDelay(); LCD_CycleMakeupDelay(); } if( InterfaceType == LCD_4_BIT_LO_NIB_MODE ) { // port upper nibble output rw = 0; // set writing mode LCD_SetupDelay(); tris &= 0xF0; data &= 0xF0; data |= d >> 4; e = 1; LCD_EnablePulseDelay(); e = 0; LCD_HoldupDelay(); LCD_CycleMakeupDelay(); } } _LCD_TEMPL inline void LCD_RawWriteNibbleInline( char d ) { // Note: this function is above, but declared inline. // this is to reduce stack depth usage volatile unsigned char data@DataPort, tris@Data_PortTris; volatile bit rw@CtrlPort.RW, e@CtrlPort.E; if( InterfaceType == LCD_4_BIT_HI_NIB_MODE ) { // port upper nibble output rw = 0; // set writing mode LCD_SetupDelay(); tris &= 0x0F; data &= 0x0F; data |= d & 0xF0; e = 1; LCD_EnablePulseDelay(); e = 0; LCD_HoldupDelay(); LCD_CycleMakeupDelay(); } if( InterfaceType == LCD_4_BIT_LO_NIB_MODE ) { // port upper nibble output rw = 0; // set writing mode LCD_SetupDelay(); tris &= 0xF0; data &= 0xF0; data |= d >> 4; e = 1; LCD_EnablePulseDelay(); e = 0; LCD_HoldupDelay(); LCD_CycleMakeupDelay(); } } _LCD_TEMPL void LCD_RawWrite( char d ) { volatile unsigned char tris@Data_PortTris, data@DataPort; volatile bit rw@CtrlPort.RW, e@CtrlPort.E; if( InterfaceType == LCD_4_BIT_HI_NIB_MODE ) { // output upper nibble, then lower nibble bit flag = 0; do { _LCD_RawWriteNibbleInline( d ); flag = !flag; d <<= 4; } while( flag ); } if( InterfaceType == LCD_4_BIT_LO_NIB_MODE ) { // output upper nibble, then lower nibble bit flag = 0; do { _LCD_RawWriteNibbleInline( d ); flag = !flag; d <<= 4; } while( flag ); } if( InterfaceType == LCD_8_BIT_MODE ) { // port b output rw = 0; // set writing mode tris = 0x00; data = d; LCD_SetupDelay(); e = 1; LCD_EnablePulseDelay(); e = 0; LCD_HoldupDelay(); LCD_CycleMakeupDelay(); } } _LCD_TEMPL inline void LCD_WaitForNotBusy() { // volatile bit rs@CtrlPort.RS; bit old_RS = rs; volatile bit rs@CtrlPort.RS; bit old_RS = rs; rs = 0; while( _LCD_Read() & 0x80 ); // wait while busy set rs = old_RS; } _LCD_TEMPL void LCD_Write(char d ) { volatile bit rs@CtrlPort.RS; if( UseBusy == 1 ) { // wait until display Not busy before sending next data if ( writeDelayType == WriteControlled ) _LCD_WaitForNotBusy(); _LCD_RawWrite( d ); } else { _LCD_RawWrite( d ); // give time to complete if ( writeDelayType == WriteControlled ) { if( !rs && (d == return_home || d == clear_lcd) ) delay_ms( 2 ); // return_home takes more time than other instructions to execute else delay_10us( 5 ); // 50us - enough time for normal command execution - clear and home need longer!! } } } _LCD_TEMPL void LCD_Clear() { _LCD_FunctionMode(); _LCD_Write( clear_lcd ); // clear display _LCD_Write( return_home ); } _LCD_TEMPL void LCD_Setup( void ) { // set control port bits used to output volatile bit trisRS@Ctrl_PortTris.RS, trisRW@Ctrl_PortTris.RW, trisE@Ctrl_PortTris.E; trisRS = 0; trisRW = 0; trisE = 0; writeDelayType = WriteNoDelay; // no delays in data writes delay_ms(16); // Power up delay _LCD_FunctionMode(); if( InterfaceType == LCD_4_BIT_HI_NIB_MODE ) { // Reset sequence as described in data sheets _LCD_RawWriteNibble( system_set_reset ); delay_ms(5); // min delay here of 4.1 ms _LCD_RawWriteNibble( system_set_reset ); delay_10us(100); // min delay here of 100us _LCD_RawWriteNibble( system_set_reset ); // LCD busy flag is valid from this point onwards if( UseBusy == 1 ) _LCD_WaitForNotBusy(); else delay_10us( 5 ); // standard command delay time _LCD_RawWriteNibble( system_set_4_bit ); if( UseBusy == 1 ) _LCD_WaitForNotBusy(); else delay_10us( 5 ); // standard command delay time writeDelayType = WriteControlled; _LCD_Write( system_set_4_bit ); } if( InterfaceType == LCD_4_BIT_LO_NIB_MODE ) { // Reset sequence as described in data sheets _LCD_RawWriteNibble( system_set_reset ); delay_ms(5); // min delay here of 4.1 ms _LCD_RawWriteNibble( system_set_reset ); delay_10us(100); // min delay here of 100us _LCD_RawWriteNibble( system_set_reset ); // LCD busy flag is valid from this point onwards if( UseBusy == 1 ) _LCD_WaitForNotBusy(); else delay_10us( 5 ); // standard command delay time _LCD_RawWriteNibble( system_set_4_bit ); if( UseBusy == 1 ) _LCD_WaitForNotBusy(); else delay_10us( 5 ); // standard command delay time writeDelayType = WriteControlled; _LCD_Write( system_set_4_bit ); } if( InterfaceType == LCD_8_BIT_MODE ) { // Reset sequence as described in data sheets _LCD_RawWrite( system_set_reset ); delay_ms(5); // min delay here of 4.1 ms _LCD_RawWrite( system_set_reset ); delay_10us(10); // min delay here of 100us _LCD_RawWrite( system_set_reset ); // busy flag is valid from this point onwards if( UseBusy == 1 ) _LCD_WaitForNotBusy(); else delay_10us( 5 ); // standard command delay time _LCD_RawWrite( system_set_8_bit ); if( UseBusy == 1 ) _LCD_WaitForNotBusy(); else delay_10us( 5 ); // standard command delay time writeDelayType = WriteControlled; // use busy } _LCD_Write( entry_mode ); _LCD_Write( display_on ); _LCD_Write( set_dd_ram ); } _LCD_TEMPL void LCD_Printf( const char *lcdptr ) { char pi = 0, c; _LCD_DataMode(); while( 1 ) { c = lcdptr[pi++]; if ( !c ) return; if ( c == '\n' ) { _LCD_FunctionMode(); // move to start second line _LCD_Write( set_dd_ram + 0x40 ); _LCD_DataMode(); } else _LCD_Write( c );// Display on LCD } } _LCD_TEMPL void LCD_Printf( rom char *lcdptr ) { char pi = 0, c; _LCD_DataMode(); while( 1 ) { c = lcdptr[pi++]; if ( !c ) return; if ( c == '\n' ) { _LCD_FunctionMode(); // move to start second line _LCD_Write( set_dd_ram + 0x40 ); _LCD_DataMode(); } else _LCD_Write( c );// Display on LCD } } _LCD_TEMPL void LCD_Printf( const char *lcdptr, int val ) { unsigned char pi = 0, bi, c, fill, baseOrBits, sign, mask; unsigned char buff[ 10 ]; // max length allow is 9 bit pad; _LCD_DataMode(); while( 1 ) { c = lcdptr[pi++]; if ( !c ) return; switch( c ) { case '\n': _LCD_FunctionMode(); // move to start second line _LCD_Write( set_dd_ram + 0x40 ); _LCD_DataMode(); break; case '%': c = lcdptr[pi++]; if ( !c ) return; // Next character if zero indicates that we should zero fill output if ( c == '0' ) { fill = '0'; c = lcdptr[pi++]; if ( !c ) return; } else fill = ' '; // Next character if valid digit indicates field width if( c > '0' && c <= '9' ) { pad = 1; bi = c - 48;; c = lcdptr[pi++]; if ( !c ) return; } else { pad = 0; bi = sizeof( buff ) - 1; } // Next character indicates the radix (number base) sign = 0; switch( c ) { case 'd': if( val < 0 ) sign = '-'; case 'u': baseOrBits = 10; // base ten, divide by ten per digit break; case 'X': baseOrBits = 4; // base 16, requires a 4 bit shift per digit mask = 0x0F; break; case 'b': baseOrBits = 1; // base 16, requires a 1 bit shift per digit mask = 0x01; break; default: return; // no radix } // null terminate, then reverse fill string buff[ bi ] = '\0'; bit first = true; while( bi ) { bi--; if( val || first ) { first = false; if( baseOrBits == 10 ) { c = (unsigned char)(val % baseOrBits); val /= baseOrBits; if( c & 0x80 ) c = 0 - c; // negative numbers } else { c = val & mask; val = ((unsigned int)val) >> baseOrBits; } if( c > 9 ) c += 55; // convert to hex digits character A-F else c += 48; // convert to digit character 0-9 } else { if( sign && (bi == 0 || fill != '0') ) { c = sign; sign = 0; } else c = fill; } buff[ bi ] = c; if( pad == 0 && val == 0 && sign == 0 ) break; } // output string to display while( 1 ) { c = buff[ bi ]; if( !c ) break; _LCD_Write( c );// Display on LCD bi++; } break; default: _LCD_Write( c );// Display on LCD break; } } } _LCD_TEMPL void LCD_GotoXy( char x, char y ) { _LCD_FunctionMode(); unsigned char offset = (y << 6) + x; _LCD_Write( set_dd_ram + offset ); } _LCD_TEMPL void LCD_Function( char func ) { _LCD_FunctionMode(); _LCD_Write( func ); } //////////////////////////////////////////////////////////////////////////// // Helpers that hide template arguments //////////////////////////////////////////////////////////////////////////// // low level functions #define lcd_write LCD_Write<LCD_ARGS> #define lcd_waitfornotbusy LCD_WaitForNotBusy<LCD_ARGS> #define lcd_read LCD_Read<LCD_ARGS> #define lcd_funcmode LCD_FunctionMode<LCD_ARGS> #define lcd_datamode LCD_DataMode<LCD_ARGS> // high level functions - these all set function or data mode as required #define lcd_setup LCD_Setup<LCD_ARGS> #define lprintf LCD_Printf<LCD_ARGS> #define lcd_clear LCD_Clear<LCD_ARGS> #define lcd_gotoxy LCD_GotoXy<LCD_ARGS> #define lcd_function LCD_Function<LCD_ARGS> Quote Share this post Link to post Share on other sites
asmallri 0 Report post Posted July 3, 2005 I have had a look at your listing but I cannot see where you made the changes. Can you point them out? Regards, Andrew Quote Share this post Link to post Share on other sites
jsobell 0 Report post Posted July 4, 2005 Sorry, I should have marked them. Search for ReadDataPort, which is the variable/template option I added throughout. I also changed the suggested configuration options: //#define LCD_ARGS 2, /* Interface type: mode 0 = 8bit, 1 = 4bit(low nibble), 2 = 4bit(upper nibble) */ \ // 1, /* Use busy signal: 1 = use busy, 0 = use time delays */\ // LATD, PORTD, TRISD, /* Write Data port, Read Data port, and data port tris register */ \ // LATD, TRISD, /* Control port and control port tris register */ \ // 3, /* Bit number of control port is connected to RS */ \ // 2, /* Bit number of control port is connected to RW */ \ // 1 /* Bit number of control port is connected to Enable */ I suspect this is all a PIC18x specific issue, as the PIC16 doesn't use the concept of a latch register. I also believe it shows a problem in the SourceBoost emulator which seems to always reflect the last byte written to the port, hence why the LCD program works fine with the LCD display plugin in emulation mode but not in practise. Cheers, Jason Quote Share this post Link to post Share on other sites
asmallri 0 Report post Posted July 4, 2005 Hi Jason, I have had a (brief) look into this. It appears to me that the original code in the driver was correct and that you might have hit a compiler bug (applogies in advance to the PICANT team if I am wrong). data |= d & 0xF0; The result of this operation should be [/code]date = data | (d & 0xF0);[/code] but I suspect the result was [/code]data = (data | d) & 0xF0;[/code] In the scenario where a PIC16F uses a single port for LCD data and control there is a possibililty of hitting a read-modify-write problem. If the output control lines to the LCD are heavily loaded (not normally the case but someone may have added a LED or some other load) then the read of the port could see one of more of the control lines appear low when in reality they are high or visa versa. In this case the LCD driver code should be modified to deal with this corner case by storing a copy of the control lines in RAM and using these values to set the output port bits. Regards, Andrew Quote Share this post Link to post Share on other sites
jsobell 0 Report post Posted July 4, 2005 In the scenario where a PIC16F uses a single port for LCD data and control there is a possibililty of hitting a read-modify-write problem. If the output control lines to the LCD are heavily loaded (not normally the case but someone may have added a LED or some other load) then the read of the port could see one of more of the control lines appear low when in reality they are high or visa versa. In this case the LCD driver code should be modified to deal with this corner case by storing a copy of the control lines in RAM and using these values to set the output port bits. From what I can see in the spec sheet, the latch register gets around the 'high load' issue. When I run the ICD2 emulator, it does indeed read the pins as being low, even when they are actually high (and that's with a 1.5K resistor to the base of a transistor!), so the latch register is the only one with the current state shown correctly on the PIC18. I do remember the old 'store a copy of the register in RAM' issue on the PIC17 series, and I assume that's one reason the latch was introduced on the PIC18. Most posts regarding this from MicroChip state that the latch should be used instead of the port register, so unless problems arise with the PIC16 then I suspect my solution may be the 'officially correct' method. I've looked at the code created, and the following appears: data |= d & 0xF0; 012A 0EF0 MOVLW 0xF0 012C 1411 ANDWF LCD_RawWri_00005_arg_d, W 012E 128C IORWF LCD_RawWri_00005_1_data, F This looks correct, as a single read is being performed from variable d, with a single immediate OR to the destination port (data), so this is not where the problem lies (although it was a good potential problem point). Once the char to int casting bug is fixed then I'd like to contribute back another fix for the led_driver.h that allows display of signed and unsigned values correctly. Who is managing this stuff? Should I email the changes to someone instead of posting them in here? Cheers, Jason Quote Share this post Link to post Share on other sites
Dave 0 Report post Posted July 4, 2005 The library is meant to be target independant. To use the LAT register for the control port instead of the port (in this example for PORTA): #define LCD_ARGS 1, /* Interface type: mode 0 = 8bit, 1 = 4bit(low nibble), 2 = 4bit(upper nibble) */ \ 1, /* Use busy signal: 1 = use busy, 0 = use time delays */\ PORTD, TRISD, /* Data port and data port tris register */ \ LATA, TRISA, /* Control port and control port tris register */ \ 3, /* Bit number of control port is connected to RS */ \ 2, /* Bit number of control port is connected to RW */ \ 1 /* Bit number of control port is connected to Enable */ then I'd like to contribute back another fix for the led_driver.h that allows display of signed and unsigned values correctly. What is this issue ? Who is managing this stuff? Should I email the changes to someone instead of posting them in here? The code is managed as part of the distribution. If you would like to submit changes please send to support, and we will review the changes. Regards Dave Quote Share this post Link to post Share on other sites
jsobell 0 Report post Posted July 5, 2005 (edited) What is this issue ? It's been posted to the 'Bug Reports' section here, and seems to me to be a fundamental and serious bug (which is why an acknowledgement/confirmation/"no Jason, you're an idiot" response would be nice ) *Edit* Ah, the bug in the LCD code is that if you try and display an unsigned integer using %u then it is always displayed as signed (only without the '-' symbol). I wanted to display numbers >32768, so had to fix it. I'll email you the fixes instead of posting in here. */Edit* Cheers, Jason Edited July 5, 2005 by jsobell Quote Share this post Link to post Share on other sites
jsobell 0 Report post Posted July 5, 2005 The library is meant to be target independant.To use the LAT register for the control port instead of the port (in this example for PORTA): #define LCD_ARGS 1, /* Interface type: mode 0 = 8bit, 1 = 4bit(low nibble), 2 = 4bit(upper nibble) */ \ 1, /* Use busy signal: 1 = use busy, 0 = use time delays */\ PORTD, TRISD, /* Data port and data port tris register */ \ LATA, TRISA, /* Control port and control port tris register */ \ 3, /* Bit number of control port is connected to RS */ \ 2, /* Bit number of control port is connected to RW */ \ 1 /* Bit number of control port is connected to Enable */ Obviously, but this does not work if Data and Control are on the same port. The problem is in modifying the Data port in nibbles, where 'OR'ing in a new value will cause an internal Read/Write to set the new bits. When the Read is performed, it reads the control bits as being zero, despite the fact they are actually high. Therefore, you *must* use the latch or you must OR in the saved state of the control pins, otherwise they will be forced back to low. Target independence is only possible if you allow for the fact that PIC18s and PIC16s handle their ports differently. Check the Microchip data sheets and see at the port circuitry for the differences. Cheers, Jason Quote Share this post Link to post Share on other sites
asmallri 0 Report post Posted July 5, 2005 The library is meant to be target independant. Obviously, but this does not work if Data and Control are on the same port. The problem is in modifying the Data port in nibbles, where 'OR'ing in a new value will cause an internal Read/Write to set the new bits. When the Read is performed, it reads the control bits as being zero, despite the fact they are actually high. Therefore, you *must* use the latch or you must OR in the saved state of the control pins, otherwise they will be forced back to low. The problem is more basic than this. The driver needs to protect the contents of output bits that are not part of the LCD data bus irrespective of the bits being used by the LCD or some other function. To OR in the state of the control bits in this case only makes sense if all bits are on the same port as the data. This can be a challenge for a general driver as it has no knowledge of how the programmer (such as yourself) are talking to the other bits on the port. Simulating the latch register of the 18F family for the 16F only makes sense if the other code written by other developers also uses the same emulated latch. The lat registers are are great help for the 18F family but you also need to attempt solve the general case for those processors without these registers which is a challenge because of the read-modify write problem of that processor class. However you have the case where some developers will write directly to the port and the other case where a bit configured to be an output is deliberately read as an input. Although a corner case - it is a valid technique. Target independence is only possible if you allow for the fact that PIC18s and PIC16s handle their ports differently. I think Dave was saying is that target independance is the GOAL of the driver . Quote Share this post Link to post Share on other sites
jsobell 0 Report post Posted July 5, 2005 The problem is more basic than this. The driver needs to protect the contents of output bits that are not part of the LCD data bus irrespective of the bits being used by the LCD or some other function. To OR in the state of the control bits in this case only makes sense if all bits are on the same port as the data. This can be a challenge for a general driver as it has no knowledge of how the programmer (such as yourself) are talking to the other bits on the port. Simulating the latch register of the 18F family for the 16F only makes sense if the other code written by other developers also uses the same emulated latch. The lat registers are are great help for the 18F family but you also need to attempt solve the general case for those processors without these registers which is a challenge because of the read-modify write problem of that processor class. However you have the case where some developers will write directly to the port and the other case where a bit configured to be an output is deliberately read as an input. Although a corner case - it is a valid technique. Hmm, I think this is a mute point, as the latter problems you describe are unsolvable on the PIC16, and is the reason the latch was introduced on the PIC18. I think Dave was saying is that target independance is the GOAL of the driver. Yes, I understand that, but Dave's response was to suggest simply replacing 'PortA' with 'LatA', but this does not work in the common scenario where the port is shared. I posted a very simple solution for this situation that works with PIC16s with separate control/data ports, and with PIC18s with either separate or combined. Hence we have a solution that is still target independant, but works in more situations than before. If someone can code up the solution to the PIC16 using a shared port, then that's great, and will only involve some compiler conditional code adding a few lines and one variable. I would do it myself, but I don't have any PIC16s at the moment (and the emulator is not consistent with the hardware port read/writes). Whatever happens, it is not going to resolve the issue you mention above where a developer writes directly to the port, but we know we can't do anything about this, so it's probably best to simply document it in the template and let the developer beware. Cheers, Jason Quote Share this post Link to post Share on other sites
jsobell 0 Report post Posted October 9, 2005 There's still nothing changed in the provided source to address this issue, and given that it's fairly standard to use a single 8-bit port for LCD display control, perhaps it should be rectified in the SourceBoost supplied version? At the moment I have to replace the installed lcd_driver.h after every upgrade with my own to fix the bug. Cheers, Jason Quote Share this post Link to post Share on other sites
Dave 0 Report post Posted October 9, 2005 Jason, There's still nothing changed in the provided source to address this issue, and given that it's fairly standard to use a single 8-bit port for LCD display control, perhaps it should be rectified in the SourceBoost supplied version? At the moment I have to replace the installed lcd_driver.h after every upgrade with my own to fix the bug. There has been alot mentioned in this thread, which issue(s) are you refering to? Changed the boostc LCD sample to a single port: RD6 RS RD5 R/W RD6 E RD0 DB4 RD1 DB5 RD2 DB6 RD3 DB7 and it all worked fine. Regards Dave Quote Share this post Link to post Share on other sites
jsobell 0 Report post Posted October 11, 2005 On which PIC chip? As I've already shown several times, this is PIC16 only, and does not work on the PIC18 with it's latch register, hence why I emailed you version that worked on both. I get this strange feeling of deja-vu... Please see my posting from "Jul 5 2005, 02:42 PM" Cheers, Jason Quote Share this post Link to post Share on other sites
Dave 0 Report post Posted October 12, 2005 Jason, As I've already shown several times, this is PIC16 only, and does not work on the PIC18 with it's latch register, hence why I emailed you version that worked on both. Let me try and get this straight; Wth PIC18 we don't have a problem, we can use the LAT registers which aren't affected by a pins drive characteristics and external loading. With PIC16 pin characteristics and external loading mean that a value can be read that is different to that which was written. If this is the case then we have a shadow register that holds the last value written. Any code in our program that uses this port(s) must also use the shadow register(s). Please re-mail your modified version of the code for further review. BTW: Another thought I had about the LCD code was to make all data pins and control pins individually port selectable. That way you would not be stuck with using a particular port nibble for data. This would be much more complicated if shadow registers where used for each port. Regards Dave Quote Share this post Link to post Share on other sites
jsobell 0 Report post Posted October 12, 2005 Let me try and get this straight;Wth PIC18 we don't have a problem, we can use the LAT registers which aren't affected by a pins drive characteristics and external loading. With PIC16 pin characteristics and external loading mean that a value can be read that is different to that which was written. If this is the case then we have a shadow register that holds the last value written. Any code in our program that uses this port(s) must also use the shadow register(s). That's almost correct On the PIC18 we can't use the latch to read the data (such as status bits), as it always reflects the last written value, so we still need to use the data register. All in all, we need to specify three ports in the setup, which is what the code I'm emailing you does. It's annoying to have to change the configuration options and add a parameter, creating a minor break in backwards compatability, but there has to be a way to specify separate data read port and data write ports. Cheers, Jason Quote Share this post Link to post Share on other sites