Forum: Mikrocontroller und Digitale Elektronik ATmega8 und UART => Code bringt Einsteiger zum Verzweifeln


von Achilles (Gast)


Angehängte Dateien:

Lesenswert?

Hallo,

ich habe nun mein erstes Projekt als Anfänger umsetzen wollen, hat aber 
nicht wirklich geklappt.

Mit einem ATmega8 will ich nur eine Spannung messen.
Diesen Spannungswert möchte ich per UART ausgeben.
Es ist ein MAX487 verbaut und kein MAX232.
Ich habe einen RS232<->RS485-Konverter gebaut, damit ich mit einem PC 
den ATmega8 auslesen kann.

Da ich nach einem fertigen Projekt gesucht habe, an dem ich üben kann, 
versuchte ich mich an einem fertig geschriebenen Code.
Aber leider mit nur mäßigem Erfolg. Dieser Code sieht zwar RS232 vor, 
aber die UART-Bedingungen sind ja gleich.
Der RS232<->RS485-Konverter wandelt nur die Pegel, nicht den Bus-Typ! 
Ich verlängere nur den "RS232-Bus" und bin mir im Klaren, dass ich damit 
den RS232-Bus half-duplex mache, da ich nur Rx und Tx verwenden kann.
Die Auswirkungen sind mir auch bekannt.

* die Spannung (0V bis 5V) lese ich am ADC0 ein
* PD2 wird high, wenn der ATmega8 den MAX487 zum Senden freigibt
* an PB1 hängt eine rote LED
* an PB2 hängt eine grüne LED

Laut dem Sourcecode wird die gemessene Spannung am UART ausgegeben, wenn 
der Mikrocontroller ein "Enter" vom PC empfängt.
Wenn ich "Enter" drücke, bekomme ich ein Leerzeichen gefolgt von einem y 
an den PC gesendet.
Sende ich im raw-Mode ein y, bekomme ich ein G zurück.
Wenn ich aber die if-Bedingung vom "Enter"-Empfang im Code wegnehme und 
PD2 high setze, sendet der Mikrocontroller ohne Pause den gemessenen 
Spannungswert.
Komisch ist aber auch noch, dass ich nur 0V auslese, obwohl mir 2,5V 
angezeigt werden sollte (jedenfalls stimmt der Bus beim Senden, denn 
wenn ich per Hand 3V eintrage, wird auch 3V an den PC übertragen).
Ich habe AVcc mit einer Spule an 5V hängen inklusive einem 
100nF-Kondensator gegen Gnd.
Aref habe ich mit einem 100nF-Kondensator auch gegen Masse geschaltet.
Beim "Build" des Codes bekomm ich folgende Warnung:
../MW-IU.c: In function 'putstring':
../MW-IU.c:46: warning: value computed is not used


Außerdem habe ich es noch nicht geschafft, dass der ATmega8 nur eine 
Messung nach dem Zeichenempfang macht, das Ergebnis sendet und wieder 
wartet, bis das Zeichen kommt.
Es soll im Grunde auf das Startzeichen gewartet werden und eine Messung 
durchgeführt und übermittelt werden.
Danach soll der Bus wieder freigegeben werden (da alles half-duplex).
Aber irgendwie läuft die ADC-Wandlung und das Senden kontinuierlich.

Ich werde mir nun weitere Dokumentation zu diesem Thema angucken und 
ausdrucken, damit ich auch verstehe, wie das alles funktioniert.
Die Tutorials hier bieten sich besonders an, aber ich dachte, dass es 
einfacher wäre ein fertiges Projekt zu bearbeiten.
Also, bitte nicht meckern, ich lese mich in das Thema ein, aber hab 
gerade erst angefangen.

Kann sich trotzdem bitte mal jemand den Code angucken?

Gruß,
Achilles

von Falk W. (dl3daz) Benutzerseite


Lesenswert?

Achilles schrieb:
> Hallo,
>
> ich habe nun mein erstes Projekt als Anfänger umsetzen wollen, hat aber
> nicht wirklich geklappt.
>
> Mit einem ATmega8 will ich nur eine Spannung messen.
> Diesen Spannungswert möchte ich per UART ausgeben.
> Es ist ein MAX487 verbaut und kein MAX232.

Übel.

> Ich habe einen RS232<->RS485-Konverter gebaut, damit ich mit einem PC
> den ATmega8 auslesen kann.

Da sehe ich ein halbes Dutzend Fallen für den Anfänger.
Wie sieht Dein RS232<->RS485-Konverter aus? Wer entscheidet wie und 
wann, ob der Sender oder der Empfänger aktiviert wird?

> Da ich nach einem fertigen Projekt gesucht habe, an dem ich üben kann,
> versuchte ich mich an einem fertig geschriebenen Code.

Das ist grundsätzlich eine gute Idee. RS485 aber nicht.

> Aber leider mit nur mäßigem Erfolg. Dieser Code sieht zwar RS232 vor,
> aber die UART-Bedingungen sind ja gleich.
> Der RS232<->RS485-Konverter wandelt nur die Pegel, nicht den Bus-Typ!
> Ich verlängere nur den "RS232-Bus" und bin mir im Klaren, dass ich damit
> den RS232-Bus half-duplex mache, da ich nur Rx und Tx verwenden kann.
> Die Auswirkungen sind mir auch bekannt.

Sicher? In den Funktionen UART_SendByte und putstring sehe ich keine 
Umschaltung zwischen Senden und Empfangen.

> * die Spannung (0V bis 5V) lese ich am ADC0 ein
> * PD2 wird high, wenn der ATmega8 den MAX487 zum Senden freigibt

Mit cbi(PORTD,PD2);

Soll der MAX487 auf RX oder TX geschaltet werden?
Und ein paar ns später erwartest Du in

       if(zeichen==0x0D)

daß ein komplettes Zeichen eingelesen wurde?

> * an PB1 hängt eine rote LED
> * an PB2 hängt eine grüne LED

Macht nix ;-)

> Laut dem Sourcecode wird die gemessene Spannung am UART ausgegeben, wenn
> der Mikrocontroller ein "Enter" vom PC empfängt.
> Wenn ich "Enter" drücke, bekomme ich ein Leerzeichen gefolgt von einem y
> an den PC gesendet.

Ist das vielleicht ein "komisches" y? Mit Punkten drauf? Das ist für 
mich immer ein Zeichen von falscher Baudrate o.ä. In diesem Fall gehe 
ich von einem schwebenden RS485-Bus aus...

Alles weitere hängt von o.g. ab...

...

> Kann sich trotzdem bitte mal jemand den Code angucken?

Teilweise tat ich das.

> Gruß,
> Achilles

Falk

von Achilles (Gast)


Lesenswert?

>> Mit einem ATmega8 will ich nur eine Spannung messen.
>> Diesen Spannungswert möchte ich per UART ausgeben.
>> Es ist ein MAX487 verbaut und kein MAX232.
> Übel.

Warum übel?

>> Ich habe einen RS232<->RS485-Konverter gebaut, damit ich mit einem PC
>> den ATmega8 auslesen kann.
> Da sehe ich ein halbes Dutzend Fallen für den Anfänger.
> Wie sieht Dein RS232<->RS485-Konverter aus? Wer entscheidet wie und
> wann, ob der Sender oder der Empfänger aktiviert wird?

Der RS232<->RS485-Konverter hat kein eigenes Hirn, der PC entscheidet, 
wann etwas gesendet oder empfangen wird.
In dem Konverter sitzen nur ein MAX232, ein MAX487, Widerstände, Elkos 
und ein Transistor.
Wenn der PC etwas sendet, wird automatisch per Rx-Leitung der MAX487 auf 
Senden geschaltet, ansonsten ist der auf Empfangen geschaltet.
Mit einem Oszilloskop sieht man, dass sich beim Senden vom PC aus am 
Ausgang des MAX487 etwas tut.
Der RS232-Stecker ist gebrückt: 1-4-6 und 7-8.

