Forum: Mikrocontroller und Digitale Elektronik ATmega und LCD-Segmentanzeige


von Rolf D. (rolfdegen)


Angehängte Dateien:

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.
1
//-------------------------------------------------------------------------
2
// Titel        : "Eberle Raumthermostat mit DS18S20"
3
//-------------------------------------------------------------------------
4
// Funktion     : Digitales Raumthermostat mit Real-Time-Clock,
5
//              : Temperatursensor DS18S20 und LCD-Segmentanzeige
6
//-------------------------------------------------------------------------
7
// Prozessor    : ATmega169V
8
// Takt         : 1.0 MHz / 3.3 Volt
9
// Sprache      : C
10
// C-Compiler   : WinAVR - 20100110 (programmiert mit Eclipse-Galileo)
11
//              : (http://sourceforge.net/projects/winavr/)
12
// Datum        : 18.05.2010
13
// Version      : 1.0
14
// Autor        : Rolf Degen
15
// eMail        : rolfdegen@hotmail.com
16
//-------------------------------------------------------------------------
17
18
#define     DF_CPU 1000000           // Taktfrequenz ATmega
19
#include    <avr/io.h>               // AVR Register und Konstantendefinitionen
20
#include    <util/delay.h>           // Bibliothek mit Warteroutinen (es wird kein Timer im ATmega verwendet)
21
22
#define     bit_0 0
23
#define     bit_1 1
24
#define     bit_2 2
25
#define     bit_3 3
26
#define     bit_4 4
27
#define     bit_5 5
28
#define     bit_6 6
29
#define     bit_7 7
30
31
32
33
void lcd_init()
34
{
35
/* Use 32 kHz crystal oscillator */
36
/* 1/3 Bias and 1/3 duty, SEG21:SEG24 is used as port pins */
37
LCDCRB = (1<<LCDCS) | (1<<LCDMUX1) | (1<<LCDMUX0) | (1<<LCDPM0) | (1<<LCDPM1) | (1<<LCDPM2);
38
/* Using 16 as prescaler selection and 7 as LCD Clock Divide */
39
/* gives a frame rate of 49 Hz */
40
LCDFRR = (1<<LCDCD2) | (1<<LCDCD1);
41
/* Set segment drive time to 125 ¼s and output voltage to 3.3 V*/
42
LCDCCR = (1<<LCDDC1) | (1<<LCDCC3) | (1<<LCDCC2) | (1<<LCDCC1);
43
/* Enable LCD, default waveform and no interrupt enabled */
44
LCDCRA = (1<<LCDEN);
45
}
46
47
void digit1_0()
48
{
49
  LCDDR1  |=(1<<bit_0);  // Ziffer "0" anzeigen
50
  LCDDR1  |=(1<<bit_1);
51
  LCDDR6  |=(1<<bit_0);
52
  LCDDR6  &=~(1<<bit_1);
53
  LCDDR11 |=(1<<bit_1);
54
  LCDDR11 |=(1<<bit_0);
55
  LCDDR16 |=(1<<bit_1);
56
}
57
void digit1_1()
58
{
59
  LCDDR1  &=~(1<<bit_0);  // Ziffer "1" anzeigen
60
  LCDDR1  &=~(1<<bit_1);
61
  LCDDR6  |=(1<<bit_0);
62
  LCDDR6  &=~(1<<bit_1);
63
  LCDDR11 &=~(1<<bit_1);
64
  LCDDR11 |=(1<<bit_0);
65
  LCDDR16 &=~(1<<bit_1);
66
}
67
//#################################################################
68
// Hauptprogramm
69
//#################################################################
70
71
int main (void)
72
     {
73
// init LCD-Controller
74
  lcd_init();
75
// Programmschleife
76
  while(1)
77
  {
78
  digit1_0();
79
  _delay_ms(500);
80
  digit1_1();
81
  _delay_ms(500);
82
    }
83
  return 0;
84
   }

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

von Lord Z. (lordziu)


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:
1
void digit0_1();
2
void digit1_0();
3
void digit1_1();
4
void digit1_2();
5
// ...
6
7
void printNumber(uint8_t number, uint8_t trailingZero)
8
{
9
    clearAllPins();   // Alte Ziffer löschen
10
11
    if(trailingZero)
12
        digit0_1();
13
14
    switch(number)
15
    {
16
    case 0:
17
        digit1_0();
18
        break;
19
    case 1:
20
        digit1_1();
21
        break;
22
    // ....
23
}

von Karl H. (kbuchegg)


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
1
                    6
2
                  *******
3
                 *       *
4
               5 *       * 0
5
                 *   4   *
6
                  *******
7
                 *       *
8
               3 *       * 1
9
                 *       *
10
                  *******
11
                    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.
1
uint8_t  digitCodes[10] =
2
           { 0b01101111,    // '0'
3
             0b00000011,    // '1'
4
             0b01011100,    // '2'
5
             0b00000000,    // '3'
6
             0b00000000,    // '4'
7
             0b00000000,    // '5'
8
             0b00000000,    // '6'
9
             0b00000000,    // '7'
10
             0b11111111,    // '8'
11
             0b00000000,    // '9'
12
           };
(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
1
void digit0( uint8_t value )
2
{
3
  uint8_t digitCode;
4
5
  if( value > 9 )             // nur zur Sicherheit. Der Fall darf nie
6
    value = 0                 // auftreten
7
8
  //
9
  // hole das Bitmuster der Segmente für diese Ziffer
10
  //
11
  digitCode = digitCodes[ value ];
12
13
  //
14
  // die einzelnen Bits betrachten
15
  // sind nur 8, daher erst mal wenig Aufwand
16
  //
17
  // um den Schreibaufwand gering zu halten, ein 'cleveres' Makro machen
18
  //
19
20
#define TEST_DIGIT(digit_bit, port, port_bit )      \
21
  if( digitCode & digit_bit )                       \
22
    port |=  (1 << port_bit);                       \
23
  else                                              \
24
    port &= ~(1 << port_bit);
25
26
  TEST_DIGIT( 0x01, LCDDR1,  bit_0 );     // Bit 0
27
  TEST_DIGIT( 0x02, LCDDR1,  bit_1 );     // Bit 1
28
  TEST_DIGIT( 0x04, LCDDR6,  bit_0 );     // Bit 2
29
  TEST_DIGIT( 0x08, LCDDR6,  bit_1 );     // Bit 3
30
  TEST_DIGIT( 0x10, LCDDR11, bit_0 );     // Bit 4
31
  TEST_DIGIT( 0x20, LCDDR11, bit_1 );     // Bit 5
32
  TEST_DIGIT( 0x40, LCDDR16, bit_0 );     // Bit 6
33
  TEST_DIGIT( 0x80, LCDDR16, bit_1 );     // Bit 7
34
35
#undef TEST_DIGIT
36
}

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.
1
void main()
2
{
3
  ....
4
5
  while( 1 ) {
6
    for( i = 0; i < 9; ++i ) {
7
      digit0( i );
8
      _delay_ms( 500 );
9
    }
10
  }
11
}

von Rolf D. (rolfdegen)


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

von Lord Z. (lordziu)


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.

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.