www.mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik ATmega und LCD-Segmentanzeige


Autor: Rolf Degen (rolfdegen)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Hallöchen..

Ich möchte an einem ATmega169 mit integriertem LCD-Controller eine 
LCD-Segmentanzeige betreiben.

Die Anschlussbelegung der LCD-Anzeige an den Atmega-Ports ist durch eine 
Kontaktfolie vorgegeben, so das ich diesbezüglich keine Änderung 
vornehmen kann.

Aus diesem Grund belegt ein Digit (Ziffer) unterschiedliche 
COM-Anschlüssen am ATmega (siehe Bild im Anhang). Das macht die 
Ansteuerung per Software etwas kompliziert, da man für die Darstellung 
einer Ziffer mehrere Ausgaberegister des ATmegas ansprechen muß.

Ich habe ein kleines C-Progrämmchen geschrieben und benutze für die 
Ausgabe der einzelnen Ziffern jeweils eine Funktions-Routine (siehe 
Code).
Das Programm zeigt abwechselnd die Ziffer "0" und die Ziffer "1" auf der 
LCD-Segmentanzeige an.

Die Belegung der Port-Anschlüsse entspricht nicht denen in der 
Abbildung.
//-------------------------------------------------------------------------
// Titel        : "Eberle Raumthermostat mit DS18S20"
//-------------------------------------------------------------------------
// Funktion     : Digitales Raumthermostat mit Real-Time-Clock,
//              : Temperatursensor DS18S20 und LCD-Segmentanzeige
//-------------------------------------------------------------------------
// Prozessor    : ATmega169V
// Takt         : 1.0 MHz / 3.3 Volt
// Sprache      : C
// C-Compiler   : WinAVR - 20100110 (programmiert mit Eclipse-Galileo)
//              : (http://sourceforge.net/projects/winavr/)
// Datum        : 18.05.2010
// Version      : 1.0
// Autor        : Rolf Degen
// eMail        : rolfdegen@hotmail.com
//-------------------------------------------------------------------------

#define     DF_CPU 1000000           // Taktfrequenz ATmega
#include    <avr/io.h>               // AVR Register und Konstantendefinitionen
#include    <util/delay.h>           // Bibliothek mit Warteroutinen (es wird kein Timer im ATmega verwendet)

#define     bit_0 0
#define     bit_1 1
#define     bit_2 2
#define     bit_3 3
#define     bit_4 4
#define     bit_5 5
#define     bit_6 6
#define     bit_7 7



void lcd_init()
{
/* Use 32 kHz crystal oscillator */
/* 1/3 Bias and 1/3 duty, SEG21:SEG24 is used as port pins */
LCDCRB = (1<<LCDCS) | (1<<LCDMUX1) | (1<<LCDMUX0) | (1<<LCDPM0) | (1<<LCDPM1) | (1<<LCDPM2);
/* Using 16 as prescaler selection and 7 as LCD Clock Divide */
/* gives a frame rate of 49 Hz */
LCDFRR = (1<<LCDCD2) | (1<<LCDCD1);
/* Set segment drive time to 125 ¼s and output voltage to 3.3 V*/
LCDCCR = (1<<LCDDC1) | (1<<LCDCC3) | (1<<LCDCC2) | (1<<LCDCC1);
/* Enable LCD, default waveform and no interrupt enabled */
LCDCRA = (1<<LCDEN);
}

void digit1_0()
{
  LCDDR1  |=(1<<bit_0);  // Ziffer "0" anzeigen
  LCDDR1  |=(1<<bit_1);
  LCDDR6  |=(1<<bit_0);
  LCDDR6  &=~(1<<bit_1);
  LCDDR11 |=(1<<bit_1);
  LCDDR11 |=(1<<bit_0);
  LCDDR16 |=(1<<bit_1);
}
void digit1_1()
{
  LCDDR1  &=~(1<<bit_0);  // Ziffer "1" anzeigen
  LCDDR1  &=~(1<<bit_1);
  LCDDR6  |=(1<<bit_0);
  LCDDR6  &=~(1<<bit_1);
  LCDDR11 &=~(1<<bit_1);
  LCDDR11 |=(1<<bit_0);
  LCDDR16 &=~(1<<bit_1);
}
//#################################################################
// Hauptprogramm
//#################################################################

int main (void)
     {
// init LCD-Controller
  lcd_init();
// Programmschleife
  while(1)
  {
  digit1_0();
  _delay_ms(500);
  digit1_1();
  _delay_ms(500);
    }
  return 0;
   }

Das C-Programm funktioniert eigentlich recht gut, aber ich möchte das 
ganze doch etwas eleganter und profezioneller lösen. Ich denke da an ein 
Array oder so was ähnliches. Aber leider habe ich diesbezüglich keine 
Erfahrung und hoffe auf Euch und ein paar Tips für die Programmierung.

Gruß Rolf

Autor: Lord Ziu (lordziu)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo Rolf,

Also wenn es wirklich nur eine 1 1/2 stellige Anzeige ist, würde ich das 
genauso machen, wie du es jetzt hast. Du musst ja nur 11 Ziffern 
darstellen (1.digit: aus/1; 2.digit: 0-9). Da machst du dir 11 
Funktionen, wie du schon angefangen hast und eine Steuerfunktion:

void digit0_1();
void digit1_0();
void digit1_1();
void digit1_2();
// ...

void printNumber(uint8_t number, uint8_t trailingZero)
{
    clearAllPins();   // Alte Ziffer löschen

    if(trailingZero)
        digit0_1();

    switch(number)
    {
    case 0:
        digit1_0();
        break;
    case 1:
        digit1_1();
        break;
    // ....
}


Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Rolf Degen schrieb:

> Ich denke da an ein
> Array oder so was ähnliches. Aber leider habe ich diesbezüglich keine
> Erfahrung und hoffe auf Euch und ein paar Tips für die Programmierung.

Arrays sind ja keine Hexerei.
Im Grunde geht es doch nur darum, dass du irgendwo her kriegen musst, ob 
für ein bestimmte darzustellende Ziffer, das entsprechende Segment auf 0 
oder auf 1 gesetzt werden muss.

Das kannst du dir zb Bitcodiert in einem uint8_t ablegen. Ein Array von 
derartigen Werten beschreibt dann die notwendigen Kombinationen für alle 
Ziffern.

Eine spezielle Funktion holt sich aus dem Array den entsprechenden Wert, 
dröselt die Bits wieder auf und setzt die entsprechenden Registerwerte 
dementsprechend.

Das ist der Plan. Fangen wir an.
Als erstes überlegen wir uns eine Zuordnung von Bits in einem uint8_t zu 
den entsprechenden Segmenten. Die erfinden wir einfach. zb so
                    6
                  *******
                 *       *
               5 *       * 0
                 *   4   *
                  *******
                 *       *
               3 *       * 1
                 *       *
                  *******
                    2
D.h. für eine darzustellende '1' müssen die Bits 0 und 1 auf 1 gesetzt 
werden, für eine '2' sind es die Bits 6, 0, 4, 3, 2 etc.
uint8_t  digitCodes[10] =
           { 0b01101111,    // '0'
             0b00000011,    // '1'
             0b01011100,    // '2'
             0b00000000,    // '3'
             0b00000000,    // '4'
             0b00000000,    // '5'
             0b00000000,    // '6'
             0b00000000,    // '7'
             0b11111111,    // '8'
             0b00000000,    // '9'
           };
(die noch fehlenden musst du ergänzen)

Soweit so gut. Jetzt wissen wir, welche Elemente eingeschaltet und 
welche ausgeschaltet werden müssen. Alles was noch fehlt, ist eine 
Funktion, die sich für eine Ziffer das entsprechende Codebyte holt, die 
einzelnen Bits rausholt und je nach Wert des Bits, das entsprechende 
Digit ein oder aus schaltet
void digit0( uint8_t value )
{
  uint8_t digitCode;

  if( value > 9 )             // nur zur Sicherheit. Der Fall darf nie
    value = 0                 // auftreten

  //
  // hole das Bitmuster der Segmente für diese Ziffer
  //
  digitCode = digitCodes[ value ];

  //
  // die einzelnen Bits betrachten
  // sind nur 8, daher erst mal wenig Aufwand
  //
  // um den Schreibaufwand gering zu halten, ein 'cleveres' Makro machen
  //

#define TEST_DIGIT(digit_bit, port, port_bit )      \
  if( digitCode & digit_bit )                       \
    port |=  (1 << port_bit);                       \
  else                                              \
    port &= ~(1 << port_bit);

  TEST_DIGIT( 0x01, LCDDR1,  bit_0 );     // Bit 0
  TEST_DIGIT( 0x02, LCDDR1,  bit_1 );     // Bit 1
  TEST_DIGIT( 0x04, LCDDR6,  bit_0 );     // Bit 2
  TEST_DIGIT( 0x08, LCDDR6,  bit_1 );     // Bit 3
  TEST_DIGIT( 0x10, LCDDR11, bit_0 );     // Bit 4
  TEST_DIGIT( 0x20, LCDDR11, bit_1 );     // Bit 5
  TEST_DIGIT( 0x40, LCDDR16, bit_0 );     // Bit 6
  TEST_DIGIT( 0x80, LCDDR16, bit_1 );     // Bit 7

#undef TEST_DIGIT
}

Und das wars dann auch schon. Ob die Zuordung und Umsetzung der 
einzelnen 'Bits' laut obiger Zeichnung zu den µC Registern und Bits 
stimmt, musst du kontrollieren.

Aber jetzt kannst du die Funktion mit einer Zahl von 0 bis 9 aufrufen, 
und auf dem LCD müssten die richtigen Segmente dafür eingeschaltet 
werden.
void main()
{
  ....

  while( 1 ) {
    for( i = 0; i < 9; ++i ) {
      digit0( i );
      _delay_ms( 500 );
    }
  }
}

Autor: Rolf Degen (rolfdegen)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Danke für Eure Vorschläge. Werde es mal ausprobieren.

In der LCD-Segmentanzeige werden insgesammt 5 Digits dargestellt. Zwei 
sind für die Uhrzeit und die anderen drei für die Temperaturanzeige.

Gruß Rolf

Autor: Lord Ziu (lordziu)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Dann macht der Weg von Karl heinz natürlich mehr Sinn. Du musst dann nur 
die Ports und Pins den Segmenten der einzelnen Anzeigen zuweisen und 
kannst alles bequem über eine Funktion steuern.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

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

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.