Forum: Mikrocontroller und Digitale Elektronik Funktion für eine LED-Matrix. Optimierung für Interupt ?


von jogy (Gast)


Lesenswert?

Hallo Community,

Ich bin recht neu mit Microcontrollern und hab mal eine Frage an die 
Cracks.
Ich hab eine alte LED-Matrixanzeige die ich mit einem ATMega644 
ansteuern möchte. Die Anzeige hat ein schieberegister von 120Bit (DATA 
und CLK) und 7 Eingänge um die entsprechende Zeile zu aktivieren.
Meine Funktion funktioniert, ist aber sehr rechenintensiv um sie durch 
einen Interupt zu starten. Die Funktion muss um eine anzeige zu bekommen 
durchgehend ausgeführt werden.


Meine Frage ist nun: Kann man die Funktion so optimieren, das es per 
Interupt ausführ bar wird ?

hier der Code:
1
char matrix_buffer[105] = { // 7 Zeilen a' 120 Bit
2
  0x00,0x22,0x08,0x3e,0x3c,0x1c,0x22,0x00,0x3c,0x3e,0x08,0x3c,0x22,0x00,0x00,
3
  0x00,0x36,0x14,0x08,0x22,0x08,0x22,0x00,0x22,0x20,0x14,0x22,0x22,0x00,0x00,
4
  0x00,0x2a,0x22,0x08,0x22,0x08,0x14,0x00,0x22,0x20,0x22,0x22,0x14,0x00,0x00,
5
  0x00,0x2a,0x22,0x08,0x3c,0x08,0x08,0x00,0x3c,0x3e,0x22,0x22,0x08,0x00,0x00,
6
  0x00,0x22,0x3e,0x08,0x28,0x08,0x14,0x00,0x28,0x20,0x3e,0x22,0x08,0x00,0x00,
7
  0x00,0x22,0x22,0x08,0x24,0x08,0x22,0x00,0x24,0x20,0x22,0x22,0x08,0x00,0x00,
8
 0x00,0x22,0x22,0x08,0x22,0x1c,0x22,0x00,0x22,0x3e,0x22,0x3c,0x08,0x00,0x00}; // entspricht " MATRIX READY  "
9
10
// Clk-Impuls für Schieberegister erzeugen
11
void mtx_clk_impuls()
12
    {
13
     _delay_us(1);                      // 1µsec warten
14
     mtx_steuer_port |= (1<<mtx_clk);      // Clk-Pin auf 1
15
     _delay_us(2);                       // 2µsec warten
16
     mtx_steuer_port &= ~(1<<mtx_clk);     // Clk-Pin auf 0
17
     _delay_us(1);                      // 1µsec warten
18
   return;
19
    }
20
21
void mtx_send_data_p() {
22
23
  uint8_t i1; // Pointer auf Zeile
24
  uint8_t i2; // Pointer auf Byte pro Zeile (Rückwärts zähler)
25
  uint8_t i3; // Pointer auf Bit 
26
  uint8_t i4; // Pointer auf erstes Byte der Zeile
27
  char c1;  // Byte zu bearbeiten
28
  
29
  i4 = 0;    // mit der ersten Zeile beginnen
30
  
31
  for(i1=0;i1<7;i1++) {             // Schleife 7 Zeilen
32
    for(i2=15;i2>0;i2--) {          // Schleife 15 Bytes der Zeile rückwärts auslesen, letztes Bit muss zuerst geschoben weren      
33
      c1 = matrix_buffer[(i4+(i2-1))];   // Byte aus dem Buffer holen
34
      // DDRB = (i4+(i2-1));         // Dummy für Emulator
35
      // PORTA = c1;             // Dummy für Emulator
36
      for(i3=0;i3<8;i3++) {        // Alle Bits des Bytes bearbeiten
37
        // PORTB = (1<<i3);       // Dummy für Emulator
38
        if (c1 & 0x01) {         // Bit0 in data prüfen (Bits in data mit 0b00000001 maskieren)
39
          mtx_steuer_port |= (1<<mtx_data);// wenn Bit0=1 dann Data-Leitung auf 1
40
        } else {
41
          mtx_steuer_port &=~(1<<mtx_data);// wenn Bit0=0 dann Data-Leitung auf 0
42
        }
43
        mtx_clk_impuls();        // Bit ins Schieberegister übertragen
44
        c1 = c1>>1;            // Nach rechts schieben
45
      }  
46
    }
47
    i4 = i4 + 15;                // Nächste Zeile
48
    mtx_zeilen_port = (1<<i1);        // Zeile aktivieren und anzeigen
49
    _delay_us(2200);            // Zeit wie lang die Zeile angezeigt wird, je schneller desto dunkler die anzeige
50
    mtx_zeilen_port = 0x00;          // Zeile abschalten
51
  }
52
}
MfG
Joerg

