mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik _delayus funktioniert nicht wie gewünscht


Announcement: there is an English version of this forum on EmbDev.net. Posts you create there will be displayed on Mikrocontroller.net and EmbDev.net.
Autor: Lars (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hey Leute vielleicht kann mir mal jemand das folgende Verhalten 
erklären.

Ich betreibe einige LEDs im Multiplexbetrieb und habe dabei das Problem, 
dass ich die betreffenden LEDs nur immer für 100 µs aufleuchten lassen 
möchte. Dies funktioniert leider nicht wie gewünscht. Laut Oszilloskop 
sind es anstatt den eingestellten 100 µs um die 200 µs. Anbei ein Auszug 
aus der gekürzten mux_display Funktion.

Testweise habe ichh noch eine "SIGNAL_ON_LED" eingebaut. Wenn ich an 
dieser mit dem Osziloskop messe, stimmen die eingestellten 100 µs 
Einschaltdauer.


#define time_on 100
#define time_off 300


void mux_display()
{

  PORTB &= ~(1<<DIGIT2);
  SevenSegment(minute/10);
  _delay_us(time_off);
  PORTB |= (1<<DIGIT1);
  PORTC |= (1<<SIGNAL_ON_LED);
  _delay_us(time_on);


  
  PORTB &= ~(1<<DIGIT1);
  SevenSegment(minute%10);
  PORTC &= ~(1<<SIGNAL_ON_LED);
  _delay_us(time_off);
  PORTB |= (1<<DIGIT2);
  _delay_us(time_on);
}

Autor: Falk B. (falk)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@ Lars (Gast)

>möchte. Dies funktioniert leider nicht wie gewünscht. Laut Oszilloskop
>sind es anstatt den eingestellten 100 µs um die 200 µs. Anbei ein Auszug
>aus der gekürzten mux_display Funktion.

Dann ist dein define für F_CPU falsch und stimmt nicht mit dem realen 
CPU-Takt überein. Oder du compilierst ohne Optimierung, dann passen die 
Zeiten auch nicht.

Autor: Peter D. (peda)
Datum:

Bewertung
3 lesenswert
nicht lesenswert
Lars schrieb:
> Laut Oszilloskop
> sind es anstatt den eingestellten 100 µs um die 200 µs.

Das ist verständlich. Jede Codezeile kostet zusätzliche Zeit und 
besonders die Berechnungen / und %.
Deshalb multiplext niemand bei Verstand mit Delay, sondern immer mit 
einem Timerinterrupt. Dann sind die Zeiten unabhängig von 
Ausführungszeiten (kein Flackern) und der MC kann noch andere Sachen 
machen.

Die Berechnungen zieht man natürlich komplett aus dem Multiplexen raus, 
denn kein Mensch kann so schnell Änderungen ablesen. Für ergonomisches 
Ablesen verlangsamt man die Rate der Neuberechnung auf 2..5 Werte/s, 
damit der Benutzer nicht nur flackernde 8-en sieht.

Autor: kyrk.5 (Gast)
Datum:

Bewertung
-1 lesenswert
nicht lesenswert
Wie lange dauert eine Instruktion bei dir? 1us? 100ns? 10ns?

Bei 1us Instruktionzeit würde 100us delay 100 instruktionen kosten. Da 
kann man schon relatiev viel machen. Aber wenn nix anderes gemacht wird 
dann könnte man noch blockierend den _delay_us verwenden. Bei schnellere 
instruktionenzeit würde ich den _delay_us vergessen und sofort auf 
interrupt gehen.

Faustregel: alles was delay_ms oder delay_us heisst muss aus der C Code 
raus!

Autor: Lars (Gast)
Datum:

Bewertung
-2 lesenswert
nicht lesenswert
OK, nur findet zwischen Einschalten von DIGIT1 und Abschalten von DIGIT1 
keine Berechnung statt.

Einleuchtend wäre es hingegen zwischen dem Einschalten SIGNAL_ON_LED und 
Abschalten von SIGNAL_ON_LED, denn dort wird SevenSegment(minute%10) 
ausgeführt. Allerdings passen gerade hier die 100 µs.

Autor: Falk B. (falk)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@ kyrk.5 (Gast)

>Faustregel: alles was delay_ms oder delay_us heisst muss aus der C Code
>raus!

Unsinn^3. Gerade _delay_us() ist für sehr kurze Zeiten optimal, weil in 
der Zeit so oder so nix anderes sinnvoll gemacht werden kann. Und auch 
ein _delay_ms() ist, RICHTIG eingesetzt, überaus OK. Man muss nicht 
immer mit einem Timer arbeiten, wenn gleich es gerade beim Multip0lexing 
der Standardansatz ist.

Autor: Matthias S. (Firma: matzetronics) (mschoeldgen)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
kyrk.5 schrieb:
> Faustregel: alles was delay_ms oder delay_us heisst muss aus der C Code
> raus!

Nö. Es hält sich hartnäckig das Gerücht, das diese beiden Funktionen 
'blocking' sind, dem ist aber nicht so. Etwaige Interrupts werden 
nämlich bedient und deswegen kann _delay_xx() durchaus sinnvoll sein.
Es muss lediglich F_CPU definiert sein, bevor util/delay.h eingebunden 
wird.

Allerdings ist ein Timerinterrupt für das Multiplex Display immer 
vorzuziehen, vor allem ist es dann ein 'Fire-and-Forget' Ding ohne 
Flimmern und als Nebeneffekt hat man dann gleich einen Ticker für z.B. 
Tasten- oder Encoderentprellung.

: Bearbeitet durch User
Autor: Lars (Gast)
Datum:

Bewertung
-1 lesenswert
nicht lesenswert
Über den Sinn und Unsinn von _delay wollte ich eigentlich nicht 
diskutieren. Viel mehr suche ich eine Erklärung für das eigenartige 
Verhalten. Warum passt das Timing bei der SIGNAL_ON_LED, jedoch nicht 
bei den DIGITs?

Autor: Falk B. (falk)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@Matthias S. (Firma: matzetronics) (mschoeldgen)

>Nö. Es hält sich hartnäckig das Gerücht, das diese beiden Funktionen
>'blocking' sind, dem ist aber nicht so.

Aber sicher sind sie das. Während der Funktion kommt das Programm nicht 
weiter, die CPU dreht Däumchen.

> Etwaige Interrupts werden
>nämlich bedient

Das ist aber nicht die Definition einer blockierenden Funktion!

Autor: Falk B. (falk)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>Testweise habe ichh noch eine "SIGNAL_ON_LED" eingebaut. Wenn ich an
>dieser mit dem Osziloskop messe, stimmen die eingestellten 100 µs
>Einschaltdauer.

Glaub ich nicht, denn dazwischen liegt noch eine Funktion mit einem 
Argument einer Modulo-Operation.

>  PORTC |= (1<<SIGNAL_ON_LED);
>  _delay_us(time_on);
>  PORTB &= ~(1<<DIGIT1);
>  SevenSegment(minute%10);
>  PORTC &= ~(1<<SIGNAL_ON_LED);

>Anbei ein Auszug aus der gekürzten mux_display Funktion.

Dort könnte ein Problem liegen. Zeig uns den ORIGINALEN, unmodifizierten 
Quelltext.

Autor: Lars (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Bitteschön:

#define time_on 100
#define time_off 300


void mux_display()
{
  PORTB &= ~(1<<DIGIT4);
  SevenSegment(minute/10);
  _delay_us(time_off);
  PORTB |= (1<<DIGIT1);
  PORTC |= (1<<SIGNAL_ON_LED);
  _delay_us(time_on);
  
  
  PORTB &= ~(1<<DIGIT1);
  SevenSegment(minute%10);
  PORTC &= ~(1<<SIGNAL_ON_LED);
  _delay_us(time_off);
  PORTB |= (1<<DIGIT2);
  _delay_us(time_on);
  
  
  PORTB &= ~(1<<DIGIT2);
  SevenSegment(dp);
  _delay_us(time_off);
  PORTB |= (1<<DIGITDP);
  _delay_us(time_on);
  
  
  PORTB &= ~(1<<DIGITDP);
  SevenSegment(sekunde/10);
  _delay_us(time_off);
  PORTB |= (1<<DIGIT3);
  _delay_us(time_on);
  
  
  PORTB &= ~(1<<DIGIT3);
  SevenSegment(sekunde%10);
  _delay_us(time_off);
  PORTB |= (1<<DIGIT4);
  _delay_us(time_on);
}

Autor: Karl M. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo Lars schrieb:
> Bitteschön:
>

> void mux_display()
> {
>   PORTB &= ~(1<<DIGIT4);
>   SevenSegment(minute/10);
>   _delay_us(time_off);
>   PORTB |= (1<<DIGIT1);
>   PORTC |= (1<<SIGNAL_ON_LED);
>   _delay_us(time_on);
:
> }

Nein nicht wirklich, eine geheime Funktion: SevenSegment(...)

Autor: Lars (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Sorry, das ist diese hier:
void SevenSegment(uint8_t n)

{

  switch (n)
  {
    case 0:
    SEVEN_SEGMENT_PORT=0b10111111; //a,b,c,d,e,f
    break;

    case 1:
    SEVEN_SEGMENT_PORT=0b10000110; // b,c
    break;

    case 2:
    SEVEN_SEGMENT_PORT=0b11011011; // a,b,d,e,g
    break;

    case 3:
    SEVEN_SEGMENT_PORT=0b11001111; // a,b,c,d,g
    break;

    case 4:
    SEVEN_SEGMENT_PORT=0b11100110; // a,b,f,g
    break;

    case 5:
    SEVEN_SEGMENT_PORT=0b11101101; // a,c,d,f,g
    break;

    case 6:
    SEVEN_SEGMENT_PORT=0b11111101; // a,c,d,e,f,g
    break;

    case 7:
    SEVEN_SEGMENT_PORT=0b10000111; // a,b,c
    break;

    case 8:
    SEVEN_SEGMENT_PORT=0b11111111; // a,b,c,d,e,f,g
    break;

    case 9:
    SEVEN_SEGMENT_PORT=0b11101111; // a,b,c,d,f,g
    break;

    case 10:
    SEVEN_SEGMENT_PORT=0b10000000; //DP
  }
}

Autor: Karl M. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Danke Peter,

kann man so void SevenSegment(uint8_t n); programmieren.
Der Define für SEVEN_SEGMENT_PORT ist da so etwas wie PORT{A,D,E,F,G} ?

Autor: Falk B. (falk)
Datum:

Bewertung
1 lesenswert
nicht lesenswert
@Lars (Gast)

>Sorry, das ist diese hier:
>void SevenSegment(uint8_t n)


Schon mal was von Arrays gehört?

const uint8_t int2seven[10] = {0b10111111, ....};

SEVEN_SEGMENT_PORT = int2seven[Minute/10];


Das ist nicht nur kürzer und übersichtlicher, es ist auch in der 
Ausführung schneller.

Autor: Michael R. (fisa)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
DDRs korrekt initialisiert? Vielleicht schaltest du nur die Pullups 
(sowas hab ich bei mir auch mal stundenlang gesucht... Pullups sind ein 
Luder...)

Autor: Lars (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ja, SEVEN_SEGMENT_PORT ist PortD
#define SEVEN_SEGMENT_PORT PORTD



In der main() setzte ich alle Pins von PortB und PortD auf Ausgang, 
sollte also auch passen.
int main(void)
{

  
  DDRB |= 0xff;
  DDRC |= (1<<SIGNAL_ON_LED);
  DDRD |= 0xff;

Autor: Michael R. (fisa)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
hast du die zu hohe Zeit auf allen Digits, oder nur auf Digit 4?

Autor: Lars (Gast)
Datum:

Bewertung
1 lesenswert
nicht lesenswert
Gute Frage, müsste ich heute Abend nochmal schauen. Habe bisher 
tatsächlich nur an Digit4 gemessen.
Könnte also sein, dass die mux_display-Funktion in der while() nicht 
rechtzeitig wieder aufgerufen wird.

Autor: J Zimmermann (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Matthias S.:
> Es muss lediglich F_CPU definiert sein
Falls das der Fehler ist, wird's in Atmel Studio als Warnung 
dokumentiert
mfg

Autor: Wolfgang (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Matthias S. schrieb:
> Nö. Es hält sich hartnäckig das Gerücht, das diese beiden Funktionen
> 'blocking' sind, dem ist aber nicht so. Etwaige Interrupts werden
> nämlich bedient und deswegen kann _delay_xx() durchaus sinnvoll sein.

Aber bestimmt nicht, wenn man auf genaues Timing wert legt. Für "xx" 
sollte dann insbesondere nicht "us" stehen, sonst wundert man sich über 
irgendwelche Ausreißer bei der Länge der Verzögerungszeit.

Autor: Carl D. (jcw2)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Wolfgang schrieb:
> Matthias S. schrieb:
>> Nö. Es hält sich hartnäckig das Gerücht, das diese beiden Funktionen
>> 'blocking' sind, dem ist aber nicht so. Etwaige Interrupts werden
>> nämlich bedient und deswegen kann _delay_xx() durchaus sinnvoll sein.
>
> Aber bestimmt nicht, wenn man auf genaues Timing wert legt. Für "xx"
> sollte dann insbesondere nicht "us" stehen, sonst wundert man sich über
> irgendwelche Ausreißer bei der Länge der Verzögerungszeit.

Man muß sich einfach darüber im Klaren sein, daß auch wenn F_CPU zum 
Quarz und der DIV8-Fuse paßt, nicht genau n μs vergehen, sondern 
mindestens so viele.
Angenommen F_CPU=1MHz, dann führt _delay_us(100); mehr oder weniger 
genau 100 Takte "Blind-Code" aus. Wenn aber ein Int dazwischen kommt, 
dann dauert die "Lücke" zwischen zwei Befehlen auch mal deutlich länger 
als geplant "Null". Ein minimal Int dauert 12Takte (Tiny25), falls IRET 
direkt in der ISR-Tabelle steht.
Wenn man so darauf wartet, daß sich die Datenleitungen zum LCD 
stabilisieren, dann ist es egal, ob 10 oder 20μs. Als Uhr taugt das aber 
nicht.

Autor: Michael R. (fisa)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Lars schrieb:
> Gute Frage, müsste ich heute Abend nochmal schauen. Habe bisher
> tatsächlich nur an Digit4 gemessen.
> Könnte also sein, dass die mux_display-Funktion in der while() nicht
> rechtzeitig wieder aufgerufen wird.

Und?

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.