Hallo,
bin neu hier und auch noch relativ unerfahren, was den Umgang mit
Mikrokontrollern angeht. Ich sitze derzeit an einem kleinen Projekt und
habe mich irgendwie festgefahren. Vieleicht kann mir ja jemand
weiterhelfen. Es geht um folgendes:
Ich modulieren mit Hilfe eines ersten Mikrokontrollers ein Signal.
Welches variable Highpulse und drei unterschiedliche Lowpulse als
Seperatoren verwendet. Alle Pulse setzten sich aus einem Lowimpuls
zusammen, sodass man unabhängig von der Taktung durch Aufzeichnen des
Signals dieses trotzdem rekonsturieren kann, indem man einfach nach dem
kürzesten Lowimpuls sucht (welcher zudem auch recht häufig vorkommt).Die
Modulierung klappt soweit sehr gut und die Daten lassen sich (ich mache
es mit einer Soundkarte) von Hand problemlos rekonstruieren.
Nun möchte ich einen zweiten Mikrokontroller als Empfänger benutzten.
Dazu nehme ich einen Atmega8 und den Timer1. Das Signal kommt an PB0
(ICP1). Den Timer inizialisiere ich wie folgt:
1 | void initTimer(void)
| 2 | {
| 3 | TCCR1B |= (1<<ICES1)|(1<<ICNC1)|(1<<CS10);
| 4 |
| 5 | TIMSK |= 0x20;
| 6 | TIMSK |= 0x04;
| 7 | }
|
Dann fange ich auch noch Overflows ab:
1 | SIGNAL(SIG_OVERFLOW1)
| 2 | {
| 3 | Timer1Overflow++;
| 4 | }
|
und behandle die Interrupts wie folgt (wobei ich mir die Werte über den
Com Port ausgeben lasse):
1 | SIGNAL(SIG_INPUT_CAPTURE1)
| 2 | {
| 3 |
| 4 | signed char buf[30];
| 5 | long signed int wert;
| 6 |
| 7 | if(TCCR1B & (1<<ICES1)) // Positive Flanke
| 8 | {
| 9 | wert = (unsigned int)ICR1;
| 10 |
| 11 | print (itoa((wert + (Timer1Overflow * 65536)), buf, 10));
| 12 |
| 13 | Timer1Overflow = 0;
| 14 | TCCR1B &= ~(1<<ICES1);
| 15 | }
| 16 | else // Negative Flanke
| 17 | {
| 18 |
| 19 | wert = (unsigned int)ICR1;
| 20 |
| 21 | print (itoa((wert + (Timer1Overflow * 65536)), buf, 10));
| 22 | Timer1Overflow = 0;
| 23 |
| 24 | TCCR1B |= (1<<ICES1);
| 25 | }
| 26 | }
|
Der Empfänger hat ein kurzes Chinchkabel mit Stecker angelötet. Von da
aus ist er über eine Krokodillklemme mit Kabel mit dem Sender verbunden.
Nun das Seltsame: Ich bekomme keinerlei konsistente Signale. Wenn ich
die Krokodillklemme auf der Senderseite abklemme aber das Kabel noch
dranlasse kommen Interrupts rein. Erst wen ich das Kabel komplett
entferne kommt nichts mehr. Wenn ich die Kontaktfläche berühre kommen
auch Interrupts rein. Ein anderes Kabel bringt leider auch keine
Abhilfe. Wahrscheinlich ist es nur ein ganz dummer Fehler, aber ich steh
auf dem Schlauch. Ich hatte auch schon versucht nach jedem Interrupt
TCNT1 wieder auf 0 zu stellen, aber das hat auch nichts gebracht.
Gruss: Chris
Christopher Hartmann wrote:
> Von da
> aus ist er über eine Krokodillklemme mit Kabel mit dem Sender verbunden.
Masse auch verbunden?
> Wenn ich
> die Krokodillklemme auf der Senderseite abklemme aber das Kabel noch
> dranlasse kommen Interrupts rein.
Klar, ist ja auch eine hübsche Antenne.
> Wenn ich die Kontaktfläche berühre kommen auch Interrupts rein.
Dito.
Was macht print denn genau?
Schreibt es in einen Puffer, dessen Inhalt dann per Interrupt gesendet
wird? Oder versucht es selber direkt zu senden? Wenn letzteres: sind die
Impulse überhaupt lang genug, dass zwischendrin was über die serielle
Schnittstelle gesendet werden kann? In welcher Größenordnung sind die
Impulse überhaupt?
Und so nebenbei: wieso ist wert signed? Und dass bei diesem Code
wert nicht der Länge der Impulse entspricht, ist dir klar?
Die Masse ist verbunden. Hängt beides an der selben Spannungsquelle.
Print gibt den Wert über die Uart raus, sodass ich mir die Werte mit
Hyperterminal anschauen kann.
Was die Variable 'Wert' angeht.. ich hatte die vorher gar nicht drin,
hat da aber auch schon nicht funktioniert.
Christopher Hartmann wrote:
> Die Masse ist verbunden. Hängt beides an der selben Spannungsquelle.
> Print gibt den Wert über die Uart raus, sodass ich mir die Werte mit
> Hyperterminal anschauen kann.
> Was die Variable 'Wert' angeht.. ich hatte die vorher gar nicht drin,
> hat da aber auch schon nicht funktioniert.
Klasse, von der Masse mal abgesehen, bist auf keine meiner Fragen weiter
eingegangen. Oder meinst du, dass "Print gibt den Wert über die Uart
raus" eine adäquate Antwort ist auf:
> Was macht print denn genau?
> Schreibt es in einen Puffer, dessen Inhalt dann per Interrupt gesendet
> wird? Oder versucht es selber direkt zu senden?
Und in welchem Zeitrahmen sich die Dinge abspielen, steht auch immer
noch in den Sternen.
Wie soll man dir da dann weiterhelfen?
Hellsehen kann hier nun mal keiner.
Der Funktion print wird ein String übergeben, der seriell gesendet
werden soll über die Uart. Print arbeitet mit putchar zusammen. Diese
Funktion gibt dann die einzelnen Zeichen auf der Uart raus, indem sie
diese der Reihe nach in UDR schreibt. Demnach würde ich sagen, dass es
selber versucht zu senden.
Wie gesagt, ich stehe selber noch relativ am Anfang was den Umgang mit
Mikrokontrollern angeht.
1 | void putChar (char data)
| 2 | {
| 3 | while ( !( UCSRA & (1<<UDRE)) );
| 4 | UDR = data;
| 5 | }
| 6 |
| 7 | void print (char buffer[])
| 8 | {
| 9 | for (int(i) = 0; buffer[i] != 0; i++)
| 10 | putChar (buffer[i]);
| 11 | }
|
Zu dem Datenformat was empfangen und ausgewertet werden soll:
Der Zeitraum sieht so aus: Der kürzeste Impuls (aus dem sich wie gesagt
auch alle späteren Impulse zusammensetzten) ist ca. 0,5ms lang (nennen
wir diesen Puls mal a). Ein Datenpacket sieht ungefähr so aus:
es werden x 3 stellige Dezimalzahlen übertragen. Der erste Highimpuls
gibt die erste Stelle wieder, dann folgt ein seperator, dann die nächste
Stelle usw. Jede Zahl wird dann nochmal durch einen doppelten low-Impuls
abgetrennt und zur Synkronisierung folgt am Schluss ein Langer 5 facher
Wartepuls. Eine Null wird als einfacher Wartepuls übertragen. Hat also
den Wert 1.
Angenommen man möchte 543 und 103 empfangen. Dann sieht die Abfolge so
aus (werte in Klammern stehen für einen Highimpuls, Punkt vor Strich
soll hier mal keine Beachtung finden):
(5+1*a) a (4+1*a) a (3+1*a) 2*a (1+1*a) a (0+1*a) a (3+1*a) 5*a
Das ist zb. eine Abfolge die ich erkennen möchte über Interrupts. Jeder
empfangene Wert zwischen einem High- und einem Lowimpuls wird dann auf
die Uart ausgegeben, Zwecks debugging.
Ich habe auch schon mehrfach mit den Impulslängen rumgespielt, aber
bislang halt ohne Erfolg.
Christopher Hartmann wrote:
> Print arbeitet mit putchar zusammen. Diese
> Funktion gibt dann die einzelnen Zeichen auf der Uart raus, indem sie
> diese der Reihe nach in UDR schreibt.
Die Funktion print braucht dann aber dafür reichlich Zeit.
Der kürzeste Impuls ist 0,5 ms lang. In der Zeit sollen bis zu 10
Zeichen verschickt werden. Dazu müsste die Baudrate >200k sein.
Welche Baudrate verwendest du? Du kannst dir jetzt vielleicht
vorstellen, welche verheerenden Auswirkungen der Aufruf von print im
Interrupt auf das Messen der Impulslänge hat, wenn die Baudrate z.B.
9600 ist.
Weitere Probleme:
1) Ich sehe nicht, dass du beim Verschicken der Zahlen irgendein
Trennzeichen einfügst. Du bekommst also am Empfänger nur eine lange
Ziffernwurst, aus der du nichts ablesen kannst.
2) Du willst offensichtlich die Länge der Impulse messen. Das geht aber
nicht durch bloßes Auslesen von ICR1. Wenn du die Zeit zwischen
aktueller und voriger Flanke haben willst, musst du schon den aktuellen
mit dem vorigen Capture-Wert vergleichen. Alternativ kannst du auch den
Zähler zurücksetzen, was aber ungenauer ist.
PS:
> (5+1*a) ...
Vermutlich meinst du hier eher (5+1)*a
Stefan Ernst wrote:
> Die Funktion print braucht dann aber dafür reichlich Zeit.
> Der kürzeste Impuls ist 0,5 ms lang. In der Zeit sollen bis zu 10
> Zeichen verschickt werden. Dazu müsste die Baudrate >200k sein.
> Welche Baudrate verwendest du? Du kannst dir jetzt vielleicht
> vorstellen, welche verheerenden Auswirkungen der Aufruf von print im
> Interrupt auf das Messen der Impulslänge hat, wenn die Baudrate z.B.
> 9600 ist.
Ich sende die Daten aber gleich nachdem ich sie ausgewertet habe. Also
werden immer maximal 3 Zeichen übertragen.
Trennzeichen (meist "." und "A") hatte ich auch drin. Habe ich aber als
ich den Code aufgeräumt habe rausgenommen um hier nicht Verwirrung zu
stiften. Natürlich wäre alles andere totaler Schwachsinn.
> 2) Du willst offensichtlich die Länge der Impulse messen. Das geht aber
> nicht durch bloßes Auslesen von ICR1. Wenn du die Zeit zwischen
> aktueller und voriger Flanke haben willst, musst du schon den aktuellen
> mit dem vorigen Capture-Wert vergleichen. Alternativ kannst du auch den
> Zähler zurücksetzen, was aber ungenauer ist.
Eigentlich hatte ich nach jedem Empfangen noch die Zeile: TCNT1 = 0
drin. Ist mir aber wohl beim posten rausgerutsch ... sorry.
> PS:
>> (5+1*a) ...
> Vermutlich meinst du hier eher (5+1)*a
Um mich selber zu zitieren:
>(werte in Klammern stehen für einen Highimpuls, Punkt vor Strich
>soll hier mal keine Beachtung finden):
Du hast aber ansonsten recht. Eine 0 wird als 1 übertragen...
Christopher Hartmann wrote:
> Ich sende die Daten aber gleich nachdem ich sie ausgewertet habe. Also
> werden immer maximal 3 Zeichen übertragen.
Ich sehe in dem Code keinerlei Auswertung. Aber auch bei 4 Zeichen (mit
Trennzeichen) müsste man schon ordentlich Stoff geben, wenn das in 0,5ms
passen soll. Welche Baudrate benutzt du denn nun?
> Um mich selber zu zitieren:
>>(werte in Klammern stehen für einen Highimpuls, Punkt vor Strich
>>soll hier mal keine Beachtung finden):
Ja, ich gebe zu, ich habe den unteren Teil vom Post nur überflogen. ;-)
Stefan Ernst wrote:
>> Welche Baudrate benutzt du denn nun?
Da hattest du richtig geraten: 9600.
Das ist echt Mist. Man sitzt nach x Tagen erfolglosem rumprobieren
genervt am PC und setzt irgendwie immer alles gleich als
selbstverständlich voraus.
Du meinst also, dass ich zum einen am besten die Baudrate höher stellen
sollte (sagen wir mal auf 57600 baud) und zum anderen die Auswertung am
besten anders machen sollte? Wie geh ich den letzteres am besten an? Ich
könnte den Timer durch laufen lassen, bei jeder Flanke in die Interrupt
Funktion springen und müsste mir dann natürlich auch immer die letzten
Timerwerte merken um die Pulslänge zu ermitteln. Senden der Daten über
die Uart könnte man dann während der Sync-Pause machen. Dazu ließe sich
die Pause ja auch noch strecken.
Mit Auswertung meinte ich ledigich die Erfassung des Timerwertes.
Christopher Hartmann wrote:
> Da hattest du richtig geraten: 9600.
Da dauert das Verschicken von 4 Zeichen ca 4,2 ms.
> Du meinst also, dass ich zum einen am besten die Baudrate höher stellen
> sollte (sagen wir mal auf 57600 baud)
Jein. Ich meine, dass das print ganz raus muss aus dem Interrupt. Setze
dort nur ein Flag, und mache die Ausgabe im Hauptprogramm. 1 | volatile uint8_t flag;
| 2 |
| 3 |
| 4 | ISR() {
| 5 | ...
| 6 | flag = 1;
| 7 | }
| 8 |
| 9 |
| 10 | int main ( void ) {
| 11 | ...
| 12 | while (1) {
| 13 | ...
| 14 | if (flag) {
| 15 | flag = 0;
| 16 | // Ausgabe
| 17 | }
| 18 | }
| 19 | }
|
Außerdem sollte die Ausgabe einen Zwischenpuffer verwenden, dann muss
sie nicht mehr in den kürzesten Impuls passen, sondern nur noch in das
Mittel aller Impulse. Du könntest z.B. die Fleury Lib verwenden.
Trotz dieser Maßnahmen muss die Baudrate aber höher sein. Beim aktuellen
Timing min 38400.
> Wie geh ich den letzteres am besten an? Ich
> könnte den Timer durch laufen lassen, bei jeder Flanke in die Interrupt
> Funktion springen und müsste mir dann natürlich auch immer die letzten
> Timerwerte merken um die Pulslänge zu ermitteln.
Ja, so würde ich es machen. Aber der letzte Capture-Wert reicht. Nimm
auch gleich die Überlauf-Sache raus, ein Überlauf wird bei der
Subtraktion automatisch mit berücksichtigt (wenn wert den richtigen
Datentyp hat, nämlich unsigned int). Du musst nur durch die
Vorteilerwahl sicherstellen, dass auch der längste Impuls in einen
Zählerdurchlauf passt.
So, ich habe es nun endlich ans Laufen bekommen. Danke nochmal für die
Hilfe. Was ich abschließend noch fragen wollte: Ich arbeite derzeit mit
einem Pegel von 5V. Wenn ich einen Pegel von 1-2V benutzen möchte, dann
müsste ich einen Operationsverstärker an den Eingang klatschen... oder
geht das irgendwie anders, indem ich zb. eine spezielle Referenzspannung
oder so an den Kontroller lege?
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
|