von Peter D. (peda)


Lesenswert?

Nimm die UART im SPI-Mode. Dann kannst Du immer 16 Bits in einem Rutsch 
senden.


Peter

von jogy (Gast)


Lesenswert?

Hi,

Ach so, vergessen !? .. Das Board zu beschreiben.
Ich nutze derzeit als Basis das Board von Ulrich Radig ETH_M32_EX
Die UART ist mit der RS232 belegt..
Ich hab an dem ATMega644 die PORTs C und D zu Verfügung da diese schön 
auf einem Connector geführt sind.
An PC0 - PC6 hängen die Zeilen , an PD2 - CLK, PD3 - DATA.

Joerg

von Falk B. (falk)


Lesenswert?

@jogy (Gast)

>Meine Funktion funktioniert, ist aber sehr rechenintensiv um sie durch
>einen Interupt zu starten.

Wer sagt das? So schlecht ist dein Code nicht. Hast du mal simuliert, 
wie lange der für eine Zeile braucht? Was du machst ist Soft-SPI, und 
das ist auf einem AVR schon eingermassen schnell.

> Die Funktion muss um eine anzeige zu bekommen
> durchgehend ausgeführt werden.

Klar, siehe LED-Matrix. Dazu nutzt man aber einen Timer.

>Meine Frage ist nun: Kann man die Funktion so optimieren, das es per
>Interupt ausführ bar wird ?

Ist sie schon. Aber man kann eine handvoll Sachen noch besser machen.


>// Clk-Impuls für Schieberegister erzeugen
>void mtx_clk_impuls()

Hier sollte man die Funktion direkt in den Code schreiben, damit spart 
man sich die Zeit für den Funktionsaufruf. Oder die Funktion mit INLINE 
deklarieren, dann macht das der Compiler.

