Forum: Mikrocontroller und Digitale Elektronik _delay_us macht eine ms


von Kein Plann (Gast)


Angehängte Dateien:

Lesenswert?

Hallo,
versuche gerade das RS232 Protokoll zu verstehen. Ich fange ganz langsam 
damit an in dem ich versuche einen Pin 104 µs auf hight und low 
zusetzten.

Hier scheitert es schon:
Folgendes Programm habe ich dazu geschrieben:
1
 
2
#include <avr/io.h>
3
#include <stdint.h>
4
#include <avr/interrupt.h>
5
#include <util/delay.h>
6
#include "var.h"
7
#include "lcd.h" ...
8
9
 ... lcd_init();
10
    lcd_loeschen();
11
    lcd_string("Test");
12
    while (1)
13
    {
14
        PORTA |= (1<<PA7);
15
        _delay_us(104);
16
        PORTA &= ~(1<<PA7);
17
        _delay_us(104);           
18
    }

Problemm:
Es sind keine 104µs sondern ca. 1ms.

Ich bin auf folgen Beitrag gestoßen und habe denke alles beachtet.
Beitrag "delay us liegt weit daneben"

Komisch an der Sache ist, schalte ich die Optimierung ein, funktioniert 
das Display nicht und der Pin bleibt auf Low.

Wo ist der Hacken?

Danke für eure Hilfe.

PS: Bitte verzeiht mir meine Rechtschreibung.

von (prx) A. K. (prx)


Lesenswert?

Kein Plann schrieb:
> Komisch an der Sache ist, schalte ich die Optimierung ein, funktioniert
> das Display nicht und der Pin bleibt auf Low.

Und ohne Optimierung bekanntermassen nicht das Delay. Was folgt daraus? 
Du hast kein Problem mit dem Delay, sondern mit dem LCD-Code.

von Andreas B. (andreasb)


Lesenswert?

Kein Plann schrieb:

> Problemm:
> Es sind keine 104µs sondern ca. 1ms.

Weil du die Optimierung ausgeschaltet hast?

> Ich bin auf folgen Beitrag gestoßen und habe denke alles beachtet.
> Beitrag "delay us liegt weit daneben"

Sicher?

>
> Komisch an der Sache ist, schalte ich die Optimierung ein, funktioniert
> das Display nicht und der Pin bleibt auf Low.

Weil deine Displayroutine hängen bleibt und er garnicht bis zur Schleife 
kommt?

> Wo ist der Hacken?

In deiner Display Routine.

>PS: Bitte verzeiht mir meine Rechtschreibung.
Gleichfalls!


mfg Andreas

von Kein Plann (Gast)


Lesenswert?

Schei...., ihr habt recht! (Wie immer)
Kommentiere ich die Routine aus, funktionierts.

Wie bekomme ich nun raus wo's in der Routine hängt?

DANKE!

von (prx) A. K. (prx)


Lesenswert?

Kein Plann schrieb:
> Wie bekomme ich nun raus wo's in der Routine hängt?

(1) Selber suchen.
(2) Suchen lassen.

Die Voraussetzung für (2) zu erkennen überlasse ich dir. ;-)

von Coder (Gast)


Lesenswert?

Mit Schweiß und Verstand... oder man nimmt sich einen Debugger zur Hand.
Vielleicht wirft der Compiler auch die eine oder andere Warnung die von 
dir ingoriert werden... Möglicherweise auch den einen oder anderen 
volatilen Fehler.

von Coder (Gast)


Lesenswert?

Vielleicht hilft auch schon der Simulator.

von Kein Plann (Gast)


Angehängte Dateien:

Lesenswert?

A. K. schrieb:
> (2) Suchen lassen.

Habe die Routine mal angehängt. Aber bitte vorher setzten. Sie sieht 
katastrophal aus. Ist von einem Lehrer programmiert wurden, der glaube 
selber keine Ahnung hatte. Wir haben nur die Befehle bekommen. Was sie 
macht, kein Plan.

