LCD Programmierung Nibble verschwinden



  • Danke erstmal für die Antworten.
    Das mit den namen werde ich überarbeiten, danke für den Hinweis.
    Das Programm habe ich schon gedebugged. Dort hat eigentlich alles gepasst. Ich glaube ich werde jetzt mal mein altes Oszi raus suchen und schauen, wo das Nibble verschwindet.



  • @DirkB Es handelt sich um ein 4x20 Display mit einem SSD1803 Controller.



  • @IchBins

    delay_display=50000/FAKTOR_US;
    while (delay_display);
    

    Sieht merkwürdig aus. Wer dekrementiert die Variable delay_display?

    Ein Timer-Interrupt? Wenn ja, läuft dieser auch? Prüfe auch ob die Variable delay_display als volatile definiert wurde, sonst bekommst du Probleme mit Compiler-Optimierungen.

    PS:
    Gibt es da nicht alternative Funktionen ala __delay_ms()?

    PIC Microcontroller - __delay_ms() no effect



  • Wir haben ein eigenes Timer Programm geschrieben.
    Hier die c Datei:

    #include "defines.h"
    
    uint16_t volatile timer_sys_intervall;      ///< system timer in ms bereich
    uint16_t volatile delay_display;            ///< delay timer display us
    uint16_t volatile delay_drucksensor;        ///< delay timer drucksensor us
    uint16_t volatile delay_ledtreiber;         ///< delay timer led-treiber us
    uint16_t volatile delay_lichtsensor;        ///< timer delay lichtsensor in us
    uint16_t volatile delay_temp_hum_ms;        ///< timer delay temperatur und Luftfeuchtigkeitssensor in ms                            
    uint16_t volatile delay_temp_hum;           ///< timer delay temperatur und Luftfeuchtigkeitssensor in us
    uint16_t volatile delay_lichtsensor_ms;     ///< timer delay lichtsensor in ms
    
    // *********************************************************
    /// \fn void TMR1_CallBack(void)
    /// \par Funktion:
    /// - interruptroutine fuer Timer 1
    /// - wird jede 1 ms aufgerufen
    /// - erhoehen des Timerintervalls
    // *********************************************************
    void TMR1_CallBack(void)                                                        
    {
        //TESTPUNKT_1_TOGGEL;
        
        timer_sys_intervall++;
        
        if(delay_temp_hum_ms)           //Timer herunterzaehlen delay temp und hum sensor
        {
            delay_temp_hum_ms--;
        }
        if(delay_lichtsensor_ms)           //Timer herunterzaehlen delay lichtsensor 
        {
            delay_lichtsensor_ms--;
        }
           
    }
    
    
    // *********************************************************
    /// \fnvoid TMR2_CallBack(void)
    /// \par Funktion:
    /// - interruptroutine fuer Timer 2
    /// - wird jede 1 us aufgerufen
    /// - veringern der delays
    // *********************************************************
    void TMR2_CallBack(void)                                                        
    {
        //TESTPUNKT_2_TOGGEL;
        
        if(delay_display)                                                           //Wenn delay_display gesetzt dann herunterzaehlen
        {
          delay_display--;  
        }
        if(delay_ledtreiber)                                                        // Delay fuer den Led-treiber
        {
            delay_ledtreiber--;
        }
        if(delay_lichtsensor)                                                       // Delay fuer den Lichtsensor
        {
            delay_lichtsensor--;
        }
        if(delay_drucksensor)                                                       // Delay fuer den Drucksensor
        {
            delay_drucksensor--;
        }
        if(delay_temp_hum)                      //Delay fuer den Temperatur und Luftfeuchtigkeitssensor 
        {
            delay_temp_hum--;
        }
        
    }
    
    
    
    
    // *********************************************************
    /// \fn void timer(void)
    /// \par Funktion:
    /// - resetet timer_sys_intervall
    // *********************************************************
    void timer()
    {
        timer_sys_intervall =0; 
        
        
        
    }
    
    
    
    
    // *********************************************************
    /// \fn void timer_init( void )
    /// \par Funktion :
    /// - Init der Variablen
    // *********************************************************
    void timer_init()
    {
        timer_sys_intervall = 0;
        delay_display = 0;
        delay_ledtreiber = 0;
        delay_lichtsensor = 0;
        delay_drucksensor = 0;
        delay_temp_hum_ms = 0;
        delay_temp_hum = 0;
    }
    

    und hier die h Datein:

    #ifndef HILF_H
    #define	HILF_H
    
    #define FAKTOR_US   10
    
    void TMR1_CallBack(void);
    void TMR2_CallBack(void);
    void timer_init();
    void timer();
    
    extern uint16_t volatile timer_sys_intervall;                                   // timer system intervall
    extern uint16_t volatile delay_display;                                         // timer delay display in us
    extern uint16_t volatile delay_drucksensor;                                     ///< delay timer drucksensor us
    extern uint16_t volatile delay_ledtreiber;                                      // timer delay led-treiber in us
    extern uint16_t volatile delay_lichtsensor;                                     // timer delay lichtsensor in us
    
    extern uint16_t volatile delay_lichtsensor_ms;                                  // timer delay lichtsensor in ms
    extern uint16_t volatile delay_temp_hum_ms;                                     // timer delay temperatur und Luftfeuchtigkeitssensor in ms
    extern uint16_t volatile delay_temp_hum;                                     // timer delay temperatur und Luftfeuchtigkeitssensor in us
    
    
    /* Beispiel programm fuer nutzen des delays fuer den display
     * 
     * Delay setzen mit mindest wartezeit + 1, in us
     * bsp. bei mindestens 59us delay muss mit 60us gesetzt werden, da dadurch die tatsaechliche zeit zwischen 59 und 60 us variiert
     * 
     * delay_display = 50;              //Delay setzen mit mindest wartezeit + 1, in us  
     * while(delay_display);            //warten bis Zeit abgelaufen
     * 
     *  
     */
    
    
    
    #endif
    

    Ja, es gibt alternative Funktionen, wir müssen jedoch eine eigene schreiben.
    Der Timer läuft auch so weit, sonst würde die initialisierung nicht funktionieren.
    MfG



  • @IchBins
    Ok, habe dein Problem erst jetzt verstanden.

    Deine Ausgabe der Zeilen scheint etwas durcheinandergewürfelt zu sein. Erst schreibst du in Linie 3, dann in Linie 1,... Auch die Wartezeiten (LCD_CURSOR_SET_MS) sind nicht einheitlich. Ich würde folgendes erwarten:

            setcursor_lcd(LCD_ADRESSE_LINIE1,0);  // Zeile 1             
            // Warte bis Cursor gesetzt wurde     
            delay_display = LCD_CURSOR_SET_MS / 10; // LCD_CURSOR_SET_MS  = Wartezeit bis Cursor gesetzt wurde?
            while(delay_display); 
            // Gebe Text aus   
            text_LCD("Temperatur:");
            // Warte bis Textausgabe fertig ist
            delay_display =10000/FAKTOR_US;
            while(delay_display);    
        
            setcursor_lcd(LCD_ADRESSE_LINIE2,0);  // Zeile 2             
            // Warte bis Cursor gesetzt wurde     
            delay_display = LCD_CURSOR_SET_MS / 10; // LCD_CURSOR_SET_MS  = Wartezeit bis Cursor gesetzt wurde?
            while(delay_display); 
            // Gebe Text aus   
            text_LCD("Luftdruck:");
            // Warte bis Textausgabe fertig ist
            delay_display =10000/FAKTOR_US;
            while(delay_display);    
    
            setcursor_lcd(LCD_ADRESSE_LINIE3,0);  // Zeile 3             
            // Warte bis Cursor gesetzt wurde     
            delay_display = LCD_CURSOR_SET_MS / 10; // LCD_CURSOR_SET_MS  = Wartezeit bis Cursor gesetzt wurde?
            while(delay_display); 
            // Gebe Text aus   
            text_LCD("Feuchtigkeit:");
            // Warte bis Textausgabe fertig ist
            delay_display =10000/FAKTOR_US;
            while(delay_display);           
    
            setcursor_lcd(LCD_ADRESSE_LINIE4,0);  // Zeile 4             
            // Warte bis Cursor gesetzt wurde     
            delay_display = LCD_CURSOR_SET_MS / 10; // LCD_CURSOR_SET_MS  = Wartezeit bis Cursor gesetzt wurde?
            while(delay_display); 
            // Gebe Text aus   
            text_LCD("Helligkeit:");
            // Warte bis Textausgabe fertig ist
            delay_display =10000/FAKTOR_US;
            while(delay_display);           
    


  • @Quiche-Lorraine Ja, das ist so richtig. Die durchgetauschten zeilen kommen noch von meiner Fehlersuche. Die sollten eigentlich so aussehen, wie in deinem Code. Trotzdem werden die Texte immernoch in der falschen Spalte ausgegeben. Meine Vermutung ist, dass es an meiner Funktion "BYTE_ZERLEGEN_TEXT"(Zeile 104ff in der c Datei)oder "text_LCD" (Zeile 138) liegt. Vielleicht fehlt irgendwo ein delay oder ein Enable Pulse. Ich finde aber nicht wo.



  • @IchBins Warum machst du denn diese komische Berechnung bei jedem delay_display?
    Kannst du die Zeit nicht gleich in µs angeben?

    Mit wieviel MHz läuft denn dein PIC?
    Ich habe etwas von maximal 40 MHz gefunden.
    Da bleibt nicht viel Luft bei einem Interrupt von 1 µs

    Es kann auch sein, dass noch irgendwo etwas anderes gesetzt werden muss.
    Denn "Temperatur:" ist ja gerade 11 Zeichen lang.

    Du solltest dir auch ein paar #define für die Magic Numbers von LCD_COMMAND_OUT machen.
    Weißt du aus dem Stehgreif was 0x24 oder 0x20 ist?



  • @IchBins
    Kann es sein das du LCD_OUT() immer mit 4 Bits aufrufst? (siehe BYTE_ZERLEGEN() und BYTE_ZERLEGEN_TEXT())

    Warum steht da innerhalb von LCD_OUT() folgendes?

    int LCD_OUT(uint8_t data)
    {
    
    	switch (data)//je nach Hex Wert werden verschiedene Datenleitungen auf die entsprechenden Pegel gelegt
    	{	
    	// ...      
    	case 0x20:   // Kann dieser Wert überhaupt erreicht werden, da in data immer nur 4 Bit angegeben sind?
    	{
    		LCD_D0_LOW;
    		LCD_D1_HIGH;
    		LCD_D2_LOW;
    		LCD_D3_LOW;
    		break;
    	}
    


  • @DirkB sagte in LCD Programmierung Nibble verschwinden:

    @IchBins Warum machst du denn diese komische Berechnung bei jedem delay_display?
    Kannst du die Zeit nicht gleich in µs angeben? -> Das ist ein Gruppenprojetkt und die delay Funktion hat jemand anderes geschrieben. Mir wurde nur die Anweisung gegeben, es so zu machen.

    Mit wieviel MHz läuft denn dein PIC? mit 25Mhz
    Ich habe etwas von maximal 40 MHz gefunden.
    Da bleibt nicht viel Luft bei einem Interrupt von 1 µs

    Es kann auch sein, dass noch irgendwo etwas anderes gesetzt werden muss.
    Denn "Temperatur:" ist ja gerade 11 Zeichen lang. -> wie meinst du das? dass die letzten 9 Zeichen mit Leerstellen gefüllt sind?

    Du solltest dir auch ein paar #define für die Magic Numbers von LCD_COMMAND_OUT machen.
    Weißt du aus dem Stehgreif was 0x24 oder 0x20 ist? -> da bin ich gerade dabei, die kann ich dann anfügen, wenn ich sie fertig habe



  • @Quiche-Lorraine Ich rufe LCD_OUT() immer mit einem Byte auf, jedoch sind davon entweder die ersten 4 Bit oder die letzen 4 Bit =0
    0b00001111 wäre in hex ja 0x0F und
    0b11110000 wäre in hex ja 0xF0. Die können beide erreicht werden



  • @IchBins sagte in LCD Programmierung Nibble verschwinden:

    wie meinst du das? dass die letzten 9 Zeichen mit Leerstellen gefüllt sind?

    Nein.
    Nach Ausgabe von "Temperatur:" steht der Cursor in der 11 Spalte.
    Der Wert bleibt dann (irgendwie) erhalten.

    Gib doch einfach nur mal "Temp:" aus und schau, wo dann die Texte in den folgenden Zeilen stehen.
    Oder fang mal mit Zeile 4 an.

    Das "Temperatur:" in der 1. Spalte steht, kann ja auch mit dem LCD_Init zu tun haben.



  • @DirkB Hätte ich vielleicht schreiben sollen. Ich habe die Temperatur schon in Zeile 1 Spalte 0, zeile 1 Spalte 8, Zeile 2 Spalte 0 und Zeile 2 Spalte 8 ausgegeben. Egal in welcher Konstellation ich die Texte ausgebe, auch wenn ich zuerst etwas in Zeile 1 und dann in Zeile 2 ausgebe. Der erste ausgegebene Text ist an der richtigen Position. Der 2. 3. und 4. ausgegebene Text ist immer in der richtigen zeile aber in Spalte 11 der jeweiligen Spalte.



  • Laut Datenblatt mußt du immer jeweils die drei Bits RE, RS und RW passend setzen (sowie dann die entsprechenden Data-Bits).
    Für "Cursor / Display Shift" also

    LCD_RE_LOW;
    LCD_RS_LOW;
    LCD_RW_LOW;
    

    Außerdem scheint das nur eine relative Positionierung zu sein (keine absolute). Dafür müßtest du dann selber die aktuelle Position dir merken und die Differenz (x / y) ausrechnen.



  • @Th69 so wie ich das verstanden habe werden Befehle immer dann vom Display eingelesen, wenn der Enable Pin von 1 auf 0 wechselt, also bei fallender Flanke. Wenn ich aber den Enable Pin auf 0 setze, bevor ich den Befehl an die Datenleitungen anlege, werden die Daten in dem Fall dann bei steigender Flanke übertragen?



  • @DirkB
    Hier ist nochmal meine überarbeitete init ohne "Magic Numbers" 😉

    void init_lcd()                                          
    {
        delay_display=LCD_BOOTUP/FAKTOR_US;                                             //Warten, bis Display bereit ist  
        while (delay_display);
        LCD_RS_LOW;                                                                     //RS Pin auf "low" -> Befehl
        delay_display=LCD_SET_PIN/FAKTOR_US;
        while (delay_display);
        LCD_RW_LOW;                                                                     //RW Pin auf "low" -> write
        delay_display=LCD_SET_PIN/FAKTOR_US;
        while (delay_display);
        LCD_EN_HIGH;                                                                    //Enable Pin auf "high"
        delay_display=LCD_SET_PIN/FAKTOR_US;
        while (delay_display);
        LCD_OUT(Function_Set_8Bit);                                                     //0x03 8Bit-Modus
        LCD_EN_PULSE();    
        delay_display=LCD_8BIT/FAKTOR_US;
        while (delay_display);
        LCD_OUT(Function_Set_8Bit);                                                     //0x03 8Bit-Modus
        LCD_EN_PULSE();    
        delay_display=LCD_8BIT/FAKTOR_US;
        while (delay_display);
        LCD_OUT(Function_Set_8Bit);                                                     //0x03 8Bit-Modus
        LCD_EN_PULSE();    
        delay_display=LCD_8BIT/FAKTOR_US;
        while (delay_display);
        LCD_OUT(Function_Set_4Bit);                                                     //0x02 4Bit-Modus
        LCD_EN_PULSE();    
        delay_display=LCD_4BIT/FAKTOR_US;
        while (delay_display);
        LCD_COMMAND_OUT(Function_Set_Reg1);                                             //0x24 Function Set extension register = 1
        delay_display=50/FAKTOR_US;
        while (delay_display);
        LCD_COMMAND_OUT(Extend_Function_Set);                                           //0x09 extend Function set 5dot font width, normal cursor,4-line display
        delay_display=50/FAKTOR_US;
        while (delay_display);
        LCD_COMMAND_OUT(Function_Set_Reg0);                                             //0x20 Function Set extension register = 0
        delay_display=50/FAKTOR_US;
        while (delay_display);
        LCD_COMMAND_OUT(CURSOR_AT_HOME);                                                //0x02 Cursor At Home
        delay_display=2000/FAKTOR_US;
        while (delay_display);
        LCD_COMMAND_OUT(LCD_ALL_ON);                                                    //0x0F Display On/Off Control display on, cursor on, cursor blink on
        delay_display=50/FAKTOR_US;
        while (delay_display);
        LCD_COMMAND_OUT(LCD_CLEAR);                                                     //0x01 Display löschen und Cursor auf Home
        delay_display=2000/FAKTOR_US;
        while (delay_display);
        LCD_COMMAND_OUT(LCD_ENTRY_MODE_SET);                                            //0x06 Entry Mode Set Cursor Moving Direction increment, shift enable bit=disable
        delay_display=50/FAKTOR_US;
        while (delay_display);
        LCD_COMMAND_OUT(Function_Set_End);                                              //Function Set: 4bit, 2-line, RE=0,dot scroll,reverse bit normal
        delay_display=50/FAKTOR_US;
        while (delay_display);  
        
    }
    


  • @Th69 das RE Bit ist ja nur für das extension Register oder? Das sit bei mir nach der init immer auf 0.



  • Ich hätte noch eine andere Frage:
    Die Befehle, welche ich übertrage werden ja in den CG RAM geschrieben oder? Also wenn ich ein Byte senden will, dann wird das zuerst übertragene Nibble in den CG RAM geschrieben bis das zweite Nibble übertragen wird.
    Gibt es eine möglichkeit, dieses zu löschen?



  • @IchBins Nein.
    CG steht für Charakter Generator. Da stehen die Muster für die Zeichen drin.
    Im ROM halt fest codiert und im RAM kannst du noch 8 Zeichen selber definieren.

    Der Anzeigetext steht im DDRAM (DisplayData)

    Hier noch das Datenblatt für den Controller: https://cdn-reichelt.de/documents/datenblatt/A500/DS_SSD1803.pdf



  • @DirkB Oh, das habe ich verwechselt. Ist es dann möglich, den DDRAM zu leeren?



  • @IchBins sagte in LCD Programmierung Nibble verschwinden:

    @DirkB Oh, das habe ich verwechselt. Ist es dann möglich, den DDRAM zu leeren?

    Ja, dann ist die Anzeige leer.

    Du möchtest aber irgendwie das Interface reseten, oder?


Log in to reply