Problem mit meinem uC - 7Seg-MUX-Anzeige flackert

Gast #3217673
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?
#3217755
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.
Gast #3217796
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?
Gast #3217828
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.
#3217837
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.
Gast #3217845
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, ...
#3217846
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.
#3217858
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
Gast #3217860
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.
#3217864
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.
Gast #3217869
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?
#3217877
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!
#3217884
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.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren