Hallo,
ich habe ein Problem mit dem uart des Mega644PU20.
In meiner Schaltung sollen 2 AD wandler abgefragt und deren Messwerte +
eine Messwertnummer per uart zu einem pc übertragen werden. Die AD
wandler werden alle 4 ms abgefragt und dann das 7 byte Packet (3 byte
messwertnummer, 2 * 2 Byte für die Messwerte via UART 38400bps
übertragen. Das ist eigentlich keine große Sache, habs schon zig mal
gemacht, aber diesmal ist der Wurm drin. Es sieht so aus als würden die
byte Packete nicht richtig übertragen, in ca. 7000 Packete ist eines
oder 2 komplett mit 0en gefüllt oder die 3 byte Messwertnummer ist alles
0. oder es fehlen einfasch ein paar bytes. 38400 ist bei 20MHz nicht die
beste wahl aber andere uart geschwindigkeiten bringen keine veränderung.
Bevor ich den code poste, hat evtl. schonmal einer so ein ähnliches
problem gehabt?
noch zur info, die ad wandler sind mit den externen interrupts 0 und 1
verbunden in den isrs setze ich ein flag und in der main wird dann der
entdprechende adwandler per spi abgefragt und danach die daten per uart
gesendet. Damit das mit den Interrupts funktioniert muss ich diese immer
wieder ein und ausschalten, könnte das ein Problem mit dem uart
verursachen?
Das komische ist echt das die Übertragungsfehler nur sporadisch
auftreten. An rx und tx des avr ist über 6cm kabel ein usb seriell
wandler angeschlossen, sowie auch eine Masseverbindng. Den usb seriell
wandler hatte ich auch schon durch ein normesl max232 modul ersetzt,
selbes Problem. Nahe bei den rx und tx anschlüssen des avr befindent
sich die stromversorgung der platine Spannungswandler, dioden etc. Das
könnte doch auch zu einem Problem werden, leider habe ich kein oszi um
mir das genauer anzuschauen. Welche Möglichkeiten gäbe es denn die rx
und tx leitung zu schirmen oder zu stabilisieren evtl. die flanken der
signale zu verbessern?
Bevor ich an der "Schirmung" arbeiten würde, würde ich im AVR
feststellen lassen, ob bereits illegale Pakete versendet werden.
Dazu würde ich am Ausgang des Datenproduzenten (SPI) die Realdaten durh
leicht erkennbare Prüfdaten ersetzen und am tiefsten Punkt des
Datensenders (dort wo UDR gefüllt wird) die Prüfdaten abfragen und eine
Error-LED bedienen.
Wenn so ein Fehler erkannt wird, kann eine Debugfunktion aufgerufen
werden, die das Anwendungsprogramm unterbricht und alle relevanten
Variablen im Programm fürs Debugging an den PC sendet.
Bei dem Verdacht "hat was mit den IRQs zu tun" würde ich als Zweites
untersuchen, ob ein Zusammenhang der Fehler mit der Aufruffrequenz der
externen IRQs besteht.
Das kann man bestimmt auch praktisch machen, wenn man mit einem zweiten
System (analoge Timerschaltung, µC, Funktionsgenerator) die IRQs von
außen von 0-x Hz triggert.
Hm, wenn die AD-Wandler alle 4ms abgefragt werden, warum dann externe
Interrupts und nicht ein interner Timer?
Wie schnell fährst du über SPI? Das Versenden von 7 Byte (8-N-1) dauert
schon ~1.8ms, wenn dann der SPI auch noch langsam ist oder die ADCs lang
brauchen zum konvertieren (was ich eher nicht glaube..) kanns schon in
blöden Fällen zu Überschneidungen kommen, was aber dann ein Ändern der
Baudrate "beheben" hätte müssen. Werden die ADCs "gleichzeitig" gelesen,
oder zeitlich versetzt?
Code+Schaltung bitte, sonst is das Rätselraten denk ich...
Die AD wandler laufen im free running mode haben sozusagen damit ihren
eigenen timer(250Hz), über den interrupt signalisieren sie conversion
ready, aber nur wenn sie auch vom SPI her ausgewählt wurden. Ich
aktiviere also INT0, wähle wandler 1 per SPI an, der Interrupt setzt ein
flag, ich deaktiviere INT0, lese per SPI den Messwert aus, deselektiere
wandler1, aktiviere INT1, wähle wandler 2 per SPI aus, INT1 setzt dann
wieder ein flag, deaktiviere INT1, lese per SPI den Messwert aus,
deselektiere wandler 2, sende die daten per uart, aktiviere INT0 und
selektiere wandler1.
Ich habe hier im Forum gelesen das es bei einer dauerübertragung per
uart sein kann das der empfänger irgendwann das startbit nicht mehr
richtig erkennt und daher die kommunikation nicht mehr funktioniert bis
er sich wieder "einschwingt" durch eine kleine pause oder so. Zwischen
meinen übertragunggen ist ja nur 4ms - 1.8ms = 2.2ms pause, ich werde
das mal ändern das sich die pause verlängert, also sowas wie 2 x 2
messwerte einlesen und dann beide messwerte aufeinmal verschicken (11
byte) alle 8ms,
müsste dann 8ms - ~2.9ms = ~5.1ms pause zwischen den übertragungen.
code :
HansFranz schrieb:> UART_send_byte((uint8_t)(MesswertNo >> 16));> UART_send_byte((uint8_t)(MesswertNo >> 8));> UART_send_byte((uint8_t)(MesswertNo & 0xFFFF));
Das ist falsch bzw. vermutlich nicht so gewollt (und die &0xffff ist
auch eher wirkungslos, wird sowieso nur das niederwertigste Byte durch
den Cast genommen..)... MesswerteNo ist uint 32 bit, Code sollte eigtl.
so aussehen:
UART_send_byte((uint8_t)(MesswertNo >> 24));
UART_send_byte((uint8_t)(MesswertNo >> 16));
UART_send_byte((uint8_t)(MesswertNo >> 8));
UART_send_byte((uint8_t)(MesswertNo & 0xff));
Außer du willst natürlich nur die 24 Bit haben wegen deiner 3 Byte
Zähler die übertragen werden...
Hast halt so einen Zählerüberlauf drinnen (das passiert bei 2^32-1) und
dann hat der nächste Messwert die Nummer 0. Weiß nicht ob das vl. evtl.
Probleme verursachen könnte, versuch mal die Überläufe wegzulassen indem
du bei 2^24-1 MesswertNo wieder auf 0 setzt.
Versteh ich das richtig, die ADCs sind extern und über SPI anzusteuern
und signalisieren (solange SS auf low) über eine Leitung die direkt mit
den Interruptpins verbunden ist, wenn fertige Daten da sind die
ausgelesen werden können?
Kann es sein dass deine Methode mal einen deadlock produziert? Sprich es
wird auf Signal von ADC0 gewartet aber ADC1 ist gerade über die SS
Leitung aktiviert, somit kann der 0er gar nicht signalisieren weil er
nicht arbeitet? Könntest mal einen Watchdog einbauen und schaun ob dein
Controller sich resettet weil er irgendwo hängenbleibt... (einfach immer
am Programmstart vor while(1) irgendwo einen Port auf low ziehen oder
ähnliches und das mitm Oszi verfolgen..) Kanns zwar jetzt aus dem Code
heraus grad nicht nachvollziehen dass es zu so einer Situation kommen
könnte, aber der Aufbau wie du ihn hast is anfällig für deadlocks wenn
irgendwas mit den Interrupts nicht nach Plan verläuft...
Welce ADCs sinds denn (Datenblatt)?
Könnte sein dass zwischen Interrupt und disablen des Interrupts noch ein
zweiter kommt (durch Störung oder sonstiges?) Versuch mal gleich in der
ISR den Interrupt auszuschalten, dann passiert da nix unkontrolliertes
und erst wenn du mit der Verarbeitung des ersten in der Main fertig bist
wieder den nächsten Interrupt setzen.
Sonst hat kann der Interrupt zwischen erstmaligem Auftreten und setzen
des Flags und erreichen der Position in der Switch jederzeit nochmal
feuern. Was jedoch zugegebenermaßen keine Auswirkung haben sollte...
HansFranz schrieb:> // INT0 ein- ausschalten> void INT0_enable(uint8_t enable)> {> if (enable == 0)> {> // diable int0> EIMSK &= ~(1<<INT0);> EIFR &= ~(1 << INT0);> }> else> {> // diable int0> EIMSK &= ~(1<<INT0);> // set falling edge> EICRA |= (1<<ISC01);> // enable int0> EIMSK |= (1<<INT0);> }> }
Noch kurz dazu
Das Flag brauchst du nicht extra zu löschen (EIFR &= ...), das wird beim
Aufruf des Interruptvektors automatisch gelöscht. Sollte es zum Eintritt
in diese Funktion noch gesetzt sein läuft irgendwas schief...
Im else wird das ganze enabled oder? Warum erst disablen und dann wieder
enablen? Wenn du vorsichtig sein willst dann musst du ein cli() vor und
ein sei() hinter dem Block setzen => Atomar
Würds generel auf eine int0_enable() und int0_disable() aufsplitten...
Braucht weniger Rechenzeit und Code is besser lesbar ;)
EICRA solltest du um sicherzugehen jedoch erst löschen und dann
beschreiben. also EICHRA &= (3<<ISC00) und dann EICRA |= (1<<ISC01).
Sollte zwar eigtl. wenn im restlichen Programm nicht damit rumgespielt
wird nix passieren weil das eine Bit eh nie geschrieben wird aber sicher
ist sicher ;)
also das mit der MesswertNo ist so gewollt nur die letzten 3 byte werden
benötigt, sind knapp 18std bis zum überlauf, das ist ok.
> Versteh ich das richtig, die ADCs sind extern und über SPI anzusteuern> und signalisieren (solange SS auf low) über eine Leitung die direkt mit> den Interruptpins verbunden ist, wenn fertige Daten da sind die> ausgelesen werden können?
ja das ist richtig, DRDY des ADC ist mit externem INT des avr verbunden.
DRDY -> H wenn conversion ready, also INT an steigender Flanke.
> Kann es sein dass deine Methode mal einen deadlock produziert? Sprich es> wird auf Signal von ADC0 gewartet aber ADC1 ist gerade über die SS> Leitung aktiviert, somit kann der 0er gar nicht signalisieren weil er> nicht arbeitet?
einen deadlock wird es meiner Meinung nach nicht geben, es mag nach dem
den ersten blick aufs programm so aussehen als könnte das passieren,
aber ich denke eher nicht. Aber werds im hinterkopf behalten.
Ich glaube wirklich nicht das es an den Interrupts liegt.
Die ADCs sind MAX1416.
unwahrscheinlich schon, würds aber zumindest trotzdem checken ob sich
der Controller wo aufhängt, auch wenns nur ist dass man es dann mit
Sicherheit ausschließen kann... ist ja jetzt nicht so viel Arbeit..
Sind halt so die Sachen die mir spontan mal einfallen würden zu
überprüfen, wenn man dann Dinge ausschließen kann is die Fehlersuche
schon eingegrenzter...
Wie stabil sind denn deine SPI-Leitungen? Kanns da wo Probleme geben
dass dort schon nur 0er gelesen werden?
Hast du schon festgestellt ob der uC falsch versendet oder ob falsch
empfangen wird? Wie sieht denn deine Empfängersoftware aus oder
kontrollierst du das übers Terminal?
Kann ja auch sein dass wir am falschen Ende suchen ;)
Noch was um Interrupts auszuschließen: die INT-Pins zu pollen dürfte
auch genügen von der Geschwindigkeit her, deine Main macht ja außer auf
Flags warte nix... sparst dir sogar Flags und ist sogar schneller.
Oh man, es funktionierte die ganze zeit über zu 100%, das Problem lag in
meiner Empfangssoftware auf der PC Seite!
Das war blöd, sorry euch damit genervt zu haben und danke Thomas für
deine Hilfe!!!!