Forum: Mikrocontroller und Digitale Elektronik Problem mit Capturefunktion Timer1 beim Atmega8


von Christopher H. (chris2798)


Lesenswert?

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

von Stefan E. (sternst)


Lesenswert?

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?

von Christopher H. (chris2798)


Lesenswert?

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.

von Stefan E. (sternst)


Lesenswert?

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.

von Christopher H. (chris2798)


Lesenswert?

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.

von Stefan E. (sternst)


Lesenswert?

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

von Christopher H. (chris2798)


Lesenswert?

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...

von Stefan E. (sternst)


Lesenswert?

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. ;-)

von Christopher H. (chris2798)


Lesenswert?

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.

von Stefan E. (sternst)


Lesenswert?

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.

von Christopher H. (chris2798)


Lesenswert?

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.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.