Forum: Mikrocontroller und Digitale Elektronik DotMatrix 16x4 in C ansteuern


von JuBa 3. (juba3006)


Angehängte Dateien:

Lesenswert?

Moin
Ich habe folgendes Problem.
Ich will mein DotMatrix 16x4 NLW LCD von Reichelt in C ansteuern. In 
Bascom läuft alles perfekt und auch einwandfrei.
In C habe ich jedoch das Problem, dass wenn ich die Datein überspielen 
will,  alles verschoben ist bzw. nur aus einem Sonderzeichen wirrwar 
besteht. Die Zeilen passen auch nicht zu den im Programm festgelegten 
passt.

Ich schaffe es auch nicht, das Display zu Clearn.




 Hier einmal der bisherige Code:

# --> Lcd_routines.h
// Ansteuerung eines HD44780 kompatiblen LCD im 4-Bit-Interfacemodus
// 
http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial/LCD-Ansteuerung
//

#ifndef LCD_ROUTINES_H
#define LCD_ROUTINES_H

#include <avr/pgmspace.h>

//////////////////////////////////////////////////////////////////////// 
////////
// Pinbelegung für das LCD, an verwendete Pins anpassen
// alle Pins müssen in einem Port liegen
// die 4 Bit für die Daten müssen zusammenliegen, können aber an einer
// beliebigen Postion anfangen

#ifndef LCD_PORTS
#define LCD_PORTS

#define LCD_PORT      PORTC
#define LCD_DDR       DDRC

// 4 Bit LCD Datenbus DB4-DB7, das unterste Bit DB4 kann auf den 
Portbits 0..4 liegen

//  LCD DB4-DB7 <-->  PORTC Bit PC0-PC3
#define LCD_DB        PC1

// LCD Steuersignale RS und EN

//  LCD RS      <-->  PORTC Bit PC4     (RS: 0=Data, 1=Command)
#define LCD_RS        PC6

//  LCD EN      <-->  PORTC Bit PC5     (EN: 1-Impuls für Daten)
#define LCD_EN        PC5

#endif // LCD_PORTS

//////////////////////////////////////////////////////////////////////// 
////////
// LCD Ausführungszeiten (MS=Millisekunden, US=Mikrosekunden)

#ifndef LCD_TIMINGS
#define LCD_TIMINGS

#define LCD_BOOTUP_MS           15
#define LCD_ENABLE_US           1
#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

#endif // LCD_TIMINGS

//////////////////////////////////////////////////////////////////////// 
////////
// Zeilendefinitionen des verwendeten LCD
// die Einträge hier sollten für ein LCD mit einer Zeilenlänge von 16 
Zeichen passen
// bei anderen Zeilenlängen müssen diese Einträge angepasst werden

#define LCD_DDADR_LINE1         0x00
#define LCD_DDADR_LINE2         0x40
#define LCD_DDADR_LINE3         0x10
#define LCD_DDADR_LINE4         0x50

//////////////////////////////////////////////////////////////////////// 
////////
// Initialisierung: muss ganz am Anfang des Programms aufgerufen werden.
void lcd_init( void );

//////////////////////////////////////////////////////////////////////// 
////////
// LCD löschen
void lcd_clear( void );

//////////////////////////////////////////////////////////////////////// 
////////
// Cursor in die erste Zeile, erste Spalte (Position 0,0)
void lcd_home( void );

//////////////////////////////////////////////////////////////////////// 
////////
// Cursor an eine beliebige Position
void lcd_setcursor( uint8_t x, uint8_t y );

//////////////////////////////////////////////////////////////////////// 
////////
// Ausgabe eines einzelnen Zeichens an der aktuellen Cursorposition
void lcd_data( uint8_t data );

//////////////////////////////////////////////////////////////////////// 
////////
// Ausgabe eines Strings an der aktuellen Cursorposition
// String liegt im RAM
void lcd_string( const char *data );

//////////////////////////////////////////////////////////////////////// 
////////
// Ausgabe eines Strings an einer bestimmten Cursorposition
// String liegt im RAM
void lcd_string_xy( uint8_t x, uint8_t y, const char *data );

//////////////////////////////////////////////////////////////////////// 
////////
// Ausgabe einer Zahl an der aktuellen Cursorposition
// Zahl liegt im RAM
void lcd_number( uint8_t number, uint8_t len, uint8_t fill );

//////////////////////////////////////////////////////////////////////// 
////////
// Ausgabe einer Zahl an einer bestimmten Cursorposition
// Zahl liegt im RAM
void lcd_number_xy( uint8_t x, uint8_t y, uint8_t number, uint8_t len, 
uint8_t fill );

//////////////////////////////////////////////////////////////////////// 
////////
// Ausgabe eines Strings an der aktuellen Cursorposition
// String liegt im Flash
void lcd_string_P( PGM_P data );

//////////////////////////////////////////////////////////////////////// 
////////
// Definition eines benutzerdefinierten Sonderzeichens.
// data muss auf ein Array mit den Zeilencodes des zu definierenden 
Zeichens
// zeigen, Daten liegen im RAM
void lcd_generatechar( uint8_t code, const uint8_t *data, uint8_t lines 
);

//////////////////////////////////////////////////////////////////////// 
////////
// Definition eines benutzerdefinierten Sonderzeichens.
// data muss auf ein Array mit den Zeilencodes des zu definierenden 
Zeichens
// zeigen, Daten liegen im FLASH
void lcd_generatechar_P( uint8_t code, PGM_P data, uint8_t lines );

//////////////////////////////////////////////////////////////////////// 
////////
// Ausgabe eines Kommandos an das LCD.
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         0x01

// 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


#endif // LCD_ROUTINES_H

# --> lcd_rountines.c
// Ansteuerung eines HD44780 kompatiblen LCD im 4-Bit-Interfacemodus
// http://www.mikrocontroller.net/articles/HD44780
// 
http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial/LCD-Ansteuerung
//
// Die Pinbelegung ist über defines in lcd.h einstellbar

#include <avr/io.h>
#include "lcd-routines.h"
#include <util/delay.h>

//////////////////////////////////////////////////////////////////////// 
/////////
// Erzeugt einen Enable-Puls

static void lcd_enable( void ) {
  LCD_PORT |= (1<<LCD_EN);            // Enable auf 1 setzen
  _delay_us( LCD_ENABLE_US );         // kurze Pause
  LCD_PORT &= ~(1<<LCD_EN);           // Enable auf 0 setzen
}

//////////////////////////////////////////////////////////////////////// 
////////
// Sendet eine 4-bit Ausgabeoperation an das LCD
static void lcd_out( uint8_t data ) {
  data &= 0xF0;                       // obere 4 Bit maskieren
  LCD_PORT &= ~(0xF0>>(4-LCD_DB));    // Maske löschen
  LCD_PORT |= (data>>(4-LCD_DB));     // Bits setzen
  lcd_enable();
}

//////////////////////////////////////////////////////////////////////// 
////////
// Initialisierung: muss ganz am Anfang des Programms aufgerufen werden.
void lcd_init( void ) {
  // verwendete Pins auf Ausgang schalten
  uint8_t pins = (0x0F << LCD_DB) |   // 4 Datenleitungen
  (1<<LCD_RS) |        // R/S Leitung
  (1<<LCD_EN);         // Enable Leitung
  LCD_DDR |= pins;

  // initial alle Ausgänge auf Null
  LCD_PORT &= ~pins;

  // warten auf die Bereitschaft des LCD
  _delay_ms( LCD_BOOTUP_MS );

  // Soft-Reset muss 3mal hintereinander gesendet werden zur 
Initialisierung
  lcd_out( LCD_SOFT_RESET );
  _delay_ms( LCD_SOFT_RESET_MS1 );

  lcd_enable();
  _delay_ms( LCD_SOFT_RESET_MS2 );

  lcd_enable();
  _delay_ms( LCD_SOFT_RESET_MS3 );

  // 4-bit Modus aktivieren
  lcd_out( LCD_SET_FUNCTION |
  LCD_FUNCTION_4BIT );
  _delay_ms( LCD_SET_4BITMODE_MS );

  // 4-bit Modus  2 Zeilen  5x7
  lcd_command( LCD_SET_FUNCTION |
  LCD_FUNCTION_4BIT |
  LCD_FUNCTION_2LINE |
  LCD_FUNCTION_5X7 );

  // Display ein  Cursor aus  Blinken aus
  lcd_command(LCD_SET_DISPLAY |
  LCD_DISPLAY_ON |
  LCD_CURSOR_OFF |
  LCD_BLINKING_OFF);

  // Cursor inkrement / kein Scrollen
  lcd_command( LCD_SET_ENTRY |
  LCD_ENTRY_INCREASE |
  LCD_ENTRY_NOSHIFT );

  lcd_clear();
}

