Forum: Mikrocontroller und Digitale Elektronik Problem mit meinem uC - 7Seg-MUX-Anzeige flackert


von Dennis (Gast)


Lesenswert?

Hi Leute!

Ich hab grad ein kleines Problem und hoffe, dass ihr mir helfen könnt. 
Ich habe eine 7-Segmentanzeige an meinem uC, welche 6-fach gemuxt wird. 
Die Anzeige hängt an zwei 595ern. Ich muss demnach 16 Bits reintakten. 
Ich habe leider keinen Pin für SPI frei, daher per Bitbang und ohne 
Interrupt fürs Senden.

Mittels Timer erzeuge ich mit 300Hz (50Hz für jede Stelle) einen 
Interrupt, in denen ich die aktuelle Stelle raustakte und den 
Stellenzähler inkrementiere. In der main wird dann das neue Datenwort 
für die nächste Übertragung vorbereitet und beim nächsten Interrupt des 
Timers rausgetaktet.

Das ganze funktioniert prinzipiell gut, aber wenn ich jetzt mehrere 
Berechnungen durchführe (mit int64, also große Zahlen, aber alles 
Festkomma), dann fängt die Anzeige an zu flackern. Es ist deutlich 
erkennbar, dass das Flackern abhängig ist von den Zahlen, die verrechnet 
werden müssen.

Wo setze ich jetzt an, um das zu beheben? Der Interrupt kommt ja 
unabhängig vom Programmablauf, also müsste das raustakten immer im 
gleichen Rhytmus erfolgen. Aber wieso verlangsamt sich das ganze dann 
so?

Jemand einen Tip?

von Karl H. (kbuchegg)


Lesenswert?

Dennis schrieb:

> Wo setze ich jetzt an, um das zu beheben?

Code herzeigen.
Irgendwo werden die Interrupts per cli()/sei() zu lange abgeschaltet. 
Und die Stelle heißt es jetzt zu finden.

von Karl H. (kbuchegg)


Lesenswert?

> Mittels Timer erzeuge ich mit 300Hz (50Hz für jede Stelle) einen
> Interrupt, in denen ich die aktuelle Stelle raustakte und den
> Stellenzähler inkrementiere. In der main wird dann das neue Datenwort
> für die nächste Übertragung vorbereitet und beim nächsten Interrupt
> des Timers rausgetaktet.

Der erste Teil dieses Absatzes klingt soweit ok.
Der zweite Teil allerdings macht mir etwas Kopfzerbrechen. Was genau ist 
damit gemeint?

-> Beschreib nicht deinen Code - zeige ihn.

von Ingo (Gast)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Der zweite Teil allerdings macht mir etwas Kopfzerbrechen. Was genau ist
> damit gemeint?
Höchstwahrscheinlich meint er, dass er die langen Berechnungen in der 
main-loop durchführt und nur die Daten in der ISR raushaut.

Dennis schrieb:
> Aber wieso verlangsamt sich das ganze dann
> so?

Karl Heinz Buchegger schrieb:
> Code herzeigen.

von Dennis (Gast)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Code herzeigen.

Controller ist ein MSP430G2553
Also hier mal ein Teil, das unwichtige ist raus:

Main:
1
...
2
uint16_t adc_value = 0;
3
int16_t  percent_of_span = 0;
4
int32_t  pressure_value = 0;
5
uint16_t dac_value = 0;
6
...
7
8
void main( void )
9
{
10
  WDTCTL = ( WDTPW | WDTHOLD );  // Stop watchdog timer
11
12
  configure_clock_module();
13
  configure_ports();
14
  configure_adc10();
15
  configure_timer_a();
16
  configure_ucb0_i2c();
17
18
  ...
19
20
  while( 1 ) // Endless loop
21
  {
22
    ...
23
    ADC/DAC-Kommunikation
24
    ...
25
26
    adc_value = adc_get_result();
27
    percent_of_span = calc_adc_percent_times_100( adc_value );
28
    pressure_value = calc_pressure_value( percent_of_span );
29
    dac_value = calc_dac_value( percent_of_span );
30
31
    display_out_pressure_value( pressure_value );
32
33
    if( sr_test_flag( SR_FLAG__BUILD_NEW_DATA ) )
34
    {
35
      sr_build_data();
36
      sr_reset_flag( SR_FLAG__BUILD_NEW_DATA );
37
    }
38
  }
39
}