>> Da ich nach einem fertigen Projekt gesucht habe, an dem ich üben kann,
>> versuchte ich mich an einem fertig geschriebenen Code.
> Das ist grundsätzlich eine gute Idee. RS485 aber nicht.

Warum ist RS485 keine gute Idee? Da erstmal sowieso nur ein Slave am Bus 
hängt, kann ich diesen doch wie mit RS232 ansprechen.
Muss nur aufpassen, dass nicht gleichzeitig gesendet und empfangen wird. 
Und das wird das Problem hier sein?!

>> Aber leider mit nur mäßigem Erfolg. Dieser Code sieht zwar RS232 vor,
>> aber die UART-Bedingungen sind ja gleich.
>> Der RS232<->RS485-Konverter wandelt nur die Pegel, nicht den Bus-Typ!
>> Ich verlängere nur den "RS232-Bus" und bin mir im Klaren, dass ich damit
>> den RS232-Bus half-duplex mache, da ich nur Rx und Tx verwenden kann.
>> Die Auswirkungen sind mir auch bekannt.
> Sicher? In den Funktionen UART_SendByte und putstring sehe ich keine
> Umschaltung zwischen Senden und Empfangen.
>> * PD2 wird high, wenn der ATmega8 den MAX487 zum Senden freigibt
> Mit cbi(PORTD,PD2);
> Soll der MAX487 auf RX oder TX geschaltet werden?
> Und ein paar ns später erwartest Du in
>       if(zeichen==0x0D)
> daß ein komplettes Zeichen eingelesen wurde?
Mit cbi(PORTD,PD2) wird der Pin low, also ist der MAX487 empfangsbereit 
(Rx enabled).
sbi(PORTD,PD2) schaltet den MAX487 auf Sendebetrieb (Tx enabled)!
Ich kann doch das Zeichen erwarten, weil der MAX487 auf Empfangen steht, 
wenn nichts gesendet wird!?

Mit...