>void mtx_send_data_p() {

>  uint8_t i1; // Pointer auf Zeile
>  uint8_t i2; // Pointer auf Byte pro Zeile (Rückwärts zähler)
>  uint8_t i3; // Pointer auf Bit
>  uint8_t i4; // Pointer auf erstes Byte der Zeile
>  char c1;  // Byte zu bearbeiten

Deine Variablennamen sind nichtssagend. Heutzutage nimmt man 
aussagekräftige Namen, siehe

http://www.mikrocontroller.net/articles/Strukturierte_Programmierung_auf_Mikrocontrollern#Formatierung_des_Quelltextes


Ausserdem sind das keine Pointer sondern einfache Zähler.

>  i4 = 0;    // mit der ersten Zeile beginnen

>      c1 = matrix_buffer[(i4+(i2-1))];   // Byte aus dem Buffer holen

Kann man einfacher machen.

c1 = matrix_buffer[i4];   // Byte aus dem Buffer holen
i4--;

>mtx_steuer_port |= (1<<mtx_data);// wenn Bit0=1 dann Data-Leitung auf 1

Hier könnte viel Rechenzeit sinnlos verbraten werden, wenn mtx_data eine 
Variable ist. Das sollte ein define sein, denn dann kann es der Compiler 
bei eingeschalteter Optimierung berechnen und statt einer echten 
Schiebeoperation zur Laufzeit eine Konstante einsetzen. DAS ist wichtig!
1
#define MTX_BIT 1
2
3
char matrix_buffer[105] = { // 7 Zeilen a' 120 Bit
4
  0x00,0x22,0x08,0x3e,0x3c,0x1c,0x22,0x00,0x3c,0x3e,0x08,0x3c,0x22,0x00,0x00,
5
  0x00,0x36,0x14,0x08,0x22,0x08,0x22,0x00,0x22,0x20,0x14,0x22,0x22,0x00,0x00,
6
  0x00,0x2a,0x22,0x08,0x22,0x08,0x14,0x00,0x22,0x20,0x22,0x22,0x14,0x00,0x00,
7
  0x00,0x2a,0x22,0x08,0x3c,0x08,0x08,0x00,0x3c,0x3e,0x22,0x22,0x08,0x00,0x00,
8
  0x00,0x22,0x3e,0x08,0x28,0x08,0x14,0x00,0x28,0x20,0x3e,0x22,0x08,0x00,0x00,
9
  0x00,0x22,0x22,0x08,0x24,0x08,0x22,0x00,0x24,0x20,0x22,0x22,0x08,0x00,0x00,
10
 0x00,0x22,0x22,0x08,0x22,0x1c,0x22,0x00,0x22,0x3e,0x22,0x3c,0x08,0x00,0x00}; // entspricht " MATRIX READY  "
11
12
// Clk-Impuls für Schieberegister erzeugen
13
inline void mtx_clk_impuls()
14
    {
15
     _delay_us(1);                      // 1µsec warten
16
     mtx_steuer_port |= (1<<mtx_clk);      // Clk-Pin auf 1
17
     _delay_us(2);                       // 2µsec warten
18
     mtx_steuer_port &= ~(1<<mtx_clk);     // Clk-Pin auf 0
19
     _delay_us(1);                      // 1µsec warten
20
   return;
21
    }
22
23
void mtx_send_data_p() {
24
25
  uint8_t zeile;
26
  uint8_t cnt_byte;
27
  uint8_t cnt_bit;
28
  uint8_t index;
29
  uint8_t data;  // Byte zu bearbeiten
30
  
31
  index = 14;    // mit der ersten Zeile, letztes Byte beginnen
32
  
33
  for(zeile=1; zeile!=0x80; zeile<<=1) {                // Schleife 7 Zeilen, Zeilensteuersignal
34
    for(cnt_byte=0; cnt_byte<15; cnt_byte++) {          // Schleife 15 Bytes der Zeile rückwärts auslesen, letztes Bit muss zuerst geschoben weren      
35
      data = matrix_buffer[index];                      // Byte aus dem Buffer holen
36
      index--;
37
      // DDRB = (i4+(i2-1));                            // Dummy für Emulator
38
      // PORTA = c1;                                    // Dummy für Emulator
39
      for(cnt_bit=0; cnt_bit<8; cnt_bit++) {            // Alle Bits des Bytes bearbeiten
40
        // PORTB = (1<<i3);                             // Dummy für Emulator
41
        if (data & 0x01) {                              // Bit0 prüfen
42
          mtx_steuer_port |= (1<<MTX_BIT);              // wenn Bit0=1 dann Data-Leitung auf 1
43
        } else {
44
          mtx_steuer_port &=~(1<<MTX_BIT);              // wenn Bit0=0 dann Data-Leitung auf 0
45
        }
46
        mtx_clk_impuls();                               // Bit ins Schieberegister übertragen
47
        data >>= 1;                                     // Nach rechts schieben
48
      }  
49
    }
50
    index += 30;                                        // nächste Zeile
51
    mtx_zeilen_port = zeile;                            // Zeile aktivieren und anzeigen
52
    _delay_us(2200);                                    // Zeit wie lang die Zeile angezeigt wird, je schneller desto dunkler die anzeige
53
    mtx_zeilen_port = 0x00;                             // Zeile abschalten
54
  }
55
}

von Peter D. (peda)


Lesenswert?

jogy schrieb:
> Die UART ist mit der RS232 belegt.

Der ATmega644p hat 2 UARTs.


Peter

von Karl H. (kbuchegg)


Lesenswert?

> Meine Funktion funktioniert, ist aber sehr rechenintensiv um sie
> durch einen Interupt zu starten.

Ähm. die Funktion bleibt aber konzeptionell sowieso nicht so, wie sie 
jetzt ist.

Deine jetzige Funktion macht einen Durchgang durch die komplette Matrix. 
Und genau das ist nicht das was passieren soll/muss/darf, wenn du 
interruptgetrieben multiplext.


Ein
1
  for( i = 0; i < 8; i++ )
2
  {
3
    Gib Zeile i aus
4
5
    _delay_us( 2200 );
6
  }

ersetzt sich im Interrupt Fall durch
1
ISR( ... )
2
{
3
  static uint8_t i;
4
5
  i++;
6
  if( i == 8 )
7
    i = 0;
8
9
  Gib Zeile i aus
10
}


Bei jédem USR Aufruf wird reihum die nächste Zeile ausgegeben. Die 
notwendige Wartezeit wird dadurch realisiert, dass die ISR fertig ist 
und anderem Code die Chance gibt auch was zu tun. Während der µC NICHT 
in der ISR ist, leuchten die LED einer Zeile. Und es braucht dann 
logischerweise 8 ISR Aufrufe, bis alle 8 Zeilen der Matrix einmal 
geleuchtet haben.

und so schlecht ist dann dein Originalcode für "Gib Zeile i aus" dann 
auch wieder nicht, dass der jetzt massiv zuschlagen würde. (Falk hat ja 
schon ein paar Änderungen vorgeschlagen)

Deine Hauptänderung ist der Ersatz von _delay_us durch eine vernünftige 
Interrupt Steuerung!

Das ist zunächst mal deine Hauptänderung! Und wenn das dann immer noch 
zu langsam ist (oder du ein wenig ISR Zeit sparen willst), dann 
optimierst du den Rest.

von DerDan (Gast)


Lesenswert?

Hallo,

um im embedded Bereich die Funktionen zu optimieren, sollte man das 
generierte Assembler Listing anschauen und einigermaßen verstehen 
können. So wie es der Falk schon gesagt hat kann man da dann einiges 
Erkennen und optimieren.
Als Anfänger, der sowieso mit „C“ arbeiten will, ist es eventuell 
einfacher mit einem 32 Bit Kontroller zu programmieren. Dort stößt man 
generell nicht so schnell an Ressourcen – Grenzen, durch die man erst 
optimieren muss.

Mfg
DerDan

von jogy (Gast)


Lesenswert?

Erstmal : Wow ...

Danke an alle für die vielen Hinweise. Mit soviel Feedback hatte ich 
jetzt nicht gerechnet.

Nochmal kurz zu mir, ich bin absolut neu in sachen Microcontroller. Ich 
habe in den 80'ern ein wenig mit Z80 und 65xx Prozessoren gearbeitet 
(Lehre), habe Programierkenntnisse in C und Assembler, wobei ich sehr 
sehr lange nichts mehr gemacht habe.

Mein Favorit von den Antworten ist, per Interupt nur eine Zeile anzeigen 
zu lassen.

Ich werde alle Vorschläge mal Testen und mein Ergebnis hier natürlich 
Posten (bzw. weiter Fragen ..)

MfG
Joerg

von MaWin (Gast)


Lesenswert?

> Meine Frage ist nun: Kann man die Funktion so optimieren,
> das es per Interupt ausführ bar wird ?

Natürlich, der delay muss da raus, ein delay in einer Interrupt-Funktion 
ist wahnsinnig.

Der Interrupt muss also 7 mal so schnell kommen.

Dann zeigt die Funktion jeweils eine Zeile an

a)  uint8_t i1; // Pointer auf Zeile