Coder schrieb:
> Vielleicht wirft der Compiler auch die eine oder andere Warnung die von
> dir ingoriert werden...

Warnungen ignorieren, wie kommst Du den darauf? ;-)
Ne, es werden nur 2 Stück ausgegeben. Beide weisen mich darauf hin das 
Delay nur mit Optimierung funktioniert.

von (prx) A. K. (prx)


Lesenswert?

Wie sieht "wartems" aus?

Ausserdem wärs sehr wohl denkbar, dass 1-2 NOPs zu wenig sind. Einfach 
mal alle diese Multi-NOP-Delays durch jeweils ein einziges _delay_us(1) 
ersetzen.

von Karl H. (kbuchegg)


Lesenswert?

Kein Plann schrieb:

> Habe die Routine mal angehängt. Aber bitte vorher setzten. Sie sieht
> katastrophal aus. Ist von einem Lehrer programmiert wurden, der glaube
> selber keine Ahnung hatte.

Danke für die Warnung.
Deine Lehrer sollte man ....


Das einzige was ich auf die schnelle sehen kann, was hängen könnte ist 
hier
1
void DISPLAY_BUSY(void)
2
{
3
    uint8_t j;
4
    j=1;
5
    DDRC=0x00;
6
    while (!(j==0))
7
    {
8
      RS_L;
9
      RW_S;
10
      EN_S;
11
12
      if (!(PINC &(1<<PIN7)))
13
      {
14
        j=0;
15
      }
16
17
      EN_L;
18
      RW_L;
19
    }  
20
    DDRC=0xFF;
21
}

Schmeiss probehalber den Teil mal raus und ersetze ihn durch ein
1
void DISPLAY_BUSY(void)
2
{
3
  _delay_ms( 30 );
4
}

(Include Files für den Delay nicht vergessen)

Aber ganz ehrlich. Den LCD Code sollte man ausdrucken und ihm solange um 
die Ohren dreschen, bis nur noch Konfetti übrig sind. Das ein LCD auch 
ein Timing braucht, scheint er noch nie gehört zu haben. Und über die 
Zahlenausgaben breiten wir lieber den Mantel des Schweigens. (PS die 
letzte Routine ist falsch.)

von Karl H. (kbuchegg)


Lesenswert?

1
  uint16_t hex_teilen[3];
2
  hex_teilen[0] = hexvar16 & 0b1111000000000000;
3
  hex_teilen[0] = (hex_teilen[0] >> 12);
4
5
  hex_teilen[1] = hexvar16 & 0b0000111100000000;
6
  hex_teilen[1] = (hex_teilen[1] >> 8);
7
8
  hex_teilen[2] = hexvar16 & 0b11110000;
9
  hex_teilen[2] = (hex_teilen[2] >> 4);
10
11
  hex_teilen[3] = hexvar16 & 0b00001111;

Autsch!

von (prx) A. K. (prx)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Das ein LCD auch
> ein Timing braucht, scheint er noch nie gehört zu haben.

Doch, er hat. Sonst hätte er nicht all diese lustigen NOPs verstreut. 
Nur kapiert hat ers wohl nicht, oder geht fest von 1MHz Takt oder so 
aus. Ich habe hier aber schon schlimmeren Anfängercode gesehen.

von Karl H. (kbuchegg)


Lesenswert?

1
void lcd_uint16(uint16_t uint16)

Selbiges: Array Overflow.

Ich korrigiere mich: Nach der Konfetti Produktion sollte man ihn teeren 
und federn und die Lizenz zum Lehren von Programmiersprachen entziehen, 
solange bis er die erfolgreiche ABsolvierung eines Anfängerkurses 
nachweisen kann.

von Karl H. (kbuchegg)


Lesenswert?

A. K. schrieb:

> Ich habe hier aber schon schlimmeren Anfängercode gesehen.