SIGNAL (SIG_UART_RECV
{
  zeichen = UDR;
}

...prüfe ich doch, ob ein Zeichen empfangen wurde, da der MAX487 auf 
Empfangen eingestellt ist.

Was mich aber jetzt stutzig macht, ist, dass in

void UART_SendByte(uint8_t data)
{
  while(bit_is_clear(UCSRA, UDRE));
  UDR = data;
}

das data nicht mehr weiter verarbeitet wird. Oder ist das so richtig?
Dieser Teil stammt nicht von mir.

>> Laut dem Sourcecode wird die gemessene Spannung am UART ausgegeben, wenn
>> der Mikrocontroller ein "Enter" vom PC empfängt.
>> Wenn ich "Enter" drücke, bekomme ich ein Leerzeichen gefolgt von einem y
>> an den PC gesendet.
> Ist das vielleicht ein "komisches" y? Mit Punkten drauf? Das ist für
> mich immer ein Zeichen von falscher Baudrate o.ä. In diesem Fall gehe
> ich von einem schwebenden RS485-Bus aus...

Nein, das ist ein ganz normales y mit einem Leerzeichen davor.
Schalte ich auf HEX, sehe ich die Zeichen als 00 79.
Der RS485-Bus muss stimmen, denn wenn ich den ATmega8 so programmiere, 
dass er mir ständig ohne Aufforderung etwas an den PC senden soll, dann 
tut der das auch.
Habe Baudraten von 1200 bis 115200 getestet, alles ohne Probleme! Das, 
was ich dem ATmega8 zum Senden per Hand reingeschrieben habe, kam auch 
ohne Fehler am PC an.

von Oliver (Gast)


Lesenswert?

>Da ich nach einem fertigen Projekt gesucht habe, an dem ich üben kann,
>versuchte ich mich an einem fertig geschriebenen Code.

Ich kann mich irren, aber die ganze Routine ist erscheint mir als 
ziemlicher bullshit.
1
void messung (void)
2
{
3
    sbi (ADCSR, ADIF);          //ADIF-Bit Setzen(Messzyklus starten)
4
    loop_until_bit_is_clear(ADCSR, ADIF);      //ersten Messwert verwerfen
5
    sbi (ADCSR, ADIF);          //ADIF-Bit Setzen(Messzyklus starten)
6
    loop_until_bit_is_clear(ADCSR, ADIF);      //warten bis Messung fertig
7
    lowByte = ADCL;            //immer zuerst das LowByte auslesen
8
    highByte = ADCH;          //dann das mittlerweile gesperrte HighByte auslesen
9
    ergebnis = highByte * 256 + lowByte;      //Zusammenführung von HighByte und LowByte zu einem Messwert
10
    ausgabe();
11
}

Nach meinem Mega8_Datenblatt heisst das Status-Register ADCSRA, nicht 
ADCSR (ok, vielleicht hieß das in den Uralt-Versionen der avr-libc 
anders).
Eine einzelne Wandlung wird gestartet, in dem das ADSC-Bit gesetzt wird, 
nicht ADIF. Am Ende der Wandlung wird ADIF gesetzt, nicht zurückgesetzt, 
und bleibt gesetzt, bis es durch beschreiben mit 1 gelöscht wird. Auch 
wenn das ganze für eine Uralt-Version der avr-libc geschrieben wurde, 
die Funktion des ADC hat sich nicht geändert. So kann das nicht 
funktionieren.

Lies die mal den Abschnitt zum ADC im avr-gcc-Tutorial durch (und dazu 
das Datenblatt), und änder den Code entsprechend ab. Der Zugriff auf das 
16-bit-Ergebnis geht inzwischen per ADCW auch einfacher.

Oliver

von Oliver (Gast)


Lesenswert?

Nachtrag:

In der Init-Routine steht zwar was vom free-running-Mode, aber ansonsten 
ist das auch nicht besser. Abgesehen von der Frage, ob das wirklich 
gewollt und erforderlich ist, wird da mit ADIE der ADC-Interrupt 
freigegeben (auch wenn der Kommentar was anderes behauptet), ohne daß es 
dafür eine ISR gibt.
1
//initialisieren des Analog-Digital-Converters
2
void adcinit (void)
3
{
4
  ADMUX = (1<<REFS0) | ADCkanal;        //AVcc als externe Referenzspannung nutzen, Spannungsmessung an PC0
5
  ADCSRA = (1<<ADEN) | (1<<ADIE) | (1<<ADFR) | (0x06);  //ADC Enable|ADC Start Conversion|Free Running|Vorteiler 64
6
}

Oliver

von Achilles (Gast)


Lesenswert?

Danke, Oliver, für deine Antwort!

Also ist der C-Code eigentlich wirklich bullshit und zum Üben 
ungeeignet?!
Ich sollte mich demnach an das Tutorial halten und wohl selber den Code 
schreiben...
Das macht es einem Anfänger nicht gerade leicht, aber ist bestimmt 
besser, als in einem fertigen Code, der quick&dirty geschrieben wurde, 
herumzupfuschen.

Ich kann auch erstmal die Umrechnung des ADC-Wertes zu einem zugehörigen 
Spannungswert auslassen und vorerst 0-1023 an den PC senden.
Wenn ich soweit bin, ist schon viel geschafft.

Noch mal eine Frage zum Senden/Empfangen:
Ich frage mich, warum ich immer komische Zeichen zurückgesendet bekomme, 
wenn ich ein "Enter" an den ATmega8 schicke.
Kann es sein, dass der ATmega8 schon etwas schickt, obwohl der PC noch 
nicht bereit ist etwas zu empfangen? Ist ja alles half-duplex und ich 
muss warten, bis der gesamte Bus frei zum Senden ist. Der Empfang ist 
immer eingeschaltet.

von Karl H. (kbuchegg)


Lesenswert?

So wie ich das sehe, machst du dir selbst das Leben unnötig schwer:
Du eröffnest 3 Baustellen gleichzeitig und verlierst den Überblick, 
welches Problem durch welche Baustelle verursacht wird.

Mach eines nach dem anderen:
Bring erst mal deine serielle Kommunikation in Ordnung.
Und zwar ohne den Umweg über 485
RS232, so wie µC/Max232  und PC das von Haus aus können.

Da kannst du dann mal deine ganzen Fähigkeiten aufbieten, die Baudrate 
richtrig einzustellen und lernen, wie man Zahlen verschickt, etc.

Wenn diese Baustelle soweit abgeschlossen ist, das sie funktioniert, 
eröffnest du die 2.te: ADC

Jetzt lernst du, wie man den ADC anspricht, wie man ihn konfiguriert, 
wie man ihn ausliest.
Da deine RS232 Baustelle soweit abgeschlossen ist, dass sie 
funktioniert, kannst du das soweit fertige RS232 MOdulk benutzen kannst 
um die ADC Ergebnisse zum PC zu übertragen. Wenn Fehler auftauchen 
werden die wahrscheinlich nicht mehr im RS232 Modul sein, sondern im ADC 
Modul deines Programmes, den den RS232 hast du ja soweit ausgetest, dass 
du dich darauf verlassen kannst, das das funktioniert.

Wenn dan RS232 + ADC funktioniert, eröffnest du deine 3. Baustelle. 
Kommunikation über RS485.
Selbes Vorgehen: Du kannst dich auf die bereits abgeschlossenen 
Baustellen verlassen, dass die sauber funktionieren. Wenn neue Fehler 
auftreten, weißt du wo du sie suchen musst.

Aber so wie du das jetzt machst, ist es sinnlos. Du verlierst dich in 
der Komplexität deiner 3 gleichzeitig öffenen Baustellen.

von Achilles (Gast)


Lesenswert?

Karl heinz Buchegger schrieb:
> Aber so wie du das jetzt machst, ist es sinnlos. Du verlierst dich in
> der Komplexität deiner 3 gleichzeitig öffenen Baustellen.

Du hast vollkommen recht!
Ich bin halt davon ausgegangen, dass das fertige Programm ohne Probleme 
funktioniert, scheint aber nicht so.
Ich bin gerade am ADC dran, und wenn diese Routine passt, werde ich 
deinen Weg gehen. Will jetzt nicht wieder wechseln sondern bei einer 
Sache bleiben.
Denn verstehen muss ich alles irgendwann sowieso.

Einen RS232-Baustein habe ich bereits gebaut, kann also den MAX487 aus 
der Schaltung nehmen und stattdessen das RS232-Modul dranhängen. Somit 
gehe ich mit dem UART direkt auf den MAX232 und gehe nicht den Weg über 
RS485.

von Achilles (Gast)


Angehängte Dateien:

Lesenswert?

Hallo,

ich habe herausgefunden, dass mein RS232<->RS485-Konverter nicht 
fehlerfrei ist, da ich eine automatische Richtungsumschaltung eingebaut 
habe, die Zeichen verschluckt.

Nun habe ich einen anderen Konverter gebaut, der keine automatische 
Richtungsumschaltung benötigt, da ich das Busprotokoll eh daran anpassen 
muss und noch in der Planung ist. Baustelle 3 ist abgeschlossen!

Folgendes soll realisiert werden:
* der Master schickt ein Zeichen an die Slaves OK
* an den Master wird das geschickte Zeichen sofort zurückgeschickt OK
* der angesprochene Slave antwortet mit dem Zeichen und liefert 
Messdaten inkl. einem definierten Stopzeichen an den Master zurück
* der Master prüft, ob die ersten beiden Zeichen im String gleich sind
* ist das der Fall, wertet der Master die Messdaten bis zum Stopzeichen 
aus
* etc.

Also, der Konverter liefert mir sofort das geschickte Zeichen zurück.
Ein Slave reagiert auf das Zeichen und liefert Messdaten.
Allerdings ist der Bus noch nicht wie die zukünftige Realisierung 
aufgebaut.

Ich hab es noch nicht hinbekommen, dass der Slave nur EINE ADC-Messung 
macht und diese an den Master sendet.
Es kommen meist 5 Messdaten an den Master an, dann hört die 
Kommunikation auf, obwohl der Slave munter weitersendet.
Wie kann man denn den Inhalt des UDR nach dem Senden löschen?

Ich glaube auch, das ich Schwierigkeiten mit der !RE/DE-Umschaltung 
habe.
Irgendwas passt noch nicht so ganz.

Baustelle 2 (ADC) ist noch in Arbeit, läuft aber teilweise "irgendwie" 
schon recht gut.

Baustelle 1 ist demnach noch nicht ganz gut umgesetzt.

----
* die Spannung (0V bis 5V) lese ich am ADC0 ein
* PD2 wird high, wenn der ATmega8 den MAX487 zum Senden freigibt
* an PB1 hängt eine rote LED (es wird was gesendet)
* an PB2 hängt eine grüne LED (empfangsbereit)
----

Außerdem bekomme ich eine Warnung:
In function 'putstring':
warning: value computed is not used

Weiteres im Anhang!

Es wäre super, wenn sich das mal jemand angucken und mir helfen könnte.

von Oliver (Gast)


Lesenswert?

>In function 'putstring':
>warning: value computed is not used
1
*s++;            //zeigt auf das nächste Zeichen

C-Buch nehmen, und diese Zeile verstehen. Die macht zwar, was du willst, 
aber eher zufällig.

>Ich hab es noch nicht hinbekommen, dass der Slave nur EINE ADC-Messung
>macht und diese an den Master sendet.

Das steht so auch nicht in deinem Programm. Wenn du ein 0x0D sendest, 
ist
1
 if(zeichen==0x0D)
 erfüllt, und bleibt es so lange, bis du ein anderes Zeichen sendest. So 
lange wird fröhlich gemessen, immer wieder. Wenn du nur eine Messung 
machen möchtest, muß zeichen nach der Messung gelöscht werden.

>Wie kann man denn den Inhalt des UDR nach dem Senden löschen?
Das "löscht" sich selber.

Oliver

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

div_t bzw. div gehen auf int-Ebene, die double-Variable ist da 
zwecklos/irreführend:
1
typedef struct {
2
 int quot;
3
 int rem;
4
} div_t;
5
6
extern div_t div(int __num, int __denom) __asm__("__divmodhi4") __attribute__((__const__));

http://www.nongnu.org/avr-libc/user-manual/structdiv__t.html

Im switch geht auch sowas:
1
switch (dividend.quot)          //�berpr�fen des Divisionsergebnisses
2
{
3
    case 0:
4
    case 1:
5
    case 2:
6
    case 3:
7
    case 4:
8
    case 5:
9
          UART_SendByte ('0' + dividend.quot);
10
          UART_SendByte ('.');
11
          break;
12
    default:  
13
          putstring("Error");
14
          break;
15
}

bzw. eine if-Abfrage wäre effizienten und auch kompakter und 
übersichtlicher.

Johann

von Achilles (Gast)


Lesenswert?

@Oliver:
> Wenn du nur eine Messung machen möchtest, muß zeichen nach der Messung gelöscht 
> werden.
>> Wie kann man denn den Inhalt des UDR nach dem Senden löschen?
> Das "löscht" sich selber.

Und genau das ist mein Problem. Da mein RS485-Bus half-duplex ist, kann 
ich dem Slave nicht zuverlässig ein weiteres Zeichen schicken, damit er 
die Schleife nur ein Mal durchläuft.

Mit
1
zeichen = 0x00;
oder etwas ähnlichem nach der Messung in der Schleife geht das nicht. 
Der Inhalt vom UDR bleibt gesetzt.
Darum die Frage, wie man den Empfangspuffer löscht, ohne etwas über UART 
senden zu müssen.
1
*s++;            //zeigt auf das nächste Zeichen
Mal ohne C-Buch: s ist ein Pointer, der auf ein Zeichen der Zeichenkette 
zeigt. Wird das Zeichen gesendet, auf dem der Pointer steht, wird der 
Pointer um eins erhöht, zeigt auf das nächste Zeichen und überträgt 
dieses. Das geht solange weiter, bis der Pointer auf kein weiteres 
Zeichen mehr zeigen kann.

---------------------------------

@Johann:
> div_t bzw. div gehen auf int-Ebene, die double-Variable ist da
> zwecklos/irreführend
Danke, dass werde ich mir mal genauer angucken.

Ich hatte in meinem Anfangspost bereits geschrieben:
"Da ich nach einem fertigen Projekt gesucht habe, an dem ich üben kann,
versuchte ich mich an einem fertig geschriebenen Code.".
Und das war wirklich nicht die beste Wahl.
Mir passt es auch nicht, was da berechnet wird.
Diese "Berechnungsroutine" muss ich eh nochmal anpassen, da ich von 0-5V 
einen Strom von 0 bis 25A einlese.
Vielleicht lasse ich mir auch erstmal nur den ADC-Wert ausgeben, also 
von 0-1023.
Wenn ich aber
1
putstring("ADCW");
eingebe, wird nicht der ADC-Wert, sondern der String ADCW übertragen.

Ich merke, mir fehlt da noch einiges an Grundlagen.
Außerdem kenne ich leider keine realen Leute, die sich mit 
Mikrocontrollern beschäftigen. :-(

von Falk W. (dl3daz) Benutzerseite


Lesenswert?

Achilles schrieb:
...
>
1
> *s++;            //zeigt auf das nächste Zeichen
2
>
> Mal ohne C-Buch: s ist ein Pointer, der auf ein Zeichen der Zeichenkette
> zeigt.

Ja. Ich würde es so schreiben:
1
UDR=*s;
2
s++;

> Wird das Zeichen gesendet, auf dem der Pointer steht, wird der
> Pointer um eins erhöht, zeigt auf das nächste Zeichen und überträgt
> dieses.

*s ist der Wert, auf den der Zeiger zeigt, s ist der Zeiger selbst.
s++ erhöht den Zeiger.
Wenn man Erfahrung mit C hat, schreibt man lieber UDR=*s++, weil klar 
ist, daß das Zeichen, auf das der Zeiger zeigt, ausgegeben wird und der 
Zeiger dann um eins erhöht wird.

> Das geht solange weiter, bis der Pointer auf kein weiteres
> Zeichen mehr zeigen kann.

Nein, in
1
void uart_puts(char *s) {
2
 while (*s) {
3
   UDR=*s; s++;
4
 }
5
}
(Die Abfrage, ob UDR frei ist, habe ich weggelassen)

Werden solange Zeichen ausgegeben, bis der Inhalt, auf den der Zeiger 
zeigt, Null wird.

> Wenn ich aber
>
1
> putstring("ADCW");
2
>
> eingebe, wird nicht der ADC-Wert, sondern der String ADCW übertragen.

Natürlich. Computer machen nicht, was man will, sondern was man ihnen 
sagt.
Du mußt den integer in eine Zeichenkette umwandeln, damit Du ihn in für 
den Menschen lesbarer Form, idealerweise im Dezimalsystem ausgeben 
kannst.

> Ich merke, mir fehlt da noch einiges an Grundlagen.

Ich würde die Routinen erstmal auf dem PC testen. Da kann man mit dem 
Debugger schön sehen, was genau passiert. (Ich mache das unter Linux mit 
gcc, das kommt der uC-programmierung viel näher als VisualC-irgendwas)

> Außerdem kenne ich leider keine realen Leute, die sich mit
> Mikrocontrollern beschäftigen. :-(

Ich fühle mich durchaus real ;-)

Falk

von Achilles (Gast)


Lesenswert?

> Computer machen nicht, was man will, sondern was man ihnen sagt.
Darum sind auch Computer genau so dumm wie ihre Programmierer!
Ich glaub, mein Mikrocontroller grunzt noch! lach

Ich sitze gerade nicht zu Hause an meinem Rechner und kann mein 
geändertes Programm nicht compilieren bzw. debuggen.
1
#include <avr/io.h>
2
#include <avr/interrupt.h>
3
#include <inttypes.h>
4
#include <stdlib.h>
5
6
#define CLK 3686400
7
#define BAUD 38400
8
#define USARTSPEED (CLK/(BAUD*16L)-1)        // Formel zur Berechnung der Werte für UBBRH/UBBRL
9
#define LOW(x)  ((x) & 0xFF)          // Makro zum Lowbyte Zugriff eines Word
10
#define HIGH(x)   (((x)  >> 8) & 0xFF)        // Makro zum Highbyte Zugriff eines Word
11
12
char string[20];
13
14
volatile uint8_t zeichen;          //definiere zeichen als unsigned integer 8bit
15
volatile uint8_t lowByte;          //definiere lowByte als unsigned integer 8bit
16
volatile uint8_t highByte;          //definiere highByte als unsigned integer 8bit
17
volatile uint16_t ergebnis;          //definiere ergebnis als unsigned integer 16Bit
18
19
uint8_t i;
20
uint16_t result;
21
22
23
SIGNAL (SIG_UART_RECV)            //Uart Receive Interrupt wurde ausgelöst
24
{
25
  zeichen = UDR;            //den empfangenen Wert nach temp holen
26
}
27
28
void UART_SendByte(uint8_t data)        //sendet ein Byte über das Uart
29
{
30
  while(bit_is_clear(UCSRA, UDRE));      //warten bis UART bereit ist zum senden
31
  UDR = data;            //data ausgeben
32
}
33
34
//sendet einen String über das Uart
35
void uart_puts(char *s)            //setze den Pointer s an den Anfang des übergebenen chararrays
36
{  
37
  while (*s != 0)            //ist der Pointer des Zeichens=0 dann chararray zu Ende
38
  {
39
    UART_SendByte=*s++;        //Übergibt das Zeichen an UART_SendByte und zeigt auf das nächste Zeichen          
40
  }
41
}
42
43
//initialisieren des UART
44
void uartinit (void)
45
{
46
  UBRRH = HIGH(USARTSPEED);        //Baudrate einstellen
47
  UBRRL = LOW(USARTSPEED);        //Baudrate einstellen
48
  UCSRB = _BV(TXEN) | _BV(RXEN) | _BV(RXCIE);    //senden, empfangen, receiveint aktivieren
49
  UCSRC = (1<<URSEL)|(3<<UCSZ0);        //Frame Format setzen:8data, 1stop bit (URSEL=1 -> UCSRC->Settings werden genutzt)
50
}
51
52
//initialisieren der I/O-schnittstellen
53
void ioinit (void)
54
{
55
  DDRB = 0b00000110;            //PortB konfigurieren
56
  PORTB = 0b11000100;            
57
  DDRD = 0b00000110;            //PortD konfigurieren
58
  PORTD = 0b00000010;            
59
}
60
61
//initialisieren des Analog-Digital-Converters
62
void adcinit (void)
63
{
64
  ADMUX = (1<<PC0);
65
  ADMUX |= (0<<REFS1) | (1<<REFS0);
66
  ADCSRA = (1<<ADEN) | (1<<ADPS2) | (0<<ADPS1) | (1<<ADPS0);
67
}
68
69
void ausgabe (void)
70
{
71
  itoa(ergebnis,string,10);
72
  uart_puts(string);          
73
}
74
75
void messung (void)
76
{
77
  {
78
  ADCSRA |= (1<<ADSC);
79
  while ( ADCSRA & (1<<ADSC) )
80
  {
81
  ;
82
  }
83
  result = ADCW;
84
 
85
  result = 0; 
86
  for( i=0; i<4; i++ )
87
  {
88
    ADCSRA |= (1<<ADSC);
89
    while ( ADCSRA & (1<<ADSC) )
90
    {
91
    ;
92
    }
93
    result += ADCW;
94
  }
95
  ADCSRA &= ~(1<<ADSC);
96
  result /= 4;
97
  
98
  ergebnis = result;
99
  ausgabe();
100
}
101
102
}
103
104
int main(void)
105
{
106
  ioinit();  
107
  uartinit();              
108
  adcinit();              
109
  sei ();                
110
  
111
  for(;;)                
112
  {
113
      if(zeichen==0x0D)
114
      {
115
      PORTD |= (1<<PD2);    // MAX485 als Sender einschalten  
116
      PORTB &= ~(1<<PB2);    // grüne LED ausschalten
117
      PORTB |= (1<<PB1);    // rote LED einschalten
118
      messung();      // Messung und Übertragung durchführen
119
      PORTD &= ~(1<<PD2);    // MAX485 als Empfänger einschalten    
120
      }
121
  }
122
}

In der for(;;)-Schleife will ich nun "zeichen" löschen bzw. den 
Empfangspuffer leeren. Wie macht man das am besten?

von Achilles (Gast)


Lesenswert?

argh

Hatte vergessen etwas in der uart_puts zu ändern:
1
void UART_SendByte(uint8_t data)        //sendet ein Byte über das Uart
2
{
3
  while(bit_is_clear(UCSRA, UDRE));      //warten bis UART bereit ist zum senden
4
  UDR = data;            //data ausgeben
5
}
6
7
//sendet einen String über das Uart
8
void uart_puts(char *s)            //setze den Pointer s an den Anfang des übergebenen chararrays
9
{  
10
  while (*s != 0)            //ist der Pointer des Zeichens=0 dann chararray zu Ende
11
  {
12
    UDR=*s++;        //Übergibt das Zeichen an UART_SendByte und zeigt auf das nächste Zeichen          
13
  }
14
}

Würde das so mit der Senderoutine funktionieren?

von Falk W. (dl3daz) Benutzerseite


Lesenswert?

Achilles schrieb:
...
> Ich sitze gerade nicht zu Hause an meinem Rechner und kann mein
> geändertes Programm nicht compilieren bzw. debuggen.
>
>
1
...
2
> char string[20];
3
> 
4
> volatile uint8_t zeichen;
5
> volatile uint8_t lowByte;
6
> volatile uint8_t highByte;
7
> volatile uint16_t ergebnis;
8
> integer 16Bit
9
> 
10
> uint8_t i;
11
> uint16_t result;
12
> 
13
> 
14
> SIGNAL (SIG_UART_RECV)
15
> {
16
>   zeichen = UDR;      //den empfangenen Wert nach temp holen
Kann man so machen. Mit mehr Erfahrung würde ich eine stabilere Lösung 
bevorzugen.
1
> }
2
> 
3
> void UART_SendByte(uint8_t data)   //sendet ein Byte über das Uart
4
> {
5
>   while(bit_is_clear(UCSRA, UDRE));
6
>   UDR = data;            //data ausgeben
7
> }
IMO OK.
1
> 
2
> //sendet einen String über das Uart
3
> void uart_puts(char *s)       
4
> {
5
>   while (*s != 0)
6
>   {
7
>     UART_SendByte=*s++;
Mööp. Das kompiliert nicht. So muß das sein: UART_SendByte(*s++);
1
>   }
2
> }
3
> 
4
> //initialisieren des UART
....
1
> int main(void)
2
> {
3
>   ioinit();
4
>   uartinit();
5
>   adcinit();
6
>   sei ();
7
> 
8
>   for(;;)
9
>   {
10
>       if(zeichen==0x0D)
11
>       {
In der ISR wird zeichen irgendwann man 0x0d gesetzt werden. Und bei der 
nächsten Abfrage hier auch, und ... und..
1
>       PORTD |= (1<<PD2);    // MAX485 als Sender einschalten
2
>       PORTB &= ~(1<<PB2);    // grüne LED ausschalten
3
>       PORTB |= (1<<PB1);    // rote LED einschalten
4
>       messung();      // Messung und Übertragung durchführen
5
>       PORTD &= ~(1<<PD2);    // MAX485 als Empfänger einschalten
zeichen ist immer noch 0x0D. Jetzt ist der Zeitpunkt gekommen,
1
zeichen=0;
zu schreiben.
1
>       }
2
>   }
3
> }
4
>
>
> In der for(;;)-Schleife will ich nun "zeichen" löschen bzw. den
> Empfangspuffer leeren. Wie macht man das am besten?

Siehe oben (zeichen=0), UDR wird in der ISR gelöscht.

Falk

von Oliver (Gast)


Lesenswert?

>Da mein RS485-Bus half-duplex ist, kann
>ich dem Slave nicht zuverlässig ein weiteres Zeichen schicken, damit er
>die Schleife nur ein Mal durchläuft.

Das ginge wohl auch nicht bei voll-duplex. Bis das neue Zeichen da ist, 
hat der Prozessor schon mehrfach gemessen.

>Mit
>zeichen = 0x00;
>oder etwas ähnlichem nach der Messung in der Schleife geht das nicht.

Warum nicht?

>Der Inhalt vom UDR bleibt gesetzt.
>Darum die Frage, wie man den Empfangspuffer löscht, ohne etwas über UART
>senden zu müssen.

UDR wird immer nur dann genau einmal gelesen, wenn ein neues Zeichen 
eingetroffen ist. Was danach in dem Register steht, ist völlig egal. Das 
ist ja gerade der Sinn der Interrupt-gesteuerten Empfangsroutine.

>>*s++;            //zeigt auf das nächste Zeichen

>Mal ohne C-Buch:

Mach dir mal klar, was diese einzelnen Ausrücke machen, die Werte von s, 
p und c vor und nach der Ausführung, und auf was die Pointer dann 
jeweils zeigen. Von mir aus ohne C-Buch:
1
void uart_puts(char *s)
2
{  
3
   char *p;
4
   char c;
5
6
   p = s;
7
   p = s++;
8
9
   c = *s;
10
   c = *s++;
11
   c = (*s)++;
12
}
13
14
uart_puts("Hallo");




Oliver

von Achilles (Gast)


Lesenswert?

> Mach dir mal klar, was diese einzelnen Ausrücke machen, die Werte von s,
> p und c vor und nach der Ausführung, und auf was die Pointer dann
> jeweils zeigen. Von mir aus ohne C-Buch:
> [...]

Bevor ich nun was falsches schreibe und ich Einsteiger verwirre, kaufe 
ich mir lieber vorher ein C-Buch! Ich werde wohl nicht drumherum kommen.
Gibt es ein gutes Buch für ATMEL Mikrocontroller-Programmierung in C?

Ich habe mein Programm nun etwas abgeändert, jetzt funktioniert auch die 
Funktion, die den !RE/DE-Pin zuverlässig beim Senden high schaltet.
Ich bekomme es aber nicht mit der Funktion "SIGNAL (SIG_UART_RECV)" hin, 
dass der !RE/DE-Pin immer low ist, damit der ATmega8 etwas empfangen 
kann.

Außerdem möchte ich, dass kontinuierlich eine Spannungsmessung 
durchgeführt wird.
Dieser Wert soll in einer Variable gespeichert werden.
Wird nun der ATmega8 vom Master angesprochen, soll der Wert per 
Interrupt an den Master ausgegeben werden.
Auch das ist gescheitert. Der folgende Code enthält noch nicht die 
ADC-Freerun-Funktion. Habe es auch noch nicht hinbekommen.
Die ADC-Mess-Routine wird eh noch dafür überarbeitet.
1
#include <avr/io.h>
2
#include <avr/interrupt.h>
3
#include <inttypes.h>
4
#include <stdlib.h>
5
6
#define CLK 3686400
7
#define BAUD 38400
8
#define USARTSPEED (CLK/(BAUD*16L)-1)        // Formel zur Berechnung der Werte für UBBRH/UBBRL
9
#define LOW(x)  ((x) & 0xFF)          // Makro zum Lowbyte Zugriff eines Word
10
#define HIGH(x)   (((x)  >> 8) & 0xFF)        // Makro zum Highbyte Zugriff eines Word
11
12
char* BusAdr = "a";              // Busadresse
13
char* Stopp = "#";              // Stoppzeichen für den String
14
char string[20];
15
16
volatile uint8_t zeichen;          //definiere zeichen als unsigned integer 8bit
17
volatile uint8_t lowByte;          //definiere lowByte als unsigned integer 8bit
18
volatile uint8_t highByte;          //definiere highByte als unsigned integer 8bit
19
volatile uint16_t ergebnis;          //definiere ergebnis als unsigned integer 16Bit
20
21
uint8_t i;
22
uint16_t result;
23
24
25
26
//SIGNAL (SIG_UART_DATA) { da kommt eigentlich das PORTD &= ~(1<<PD2); rein}
27
28
29
30
SIGNAL (SIG_UART_RECV)            //Uart Receive Interrupt wurde ausgelöst
31
{
32
  while ( !(UCSRA & (1<<RXC)) );  
33
  zeichen = UDR;            //den empfangenen Wert nach zeichen holen
34
  PORTD &= ~(1<<PD2);
35
}
36
37
void UART_SendByte(uint8_t data)        //sendet ein Byte über das Uart
38
{
39
  while (!(UCSRA & (1<<UDRE)) );        //warten bis UART bereit ist zum senden
40
  PORTD |= (1<<PD2);
41
  UDR = data;            //data ausgeben
42
}
43
44
//sendet einen String über das Uart
45
void uart_puts(char *s)            //setze den Pointer s an den Anfang des übergebenen chararrays
46
{  
47
  while (*s != '\0')            //ist der Pointer des Zeichens=0 dann chararray zu Ende
48
  {
49
    UART_SendByte(*s++);        //Übergibt das Zeichen an UART_SendByte und zeigt auf das nächste Zeichen          
50
  }
51
}
52
53
//initialisieren des UART
54
void uartinit (void)
55
{
56
  UBRRH = HIGH(USARTSPEED);        //Baudrate einstellen
57
  UBRRL = LOW(USARTSPEED);        //Baudrate einstellen
58
  UCSRB = (1<<TXEN) | (1<<RXEN) | (1<<RXCIE) | (1<<UDRIE);
59
  UCSRC = (1<<URSEL)|(3<<UCSZ0);        //Frame Format setzen:8data, 1stop bit (URSEL=1 -> UCSRC->Settings werden genutzt)
60
}
61
62
//initialisieren der I/O-Schnittstellen
63
void ioinit (void)
64
{
65
  DDRB = 0b00000110;            //PortB konfigurieren
66
  PORTB = 0b11000000;            
67
  DDRD = 0b00000110;            //PortD konfigurieren
68
  PORTD = 0b00000010;            
69
}
70
71
//initialisieren des Analog-Digital-Converters
72
void adcinit (void)
73
{
74
  ADMUX = (1<<PC0);
75
  ADMUX |= (0<<REFS1) | (1<<REFS0);
76
  ADCSRA = (1<<ADEN) | (1<<ADPS2) | (0<<ADPS1) | (1<<ADPS0);
77
}
78
79
void ausgabe (void)
80
{
81
82
  itoa(ergebnis,string,10);
83
  uart_puts(BusAdr);
84
  uart_puts(string);
85
  uart_puts(Stopp);
86
87
}
88
89
void messung (void)
90
{
91
  {
92
  ADCSRA |= (1<<ADSC);
93
  while ( ADCSRA & (1<<ADSC) )
94
  {
95
  ;
96
  }
97
  result = ADCW;
98
 
99
  result = 0; 
100
  for( i=0; i<4; i++ )
101
  {
102
    ADCSRA |= (1<<ADSC);
103
    while ( ADCSRA & (1<<ADSC) )
104
    {
105
    ;
106
    }
107
    result += ADCW;
108
  }
109
  //ADCSRA &= ~(1<<ADSC);
110
  result /= 4;
111
  
112
  ergebnis = result;
113
  ausgabe();
114
}
115
116
}
117
118
int main(void)
119
{
120
  ioinit();  
121
  uartinit();              
122
  adcinit();              
123
  sei ();                
124
  for(;;)                
125
  {
126
       if(zeichen==0x61)
127
      {
128
      ADCSRA |= (1<<ADSC);
129
      PORTB &= ~(1<<PB2);    // grüne LED ausschalten
130
      PORTB |= (1<<PB1);    // rote LED einschalten
131
      messung();        
132
      zeichen=0;
133
      PORTB &= ~(1<<PB1);  
134
      ADCSRA &= ~(1<<ADSC);
135
      }
136
      else
137
      {
138
      PORTB |= (1<<PB2);    // grüne LED einschalten
139
      }
140
  }
141
}

Mir ist jetzt erstmal wichtig, dass der !RE/DE-Pin mit der Funktion 
"SIGNAL (SIG_UART_RECV)" zuverlässig geschaltet wird.
Wenn ich das so programmiere, empfängt der ATmega8 nichts mehr.
Hat jemand einen Tipp?

Außerdem ist mir aufgefallen, dass sich der ATmega8 bei dieser 
ADC-Einstellung vermisst. Ich habe 2,5V anliegen, Referenz liegt auf 5V.
Jetzt müsste doch eigentlich der ADC 511 oder so in etwa ausspucken, 
macht der aber nicht. Ich bekomme 341 bis 302 zurück. Je öfter ich die 
MEssung hintereinander anspreche, desto niedriger ist auch der ADC-Wert. 
Liegt eine Pause dazwischen, fängt der Wert wieder bei ~340 an.
Woran könnte das liegen?

von Achilles (Gast)


Lesenswert?

N A C H T R A G

Ist eigentlich nicht auskommentiert, sorry:
//SIGNAL (SIG_UART_DATA) { da kommt eigentlich das PORTD &= ~(1<<PD2); 
rein}


Es steht so bei mir:
1
SIGNAL (SIG_UART_DATA)
2
{ 
3
PORTD &= ~(1<<PD2);
4
}

von Falk W. (dl3daz) Benutzerseite


Lesenswert?

Achilles schrieb:
>
1
> SIGNAL (SIG_UART_RECV)            //Uart Rx Interrupt wurde ausgelöst
2
> {
3
>   while ( !(UCSRA & (1<<RXC)) );
4
>   zeichen = UDR;            //den empfangenen Wert nach zeichen holen
5
>   PORTD &= ~(1<<PD2);
6
> }
7
>
...

> Mir ist jetzt erstmal wichtig, dass der !RE/DE-Pin mit der Funktion
> "SIGNAL (SIG_UART_RECV)" zuverlässig geschaltet wird.
> Wenn ich das so programmiere, empfängt der ATmega8 nichts mehr.
> Hat jemand einen Tipp?

In der Routine, die immer nur dann aufgerufen wird, wenn ein Zeichen 
komplett in das UDR empfangen wurde, wartest Du darauf, daß ein Zeichen 
empfangen wird (while(...)) um danach(!) den Empfänger einzuschalten.

Du hast zu viele Baustellen! So wird das nichts.

Falk

von gast (Gast)


Lesenswert?

der 485 treiber mus immer auf empfang stehen außer beim senden

also funktionen sollten dementsprechend angepasst sein

uart_send
{
 while ( prüfenob sednen bereit )
  {}
 senden freischalten am 485 treiber
 UDR = data
 485 treiber auf emfang zurückschalten
}

du wartest jetz im receive interrupt auf ein zeichen udn willst dann 
erst auf emfang schalten

da der treiber aber alles blockt und senden will kommt er dort nie hin

von Achilles (Gast)


Lesenswert?

Hallo und vielen Dank für die Antworten.

Ihr habt natürlich Recht, die Info mit SIG_UART_DATA hatte ich aus dem 
Chat hier erhalten, aber danach hat nichts mehr funktioniert.
Habe diese Funktion wieder aus dem Code entfernt.

Also, es ist richtig, dass der MAX487 immer auf Empfang steht und nur 
auf Senden geschaltet wird, wenn der UART etwas senden will.

In der ioinit() habe ich beim Starten bereits angegeben, dass der MAX487 
auf Empfangen geschaltet ist:
1
DDRD = 0b00000110;            //PortD konfigurieren
2
PORTD = 0b00000010;           //PD2 ist ein Ausgang, aber abgeschaltet
Jetzt brauche ich nur in der Funktion UART_SendByte() folgendes zu 
schreiben?
1
void UART_SendByte(uint8_t data)        //sendet ein Byte über das UART
2
{
3
  while (!(UCSRA & (1<<UDRE)) );        //warten bis UART bereit ist zum senden
4
  PORTD |= (1<<PD2);                    //MAX487 auf senden schalten
5
  UDR = data;                           //data ausgeben
6
  PORTD &= ~(1<<PD2);                   //MAX487 auf empfangen schalten
7
}

(SIG_UART_RECV) sieht jetzt so aus:
1
SIGNAL (SIG_UART_RECV)            //Uart Receive Interrupt wurde ausgelöst
2
{
3
  while ( !(UCSRA & (1<<RXC)) );  
4
  zeichen = UDR;                  //den empfangenen Wert nach zeichen holen
5
}
Alle anderen Zuweisungen von PD2 habe ich aus dem Programm genommen.
Die Richtungsumschaltung übernimmt jetzt nur die Funktion 
UART_SendByte()?!
Sowas, wie es jetzt in der Funktion UART_SendByte() steht, hatte ich 
bereits in meiner Main, aber da wurde der MAX487 während des Sendens 
abgeschaltet.

Testen kann ich das leider erst heute Abend!

von Falk W. (dl3daz) Benutzerseite


Lesenswert?

Achilles schrieb:
...
> (SIG_UART_RECV) sieht jetzt so aus:
>
1
> SIGNAL (SIG_UART_RECV)  //Uart Receive Interrupt wurde ausgelöst
2
> {
3
>   while ( !(UCSRA & (1<<RXC)) );
Wozu prüfst Du in der ISR, ob ein Zeichen empfangen wurde? Ich werde 
jetzt nicht für Dich im Datenblatt nachschlagen, RXC bei Eintritt in die 
ISR noch gesetzt ist.
1
>   zeichen = UDR;        //den empfangenen Wert nach zeichen holen
2
> }
3
>

Falk
P.S.: Du verrennst Dich.

von Karl H. (kbuchegg)


Lesenswert?

Achilles schrieb:

> Jetzt brauche ich nur in der Funktion UART_SendByte() folgendes zu
> schreiben?
>
1
> void UART_SendByte(uint8_t data)        //sendet ein Byte über das UART
2
> {
3
>   while (!(UCSRA & (1<<UDRE)) );        //warten bis UART bereit ist zum
4
> senden
5
>   PORTD |= (1<<PD2);                    //MAX487 auf senden schalten
6
>   UDR = data;                           //data ausgeben
7
>   PORTD &= ~(1<<PD2);                   //MAX487 auf empfangen schalten
8
> }
9
>

Ich denke mal, dein MAX487 wird das nicht so toll finden, wenn du ihn 
während ein Sendevorgang läuft, auf Empfang umschaltest.
Ich würd zumindest abwarten, bis das Zeichen draussen ist, ehe ich 
wieder auf Empfang umschalte.

Geh mit gesundem Hausverstand an die Dinge ran. Überleg was wohl logisch 
sein wird, und viele Programmierprobleme sind plötzlich sonnenklar. 
Stell dir von mir aus vor, dein MAX487 ist eine Klapptür, der du die 
Richtung vorgeben kannst, in der die Menschen oder Objekte (deine Daten) 
durchgehen können. Wenn die auf 'aus dem Gebäude raus' steht, wird 
niemand durch die Tür reinkönnen um bei dir Pizza abliefern zu können. 
Auf der anderen Seite ist es auch kontraproduktiv, wenn du die Tür auf 
'jetzt gehts raus' stellst, die Pizzaschachtel gegen die Tür wirfst und 
während die Schachtel noch fliegt die Tür auf 'jeder darf rein, aber 
nichts geht raus' stellst.

von Falk W. (dl3daz) Benutzerseite


Lesenswert?

Karl heinz Buchegger schrieb:
> Achilles schrieb:
>
>> Jetzt brauche ich nur in der Funktion UART_SendByte() folgendes zu
>> schreiben?
>>
1
>> void UART_SendByte(uint8_t data)        //sendet ein Byte über das UART
2
>> {
3
>>   while (!(UCSRA & (1<<UDRE)) );        //warten bis UART bereit ist zum
4
>> senden
5
>>   PORTD |= (1<<PD2);                    //MAX487 auf senden schalten
6
>>   UDR = data;                           //data ausgeben
7
>>   PORTD &= ~(1<<PD2);                   //MAX487 auf empfangen schalten
8
>> }
9
>>
>
> Ich denke mal, dein MAX487 wird das nicht so toll finden, wenn du ihn
> während ein Sendevorgang läuft, auf Empfang umschaltest.

Dem wird das egal sein.

> Ich würd zumindest abwarten, bis das Zeichen draussen ist, ehe ich
> wieder auf Empfang umschalte.

Weil ich gerade gute Laune habe:
1
#define RS485RX    PORTD &= ~(1<<PD2)
2
#define RS485TX    PORTD |= (1<<PD2)
3
4
void UART_SendByte(uint8_t data)        //sendet ein Byte über das UART
5
{
6
  while (!(UCSRA & (1<<UDRE)));
7
    UDR = data;                            //data ausgeben
8
}
9
10
void uart_puts(char *s) {  
11
  RS485TX;                     //Sender an
12
  while (*s) {
13
    UART_SendByte(*s++);         
14
  }
15
  while (!(UCSRA & (1<<TXC))); //Warte, bis Senden komplett
16
  RS485RX;                     //Sender aus
17
}

HTH,
Falk

von Stefan E. (sternst)


Lesenswert?

1
  while (!(UCSRA & (1<<TXC))); //Warte, bis Senden komplett
Dann sollte man das Bit vorher aber auch mal löschen.

von Falk W. (dl3daz) Benutzerseite


Lesenswert?

Stefan Ernst schrieb:
>
1
  while (!(UCSRA & (1<<TXC))); //Warte, bis Senden komplett
2
>
Dann sollte man das Bit vorher aber auch mal löschen.

Stimmt. Sogar jedesmal. Logischerweise mit
1
UCSRA|=(1<<TXC);
;-)