b)  for(i1=0;i1<7;i1++) {             // Schleife 7 Zeilen

c)    _delay_us(2200);

ersetze durch

a)  static uint8_t i1=0; // Pointer auf Zeile

b)  {

c)   i1=(i1+1)%7;

von Falk B. (falk)


Lesenswert?

Ach ja, hier natürlich auch so, es muss ein Define sein, damit es 
optimiert werden kann.
1
#define MTX_CLK 2
2
3
// Clk-Impuls für Schieberegister erzeugen
4
inline void mtx_clk_impuls()
5
    {
6
     _delay_us(1);                      // 1µsec warten
7
     mtx_steuer_port |= (1<<MTX_CLK);      // Clk-Pin auf 1
8
     _delay_us(2);                       // 2µsec warten
9
     mtx_steuer_port &= ~(1<<MTX_CLK);     // Clk-Pin auf 0
10
     _delay_us(1);                      // 1µsec warten
11
   return;
12
    }

von jogy (Gast)


Lesenswert?

So,
Ich hab mir jetzt den Optimierten Code von Falk Brunner (Danke nochmal) 
genommen und mit der Idee von Karl Heinz Buchegger (ebenfalls danke) 
kombiniert.
Also ich zeige pro Interupauslösung nur eine Zeile an, das "Delay" hab 
ich durch die vorbelgung des Timer Counters (nutze Timer0) gelöst. 
Bestimmt kann man einiges im Code noch eleganter lösen, aber so klappt 
es erstmal. Zusätzlich kann ich noch die Helligkeit regeln in dem ich 
später den Timercounter variabel ändere (hoffe ich zumindest).

Folgendes ist dabei herausgekommen (diesmal mit den define's, ich hoffe 
ich hab keine vergessen):
1
// Pinbelegung für die Matrix, an verwendete Pins anpassen
2
   
3
  #define mtx_zeilen_port    PORTC         // Port für Matrix Zeilen Ansteuerung
4
  #define mtx_zeilen_ddr    DDRC          // Port-Richtung
5
  #define mtx_z1        PC0
6
  #define mtx_z2        PC1
7
  #define mtx_z3        PC2
8
  #define mtx_z4        PC3
9
  #define mtx_z5        PC4
10
  #define mtx_z6        PC5
11
  #define mtx_z7        PC6
12
  #define mtx_steuer_port      PORTD         // Port für Steuerung
13
  #define mtx_steuer_ddr    DDRD       // Port-Richtung 
14
  #define mtx_clk       PD2           // Clk-Leitung an Schieberegister 74HCT164
15
  #define mtx_data      PD3           // Data-Leitung an Schieberegister
16
  #define mtx_rst      PD4      // Resetleitung 
17
  #define TCCR_MTX  TCCR0B      // Timer0
18
  #define TIMSK_MTX   TIMSK0    // Timer0
19
  #define MTX_COUNTER 0x7F // Timer Counter vorbelegung 
20
21
volatile char matrix_buffer[105] = { // 7 Zeilen a' 120 Bit
22
  0x00,0x22,0x08,0x3e,0x3c,0x1c,0x22,0x00,0x3c,0x3e,0x08,0x3c,0x22,0x00,0x00,
23
  0x00,0x36,0x14,0x08,0x22,0x08,0x22,0x00,0x22,0x20,0x14,0x22,0x22,0x00,0x00,
24
  0x00,0x2a,0x22,0x08,0x22,0x08,0x14,0x00,0x22,0x20,0x22,0x22,0x14,0x00,0x00,
25
  0x00,0x2a,0x22,0x08,0x3c,0x08,0x08,0x00,0x3c,0x3e,0x22,0x22,0x08,0x00,0x00,
26
  0x00,0x22,0x3e,0x08,0x28,0x08,0x14,0x00,0x28,0x20,0x3e,0x22,0x08,0x00,0x00,
27
  0x00,0x22,0x22,0x08,0x24,0x08,0x22,0x00,0x24,0x20,0x22,0x22,0x08,0x00,0x00,
28
  0x00,0x22,0x22,0x08,0x22,0x1c,0x22,0x00,0x22,0x3e,0x22,0x3c,0x08,0x00,0x00}; // entspricht " MATRIX READY  "
29
30
// Clk-Impuls für Schieberegister erzeugen
31
inline void mtx_clk_impuls()
32
    {
33
     _delay_us(1);                      // 1µsec warten
34
     mtx_steuer_port |= (1<<mtx_clk);      // Clk-Pin auf 1
35
     _delay_us(2);                       // 2µsec warten
36
     mtx_steuer_port &= ~(1<<mtx_clk);     // Clk-Pin auf 0
37
     _delay_us(1);                      // 1µsec warten
38
   return;
39
    }
40
// Ports/Timer Initialisieren
41
void mtx_init()
42
  {
43
    // Portleitung CLK, DATA, Reset (Schieberegister) auf Ausgang konfigurieren
44
    mtx_steuer_ddr |= (1<<mtx_clk) | (1<<mtx_data) | (1<<mtx_rst);      
45
    // Portleitungen für Zeilenansteuerung auf Ausgang
46
    mtx_zeilen_ddr |= (1<<mtx_z1) | (1<<mtx_z2) | (1<<mtx_z3) | (1<<mtx_z4) | (1<<mtx_z5) | (1<<mtx_z6) | (1<<mtx_z7);
47
    // Resetleitung auf High setzen, sonst sieht man nix
48
    mtx_steuer_port |= (1<<mtx_rst);
49
    
50
    // Timer 0 Initialisieren
51
    TCCR_MTX |= (1<<CS02);    // Vorteiler 256
52
    TIMSK_MTX |= (1<<TOIE0);   // Interuptsteuerung
53
    
54
    // Anfangsstand des Zählers korrigieren (kompromiss zwischen Flimmerfrei und maximale helligkeit)
55
    // Zählung bis 2   = Flimmerfrei, Anzeigenhelligkeit gering
56
    // Zählung bis 255 = Flimmert, Anzeigenhelligkeit hoch.
57
    TCNT0 = MTX_COUNTER;    
58
    return;
59
  }