//////////////////////////////////////////////////////////////////////// 
////////
// Sendet ein Datenbyte an das LCD
void lcd_data( uint8_t data ) {
  LCD_PORT |= (1<<LCD_RS);    // RS auf 1 setzen

  lcd_out( data );            // zuerst die oberen,
  lcd_out( data<<4 );         // dann die unteren 4 Bit senden

  _delay_us( LCD_WRITEDATA_US );
}

//////////////////////////////////////////////////////////////////////// 
////////
// Sendet einen Befehl an das LCD
void lcd_command( uint8_t data ) {
  LCD_PORT &= ~(1<<LCD_RS);    // RS auf 0 setzen

  lcd_out( data );             // zuerst die oberen,
  lcd_out( data<<4);           // dann die unteren 4 Bit senden

  _delay_us(LCD_COMMAND_US );
}

//////////////////////////////////////////////////////////////////////// 
////////
// Sendet den Befehl zur Löschung des Displays
void lcd_clear( void ) {
  lcd_command( LCD_CLEAR_DISPLAY );
  _delay_ms( LCD_CLEAR_DISPLAY_MS );
}

//////////////////////////////////////////////////////////////////////// 
////////
// Sendet den Befehl: Cursor Home
void lcd_home( void ) {
  lcd_command( LCD_CURSOR_HOME );
  _delay_ms( LCD_CURSOR_HOME_MS );
}

//////////////////////////////////////////////////////////////////////// 
////////
// Setzt den Cursor in Zeile y (0..3) Spalte x (0..15)

void lcd_setcursor( uint8_t x, uint8_t y ) {
  uint8_t data;

  switch (y) {
    case 0:    // 1. Zeile
    data = LCD_SET_DDADR + LCD_DDADR_LINE1 + x;
    break;
    case 1:    // 2. Zeile
    data = LCD_SET_DDADR + LCD_DDADR_LINE2 + x;
    break;
    case 2:    // 3. Zeile
    data = LCD_SET_DDADR + LCD_DDADR_LINE3 + x;
    break;
    case 3:    // 4. Zeile
    data = LCD_SET_DDADR + LCD_DDADR_LINE4 + x;
    break;
    default:
    return; // für den Fall einer falschen Zeile
  }

  lcd_command( data );
}

//////////////////////////////////////////////////////////////////////// 
////////
// Schreibt einen String auf das LCD

void lcd_string( const char *data ) {
  while( *data != '\0' )
  lcd_data( *data++ );
}

void lcd_string_xy( uint8_t x, uint8_t y, const char *data ) {
  lcd_setcursor( x, y );
  lcd_string( data );
}

//////////////////////////////////////////////////////////////////////// 
////////
// Schreibt eine Zahl auf das LCD

void lcd_number( uint8_t number, uint8_t len, uint8_t fill ) {
  uint8_t digit1 = 0;
  uint8_t digit2 = 0;
  while (number >= 100) {
    digit1++;
    number -= 100;
  }
  while (number >= 10) {
    digit2++;
    number -= 10;
  }
  if (len > 2) lcd_data( (digit1 != 0) ? digit1+'0' : fill );
  if (len > 1) lcd_data( ((digit1 != 0) || (digit2 != 0)) ? digit2+'0' : 
fill );
  lcd_data( number+'0' );
}