Falk

von Achilles (Gast)


Lesenswert?

Sorry, bin gestern nicht mehr zum Testen gekommen, muss auch gleich 
wieder los zur Arbeit.

Frühestens heut Abend kann ich das testen.

Wo muss ich denn
1
UCSRA|=(1<<TXC);
einfügen?

So?
1
void UART_SendByte(uint8_t data)        // sendet ein Byte über das UART
2
{
3
  while (!(UCSRA & (1<<UDRE)));
4
    UDR = data;                         // data ausgeben
5
UCSRA|=(1<<TXC);                        // --> kommt das hier hin?
6
}

@Karl heinz Buchegger:
Ich bin doch schon logisch an die Sache rangegangen (ALTER CODE in der 
MAIN):
1
[...]
2
PORTD |= (1<<PD2);    // MAX487 auf Sender schalten  
3
messung();            // Messung durchführen    
4
PORTD &= ~(1<<PD2);    // MAX487 auf Empfänger schalten
5
[...]
Logisch wäre, wenn der MAX487 auf Sender gestellt wird, dann die 
Funktion messung() abgearbeitet wird (darin ist auch die Ausgabe 
enthalten) und erst danach wird der MAX487 wieder auf Empfänger 
geschaltet.
Aber so logisch macht das der ATmega nicht. Denn während der Ausgabe 
wurde der MAX487 wieder auf Empfänger geschaltet.