Meine Schieberegister-Daten:
1
struct sr_data
2
{
3
  volatile uint8_t  digit_nr;
4
           uint16_t next_display_data;
5
           uint16_t digit_data[4];
6
  volatile uint16_t flags;
7
};

Die Timer ISR - 300Hz
1
// Timer 1 A1 interrupt service routine
2
#pragma vector = TIMER1_A1_VECTOR
3
__interrupt void Timer1_A1_ISR( void )
4
{
5
  switch( TA1IV )
6
  {
7
    case 4: // CCR2
8
    {
9
      TA1CCR2 += DISPLAY_REFRESH_TIME;
10
11
      sr_bitbang_display_data_out();
12
13
      if( ++sr_control.digit_nr > 5 )
14
      {
15
      sr_control.digit_nr = 0;
16
      }
17
18
      sr_set_flag( SR_FLAG__BUILD_NEW_DATA );
19
20
      break;
21
    }
22
  }
23
}

Und hier werden die Daten zusammengebaut:
1
void sr_build_data( void )
2
{
3
  uint16_t new_data = 0x0000;
4
5
  if( sr_control.digit_nr < 4 )       // Digit 1 to 4
6
  {
7
    new_data = sr_control.digit_data[sr_control.digit_nr];
8
9
    if( (sr_control.flags >> sr_control.digit_nr) & 0x0001 )
10
    {
11
      new_data |= SEG_DP;
12
    }
13
14
    switch( sr_control.digit_nr )
15
    {
16
      case 0: { new_data |= ( ANODE_BITMAP & ~DIGIT_1 ); break; }
17
      case 1: { new_data |= ( ANODE_BITMAP & ~DIGIT_2 ); break; }
18
      case 2: { new_data |= ( ANODE_BITMAP & ~DIGIT_3 ); break; }
19
      case 3: { new_data |= ( ANODE_BITMAP & ~DIGIT_4 ); break; }
20
    }
21
  }
22
  else if( sr_control.digit_nr == 4 ) // Additional "L"-points
23
  {
24
    if( sr_control.flags & L_POINT_1 )
25
    {
26
      new_data |= SEG_a_L1;
27
    }
28
29
    if( sr_control.flags & L_POINT_2 )
30
    {
31
      new_data |= SEG_b_L2;
32
    }
33
34
    if( sr_control.flags & L_POINT_3 )
35
    {
36
      new_data |= SEG_c_L3;
37
    }
38
39
    new_data |= ( ANODE_BITMAP & ~L_POINTS );
40
  }
41
  else if( sr_control.digit_nr == 5 ) // Switching-status-LEDs
42
  {
43
    if( sr_control.flags & OUTPUT_LED_1 )
44
    {
45
      new_data |= LED1;
46
    }
47
  
48
    if( sr_control.flags & OUTPUT_LED_2 )
49
    {
50
      new_data |= LED2;
51
    }
52
53
    new_data |= ( ANODE_BITMAP & ~SW_LEDS );
54
  }
55
56
  if( !(sr_control.flags & OUTPUT_SWITCH_1) )
57
  {
58
    new_data |= SWITCH_1;
59
  }
60
61
  if( !(sr_control.flags & OUTPUT_SWITCH_2) )
62
  {
63
    new_data |= SWITCH_2;
64
  }
65
66
  new_data = ~new_data;
67
68
  sr_control.next_display_data = new_data;
69
}

Ingo schrieb:
> Höchstwahrscheinlich meint er, dass er die langen Berechnungen in der
> main-loop durchführt und nur die Daten in der ISR raushaut.