void lcd_number_xy( uint8_t x, uint8_t y, uint8_t number, uint8_t len, 
uint8_t fill ) {
  lcd_setcursor( x, y );
  lcd_number( number, len, fill );
}

//////////////////////////////////////////////////////////////////////// 
////////
// Schreibt einen String auf das LCD
// String liegt direkt im Flash Speicher

void lcd_string_P( PGM_P data ) {
  uint8_t tmp;

  tmp = pgm_read_byte( data );
  while( tmp != '\0' ) {
    lcd_data( tmp );
    data++;
    tmp = pgm_read_byte( data );
  }
}

//////////////////////////////////////////////////////////////////////// 
////////
// Schreibt ein Zeichen in den Character Generator RAM
// Daten liegen direkt im RAM

void lcd_generatechar( uint8_t code, const uint8_t *data, uint8_t lines 
) {
  uint8_t i;

  // Startposition des Zeichens einstellen
  lcd_command( LCD_SET_CGADR | (code<<3) );
  // Bitmuster übertragen
  for ( i=0; i<lines; i++ ) {
    lcd_data( *data++ );
  }
}

//////////////////////////////////////////////////////////////////////// 
////////
// Schreibt ein Zeichen in den Character Generator RAM
// Daten liegen direkt im Flash-Speicher

void lcd_generatechar_P( uint8_t code, PGM_P data, uint8_t lines ) {
  uint8_t i;

  // Startposition des Zeichens einstellen
  lcd_command( LCD_SET_CGADR | (code<<3) );
  // Bitmuster übertragen
  for ( i=0; i<lines; i++ ) {
    lcd_data( pgm_read_byte(data) );
    data++;
  }
}

# --> GCC_Application_1
/*
 * GccApplication1.c
 *
 * Created: 07.03.2015 22:47:45
 *  Author: Asus
 */

#define F_CPU 10000000UL

#include <avr/io.h>
#include "lcd-routines.h"

int main(void)
{
  DDRD |= (1 << PD7);
  PORTD |= (1 << PD7);

  lcd_init();
  lcd_clear();
  lcd_setcursor(0, 0);
  lcd_string("Hello World");

    while(1)
    {
        //TODO:: Please write your application code
    }
}


 Das ist unsere momentanes LCD Programm zum testen. Also kommends sind
 teils falsch und verschoben und alles ;) viel copy and past und wieder
 löschen ... zum ausprobieren, was funzt und was nicht. ;)

 Momentan haben wir das Problem, das wir nichts geschrieben bekommen und
 auch ncihts gelöscht bekommen. Wir haben irgend welche willkürlichen
 Zeichen auf dem Display, die bei immer wieder aufspielen einfach nur
 verschoben werden. Ansonsten verändert sich nichts.

 Vllt. könnt ihr mir ja helfen.



// Über eure Hilfe würde ich mich wirklich SEHR dolle freuen.

Mfg
JuBa3006

: Bearbeitet durch User
von Peter D. (peda)


Lesenswert?

Antwort schreiben
Wichtige Regeln - erst lesen, dann posten!

    Groß- und Kleinschreibung verwenden
    Längeren Sourcecode nicht im Text einfügen, sondern als 
Dateianhang

: Bearbeitet durch User
von Marco G. (grmg2010)


Lesenswert?

JuBa 3006 schrieb:
> lcd_setcursor(0, 0);

Die 0. Zeile gibt es nicht auf dem LCD, sie fängt mit eins an. Dürfte 
aber nicht zu viele Auswirkungen haben.

Habt ihr die Adressen der Zeilen noch einmal überprüft, ob die stimmen? 
Ist die Taktfrequenz richtig eingestellt? Ist das Display richtig 
angeschlossen (wahrscheinlich vom vorhergehenden Programm mit Bascom, 
oder wurde etwas geändert)

von Peter D. (peda)


Lesenswert?