Wie dem auch sei, ich kann es frühestens heute Abend testen.
Ich melde mich auf jeden Fall!

von Karl H. (kbuchegg)


Lesenswert?

Achilles schrieb:
> Wo muss ich denn
>
1
> UCSRA|=(1<<TXC);
2
>
> einfügen?
>
> So?
>
1
> void UART_SendByte(uint8_t data)        // sendet ein Byte über das UART
2
> {
3
>   while (!(UCSRA & (1<<UDRE)));
4
>     UDR = data;                         // data ausgeben
5
> UCSRA|=(1<<TXC);                        // --> kommt das hier hin?
6
> }
7
>

Nachdenken!
Was zeigt dein das TXC Bit in UCSRA an?
Welchen Zweck hat das Abfragen des Bits?

Wann wird man es daher zurücksetzen?

> Aber so logisch macht das der ATmega nicht.

Natürlich nicht.
Der ATMega ist dein Sklave. Der tut nur das, was du in deinem Program 
hast. Das aber zu 100%

Du musst dich von der Vorstellung lösen, dass Computer programmieren 
etwas kochrezeptartiges ist. In einigen Bereichen geht das: Erst drück 
ich das, dann das und plötzlich ist mein Programm fertig und 
funktioniert. Aber mit programmieren im eigentlichen Sinne hat das 
nichts zu tun. Ein Computer denkt nicht mit, der führt nur zu 100% deine 
Befehle aus. Und wenn du ihm befiehlst "Erst Tür zumachen, dann 
durchgehen", dann macht der das genau in dieser Reihenfolge! Er denkt 
nicht nach, er denkt nicht mit, ob die Reihenfolge Sinn macht oder 
nicht. Darüber zu befinden ist einzig und alleine dein Bier!

