mikrocontroller.net

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


Autor: Achilles (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht 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

Autor: Falk Willberg (dl3daz) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Achilles (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Oliver (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.
void messung (void)
{
    sbi (ADCSR, ADIF);          //ADIF-Bit Setzen(Messzyklus starten)
    loop_until_bit_is_clear(ADCSR, ADIF);      //ersten Messwert verwerfen
    sbi (ADCSR, ADIF);          //ADIF-Bit Setzen(Messzyklus starten)
    loop_until_bit_is_clear(ADCSR, ADIF);      //warten bis Messung fertig
    lowByte = ADCL;            //immer zuerst das LowByte auslesen
    highByte = ADCH;          //dann das mittlerweile gesperrte HighByte auslesen
    ergebnis = highByte * 256 + lowByte;      //Zusammenführung von HighByte und LowByte zu einem Messwert
    ausgabe();
}

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

Autor: Oliver (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.
//initialisieren des Analog-Digital-Converters
void adcinit (void)
{
  ADMUX = (1<<REFS0) | ADCkanal;        //AVcc als externe Referenzspannung nutzen, Spannungsmessung an PC0
  ADCSRA = (1<<ADEN) | (1<<ADIE) | (1<<ADFR) | (0x06);  //ADC Enable|ADC Start Conversion|Free Running|Vorteiler 64
}

Oliver

Autor: Achilles (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Achilles (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Achilles (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht 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.

Autor: Oliver (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>In function 'putstring':
>warning: value computed is not used
*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
 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

Autor: Johann L. (gjlayde) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
div_t bzw. div gehen auf int-Ebene, die double-Variable ist da 
zwecklos/irreführend:
typedef struct {
 int quot;
 int rem;
} div_t;

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:
switch (dividend.quot)          //�berpr�fen des Divisionsergebnisses
{
    case 0:
    case 1:
    case 2:
    case 3:
    case 4:
    case 5:
          UART_SendByte ('0' + dividend.quot);
          UART_SendByte ('.');
          break;
    default:  
          putstring("Error");
          break;
}

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

Johann

Autor: Achilles (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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
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.
*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
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. :-(

Autor: Falk Willberg (dl3daz) Benutzerseite
Datum:

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

Ja. Ich würde es so schreiben:
UDR=*s;
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
void uart_puts(char *s) {
 while (*s) {
   UDR=*s; s++;
 }
}
(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
>
> putstring("ADCW");
> 
> 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

Autor: Achilles (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.
#include <avr/io.h>
#include <avr/interrupt.h>
#include <inttypes.h>
#include <stdlib.h>

#define CLK 3686400
#define BAUD 38400
#define USARTSPEED (CLK/(BAUD*16L)-1)        // Formel zur Berechnung der Werte für UBBRH/UBBRL
#define LOW(x)  ((x) & 0xFF)          // Makro zum Lowbyte Zugriff eines Word
#define HIGH(x)   (((x)  >> 8) & 0xFF)        // Makro zum Highbyte Zugriff eines Word

char string[20];

volatile uint8_t zeichen;          //definiere zeichen als unsigned integer 8bit
volatile uint8_t lowByte;          //definiere lowByte als unsigned integer 8bit
volatile uint8_t highByte;          //definiere highByte als unsigned integer 8bit
volatile uint16_t ergebnis;          //definiere ergebnis als unsigned integer 16Bit

uint8_t i;
uint16_t result;


SIGNAL (SIG_UART_RECV)            //Uart Receive Interrupt wurde ausgelöst
{
  zeichen = UDR;            //den empfangenen Wert nach temp holen
}

void UART_SendByte(uint8_t data)        //sendet ein Byte über das Uart
{
  while(bit_is_clear(UCSRA, UDRE));      //warten bis UART bereit ist zum senden
  UDR = data;            //data ausgeben
}

//sendet einen String über das Uart
void uart_puts(char *s)            //setze den Pointer s an den Anfang des übergebenen chararrays
{  
  while (*s != 0)            //ist der Pointer des Zeichens=0 dann chararray zu Ende
  {
    UART_SendByte=*s++;        //Übergibt das Zeichen an UART_SendByte und zeigt auf das nächste Zeichen          
  }
}

//initialisieren des UART
void uartinit (void)
{
  UBRRH = HIGH(USARTSPEED);        //Baudrate einstellen
  UBRRL = LOW(USARTSPEED);        //Baudrate einstellen
  UCSRB = _BV(TXEN) | _BV(RXEN) | _BV(RXCIE);    //senden, empfangen, receiveint aktivieren
  UCSRC = (1<<URSEL)|(3<<UCSZ0);        //Frame Format setzen:8data, 1stop bit (URSEL=1 -> UCSRC->Settings werden genutzt)
}

//initialisieren der I/O-schnittstellen
void ioinit (void)
{
  DDRB = 0b00000110;            //PortB konfigurieren
  PORTB = 0b11000100;            
  DDRD = 0b00000110;            //PortD konfigurieren
  PORTD = 0b00000010;            
}

//initialisieren des Analog-Digital-Converters
void adcinit (void)
{
  ADMUX = (1<<PC0);
  ADMUX |= (0<<REFS1) | (1<<REFS0);
  ADCSRA = (1<<ADEN) | (1<<ADPS2) | (0<<ADPS1) | (1<<ADPS0);
}

void ausgabe (void)
{
  itoa(ergebnis,string,10);
  uart_puts(string);          
}

void messung (void)
{
  {
  ADCSRA |= (1<<ADSC);
  while ( ADCSRA & (1<<ADSC) )
  {
  ;
  }
  result = ADCW;
 
  result = 0; 
  for( i=0; i<4; i++ )
  {
    ADCSRA |= (1<<ADSC);
    while ( ADCSRA & (1<<ADSC) )
    {
    ;
    }
    result += ADCW;
  }
  ADCSRA &= ~(1<<ADSC);
  result /= 4;
  
  ergebnis = result;
  ausgabe();
}

}

int main(void)
{
  ioinit();  
  uartinit();              
  adcinit();              
  sei ();                
  
  for(;;)                
  {
      if(zeichen==0x0D)
      {
      PORTD |= (1<<PD2);    // MAX485 als Sender einschalten  
      PORTB &= ~(1<<PB2);    // grüne LED ausschalten
      PORTB |= (1<<PB1);    // rote LED einschalten
      messung();      // Messung und Übertragung durchführen
      PORTD &= ~(1<<PD2);    // MAX485 als Empfänger einschalten    
      }
  }
}

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

Autor: Achilles (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
argh

Hatte vergessen etwas in der uart_puts zu ändern:
void UART_SendByte(uint8_t data)        //sendet ein Byte über das Uart
{
  while(bit_is_clear(UCSRA, UDRE));      //warten bis UART bereit ist zum senden
  UDR = data;            //data ausgeben
}

//sendet einen String über das Uart
void uart_puts(char *s)            //setze den Pointer s an den Anfang des übergebenen chararrays
{  
  while (*s != 0)            //ist der Pointer des Zeichens=0 dann chararray zu Ende
  {
    UDR=*s++;        //Übergibt das Zeichen an UART_SendByte und zeigt auf das nächste Zeichen          
  }
}

Würde das so mit der Senderoutine funktionieren?

Autor: Falk Willberg (dl3daz) Benutzerseite
Datum:

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

Autor: Oliver (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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:
void uart_puts(char *s)
{  
   char *p;
   char c;

   p = s;
   p = s++;

   c = *s;
   c = *s++;
   c = (*s)++;
}

uart_puts("Hallo");




Oliver

Autor: Achilles (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.
#include <avr/io.h>
#include <avr/interrupt.h>
#include <inttypes.h>
#include <stdlib.h>

#define CLK 3686400
#define BAUD 38400
#define USARTSPEED (CLK/(BAUD*16L)-1)        // Formel zur Berechnung der Werte für UBBRH/UBBRL
#define LOW(x)  ((x) & 0xFF)          // Makro zum Lowbyte Zugriff eines Word
#define HIGH(x)   (((x)  >> 8) & 0xFF)        // Makro zum Highbyte Zugriff eines Word

char* BusAdr = "a";              // Busadresse
char* Stopp = "#";              // Stoppzeichen für den String
char string[20];

volatile uint8_t zeichen;          //definiere zeichen als unsigned integer 8bit
volatile uint8_t lowByte;          //definiere lowByte als unsigned integer 8bit
volatile uint8_t highByte;          //definiere highByte als unsigned integer 8bit
volatile uint16_t ergebnis;          //definiere ergebnis als unsigned integer 16Bit

uint8_t i;
uint16_t result;



//SIGNAL (SIG_UART_DATA) { da kommt eigentlich das PORTD &= ~(1<<PD2); rein}



SIGNAL (SIG_UART_RECV)            //Uart Receive Interrupt wurde ausgelöst
{
  while ( !(UCSRA & (1<<RXC)) );  
  zeichen = UDR;            //den empfangenen Wert nach zeichen holen
  PORTD &= ~(1<<PD2);
}

void UART_SendByte(uint8_t data)        //sendet ein Byte über das Uart
{
  while (!(UCSRA & (1<<UDRE)) );        //warten bis UART bereit ist zum senden
  PORTD |= (1<<PD2);
  UDR = data;            //data ausgeben
}

//sendet einen String über das Uart
void uart_puts(char *s)            //setze den Pointer s an den Anfang des übergebenen chararrays
{  
  while (*s != '\0')            //ist der Pointer des Zeichens=0 dann chararray zu Ende
  {
    UART_SendByte(*s++);        //Übergibt das Zeichen an UART_SendByte und zeigt auf das nächste Zeichen          
  }
}

//initialisieren des UART
void uartinit (void)
{
  UBRRH = HIGH(USARTSPEED);        //Baudrate einstellen
  UBRRL = LOW(USARTSPEED);        //Baudrate einstellen
  UCSRB = (1<<TXEN) | (1<<RXEN) | (1<<RXCIE) | (1<<UDRIE);
  UCSRC = (1<<URSEL)|(3<<UCSZ0);        //Frame Format setzen:8data, 1stop bit (URSEL=1 -> UCSRC->Settings werden genutzt)
}

//initialisieren der I/O-Schnittstellen
void ioinit (void)
{
  DDRB = 0b00000110;            //PortB konfigurieren
  PORTB = 0b11000000;            
  DDRD = 0b00000110;            //PortD konfigurieren
  PORTD = 0b00000010;            
}

//initialisieren des Analog-Digital-Converters
void adcinit (void)
{
  ADMUX = (1<<PC0);
  ADMUX |= (0<<REFS1) | (1<<REFS0);
  ADCSRA = (1<<ADEN) | (1<<ADPS2) | (0<<ADPS1) | (1<<ADPS0);
}

void ausgabe (void)
{

  itoa(ergebnis,string,10);
  uart_puts(BusAdr);
  uart_puts(string);
  uart_puts(Stopp);

}

void messung (void)
{
  {
  ADCSRA |= (1<<ADSC);
  while ( ADCSRA & (1<<ADSC) )
  {
  ;
  }
  result = ADCW;
 
  result = 0; 
  for( i=0; i<4; i++ )
  {
    ADCSRA |= (1<<ADSC);
    while ( ADCSRA & (1<<ADSC) )
    {
    ;
    }
    result += ADCW;
  }
  //ADCSRA &= ~(1<<ADSC);
  result /= 4;
  
  ergebnis = result;
  ausgabe();
}

}

int main(void)
{
  ioinit();  
  uartinit();              
  adcinit();              
  sei ();                
  for(;;)                
  {
       if(zeichen==0x61)
      {
      ADCSRA |= (1<<ADSC);
      PORTB &= ~(1<<PB2);    // grüne LED ausschalten
      PORTB |= (1<<PB1);    // rote LED einschalten
      messung();        
      zeichen=0;
      PORTB &= ~(1<<PB1);  
      ADCSRA &= ~(1<<ADSC);
      }
      else
      {
      PORTB |= (1<<PB2);    // grüne LED einschalten
      }
  }
}

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?

Autor: Achilles (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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:
SIGNAL (SIG_UART_DATA)
{ 
PORTD &= ~(1<<PD2);
}

Autor: Falk Willberg (dl3daz) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Achilles schrieb:
>
> SIGNAL (SIG_UART_RECV)            //Uart Rx Interrupt wurde ausgelöst
> {
>   while ( !(UCSRA & (1<<RXC)) );
>   zeichen = UDR;            //den empfangenen Wert nach zeichen holen
>   PORTD &= ~(1<<PD2);
> }
> 
...

> 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

Autor: gast (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Achilles (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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:
DDRD = 0b00000110;            //PortD konfigurieren
PORTD = 0b00000010;           //PD2 ist ein Ausgang, aber abgeschaltet
Jetzt brauche ich nur in der Funktion UART_SendByte() folgendes zu 
schreiben?
void UART_SendByte(uint8_t data)        //sendet ein Byte über das UART
{
  while (!(UCSRA & (1<<UDRE)) );        //warten bis UART bereit ist zum senden
  PORTD |= (1<<PD2);                    //MAX487 auf senden schalten
  UDR = data;                           //data ausgeben
  PORTD &= ~(1<<PD2);                   //MAX487 auf empfangen schalten
}

(SIG_UART_RECV) sieht jetzt so aus:
SIGNAL (SIG_UART_RECV)            //Uart Receive Interrupt wurde ausgelöst
{
  while ( !(UCSRA & (1<<RXC)) );  
  zeichen = UDR;                  //den empfangenen Wert nach zeichen holen
}
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!

Autor: Falk Willberg (dl3daz) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Achilles schrieb:
...
> (SIG_UART_RECV) sieht jetzt so aus:
>
> SIGNAL (SIG_UART_RECV)  //Uart Receive Interrupt wurde ausgelöst
> {
>   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.

>   zeichen = UDR;        //den empfangenen Wert nach zeichen holen
> }
> 

Falk
P.S.: Du verrennst Dich.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Achilles schrieb:

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

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.

Autor: Falk Willberg (dl3daz) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Karl heinz Buchegger schrieb:
> Achilles schrieb:
>
>> Jetzt brauche ich nur in der Funktion UART_SendByte() folgendes zu
>> schreiben?
>>
>> void UART_SendByte(uint8_t data)        //sendet ein Byte über das UART
>> {
>>   while (!(UCSRA & (1<<UDRE)) );        //warten bis UART bereit ist zum
>> senden
>>   PORTD |= (1<<PD2);                    //MAX487 auf senden schalten
>>   UDR = data;                           //data ausgeben
>>   PORTD &= ~(1<<PD2);                   //MAX487 auf empfangen schalten
>> }
>> 
>
> 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:
#define RS485RX    PORTD &= ~(1<<PD2)
#define RS485TX    PORTD |= (1<<PD2)

void UART_SendByte(uint8_t data)        //sendet ein Byte über das UART
{
  while (!(UCSRA & (1<<UDRE)));
    UDR = data;                            //data ausgeben
}

void uart_puts(char *s) {  
  RS485TX;                     //Sender an
  while (*s) {
    UART_SendByte(*s++);         
  }
  while (!(UCSRA & (1<<TXC))); //Warte, bis Senden komplett
  RS485RX;                     //Sender aus
}

HTH,
Falk

Autor: Stefan Ernst (sternst)
Datum:

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

Autor: Falk Willberg (dl3daz) Benutzerseite
Datum:

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

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

Falk

Autor: Achilles (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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
UCSRA|=(1<<TXC);
einfügen?

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

@Karl heinz Buchegger:
Ich bin doch schon logisch an die Sache rangegangen (ALTER CODE in der 
MAIN):
[...]
PORTD |= (1<<PD2);    // MAX487 auf Sender schalten  
messung();            // Messung durchführen    
PORTD &= ~(1<<PD2);    // MAX487 auf Empfänger schalten
[...]
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!

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

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

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
>
> void UART_SendByte(uint8_t data)        // sendet ein Byte über das UART
> {
>   while (!(UCSRA & (1<<UDRE)));
>     UDR = data;                         // data ausgeben
> UCSRA|=(1<<TXC);                        // --> kommt das hier hin?
> }
> 
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.

Autor: Achilles (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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?!
void UART_SendByte(uint8_t data)
{
  while (!(UCSRA & (1<<UDRE)));
  UCSRA|=(1<<TXC);
  UDR = data;
}

Mein gesamter C-Code sieht nun so aus:
#include <avr/io.h>
#include <avr/interrupt.h>
#include <inttypes.h>
#include <stdlib.h>

#define CLK 3686400
#define BAUD 38400
#define USARTSPEED (CLK/(BAUD*16L)-1)        // Formel zur Berechnung der Werte für UBBRH/UBBRL
#define LOW(x)  ((x) & 0xFF)          // Makro zum Lowbyte Zugriff eines Word
#define HIGH(x)   (((x)  >> 8) & 0xFF)        // Makro zum Highbyte Zugriff eines Word
#define RS485RX    PORTD &= ~(1<<PD2)
#define RS485TX    PORTD |= (1<<PD2)

char* BusAdr = "a";              // Busadresse
char* Stopp = "#";              // Stoppzeichen für den String
char string[20];

volatile uint8_t zeichen;          //definiere zeichen als unsigned integer 8bit
volatile uint8_t lowByte;          //definiere lowByte als unsigned integer 8bit
volatile uint8_t highByte;          //definiere highByte als unsigned integer 8bit
volatile uint16_t ergebnis;          //definiere ergebnis als unsigned integer 16Bit

SIGNAL (SIG_UART_RECV)            //Uart Receive Interrupt wurde ausgelöst
{
  while ( !(UCSRA & (1<<RXC)) );  
  zeichen = UDR;            //den empfangenen Wert nach zeichen holen
}

void UART_SendByte(uint8_t data)        //sendet ein Byte über das UART
{
  while (!(UCSRA & (1<<UDRE)));
  UCSRA|=(1<<TXC);                    //vorangegangene Übertragung ist zu Ende
  UDR = data;                            //data ausgeben
}

void uart_puts(char *s) {  
  RS485TX;                     //Sender an
  while (*s) {
    UART_SendByte(*s++);         
  }
  while (!(UCSRA & (1<<TXC))); //Warte, bis Senden komplett
  RS485RX;                     //Sender aus
}


//initialisieren des UART
void uartinit (void)
{
  UBRRH = HIGH(USARTSPEED);        //Baudrate einstellen
  UBRRL = LOW(USARTSPEED);        //Baudrate einstellen
  UCSRB = (1<<TXEN) | (1<<RXEN) | (1<<RXCIE);
  UCSRC = (1<<URSEL)|(3<<UCSZ0);        //Frame Format setzen:8data, 1stop bit (URSEL=1 -> UCSRC->Settings werden genutzt)
}

//initialisieren der I/O-Schnittstellen
void ioinit (void)
{
  DDRB = 0b00000110;            //PortB konfigurieren
  PORTB = 0b11000000;            
  DDRD = 0b00000110;            //PortD konfigurieren
  PORTD = 0b00000010;            
}

//initialisieren des Analog-Digital-Converters im Single-Conversion-Mode ohne Interrupt
void adcinit (void)
{
  ADMUX = (1<<PC0);
  ADMUX |= (1<<REFS0);
  ADCSRA = (1<<ADEN) | (1<<ADPS2) | (0<<ADPS1) | (1<<ADPS0);  // Die Wandlung wird im Code durch explizites Setzen des Bits ADSC gestartet
}

void ausgabe (void)
{

  itoa(ergebnis,string,10);
  uart_puts(BusAdr);
  uart_puts(string);
  uart_puts(Stopp);

}

void messung (void)
{
  {
  ADCSRA |= (1<<ADSC);
  while ( ADCSRA & (1<<ADSC) )
  {
  ;
  }
  ADCSRA &= ~(1<<ADSC);
  ergebnis = ADCW;
  ausgabe();
}

}

int main(void)
{
  ioinit();  
  uartinit();              
  adcinit();              
  sei ();                
  for(;;)                
  {
      if(zeichen==0x61)
      {
      PORTB &= ~(1<<PB2);    // grüne LED ausschalten
      PORTB |= (1<<PB1);    // rote LED einschalten
      messung();        
      zeichen=0;
      PORTB &= ~(1<<PB1);  
      }
      else
      {
      PORTB |= (1<<PB2);    // grüne LED einschalten
      }
  }
}
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)

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.