60
61
ISR (TIMER0_OVF_vect) {
62
  
63
  static uint8_t zeile;
64
  static uint8_t cnt_byte;
65
  static uint8_t cnt_bit;
66
  static uint8_t index;
67
  static uint8_t data;    // Byte zu bearbeiten
68
  
69
  if( !index ) {      // Variable unbelegt, dann mit 14 Vorbelegen
70
    index = 14;
71
  } 
72
  if ( index > 119 ) {  // Bufferzähler über Buffer hinaus, dann an den start
73
    index = 14;
74
  }
75
  
76
  if( !zeile) {      // Variable unbelegt, dann mit 0 vorbelegen
77
    zeile = 0;
78
  }
79
  if( zeile == 8 ) {    // Zähler steht auf 8.te Zeile, dann Zähler auf 0
80
    zeile = 0;
81
  }
82
   
83
  mtx_zeilen_port = 0x00;               // Zeile abschalten  
84
  
85
  for(cnt_byte=0; cnt_byte<15; cnt_byte++) {      // Schleife 15 Bytes der Zeile rückwärts auslesen, letztes Bit muss zuerst geschoben weren      
86
    data = matrix_buffer[index];                // Byte aus dem Buffer holen
87
    index--;
88
    for(cnt_bit=0; cnt_bit<8; cnt_bit++) {     // Alle Bits des Bytes bearbeiten
89
      if (data & 0x01) {                      // Bit0 prüfen
90
        mtx_steuer_port |= (1<<mtx_data);    // wenn Bit0=1 dann Data-Leitung auf 1
91
      } else {
92
        mtx_steuer_port &=~(1<<mtx_data);  // wenn Bit0=0 dann Data-Leitung auf 0
93
      }
94
      mtx_clk_impuls();                     // Bit ins Schieberegister übertragen
95
      data >>= 1;                            // Nach rechts schieben
96
     }  
97
  }
98
  
99
  mtx_zeilen_port = (1<<zeile);
100
  
101
  zeile++;        // nächste Zeile für Anzeige
102
  index += 30;            // nächste Zeile im Buffer
103
  TCNT0 = MTX_COUNTER;  // Counter vorbelegung vom Timer neu setzen.
104
}

MfG
Joerg

von Falk B. (falk)


Lesenswert?

@  jogy (Gast)

>ich durch die vorbelgung des Timer Counters (nutze Timer0) gelöst.
Bestimmt kann man einiges im Code noch eleganter lösen, aber so klappt

>es erstmal. Zusätzlich kann ich noch die Helligkeit regeln in dem ich
>später den Timercounter variabel ändere (hoffe ich zumindest).

Das klingt nach einem Bug. Denn die Helligkeit der Anzeige ist von der 
Multiplexfrequenz in erster Linie UNABHÄNGIG! Wenn es dennoch eine 
Abhängikeit gitb, deutet das auf langsame Treiber oder so hin.

>Folgendes ist dabei herausgekommen (diesmal mit den define's, ich hoffe
>ich hab keine vergessen):

Defines schreibt man meisten komplett groß, um sie als solche zu 
erkennen. Ist eine allgemeine Vereinbarung.

>  #define mtx_z1        PC0
>  #define mtx_z2        PC1
>  #define mtx_z3        PC2
>  #define mtx_z4        PC3
>  #define mtx_z5        PC4
>  #define mtx_z6        PC5
>  #define mtx_z7        PC6

Ist im Code gar nicht genutzt, wozu also die defines? Das sind reine 
Textersetzungen, welche aber den Code deutlich lesbarer und leichter 
änderbar machen.

>  #define TCCR_MTX  TCCR0B      // Timer0
>  #define TIMSK_MTX   TIMSK0    // Timer0