Nicht zielgerichtetes Raten funktioniert in der Programmierung nicht!
Entweder du hast dir bei
>
1
> void UART_SendByte(uint8_t data)        // sendet ein Byte über das UART
2
> {
3
>   while (!(UCSRA & (1<<UDRE)));
4
>     UDR = data;                         // data ausgeben
5
> UCSRA|=(1<<TXC);                        // --> kommt das hier hin?
6
> }
7
>
etwas gedacht, als du die Position des Bitrücksetzens vorgeschlagen 
hast, oder du hast geraten. Da das Bitrücksetzen an dieser Stelle keinen 
Sinn ergibt, bleibt nur letzteres: Du hast geraten. Und das ist bei 
solchen Sachen schlecht. Genau deshalb hab ich gesagt: Denk logisch 
nach!
Wenn dir eine Markierung anzeigt, dass ein Vorgang abgeschlossen ist, 
dann wird man wohl die Markierung vor dem Starten des Vorgangs 
zurücksetzen und nicht danach, wenn der Vorgang schon im Gange ist.

von Achilles (Gast)


Lesenswert?

Ich habe mir bei der Funktion etwas gedacht, denn
> Was zeigt dein das TXC Bit in UCSRA an?
UCSRA|=(1<<TXC) setzt mir doch das Bit, dass die TX-Übertragung beendet 
wird.
Also, es setzt das TXC-Bit im UCSRA-Register auf Eins.

