// // Ansteuerung eines HD44780 kompatiblen LCD im 4-Bit-Interfacemodus V3 // V3 Getestet auf uC : ATmega 328 // V3 Getestet mit LCD: BT 42012 V1 (4x20, Fa. Batron) // V3 IDE / Compiler : Atmel Studio 7.0, Standart-Compiler dieser IDE // #ifndef LCD_HD4478_4BIT_v3_H_ #define LCD_HD4478_4BIT_v3_H_ 0x300 // Version 3.0.0, 25.04.2021 #include #define F_CPU 8000000 #include #define DATA_PINS_in_a_ROW 0 // 4 Datenpins in einer Reihe an einem Port 0=nein // neu bei v3 //////////////////////////////////////////////////////////////////////////////// // Pinbelegung für das LCD, an verwendete Pins anpassen // // Version 1 Alle Pins muessen in einer Reihe u. am selben Port sein // Version 2.1 Die RS und EN Pins koennen an beliebigen Port's sein. // Version 3 Die Datenleitungen koennen an jedem beliebigen Pin/Port sein // // LCD DB4-DB7 <--> PORTx Bit Px4-Px7 #if DATA_PINS_in_a_ROW == 1 // Daten-Pins in einer Reihe an einem Port # define LCD_PORT PORTD # define LCD_DDR DDRD # define LCD_DB PIND3 // Erster Daten-Pin // #else // Daten-Pins an unterschiedlichen Ports und/oder nicht in einer Reihe // # define LCD_PORT_D1 PORTB // Data Pin 1 == LCD-Pin D4 # define LCD_DDR_D1 DDRB # define LCD_PIN_D1 PINB4 // # define LCD_PORT_D2 PORTD // Data Pin 2 == LCD-Pin D5 # define LCD_DDR_D2 DDRD # define LCD_PIN_D2 PIND4 // # define LCD_PORT_D3 PORTD // Data Pin 3 == LCD-Pin D6 # define LCD_DDR_D3 DDRD # define LCD_PIN_D3 PIND5 // # define LCD_PORT_D4 PORTD // Data Pin 4 == LCD-Pin D7 # define LCD_DDR_D4 DDRD # define LCD_PIN_D4 PIND2 // # define SET_D1 LCD_PORT_D1 |=( 1<< LCD_PIN_D1 ) // Setzen Pin # define SET_D2 LCD_PORT_D2 |=( 1<< LCD_PIN_D2 ) # define SET_D3 LCD_PORT_D3 |=( 1<< LCD_PIN_D3 ) # define SET_D4 LCD_PORT_D4 |=( 1<< LCD_PIN_D4 ) // # define CLEAR_D1 LCD_PORT_D1 &=~( 1<< LCD_PIN_D1 ) // Loeschen Pin # define CLEAR_D2 LCD_PORT_D2 &=~( 1<< LCD_PIN_D2 ) # define CLEAR_D3 LCD_PORT_D3 &=~( 1<< LCD_PIN_D3 ) # define CLEAR_D4 LCD_PORT_D4 &=~( 1<< LCD_PIN_D4 ) // // Array's mit den Ports u. Pin's, wird nur gebraucht wenn Pins verteilt sind // (Pins an untersch. Ports) volatile uint8_t *LCD_Port_ar[ 4 ] = { &LCD_PORT_D1, &LCD_PORT_D2, &LCD_PORT_D3, &LCD_PORT_D4 }; uint8_t LCD_Pin_ar[ 4 ] = { LCD_PIN_D1, LCD_PIN_D2, LCD_PIN_D3, LCD_PIN_D4 }; #endif //DATA_PINS_in_a_ROW != 1 // LCD RS <--> PORTx Bit PXy (RS: 1=Data, 0=Command) #define LCD_RS_PORT PORTB #define LCD_RS_DDR DDRB #define LCD_RS PINB2 #define SET_RS LCD_RS_PORT |= ( 1<< LCD_RS ) #define CLEAR_RS LCD_RS_PORT &=~( 1<< LCD_RS ) // LCD EN <--> PORTx Bit PXz (E: 1-Impuls für Daten) #define LCD_EN_PORT PORTC #define LCD_EN_DDR DDRC #define LCD_EN PINC5 #define SET_EN LCD_EN_PORT |= ( 1<< LCD_EN ) #define CLEAR_EN LCD_EN_PORT &=~( 1<< LCD_EN ) //////////////////////////////////////////////////////////////////////////////// // LCD Ausfuehrungszeiten (MS=Millisekunden, US=Mikrosekunden) // #define LCD_BOOTUP_MS 15 #define LCD_ENABLE_US 20 #define LCD_WRITEDATA_US 46 #define LCD_COMMAND_US 42 #define LCD_SOFT_RESET_MS1 5 #define LCD_SOFT_RESET_MS2 1 #define LCD_SOFT_RESET_MS3 1 #define LCD_SET_4BITMODE_MS 5 #define LCD_CLEAR_DISPLAY_MS 2 #define LCD_CURSOR_HOME_MS 2 //////////////////////////////////////////////////////////////////////////////// // Zeilendefinitionen des verwendeten LCD // Die Eintraege hier sollten für ein LCD mit einer Zeilenlaenge von 20 // Zeichen passen. Bei anderen Zeilenlaengen muessen diese Eintraege angepasst werden // #define LCD_DDADR_LINE1 0x00 #define LCD_DDADR_LINE2 0x40 #define LCD_DDADR_LINE3 0x14 #define LCD_DDADR_LINE4 0x54 //////////////////////////////////////////////////////////////////////////////// // Initialisierung: muss ganz am Anfang des Programms aufgerufen werden. // extern void lcd_init( void ); //////////////////////////////////////////////////////////////////////////////// // LCD loeschen // extern void lcd_clear( void ); //////////////////////////////////////////////////////////////////////////////// // Cursor in die 1. Zeile, 0-te Spalte // extern void lcd_home( void ); //////////////////////////////////////////////////////////////////////////////// // Cursor an eine beliebige Position // extern void lcd_setcursor( uint8_t spalte, uint8_t zeile ); //////////////////////////////////////////////////////////////////////////////// // Ausgabe eines einzelnen Zeichens an der aktuellen Cursorposition // extern void lcd_data( uint8_t data ); //////////////////////////////////////////////////////////////////////////////// // Ausgabe eines Strings an der aktuellen Cursorposition // extern void lcd_string( const char *data ); //////////////////////////////////////////////////////////////////////////////// // Definition eines benutzerdefinierten Sonderzeichens. // data muss auf ein Array[8] mit den Zeilencodes des zu definierenden Zeichens // zeigen extern void lcd_generatechar( uint8_t code, const uint8_t *data ); //////////////////////////////////////////////////////////////////////////////// // Setzt Cursor Spalte x (0..20) Zeile y (1..4) und schreibt String // neu bei Version 2.2 extern void lcd_set_x_y__write_string( uint8_t x, uint8_t y, const char *string ); //////////////////////////////////////////////////////////////////////////////// // Ausgabe eines Kommandos an das LCD. // extern void lcd_command( uint8_t data ); //////////////////////////////////////////////////////////////////////////////// // LCD Befehle und Argumente. // Zur Verwendung in lcd_command // Clear Display -------------- 0b00000001 #define LCD_CLEAR_DISPLAY 0x01 // Cursor Home ---------------- 0b0000001x #define LCD_CURSOR_HOME 0x02 // Set Entry Mode ------------- 0b000001xx #define LCD_SET_ENTRY 0x04 #define LCD_ENTRY_DECREASE 0x00 #define LCD_ENTRY_INCREASE 0x02 #define LCD_ENTRY_NOSHIFT 0x00 #define LCD_ENTRY_SHIFT 0x01 // Set Display ---------------- 0b00001xxx #define LCD_SET_DISPLAY 0x08 #define LCD_DISPLAY_OFF 0x00 #define LCD_DISPLAY_ON 0x04 #define LCD_CURSOR_OFF 0x00 #define LCD_CURSOR_ON 0x02 #define LCD_BLINKING_OFF 0x00 #define LCD_BLINKING_ON 0x01 // Set Shift ------------------ 0b0001xxxx #define LCD_SET_SHIFT 0x10 #define LCD_CURSOR_MOVE 0x00 #define LCD_DISPLAY_SHIFT 0x08 #define LCD_SHIFT_LEFT 0x00 #define LCD_SHIFT_RIGHT 0x04 // Set Function --------------- 0b001xxxxx #define LCD_SET_FUNCTION 0x20 #define LCD_FUNCTION_4BIT 0x00 #define LCD_FUNCTION_8BIT 0x10 #define LCD_FUNCTION_1LINE 0x00 #define LCD_FUNCTION_2LINE 0x08 #define LCD_FUNCTION_5X7 0x00 #define LCD_FUNCTION_5X10 0x04 #define LCD_SOFT_RESET 0x30 // Set CG RAM Address --------- 0b01xxxxxx (Character Generator RAM) #define LCD_SET_CGADR 0x40 #define LCD_GC_CHAR0 0 #define LCD_GC_CHAR1 1 #define LCD_GC_CHAR2 2 #define LCD_GC_CHAR3 3 #define LCD_GC_CHAR4 4 #define LCD_GC_CHAR5 5 #define LCD_GC_CHAR6 6 #define LCD_GC_CHAR7 7 // Set DD RAM Address --------- 0b1xxxxxxx (Display Data RAM) #define LCD_SET_DDADR 0x80 //////////////////////////////////////////////////////////////////////////////// // Erzeugt einen Enable-Puls geandert von duh, 24.04.2021 v2.2 // static void lcd_enable( void ) { SET_EN; // Enable auf 1 setzen _delay_us( LCD_ENABLE_US ); // kurze Pause CLEAR_EN; // Enable auf 0 setzen } //////////////////////////////////////////////////////////////////////////////// // Sendet eine 4-bit Ausgabeoperation an das LCD // #if DATA_PINS_in_a_ROW == 1 // Originale Version, hier muessen die Pins static void lcd_out( uint8_t data ) // zur Datenuebertragung aufeinanderfolgen { // und an einem gemeinsamen Port liegen. data &= 0xF0; // obere 4 Bit maskieren LCD_PORT &= ~(0xF0 >>(4-LCD_DB)); // Maske loeschen LCD_PORT |= (data >>(4-LCD_DB)); // Bits setzen lcd_enable(); } #endif #if DATA_PINS_in_a_ROW != 1 # define IS_BIT_LOW( int8_var, nr_of_bit ) ( !((int8_var) & (1 << (nr_of_bit))) ) # define IS_BIT_HIGH( int8_var, nr_of_bit ) ( ((int8_var) & (1 << (nr_of_bit))) ) static void lcd_out( uint8_t data ) // Den jeweiligem Bit des Nibble den { // entsprechenden Port/Pin zuordenen. for( int i = 0; i < 4 ; i++ ) { if( IS_BIT_HIGH( data, i+4 ) != 0 ){ *LCD_Port_ar[ i ] |= (1 << LCD_Pin_ar[ i ]); } else { *LCD_Port_ar[ i ] &=~(1 << LCD_Pin_ar[ i ]); } } lcd_enable(); } #endif //DATA_PINS_in_a_ROW != 1 //////////////////////////////////////////////////////////////////////////////// // Initialisierung: muss ganz am Anfang des Programms aufgerufen werden. // void lcd_init( void ) { #if DATA_PINS_in_a_ROW == 1 // Pins an einem Port u. hintereinander uint8_t pins = (0x0F << LCD_DB); // 4 Datenleitungen LCD_DDR |= pins; #else // Pins an unterschiedlichen Ports o. nicht hintereinander LCD_DDR_D1 |= (1<