Sowas ist unüblich.

>  #define MTX_COUNTER 0x7F // Timer Counter vorbelegung

Für sowas gibt es den CTC Modus. Ausserdem sind hier Dezimalzahlen 
deutlich besser verständlich.


>ISR (TIMER0_OVF_vect) {

>  static uint8_t zeile;
>  static uint8_t index;

OK.

>  static uint8_t cnt_byte;
>  static uint8_t cnt_bit;
>  static uint8_t data;    // Byte zu bearbeiten

Das static ist überflüssig und ggf. kontraproduktiv. Diese Variablen 
werden bei jedem Aufruf neu initialisiert, die muss man sich 
zwischendurch nicht merken.

>  if( !index ) {      // Variable unbelegt, dann mit 14 Vorbelegen

Variablen sind nicht unbelegt, sie haben immer einen Wert.

>    index = 14;
>  }

Diese Steuerung machst du besser über die Variable Zeile, der Rest 
ergibt sich daraus.

>  if( !zeile) {      // Variable unbelegt, dann mit 0 vorbelegen
>    zeile = 0;
>  }
>  if( zeile == 8 ) {    // Zähler steht auf 8.te Zeile, dann Zähler auf 0
>    zeile = 0;
>  }

Eben hier kann man auch index rücksetzen.

>  TCNT0 = MTX_COUNTER;  // Counter vorbelegung vom Timer neu setzen.

CTC Modus ist hier einfacher und zeitgemäß.
Geht aber vorerst auch so.

Hast du mal simuliert, wie lange deine alte und deine neue Routine 
brauchen?

MFG
Falk

von Karl H. (kbuchegg)


Lesenswert?

Den Teil hier
1
  static uint8_t zeile;
2
3
  ....
4
  
5
  if( !zeile) {      // Variable unbelegt, dann mit 0 vorbelegen
6
    zeile = 0;
7
  }
8
  if( zeile == 8 ) {    // Zähler steht auf 8.te Zeile, dann Zähler auf 0
9
    zeile = 0;
10
  }
11
   
12
 ...
13
14
  zeile++;        // nächste Zeile für Anzeige
15
16
 ...
17
}

kannst du auch DEUTLICH einfacher schreiben.
Variablen sind nie 'unbelegt'. Sie haben IMMER irgendeinen Wert, denn 
die Bits sind ja da und sind entweder 0 oder 1. Die erscheinen ja nicht 
einfach irgendwie magisch im µC wenn du das erste mal einen Wert 
zuweist.

Was du geschrieben hast, ist eine Variante von
     if( zeile == 0 )
       zeile = 0;
und das ist augenscheinlich ziemlich unsinnig. Es bewirkt zwar nichts, 
es macht aber auch nichts Sinnvolles.

Und auch static Variablen kann man initialisieren, so wie alles andere 
auch.
1
  static uint8_t zeile = 0;
2
3
  ....
4
  
5
   
6
 ...
7
8
  zeile++;        // nächste Zeile für Anzeige
9
  if( zeile == 8 )
10
    zeile = 0;
11
 ...
12
}

dann hast du das korrekte Hochzählen mit 'Überlaufbehandlung' bei 8 an 
einer Stelle beisammen. Und die Initialisierung sorgt dafür, dass 
'zeile', wenn es zur Welt kommt, auf jeden Fall eine 0 enthält.

von Karl H. (kbuchegg)


Lesenswert?

1
inline void mtx_clk_impuls()
2
    {
3
     _delay_us(1);                      // 1µsec warten
4
     mtx_steuer_port |= (1<<mtx_clk);      // Clk-Pin auf 1
5
     _delay_us(2);                       // 2µsec warten
6
     mtx_steuer_port &= ~(1<<mtx_clk);     // Clk-Pin auf 0
7
     _delay_us(1);                      // 1µsec warten
8
   return;
9
    }

Nachdem du den Clock Impuls abgesetzt hast (den Pin also wieder auf 0 
hast), macht ja der Aufrufer noch Zeugs. D.h. du brauchst du nicht mehr 
zuwarten.

von jogy (Gast)


Lesenswert?

Danke für die Hinweise auf meinen "Code" , naja, bitte um nachsicht , 
ich lerne noch..