Aber nicht viel schlimmer.
Die meisten Anfänger haben den Dreh schnell raus, dass
* in C bei 0 begonnen wird zu zählen (siehe die Cursor Positionierung
* daraus folgt, dass der maximal mögliche Array Index um 1 kleiner
  ist, als die definierte Größe.

Noch schlimmer ist allerdings, dass dieses Machwerk von seinem Lehrer 
stammt, der eigentlich kein Anfänger sein sollte.

von (prx) A. K. (prx)


Lesenswert?

Der kommt garantiert von Microsoft-Basic. Da hat ein Array(10) 
tatsächlich 11 Elemente.

von Kein Plan (Gast)


Lesenswert?

Karl Heinz Buchegger schrieb:
> void DISPLAY_BUSY(void)
> {
>   _delay_ms( 30 );
> }


Okay, auch das hat funktioniert.
Aber damit vergeude ich nun Zeit?
Wo liegt nun in der Routine der Fehler?

Ihr seit wie immer spitze!!!

Danke!

von Karl H. (kbuchegg)


Lesenswert?

Kein Plan schrieb:
> Karl Heinz Buchegger schrieb:
>> void DISPLAY_BUSY(void)
>> {
>>   _delay_ms( 30 );
>> }
>
>
> Okay, auch das hat funktioniert.
> Aber damit vergeude ich nun Zeit?
> Wo liegt nun in der Routine der Fehler?

Wahrscheinlich darin, dass er sich einen Dreck um Timings geschi.... 
hat.
Man kann nicht einfach 'Pin hoch' - 'Pin runter'. Da muss eine Wartezeit 
dazwischen. Dem LCD muss man schon einen Puls mit Mindestbreite zeigen, 
damit es auch eine Chance hat, den Puls als solchen zu sehen.

Wie ist denn die Pinbelegung im lcd.h?

(Dass er da einfach den Port C komplett von Ein auf Ausgang umschaltet, 
stösst mir auch sauer auf. Wozu gibts denn die schönen #define in der 
lcd.h! Amateur)

von (prx) A. K. (prx)


Lesenswert?

Ich tippe auf hier:
1
      EN_S;
2
3
      if (!(PINC &(1<<PIN7)))
denn da fragt er den Pin ab, noch bevor E überhaupt richtig oben ist. 
Jedenfalls wenns ein ATmega ist. Bei einem 1MHz AT90 hingegen dürfte das 
funktionieren, da denen die Input-Latch-Kette zur Vermeidung 
metastabiler Zustände fehlt und daher zwischen der Aktivierung von E und 
dem abgefragten Zustand etwas Zeit bleibt.

von Kein Plan (Gast)


Angehängte Dateien:

Lesenswert?

Karl Heinz Buchegger schrieb:
> Man kann nicht einfach 'Pin hoch' - 'Pin runter'. Da muss eine Wartezeit
> dazwischen. Dem LCD muss man schon einen Puls mit Mindestbreite zeigen,
> damit es auch eine Chance hat, den Puls als solchen zu sehen.

Wie meinst Du das?

Im Anhang noch die LCD.h.

von Karl H. (kbuchegg)


Lesenswert?

A. K. schrieb:
> Ich tippe auf hier:
>
1
>       EN_S;
2
> 
3
>       if (!(PINC &(1<<PIN7)))
4
>
> denn da fragt er den Pin ab, noch bevor E überhaupt richtig oben ist.

Jo, hätt ich jetzt auch gesagt.
Ich weiß aber nicht auswendig, welche Wartezeit da angemessen ist.
Ich würds mal mit ein paar µs versuchen. Sagen wir mal 50. KLeiner gehen 
kann man immer noch (oder im Datenblatt nachsehen)

von (prx) A. K. (prx)


Lesenswert?

Kein Plan schrieb:
> Wie meinst Du das?

Schau mal ins aktuelle Datasheet vom ATmega8 (wegen der Seitennummer) 
auf Seite 53. Der Abschnitt "Reading the Pin Value".

Karl Heinz Buchegger schrieb:
> Ich würds mal mit ein paar µs versuchen. Sagen wir mal 50.

_delay_us(1) reicht.

von Karl H. (kbuchegg)


Lesenswert?

A. K. schrieb:
> Wie sieht "wartems" aus?

Guter Einwand.
Sollte man auch durch die gcc-Standard Routine ersetzen. Wer weiß, was 
da wieder dahinter steckt.

von Karl H. (kbuchegg)


Lesenswert?

A. K. schrieb:

>> Ich würds mal mit ein paar µs versuchen. Sagen wir mal 50.
>
> _delay_us(1) reicht.

Ah. ok. Danke

Also. Vorschlag
1
void DISPLAY_BUSY(void)
2
{
3
    uint8_t j = 1;
4
    DDRC=0x00;
5
 
6
    while ( j )
7
    {
8
      RS_L;
9
      RW_S;
10
      EN_S;
11
12
      _delay_us( 1 );
13
14
      if (!(PINC &(1<<PIN7)))
15
      {
16
        j=0;
17
      }
18
19
      EN_L;
20
      RW_L;
21
    }  
22
    DDRC=0xFF;
23
}

von Kein Plan (Gast)


Lesenswert?

Ihr seit so gut!!!!!

So siehts jetzt aus:
1
void DISPLAY_BUSY(void)
2
{
3
     uint8_t j;
4
    j=1;
5
    DDRC=0x00;
6
    while (!(j==0))
7
    {
8
      RS_L;
9
      RW_S;
10
      EN_S;
11
      _delay_us(1);
12
      
13
      if (!(PINC &(1<<PIN7)))
14
      {
15
        j=0;
16
      }
17
18
      EN_L;
19
      RW_L;
20
    }  
21
    DDRC=0xFF;
22
}

Ausserdem habe ich alle Multi-NOP-Delays ersetzt.

DANKE!!

Guten Rutsch und macht auch im Jahr 2013 genau so weiter.

von Karl H. (kbuchegg)


Lesenswert?

Wenns damit erst mal klappt, dann machen wir den nächsten Schritt.

Ich kann nämlich dieses Elend nicht mehr sehen.
Die Zahlenausgaben ersetzt du durch
1
void lcd_char(char c)
2
{
3
  RS_S;
4
  DISPLAY_PORT = c;
5
  DISPLAY_EN();
6
  DISPLAY_BUSY();
7
}
8
9
void lcd_string( const char* str )
10
{
11
  while( *str )
12
    lcd_char( *str++ );
13
}
14
15
void lcd_uint8(uint8_t uint8)
16
{
17
  char str[4];
18
19
  utoa( uint8, str, 10 );
20
  lcd_string( str );
21
}
22
23
void lcd_uint16(uint16_t uint16)
24
{
25
  char str[6];
26
27
  utoa( uint16, str, 10 );
28
  lcd_string( str );
29
}
30
31
static void lcd_nibble(uint8_t nibble)
32
{
33
  nibble &= 0x0F;
34
35
  if( nibble < 10 )
36
    lcd_char( nibble + '0' );
37
  else
38
    lcd_char( nibble - 10 + 'A' );
39
}
40
 
41
void lcd_hex8(uint8_t hexvar8)
42
{
43
  lcd_nibble( hexvar8 >> 4 );
44
  lcd_nibble( hexvar8 );
45
}
46
47
void lcd_hex16(uint16_t hexvar16)
48
{
49
  lcd_nibble( hexvar8 >> 12 );
50
  lcd_nibble( hexvar8 >> 8 );
51
  lcd_nibble( hexvar8 >> 4 );
52
  lcd_nibble( hexvar8 );
53
}

Das ist übrigens keine Frage der Schönheit. Die Originalroutinen sind 
schlicht und ergreifend fehlerhaft (mal ganz davon abgesehen, dass sie 
horrend umständlich programmiert sind).

Und über die lcd_komma reden wir nochmal, wenn du sie brauchst. Die ist 
auch falsch.

von Karl H. (kbuchegg)


Lesenswert?

Und tu mir noch einen Gefallen

Da kriegt man ja Augenkrebs
1
    while (!(j==0))

ersetzen durch
1
    while (j != 0)

oder gleich
1
    while (j)

von Kein Plan (Gast)


Lesenswert?

Danke euch zwei.
Habe fast alles umgesetz und soga verstanden.
Außer:

Karl Heinz Buchegger schrieb:
> void lcd_uint8(uint8_t uint8)
> {
>   char str[4];
>
>   utoa( uint8, str, 10 );
>   lcd_string( str );
> }
>
> void lcd_uint16(uint16_t uint16)
> {
>   char str[6];
>
>   utoa( uint16, str, 10 );
>   lcd_string( str );
> }

"utoa" habe ich gerade gelesen braucht viel Speicher. Deshalb hab ichs 
gelassen.

DANKE!!!

von Karl H. (kbuchegg)


Lesenswert?

Kein Plan schrieb:

> "utoa" habe ich gerade gelesen braucht viel Speicher.

utoa braucht auch nicht viel Speicher. Du verwechselst das mit printf.

Abegesehen davon. Was glaubst du, was der Original-Code da braucht? Wer 
sowas schreibt, dem spreche ich das Recht ab, sich über 
Speicherverbrauch Gedanken zu machen :-)