Marco G. schrieb:
> Die 0. Zeile gibt es nicht auf dem LCD, sie fängt mit eins an.

D.h. Du hast Dir den Quelltext von lcd_setcursor() angesehen und 
festgestellt, daß die 0 verboten ist?

Unter Programmierern ist es gängige Praxis, bei 0 anzufangen. Warum soll 
man einen Zählwert verschenken?

von Nil (nilsnilss)


Lesenswert?

1
////////////////////////////////////////////////////////////////////////////////
2
// Setzt den Cursor in Zeile y (0..3) Spalte x (0..15)
3
void lcd_setcursor( uint8_t x, uint8_t y ) 
4
{
5
...
6
}

von JuBa 3. (juba3006)


Lesenswert?

Moin
Danke schon einmal für die ganzen Antworten. !

Wir haben das ganze auch mal mit lcd_setcoursor( 1 ,1 ) ausprobiert und 
mussten feststellen, das sich nichts verändert.

Die Taktfrequenz liegt bei 10 MHz momentan. Original wären 1 MHz.
Das können wir noch einmal ausprobieren.
Wie könnte man das ganze denn in C lösen wenn man auf 10MHz laufen 
wollen würde ?

//Ich werde das mal mit 1 MHz ausprobieren und dann euch berichten ;)



Mfg
JuBa3006

von Nil (nilsnilss)


Lesenswert?

Läuft der Controller wirklich mit 10 MHz oder habt ihr einfach nur 
10000000 in F_CPU geschrieben?

von JuBa 3. (juba3006)


Lesenswert?

Moin
Ich haben das nur f_CPU (10 MHz )  eingestellt .... ich musste gerade 
feststellen, das ich die crystal frequnez ja im compiler einstellen muss 
und nicht im Programm.

//Also der Compiler sollte noch im original modus sein ....

: Bearbeitet durch User
von Nil (nilsnilss)


Lesenswert?

Nein die musst du nicht im Compiler einstellen (was auch immer du damit 
meinst). Such mal nach dem Stichwort Fuses.

von Joachim B. (jar)


Angehängte Dateien:

Lesenswert?

Nils Friess schrieb:
> Nein die musst du nicht im Compiler einstellen (was auch immer du damit
> meinst). Such mal nach dem Stichwort Fuses.

wie kommst du auf das schmale Brett?


hier wird doch durch Wahl der Bauteile auch die F_CPU festgelegt.

von Karl H. (kbuchegg)


Lesenswert?

Joachim B. schrieb:
> Nils Friess schrieb:
>> Nein die musst du nicht im Compiler einstellen (was auch immer du damit
>> meinst). Such mal nach dem Stichwort Fuses.
>
> wie kommst du auf das schmale Brett?

Weil das der Regelfall ist.
Bei F_CPU kann ich hinschreiben, was ich will. Das kümmert den 
tatsächlichen µC herzlich wenig. Der läuft so schnell oder so langsam 
wie es seine Fuseeinstellung zusammen mit einem ev. Quarz vorgibt.

> hier wird doch durch Wahl der Bauteile auch die F_CPU festgelegt.

Arduino ist eine andere Geschichte. Einen Arduino kauft man fertig und 
da ist dann schon die Fuse Einstellung vom Hersteller erledigt worden.

von Karl H. (kbuchegg)


Lesenswert?

Bei ihm sieht die Sache aber so aus, dass das Display anscheinend 
grundsätzlich ja läuft und auch angesteuert wird.
Ich schätze mal, da sind ein paar Datenleitungen vertauscht und oder 
haben Kurzschluss mit Vcc oder GND. Verkabelung noch mal prüfen.

von Karl H. (kbuchegg)


Lesenswert?

>
1
> #define LCD_PORT PORTC
2
> #define LCD_DDR DDRC
3
>

Auch wäre es gut zu wissen, welcher AVR da am Werk ist.
Einige Prominente haben am Port C das JTAG Interface liegen, das per 
Default ab Werk eingeschaltet ist, wodurch die Pins nicht benutzt werden 
können.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.