Genauso meine ich es.
Ich habe gerade mal mit dem Oszi gemessen, indem ich mir hier und da ein 
Bein toggeln lasse - die Programmlaufzeit und die Interruptfrequenz 
liegen sehr nahe beieinander, ein bisschen Varianz je nach aktuellem 
Rechenaufwand natürlich außen vor gelassen. Kann das das Problem sein?

von Dennis (Gast)


Lesenswert?

Nachtrag - hab jetzt gerade nochmal was geändert, und zwar in der 
Funktion sr_build_data( void ) übergebe ich jetzt die variable 
sr_control.digit_nr im Funktionsargument, da es sich ja theoretisch 
während der Abarbeitung durch den Interrupt ändern könnte. Dennoch 
ändert das nichts. Es flackert trotzdem. Probehalber habe ich mal den 
Controller auf 12MHz gestellt (vorher 8), damit geht es dann. Dennoch 
wüsste ich gerne, ob da vielleicht einfach ein Fehler im Code ist und es 
auch mit 8MHz gehen würde.

von Dennis (Gast)


Lesenswert?

Es flackert sogar (wenn auch nurnoch ganz leicht) wenn ich die 
Buildfunktion direkt mit in die ISR nehme...das sollte doch nun 
eigentlich echt nicht sein?!

von Karl H. (kbuchegg)


Lesenswert?

Dennis schrieb:


> Also hier mal ein Teil, das unwichtige ist raus:

Moment