von Kein Plan (Gast)


Lesenswert?

Hallo,

Leider muss ich dieses Thread zum leben erwecken.

Ich brauche eine Funktion die unsigned char ausgibt.
Habe es wie folgt versucht:
1
void lcd_uchar(unsigned char c)
2
{
3
  RS_S;
4
  DISPLAY_PORT = c;
5
  DISPLAY_EN();
6
  DISPLAY_BUSY();
7
}
Geht aber natürlich nicht.

Was muss ich ändern?

von C.Type (Gast)


Lesenswert?

Kein Plan schrieb:
> Geht aber natürlich nicht.

Dem Port ist das egal, wie der Datentyp heißt. Hauptsache die Bits sind 
richtig.

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

Kein Plan schrieb:
> Ich brauche eine Funktion die unsigned char ausgibt.

Möchtest du einen Buchstaben auf dem LCD haben oder einen Hexwert? Für 
den Hexwert hat Karl Heinz eigentlich alles vorbereitet:
1
static void lcd_nibble(uint8_t nibble)
2
{
3
  nibble &= 0x0F;
4
5
  if( nibble < 10 )
6
    lcd_char( nibble + '0' );
7
  else
8
    lcd_char( nibble - 10 + 'A' );
9
}
10
 
11
void lcd_hex8(uint8_t hexvar8)
12
{
13
  lcd_nibble( hexvar8 >> 4 );
14
  lcd_nibble( hexvar8 );
15
}
An lcd_hex übergibst du den Wert, der dann als Hex (00-FF) auf dem LCD 
angezeigt wird.