Falk Brunner schrieb:
> Das klingt nach einem Bug. Denn die Helligkeit der Anzeige ist von der
> Multiplexfrequenz in erster Linie UNABHÄNGIG! Wenn es dennoch eine
> Abhängikeit gitb, deutet das auf langsame Treiber oder so hin.

>>ich durch die vorbelgung des Timer Counters (nutze Timer0) gelöst.
>>Bestimmt kann man einiges im Code noch eleganter lösen, aber so klappt
>
>>es erstmal. Zusätzlich kann ich noch die Helligkeit regeln in dem ich
>>später den Timercounter variabel ändere (hoffe ich zumindest).
>
> Das klingt nach einem Bug. Denn die Helligkeit der Anzeige ist von der
> Multiplexfrequenz in erster Linie UNABHÄNGIG! Wenn es dennoch eine
> Abhängikeit gitb, deutet das auf langsame Treiber oder so hin.

Bug, ja eher nicht.. Denn folgendes Passiert doch (jetzt):
Zeile abschalten (LED'aus). 120Bits in das Display schieben, Zeile 
einschalten (LED's an). Nun mit Eurer hilfe Interuptgesteuert.
Die Zeile in der Anzeige wird also nur während der Pausen zwischen den 
Interuptauslösungen angezeigt.
Demnach , je schneller die Interruptreoutine aufgerufen wird, haben die 
LED's der Anzeige weniger Zeit zu ihre vollen Leuchtstärke zu finden.

Die LED-Matrix die ich hier habe ist aus den 90'er Jahren und ich will 
die alte Prozessorplatine zu der es keinerlei doku gibt, ersetzen.
Das habe ich durch Zufall gefunden und hat mir das Reverse Engeneering 
erspart :
http://circuitcellar.com/contests/nxpmbeddesignchallenge/winners/DEs/Abstract_3835-Kalk_DE.pdf

Die Verbesserungsvorschläge , auch mit der GROß und kleinschreibung, 
werde ich noch ändern. Das mit den unbelegten Variablen, kommt 
wahrscheinlich aus meiner Windows VB ecke im Kopf...

Und zum Counter: über den CTC-Modus bin ich auch gestolpert. Wußte aber 
nicht wie ich den aktiviere. Werde ich mal ergoogeln.

Grüsse

von Karl H. (kbuchegg)


Lesenswert?

jogy schrieb:


> Demnach , je schneller die Interruptreoutine aufgerufen wird, haben die
> LED's der Anzeige weniger Zeit zu ihre vollen Leuchtstärke zu finden.

Ähm.
Das ist ein kleiner Irrtum.
Du schaltest die LED ein. Die brennen sofort mit voller Helligkeit.

Wenn du die Helligkeit variieren willst, dann musst du das Verhältnis 
von Ein zu Aus variieren.
Aber nur weil du schneller blinkst (und was anderes ist Multiplexen 
nicht) werden LED nicht heller.

von Falk B. (falk)


Lesenswert?

@  jogy (Gast)

>Zeile abschalten (LED'aus). 120Bits in das Display schieben, Zeile
>einschalten (LED's an). Nun mit Eurer hilfe Interuptgesteuert.

Jo.

>Die Zeile in der Anzeige wird also nur während der Pausen zwischen den
>Interuptauslösungen angezeigt.

Das ist die allermeiste Zeit. Simulier mal wie lange dein ISR dauert und 
dann denk mal nach wie oft sie aufgerufen wird. Siehe LED-Matrix, 
dort ist das Thema Multiplexing noch mal beschrieben.

>Demnach , je schneller die Interruptreoutine aufgerufen wird, haben die
>LED's der Anzeige weniger Zeit zu ihre vollen Leuchtstärke zu finden.

Alles relativ, nicht erst seit Einstein. EINE Zeile wird immer 1/7 der 
Zeit eines vollen Durchlaufs für alle Zeilen angezeigt. Ob das nunr 
7x1ms oder 7x10ms ist, ist erstmal egal.


>Und zum Counter: über den CTC-Modus bin ich auch gestolpert. Wußte aber
>nicht wie ich den aktiviere. Werde ich mal ergoogeln.

Es reicht, das Datenblatt des AVRs zu lesen. Oder das hier.

http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial/Die_Timer_und_Z%C3%A4hler_des_AVR

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.