Demnach müsste ich das UCSRA|=(1<<TXC) vor dem eigentlichen Senden 
schreiben?!
1
void UART_SendByte(uint8_t data)
2
{
3
  while (!(UCSRA & (1<<UDRE)));
4
  UCSRA|=(1<<TXC);
5
  UDR = data;
6
}

Mein gesamter C-Code sieht nun so aus:
1
#include <avr/io.h>
2
#include <avr/interrupt.h>
3
#include <inttypes.h>
4
#include <stdlib.h>
5
6
#define CLK 3686400
7
#define BAUD 38400
8
#define USARTSPEED (CLK/(BAUD*16L)-1)        // Formel zur Berechnung der Werte für UBBRH/UBBRL
9
#define LOW(x)  ((x) & 0xFF)          // Makro zum Lowbyte Zugriff eines Word
10
#define HIGH(x)   (((x)  >> 8) & 0xFF)        // Makro zum Highbyte Zugriff eines Word
11
#define RS485RX    PORTD &= ~(1<<PD2)
12
#define RS485TX    PORTD |= (1<<PD2)
13
14
char* BusAdr = "a";              // Busadresse
15
char* Stopp = "#";              // Stoppzeichen für den String
16
char string[20];
17
18
volatile uint8_t zeichen;          //definiere zeichen als unsigned integer 8bit
19
volatile uint8_t lowByte;          //definiere lowByte als unsigned integer 8bit
20
volatile uint8_t highByte;          //definiere highByte als unsigned integer 8bit
21
volatile uint16_t ergebnis;          //definiere ergebnis als unsigned integer 16Bit
22
23
SIGNAL (SIG_UART_RECV)            //Uart Receive Interrupt wurde ausgelöst
24
{
25
  while ( !(UCSRA & (1<<RXC)) );  
26
  zeichen = UDR;            //den empfangenen Wert nach zeichen holen
27
}
28
29
void UART_SendByte(uint8_t data)        //sendet ein Byte über das UART
30
{
31
  while (!(UCSRA & (1<<UDRE)));
32
  UCSRA|=(1<<TXC);                    //vorangegangene Übertragung ist zu Ende
33
  UDR = data;                            //data ausgeben
34
}
35
36
void uart_puts(char *s) {  
37
  RS485TX;                     //Sender an
38
  while (*s) {
39
    UART_SendByte(*s++);         
40
  }
41
  while (!(UCSRA & (1<<TXC))); //Warte, bis Senden komplett
42
  RS485RX;                     //Sender aus
43
}
44
45
46
//initialisieren des UART
47
void uartinit (void)
48
{
49
  UBRRH = HIGH(USARTSPEED);        //Baudrate einstellen
50
  UBRRL = LOW(USARTSPEED);        //Baudrate einstellen
51
  UCSRB = (1<<TXEN) | (1<<RXEN) | (1<<RXCIE);
52
  UCSRC = (1<<URSEL)|(3<<UCSZ0);        //Frame Format setzen:8data, 1stop bit (URSEL=1 -> UCSRC->Settings werden genutzt)
53
}
54
55
//initialisieren der I/O-Schnittstellen
56
void ioinit (void)
57
{
58
  DDRB = 0b00000110;            //PortB konfigurieren
59
  PORTB = 0b11000000;            
60
  DDRD = 0b00000110;            //PortD konfigurieren
61
  PORTD = 0b00000010;            
62
}
63
64
//initialisieren des Analog-Digital-Converters im Single-Conversion-Mode ohne Interrupt
65
void adcinit (void)
66
{
67
  ADMUX = (1<<PC0);
68
  ADMUX |= (1<<REFS0);
69
  ADCSRA = (1<<ADEN) | (1<<ADPS2) | (0<<ADPS1) | (1<<ADPS0);  // Die Wandlung wird im Code durch explizites Setzen des Bits ADSC gestartet
70
}
71
72
void ausgabe (void)
73
{
74
75
  itoa(ergebnis,string,10);
76
  uart_puts(BusAdr);
77
  uart_puts(string);
78
  uart_puts(Stopp);
79
80
}
81
82
void messung (void)
83
{
84
  {
85
  ADCSRA |= (1<<ADSC);
86
  while ( ADCSRA & (1<<ADSC) )
87
  {
88
  ;
89
  }
90
  ADCSRA &= ~(1<<ADSC);
91
  ergebnis = ADCW;
92
  ausgabe();
93
}
94
95
}
96
97
int main(void)
98
{
99
  ioinit();  
100
  uartinit();              
101
  adcinit();              
102
  sei ();                
103
  for(;;)                
104
  {
105
      if(zeichen==0x61)
106
      {
107
      PORTB &= ~(1<<PB2);    // grüne LED ausschalten
108
      PORTB |= (1<<PB1);    // rote LED einschalten
109
      messung();        
110
      zeichen=0;
111
      PORTB &= ~(1<<PB1);  
112
      }
113
      else
114
      {
115
      PORTB |= (1<<PB2);    // grüne LED einschalten
116
      }
117
  }
118
}
Und das funktioniert auch!

Sieht die Sende- und Empfangsroutine jetzt gut aus?

> Ein Computer denkt nicht mit, der führt nur zu 100% deine
> Befehle aus.
Ja, ich weiß, deshalb ist mein ATmega8 genau so doof wie ich! ;o)

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.