von Kein Plan (Gast)


Lesenswert?

Danke für die Antworten.

Kurz mein Vorhaben:
Zitat aus der I2C Routine von Peter Fleury:
1
unsigned char i2c_readAck(void)
2
{
3
  TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWEA);
4
  while(!(TWCR & (1<<TWINT)));    
5
6
    return TWDR;
7
8
}

Denn erhaltenen Wert würde ich gerne als dezimal Zahl ausgeben.

von Achim M. (minifloat)


Lesenswert?

itoa() selbstgestrickt:
http://www.mikrocontroller.net/articles/FAQ#Eigene_Umwandlungsfunktionen

Den erhaltenen String kannste dann auf dein LCD schieben. mfg mf

von Karl H. (kbuchegg)


Lesenswert?

Kein Plan schrieb:
> Danke für die Antworten.
>
> Kurz mein Vorhaben:
> Zitat aus der I2C Routine von Peter Fleury:
>
1
> unsigned char i2c_readAck(void)
2
> {
3
>   TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWEA);
4
>   while(!(TWCR & (1<<TWINT)));
5
> 
6
>     return TWDR;
7
> 
8
> }
>
> Denn erhaltenen Wert würde ich gerne als dezimal Zahl ausgeben.

die lcd_uint8 von oben macht das.

unsigned char ist auch nichts anderes als ein uint8_t

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.