> Die Timer ISR - 300Hz
> [code]
> // Timer 1 A1 interrupt service routine
> #pragma vector = TIMER1_A1_VECTOR
> __interrupt void Timer1_A1_ISR( void )
> {
>   switch( TA1IV )
>   {
>     case 4: // CCR2
>     {
>       TA1CCR2 += DISPLAY_REFRESH_TIME;
>
>       sr_bitbang_display_data_out();
>
>       if( ++sr_control.digit_nr > 5 )
>       {
>       sr_control.digit_nr = 0;
>       }
>
>       sr_set_flag( SR_FLAG__BUILD_NEW_DATA );


Ähm.
No.

Du willst hier sr_build_data aufrufen und das so zusammengebaute dann 
auch tatsächlich auf den Port schreiben.

An dieser Stelle: kein Flag setzen und dann erst in der Hauptschleife 
darauf reagieren. Hier - in der ISR - muss der tatsächlie Portzugriff 
erfolgen, der dann auch das jeweils nächste Digit (bzw. die LED) 
aufleuchten lässt.



>  In der main wird dann das neue Datenwort für die nächste Übertragung
>  vorbereitet und beim nächsten Interrupt des Timers rausgetaktet.

Hat mich mein Gefühl doch nicht getrogen, dass da was nicht stimmt.

von Dennis (Gast)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Du willst hier sr_build_data aufrufen und das so zusammengebaute dann
> auch tatsächlich auf den Port schreiben.

Das passiert auch so. In der main wird die nächste Stelle generiert. In 
der ISR wird die Stelle dann rausgeschoben und die Stelle inkrementiert. 
Dann ein Flag gesetzt, welches in der main signalisiert, dass die 
nächste Stelle zusammengabeut werden muss. In der main wieder 
zusammenbauen, in der ISR verschiecken, inkrementieren, ...

von Peter D. (peda)


Lesenswert?

Multiplexen geht so:
Der Timerinterrupt gibt die Bitmuster aus, völlig egal, wann die erzeugt 
wurden.

Und das Main wandelt ne Zahl in Bitmuster und speichert sie in einem 
Array ab.
Braucht es dafür Jahre, gibt der Interrupt eben jahrelang den gleichen 
Wert aus.
Flackern tut aber nichts.

Will mans ergonomisch, wartet das Main >=200ms, ehe es ne neue Zahl 
berechnet. Dann kann man die Zahlen gut ablesen.

von Karl H. (kbuchegg)


Lesenswert?

Dennis schrieb:
> Es flackert sogar (wenn auch nurnoch ganz leicht) wenn ich die
> Buildfunktion direkt mit in die ISR nehme...das sollte doch nun
> eigentlich echt nicht sein?!

Hast du dran gedacht, die Flagsteuerung in der Hauptschleife still zu 
legen?

von Karl H. (kbuchegg)


Lesenswert?

Dennis schrieb:
> Karl Heinz Buchegger schrieb:
>> Du willst hier sr_build_data aufrufen und das so zusammengebaute dann
>> auch tatsächlich auf den Port schreiben.
>
> Das passiert auch so. In der main wird die nächste Stelle generiert.

Die aszugebende Zahl berechnet. Ist ja auch ok.
Aber

> In
> der ISR wird die Stelle dann rausgeschoben und die Stelle inkrementiert.
> Dann ein Flag gesetzt, welches in der main signalisiert, dass die
> nächste Stelle zusammengabeut werden muss.

Nein.
Wenn die main 5 Jahre nicht dazukommt diesen 'Stelle zusammenzubauen', 
dann zeigen alle Digits 5 Jahre lang die zuletzt zusammengebaute Stelle 
an.

In der ISR passiert alles.
weiterschalten zum nächsten Digit. Was ist dort anzuzeigen.
Das was dort anzuzeigen ist, das richtet man sich in einem Array her. 
Bei dir sind es eben 6 Stück, weil du die einzelnen LED im Grunde wie 
eine abgespeckte 7_Segment Anzeige behandlen kannst
1
uint16_t segmentPattern[6];
2
uint8_t  nextDigit;
3
4
.... ISR(  ... )
5
{
6
  nextDigit++;
7
  if( nextDigit == 7 )
8
    nextDigit = 0;
9
10
  sr_bitbang_display_data_out( segmentPattern[nextDigit] );
11
}

und in segmentPattern kannst du jetzt in den einzelnen Bytes die Muster 
zusammenstellen wann du willst und du kannst dir Zeit lassen so lange du 
willst. Deswegen wird da nichts flackern. Aber dieser ISR-Raustakt 
Mechanismus mit jeweils dem richtigen Word zum richtigen nextDigit, der 
darf nie ins Stocken oder ausser Sync kommen. Dann flackerts

von Dennis (Gast)


Lesenswert?

Peter Dannegger schrieb:
> Der Timerinterrupt gibt die Bitmuster aus, völlig egal, wann die erzeugt
> wurden.

So ist es bei mir. sr_bitbang_data_out() in der ISR mit 300Hz für 6 
Stellen.

Peter Dannegger schrieb:
> Und das Main wandelt ne Zahl in Bitmuster und speichert sie in einem
> Array ab.

Das passiert bei mir auch in der main wird ein Array, welches die 
einzelnen Stellen enthält (sr_control.digit_data[4]) in den nächsten 
Datensatz umgewandelt. Aber jeweils nur der nächste Datensatz, abhängig 
von der Stelle, welche als nächstes gebraucht wird (es liegt nicht schon 
für jede Stelle der Datensatz für die SRs vor).

Man kann im Display leicht erkennen, dass es von der einen Seite zur 
anderen quasi "durchflackert". Als wenn sich das Flackern immer wieder 
einmal durchschiebt und wieder beginnt.

von Peter D. (peda)


Lesenswert?

Will man es ergonomisch, muß jede Ziffer mindestens 20-mal angezeigt 
werden.
Es wäre Schwachsinn, dann 20-mal wieder und wieder die gleiche Rechnung 
auszuführen.
Man berechnet daher einmal und läßt 20-mal anzeigen.
Berechnung und Darstellung sind also 2 völlig getrennte Tasks.

von Dennis (Gast)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Wenn die mein 5 Jahre nicht dazukommt diesen 'Stelle zusammenzubauen',
> dann zeigen alle Digits 5 Jahre lang die zuletzt zusammengebaute Stelle
> an.

Nee, das geht eigentlich nicht, da wegen der Schieberegister ja nur 
immer eine leuchten kann. Kommt die main z.B. nur einmal dazu, einen 
Datensatz für das SR zu berechnen, dann ist da ja sowohl die 
Anodeninformation, sowie die Kathodeninformation drin. Es würde dann 
wirklich nur die eine Stelle leuchten. OK, sorry, falls die Info 
fehlte...mit dem einen SR schalte ich die Anoden, mit dem anderen die 
Kathoden.

Karl Heinz Buchegger schrieb:
> und in segmentPattern kannst du jetzt in den einzelnen Bytes die Muster
> zusammenstellen wann du willst und du kannst dir zeit lassen so lange du
> willst. Deswegen wird da nichts flackern.

Also meinst du es liegt daran, dass ich immer nur die Daten für das 
nächste anzzeigende Segment generiere und nicht schon für alle vorrätig 
habe?

von Dennis (Gast)


Lesenswert?

Peter Dannegger schrieb:
> Man berechnet daher einmal und läßt 20-mal anzeigen.

Dafür war ja das Flag in der ISR, welches in der main dann nach der 
Berechnung gelöscht wird.

von spontan (Gast)


Lesenswert?

Ja, aber Du läßt ja jede Stelle nach jedem Interrupt einzeln und neu 
berechnen.

von Karl H. (kbuchegg)


Lesenswert?

Dennis

Du hast dir in der Hauptschleife eine Abhängigkeit vom restlichen 
Programm eingehandelt!

Dein 'update' Methode wird erst aufgerufen, wenn die Hauptschleife dazu 
Zeit hat. Da kann die ISR 100-mal das bewusste Flag setzen. Wenn die 
Hauptschleife nicht bereit ist, dann ist sie nicht bereit und das 
korrekte Word für die Ausgabe wird nicht erzeugt.

Du hast nichts davon, wenn du 5 Millionen mal in der ISR immer wieder 
das Digit Nummer 2 neu ausgibst, weil die Hauptschleife noch keine Zeit 
hatte auf das Flag zu reagieren und die Information für Digit Nummer 3 
zusammenzustellen.

Pro ISR AUfruf muss auf das nächste Digit weiter geschaltet werden. Und 
zwar zuverlässig!

von Dennis (Gast)


Lesenswert?

spontan schrieb:
> Ja, aber Du läßt ja jede Stelle nach jedem Interrupt einzeln und neu
> berechnen.

Das stimmt.

Karl Heinz Buchegger schrieb:
> Du hast dir in der Hauptschleife eine Abhängigkeit vom restlichen
> Programm eingehandelt!

OK, also alles im Vorfeld berechnen für jede Stelle und dann in der ISR 
nurnoch durchrattern?

von Karl H. (kbuchegg)


Lesenswert?

Dennis schrieb:

> OK, also alles im Vorfeld berechnen für jede Stelle und dann in der ISR
> nurnoch durchrattern?


Exakt.

Jetzt kommen wir auf einen grünen Zweig.


So sieht deine ISR aus
1
uint16_t segmentPattern[6];
2
uint8_t  nextDigit;
3
4
.... ISR(  ... )
5
{
6
  nextDigit++;
7
  if( nextDigit == 7 )
8
    nextDigit = 0;
9
10
  sr_bitbang_display_data_out( segmentPattern[nextDigit] );
11
}

und da baust du dir jetzt noch Hilfsfunktionen drumm herum, die in den 
segmentPattern die korrekten Werte reinschreiben.

von Dennis (Gast)


Lesenswert?

Ich probiere es eben mal aus - danke schonmal!!

von Dennis (Gast)


Lesenswert?

Funktioniert tadellos! Vielen Dank!

von Karl H. (kbuchegg)


Lesenswert?

Wow.
Das ging aber schnell

von Dennis (Gast)


Lesenswert?

Bei der guten Hilfe ;-)

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.