Forum: Compiler & IDEs Hilfe bei ATmega 16


von Mapamann (Gast)


Lesenswert?

Hi,

erstmal ein großes Lob an alle, die hier imm fleißig lesen und helfen.
Hat mir schon paarmal geholfen:)

Aber nun hab ich ein Problem mit meinem Atmega 16.

Der controller soll, wenn mal alles so klappt wie's soll:

- über einen reed kontak ein relais ein- bzw. ausschalten
- 3 RC Kanäle einlesen bzw. erkennen
- Schaltfunktionen in Abhängigkeit vom ankommenden RC Sigal ausführen
- viell noch die akkuspannung erkennen bzw. überwachen
- diese funktionen aber nur ausführen bzw. nicht ausführen sobald der 
reed kontake betätigt wurde


Habe zwar schon etwas Erfahrungen sammeln können, komme jedoch seit ein 
paar tagen nicht weiter.

Liege ich erstmal richtig, dass ich mit einem Timer nur jeweils einen RC 
Kanal erfassen bzw. auswerten kann?
Komme ich da unter Umständen besser, wenn ich das RC Summensignal 
auswerte, wenn ich es schaffe das aus dem Empfänger zu bekommen?

Habe zuerst einmal versucht ein RC Sigal über den INT0 Eingang versucht 
zu erkennen bzw. auszuwerten, was mir mit Hilfe des Forums hier auch gut 
gelungen ist.
Sobald ich aber auch den INT1 nutzen will geht garnix mehr.
Speziell sobald ich nach INT0 bzw. ISC00 das Semikolon und die 
Schrägstriche entferne

GICR =  (1<<INT0);//  | (1<<INT1); // INT0/INT1  enable
MCUCR = (1<<ISC01) | (1<<ISC00);// | (1<<ISC11) | (1<<ISC10); 
//Int0/INT1 steigende Flanke

Sehe echt nicht so recht durch dabei.
Hier noch der Rest des Programms, vielleicht kann mir einer von euch 
alten Hasen helfen.

Vielleicht habt Ihr auch noch paar Tips, wie ich die oben genannten 
Funktionen gut realisieren kann bzw. könnte.

#include <avr/io.h>
#include <util/delay.h>
#include <inttypes.h>
#ifndef F_CPU
#warning "F_CPU war noch nicht definiert, wird nun mit 10000000 
definiert"
#define F_CPU 10000000UL     /* Quarz mit 10 Mhz  */
#endif
#include <avr/interrupt.h>


volatile uint8_t Pulsdauer0 = 0;
volatile uint8_t Pulsdauer2 = 0;

void (steigende_Flanke0)(void);
void (fallende_Flanke0)(void);
void (steigende_Flanke2)(void);
void (fallende_Flanke2)(void);

SIGNAL(SIG_INTERRUPT0)
{
  if(MCUCR == 0x03) // Testen auf steigende Flanke
  {
  steigende_Flanke0();
  return;
  }

  if( MCUCR == 0x02) // Testen auf fallende Flanke
  {
  fallende_Flanke0();
  return;
  }
}
void (steigende_Flanke0)(void)
{
  TCNT0 = 0; // Timer Reset
  TCCR0 = (1<<CS02); // Prescaler 1/256
  MCUCR = (1<<ISC01) | (0<<ISC00); // INT0 fallende Flanke
}
void (fallende_Flanke0)(void)
{
  TCCR0 = 0x00; // Timer0 Stoppen
  Pulsdauer0 = TCNT0; // Timer Wert = Pulsdauer0
  MCUCR = (1<<ISC01) | (1<<ISC00); // INT0 auf steigende Flanke
}



/*SIGNAL(SIG_INTERRUPT1)
{
  if( MCUCR == 0x0c) // Testen auf steigende Flanke
  {
  steigende_Flanke2();
  return;
  }

  if( MCUCR == 0x04 ) // Testen auf fallende Flanke
  {
  fallende_Flanke2();
  return;
  }
}
void (steigende_Flanke2)(void)
{
  TCNT2 = 0; // Timer Reset
  TCCR2 = (1<<CS22) | (1<<CS21); // Prescaler 1/256
  MCUCR = 0x04; // INT1 fallende Flanke
}
void (fallende_Flanke2)(void)
{
  TCCR2 = 0x00; // Timer0 Stoppen
  Pulsdauer2 = TCNT2; // Timer Wert = Pulsdauer2
  MCUCR = 0x0c; // INT1 auf steigende Flanke
}

/*

int main (void)
{


DDRA = 0xff;  // PORT A komplett Output
DDRC = 0x00;  // PORT C komplett Input
PORTC = 0xff; // PORT C Pullup's aktiv


DDRD = (0<<PD2) | (0<<PD3);
PORTD = (1<<PD2) | (1<<PD3);

GICR =  (1<<INT0);//  | (1<<INT1);   // INT0/INT1  enable
MCUCR = (1<<ISC01) | (1<<ISC00);// | (1<<ISC11) | (1<<ISC10); 
//Int0/INT1 steigende Flanke

TCCR0 = (1<<CS02) | (1<<CS00); //Prescaler = FCPU/1024 Timer 0
TCCR2 = (1<<CS22) | (1<<CS21) | (1<<CS20); // Prescaler = FCPU/1024 
Timer 2

TIMSK = (1<<TOIE0) | (1<<TOIE2); //Enable Overflow Interrupt Enable
TCNT0 = 0; // Initianlisiere Counter 0
TCNT2 = 0; // Initianlisiere Counter 2
sei(); // global Interupt enable


  while(1)
  {

if (Pulsdauer0 >= 38 && Pulsdauer0 < 45)
    {
      PORTA |= (1<<PA2);
    }

else if (Pulsdauer0 >= 45 && Pulsdauer0 < 65)
    {
      PORTA &= ~((1<<PA1) | (1<<PA2));

    }

else if (Pulsdauer0 >= 65 && Pulsdauer0 < 79)
    {
      PORTA |= (1<<PA1);
    }
/*
if (Pulsdauer2 >= 38 && Pulsdauer2 < 50)
    {
      PORTA |= (1<<PA4);
    }

else if (Pulsdauer2 >= 50 && Pulsdauer2 < 58)
    {
      PORTA &= ~((1<<PA3) | (1<<PA4));

    }

else if (Pulsdauer2 >= 58 && Pulsdauer2 < 79)
    {
      PORTA |= (1<<PA3);
    }
*/
  }

}

von Stefan B. (stefan) Benutzerseite


Lesenswert?

Mapamann schrieb:
> Hi,
>
> erstmal ein großes Lob an alle, die hier imm fleißig lesen und helfen.
> Hat mir schon paarmal geholfen:)
>
> Aber nun hab ich ein Problem mit meinem Atmega 16.
>
> Der controller soll, wenn mal alles so klappt wie's soll:
>
> - über einen reed kontak ein relais ein- bzw. ausschalten
> - 3 RC Kanäle einlesen bzw. erkennen
> - Schaltfunktionen in Abhängigkeit vom ankommenden RC Sigal ausführen

RC ist ein Funksignal einer Fernsteuerung?

> - viell noch die akkuspannung erkennen bzw. überwachen
> - diese funktionen aber nur ausführen bzw. nicht ausführen sobald der
> reed kontake betätigt wurde
>
>
> Habe zwar schon etwas Erfahrungen sammeln können, komme jedoch seit ein
> paar tagen nicht weiter.
>
> Liege ich erstmal richtig, dass ich mit einem Timer nur jeweils einen RC
> Kanal erfassen bzw. auswerten kann?
> Komme ich da unter Umständen besser, wenn ich das RC Summensignal
> auswerte, wenn ich es schaffe das aus dem Empfänger zu bekommen?

Kann ich nicht beantworten, weil ich nicht weiss, wie die RC Signale 
einzeln und als Summe aussehen.

Die These 1 Timer = 1 Kanal bezweifle ich. Wenn die Timerschritte klein 
genug sind, sollten die Timerroutine auch mehrere Eingänge abfragen 
können.

> Habe zuerst einmal versucht ein RC Sigal über den INT0 Eingang versucht
> zu erkennen bzw. auszuwerten, was mir mit Hilfe des Forums hier auch gut
> gelungen ist.

Die Auswertung über INT ist ein anderes Verfahren als über TIMER.

> Sobald ich aber auch den INT1 nutzen will geht garnix mehr.
> Speziell sobald ich nach INT0 bzw. ISC00 das Semikolon und die
> Schrägstriche entferne
>
> GICR =  (1<<INT0);//  | (1<<INT1); // INT0/INT1  enable
> MCUCR = (1<<ISC01) | (1<<ISC00);// | (1<<ISC11) | (1<<ISC10);
> //Int0/INT1 steigende Flanke

Das reicht noch nicht. Es muss mindestens eine weitere ISR Funktion für 
den zweiten Interrupt vorhanden sein. Im Beispiel unten ist diese 
auskommentiert:

> /*SIGNAL(SIG_INTERRUPT1)
> {
>   if( MCUCR == 0x0c) // Testen auf steigende Flanke
>   {
>   steigende_Flanke2();
>   return;
>   }
>
>   if( MCUCR == 0x04 ) // Testen auf fallende Flanke
>   {
>   fallende_Flanke2();
>   return;
>   }
> }
> void (steigende_Flanke2)(void)
> {
>   TCNT2 = 0; // Timer Reset
>   TCCR2 = (1<<CS22) | (1<<CS21); // Prescaler 1/256
>   MCUCR = 0x04; // INT1 fallende Flanke
> }
> void (fallende_Flanke2)(void)
> {
>   TCCR2 = 0x00; // Timer0 Stoppen
>   Pulsdauer2 = TCNT2; // Timer Wert = Pulsdauer2
>   MCUCR = 0x0c; // INT1 auf steigende Flanke
> }
>
> /*

Anm.: Die Kommentarendezeichen hier sind falsch. Das kann kein 
lauffähiger Code sein.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Stefan B. schrieb:

>> Liege ich erstmal richtig, dass ich mit einem Timer nur jeweils einen RC
>> Kanal erfassen bzw. auswerten kann?
>> Komme ich da unter Umständen besser, wenn ich das RC Summensignal
>> auswerte, wenn ich es schaffe das aus dem Empfänger zu bekommen?

> Kann ich nicht beantworten, weil ich nicht weiss, wie die RC Signale
> einzeln und als Summe aussehen.

Vermutlich meint er ein PWM-Signal wie dieses:

http://de.wikipedia.org/wiki/Funkfernsteuerung#Vollelektronische_AM_.2F_FM-Proportionalsteuerung_27.C2.A0MHz_.E2.80.93_40.C2.A0MHz

Da die Kanäle Zeitschlitze innerhalb eines PWM-Datenstroms sind,
benötigt man nur einen Timer-Kanal für die Auswertung.  Was das
Summensignal dabei sein soll, erschließt sich mir aber nicht so
ganz.

von Oliver (Gast)


Lesenswert?

>Liege ich erstmal richtig, dass ich mit einem Timer nur jeweils einen RC
>Kanal erfassen bzw. auswerten kann?

Wenn "RC-Kanal" ein einzelner Servokanal ist, dann ja.

>Komme ich da unter Umständen besser, wenn ich das RC Summensignal
>auswerte, wenn ich es schaffe das aus dem Empfänger zu bekommen?

Das Summensignal ist die PWM-Folge mit der Info für alle Kanäle. 
Insofern lässt sich daraus mit einem Timer alles dekodieren.

Oliver

von Mapamann (Gast)


Lesenswert?

Hi,

erstmal Danke für eure Antworten und sorry für meine undeutliche 
Ausdrucksweise.

Ich meinte natürlich das Signal einer Fernsteuerung und mit Summensignal 
das PWM Signal, wie Jörg es vermutet hat.Ich werde versuchen mich in 
Zukunft besser auszudrücken.

Bin auch inzwischen soweit (mit etwas Copy und Paste ;) ), dass ich das 
PWM Summensignal am INT 0 Eingang anliegen habe und auswerten 
kann.Jedoch habe ich dazu noch ein paar Fragen.

Die Pulslänge des jeweiligen Kanals wird in ein Array namens cSumSig 
eingelesen. Habe einen 10MHZ Quarz an meinem Atmega16 mit Prescaler 256.

Also taste ich ja das Summensignal mit 39 KHz ab, oder?

Der Prescaler von 64 ist ja dann zu klein, weil der timer ja eine max. 
Auflösung von 8 Bit hat, oder?

Meine vorerst letzte Frage ist folgende. Da ich wie gesagt relativ neu 
in die Materie eingestiegen bin, frage ich in meinem Programm in der 
main Funktion das Array cSumSig[i] mit einer if/else Anweisung ab. 
Vorerst gehen die jeweils an Port A angeschlossenen Leds dabei an bzw. 
bleiben aus.
Das sieht dann in etwa so aus:

if (cSumSig[4] >= 35 && cSumSig[4] < 57)
    {
      PORTA |= (1<<PA3);
    }

else if (cSumSig[4] >= 57 && cSumSig[4] < 60)
    {
      PORTA &= ~((1<<PA2) | (1<<PA3));
    }

else if (cSumSig[4] >= 60 && cSumSig[4] < 80)
    {
      PORTA |= (1<<PA2);
    }

if (cSumSig[5] >= 35 && cSumSig[5] < 57)
    {
      PORTA |= (1<<PA1);
    }

else if (cSumSig[5] >= 57 && cSumSig[5] < 60)
    {
      PORTA &= ~((1<<PA0) | (1<<PA1));
    }

else if (cSumSig[5] >= 60 && cSumSig[5] < 80)
    {
      PORTA |= (1<<PA0);
    }



Kann mir aber vorstellen, dass es da sicher etwas "eleganteres" gibt.
Hat einer von euch nen Tip dafür?

von Karl H. (kbuchegg)


Lesenswert?

Mapamann schrieb:
1
  if (cSumSig[4] > 34)
2
  {
3
    if(cSumSig[4] < 57)
4
      PORTA |= (1<<PA3);
5
6
    else if (cSumSig[4] < 60)
7
      PORTA &= ~((1<<PA2) | (1<<PA3));
8
9
    else if (cSumSig[4] < 80)
10
      PORTA |= (1<<PA2);
11
  }
12
13
14
  if (cSumSig[5] > 34)
15
  {
16
    if (cSumSig[5] < 57)
17
      PORTA |= (1<<PA1);
18
19
    else if (cSumSig[5] < 60)
20
      PORTA &= ~((1<<PA0) | (1<<PA1));
21
22
    else if (cSumSig[5] < 80)
23
      PORTA |= (1<<PA0);
24
  }

> Kann mir aber vorstellen, dass es da sicher etwas "eleganteres" gibt.

Nicht wirklich.
Das einzige was du ausnutzen kannst.
Wenn in einer x kleiner y Abfrage der else Zweig genommen wird, dann 
steht bereits fest, dass x größer gleich y sein muss.

(Was soll eigentlich passieren, wenn deine Signale kleiner 35 bzw. 
größer 80 sind? An den Ausgängen änderst du ja in diesen Fällen nichts

Da du Relais schalten willst (offenbar mit einem Zwischenzustand, in dem 
kein Ausgang High ist): Was hältst du von
1
  if(cSumSig[4] < 57)
2
    PORTA |= (1<<PA3);
3
4
  else if (cSumSig[4] > 60)
5
    PORTA |= (1<<PA2);
6
7
  else
8
    PORTA &= ~((1<<PA2) | (1<<PA3));
9
10
  if(cSumSig[5] < 57)
11
    PORTA |= (1<<PA1);
12
13
  else if (cSumSig[5] > 60)
14
    PORTA |= (1<<PA0);
15
16
  else
17
    PORTA &= ~((1<<PA0) | (1<<PA1));

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Ich würde die input capture Funktion eines Timers für die Abtastung
benutzen.  Damit hast du einen Zeitstempel in Echtzeit, und musst
dann nur noch die Auswertung hintendran machen.  Innerhalb der ISR
schaltest du dabei die jeweilige Flanke fürs input capture um.

von Mapamann (Gast)


Lesenswert?

Hallo,

ihr seit ja heute richtig schnell mit dem Antworten :)

@Kbuchegg

Vorerst geht es nur darum, dass ich verstehe was mit diesem Quellcode in 
meinem Controller geschieht, bzw. was ich damit machen kann.
Aber es ist in der Tat korrekt, weil kleiner 34 bzw. größer 80 müsste 
ich nicht abfragen.

Später soll die fertige Schaltung in einem Modell U-Boot seine Dienste 
verrichten.

Eine Schaltfunktion (positv) soll dabei die Beleuchtung einschalten, 
negativ auch diese Beleuchtung plus Suchscheiwerfer.(Mit Beleuchtung 
sind in dem Fall die Positionslichter für Steuerbord und Backbord 
gemeint)

Die Zweite Schaltfunktion dann die Torpedoklappen über ein Servo öffnen 
bzw. schließen. Und die dritte Schaltfunktion einen Getriebemotor 
ansteueren, der das Seerohr und den Schnorchel aus- bzw. einfährt.

Hoffe ich bekomme das alles hin ;)

@dl8dtl

Werde ich mir mal anschauen.
Meinst du mit "Abtastung" das PWM Summensignal und findest es wäre eine 
"bessere" Lösung für meine Aufgaben?

Möchte halte nicht paarmal anfangen, weil sich später herrausstellt, 
dass ich dass so nicht realisieren kann, da mir ja etwas die Erfahrung 
fehlt alles gleich auf einmal zu Überblicken.

von Stefan B. (stefan) Benutzerseite


Lesenswert?

Mapamann schrieb:

> Die Pulslänge des jeweiligen Kanals wird in ein Array namens cSumSig
> eingelesen. Habe einen 10MHZ Quarz an meinem Atmega16 mit Prescaler 256.
>
> Also taste ich ja das Summensignal mit 39 KHz ab, oder?
>
> Der Prescaler von 64 ist ja dann zu klein, weil der timer ja eine max.
> Auflösung von 8 Bit hat, oder?

Dazu muss man deinen Code oben anschauen.

1/ Deine Zeitmessung benutzt keine eigene Timerinterruptroutine, sondern 
du stellst beim Auftreten einer Flanke den Timertakt ein (10 MHz/256) 
und nullst das Timerzahlregister. Dann schaust du beim Auftreten der 
entgegengesetzten Flanke nach, welcher Wert im Timerzählregister steht. 
Der Timertakt ist in der Tat ca. 39 kHz (10000000/256 Hz = 39062,5 Hz)

2a/ Du arbeitest mit Timer0, d.h. einem 8-Bit Timer. Du könntest auch 
mit einem anderen Timer auf 16-Bit Zählrange kommen.

2b/ Oder du lässt einen Overflow bei Timer0 zu und zählst in der 
entsprechenden Timer0-Overflow-ISR deren Anzahl. Die Gesamtzahl der 
Timerticks ist dann Anzahl der Overflows * 256 + Zahl im 
Timerzählregister. Die 256 hier ist wenn die Overflows erzeugt werden, 
wenn der volle Bereich des Timerzählregisters durchlaufen wird (0 bis 
255).

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Mapamann schrieb:

> Meinst du mit "Abtastung" das PWM Summensignal und findest es wäre eine
> "bessere" Lösung für meine Aufgaben?

Ja, ich würde es so machen.  Das Signal ist vergleichsweise langsam,
daher ist eine Softwaredekodierung des Summensignals nicht sehr
schwierig.  Die kürzestmögliche Zeit ist ja immerhin 500 µs, das
sind selbst bei einem mit nur 1 MHz getakteten AVR noch ca. 400
Befehle, die man da abarbeiten kann.

> Möchte halte nicht paarmal anfangen, ...

Illusion. :-)  Man fängt sowieso jedes Projekt zweimal an. ;-)  Das
ist aber auch nicht schlimm, der erste Versuch ist halt die Problem-
analyse, der zweite führt dann zum eigentlichen Ziel.  Für den
ersten Teil benutzt man irgendein Entwicklungssystem, damit man in
der Verdrahtung möglichst flexibel ist.

von Mapamann (Gast)


Angehängte Dateien:

Lesenswert?

Hallo,

ich mal wieder.

Hatte das Programm soweit am laufen mit dem PWM Signal am Int 0.
Möchte aber nun dem Verständniss halber die Sache auch mit dem Interrupt 
Capture probieren.

Leider fehlen mir dabei wieder ein paar Zusammenhänge bzw. die Erfahrung 
bei der Umsetzung.

Der Code im Anhang ist erstmal das, was ich mir bis jetzt gebastelt 
habe, fehlt noch viel, ich weis.

Aber wie gesagt habe ich ein paar Verständnissprobleme bzw. Fragen.
Ich versuche mal diese zu erklären

Habe die Thoerie bzw. Datenblatt so verstanden:

Bei dem PWM Signal gehe ich von dem hier aus:

http://www.mftech.de/ppm.htm

IPC enable --> init mit steigender Flanke --> Globale Interrups enable 
-->
bei erster steigender Flanke gehts in die "ISR(TIMER1_CAPT_vect)" (ist 
das soweit richtig?)

Dabei soll aber der erste Impuls nicht ausgewertet werden, weil dieser 
ja der Startimpuls ist.Somit gehts erstmal in den "else Zweig" (auch 
richtig?)

Nächster Innterrupt ist wieder steigende Flanke Kanal eins, es geht also 
wieder in die ISR, diesmal der "if Zweig". (richtig?)

Und nun kommen auch schon meine Fragen.

Zählt TCNT1 jetzt ab dem Interrupt von Kanal 1 steigende Flanke?
Wann muss ich die Umschaltung auf die fallende Flanke machen?
Kann ich dann einfach so den Wert von ICR1 in csumme[ikanal] lesen?

Vorstellen könnte ich mir das in etwa so:

ISR(TIMER1_CAPT_vect) {

if(ipuls==1)
 {    // Nicht vor dem ersten Puls das zählen beginnen
TCCR1B &= ~(1<<ICES1);    // fallende Flanke
csumme[ikanal] = ICR1;  // Pulslänge im Array speichern
TCCR1B |= (1<<ICES1);      // steigende Flanke
ikanal++;      // Counter für nächsten Puls erhöhen
  }
 else
 {
ipuls = 1;  // Messung nach erstem Puls freigeben
  }
  TCNT1 = 0;  // Counter zurücksetzen

}

Muss ich den Counter jedesmal auf 0 setzen?

Den nächsten Startimpuls wollte ich wieder mit einem if abfangen, in 
etwa so (wenn es so bleibt das TCNT1 immer wieder 0 gesetzt wird):

if (ICR1 >= 138)  // Impuls größer 2,2ms bei T= 16µs nach prescaler
                  // 2,2 dabei willkürlich gewählt, mit "etwas Luft"
                  // zu den 2ms für 100%
  {
   ikanal=0;
   ipuls=0;
  }

Jedoch weis ich nicht so recht, wie ich das mit in die ISR bekomme?!?!

Letzte Frage vorerst, bezieht sich auf dem im Datenblatt erwähnten "Time 
Stamp".
Wie muss ich das verstehen? Werden beim Interrupt Capture nicht "die 
Perioden gezählt" bis zum nächsten Interrupt? (meine das im Vergleich 
dazu, das selbe mit dem INT0 Eingang zu realisieren)

So, werd dann mal weiter probieren, vielleicht bekomme ich 
zwischenzeitlich schon selbst einen Lichtblick:)

von Oliver (Gast)


Lesenswert?

>Zählt TCNT1 jetzt ab dem Interrupt von Kanal 1 steigende Flanke?

TCNT1 zählt im Input-Capture-Mode überhaupt keine Flanken. Das 
funktioniert anders:

Der Zähler zählt (am einfachsten im Normal-Modus) völlig frei und 
ungestört mit dem eingestellten Zählertakt vor sich hin. Bei jeder 
passenden Flanke am ICP-Pin wird automatisch und "sofort" der gerade 
aktuelle Zählerstand in das ICR1-Register kopiert, und danach der 
Capture-Interrupt ausgelöst (falls freigegeben). Die vergangene Zeit 
zwischen zwei Flanken ist demnach die Differenz zwischen dem aktuellen 
und dem vorherigen ICR1-Wert.

In der ISR musst du also das ICR1-Register auslesen und irgendwo global 
speichern. Falls du eine Impulsbreite messen möchtest, muß hier auch von 
steigender auf fallende Flanke umgeschaltet werden (und umgekehrt). Dazu 
bitte das Datenblatt lesen. Auswerten würde ich das alles in der 
main-loop, entweder nach jedem Impuls, oder komplett nach Ende eines 
Telegrams.

Bei der Differenzbildung musst du beachten, daß der Zähler zwischen zwei 
Flanken überlaufen kann. Wenn sichergestellt ist, daß das nur einmal 
passieren kann, ist es einfach, ansonsten brauchst du auch noch den 
Overflow-Interrupt, um die Anzahl der Überläufe mitzuzählen. Dabei gebt 
es dann den Sonderfall, daß Overflow und Flanke gleichzeitig auftreten, 
aber auch das kriegst du mit etwas Nachdenken und Datenblatt lesen in 
den Griff.

Oliver

von Mapamann (Gast)


Lesenswert?

@ Oliver

Danke erstmal für die Auskunft.

Wenn ich richtig liege, sollte mein Zähler ja im Normal Mode mit 
Prescaler 256 konfiguriert sein.

Hmm, bei max Länge 22,5 ms des kompletten Impulsdiagramms bzw. max Länge 
eines Impulses kann mein Zähler doch garnicht überlaufen, oder?

Hmm, stehe trotzdem noch etwas auf dem Schlauch.
Der Timer1 Wert wird bei nem interrupt in das ICR1 Register geschrieben.
Trotzdem ist das doch eine bestimmte Bitfolge bei z.B. 1,5ms oder?

Mir fehlt da immernoch die "Brücke" zu dem "Zeitstempel".

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Mapamann schrieb:

> Der Timer1 Wert wird bei nem interrupt in das ICR1 Register geschrieben.

Nein, er wird beim Eintreten der input capture Bedingung in das
ICR1 geschrieben.  Optional kannst dich darüber dann mit einem
Interrupt informieren lassen (und das solltest du auch so tun :).

> Trotzdem ist das doch eine bestimmte Bitfolge bei z.B. 1,5ms oder?

Das input capture Ereignis ist eine Flanke aus deinem Bitstrom.
Diese hast du aber mit einem genauen (so genau wie dein Timer halt
ist) Zeitstempel.  In der ISR schaltest du dann jeweils die Flanke
fürs input capture um, damit du bei der entgegengesetzten Flanke
wieder ein solches Ereignis erhälst.

Aus der Zeitdifferenz zweier aufeinanderfolgender Ereignisse weißt
du die Zeit, die zwischen beiden lag.

Rundrum muss dann noch eine einfache state machine, die die lange
Lücke zwischen den Zyklen erkennt.  Wenn man diese das erste Mal
erkannt hat, kann man anschließend den gemessenen Impulsbreiten
die Kanäle zuordnen.

von Mapamann (Gast)


Lesenswert?

> Optional kannst dich darüber dann mit einem
Interrupt informieren lassen (und das solltest du auch so tun :).

Heist die entsprechende ISR wird aufgerufen?

> In der ISR schaltest du dann jeweils die Flanke
fürs input capture um, damit du bei der entgegengesetzten Flanke
wieder ein solches Ereignis erhälst.

ISR(TIMER1_CAPT_vect) {
if(ipuls==1)
{   // Nicht vor dem ersten Puls das zählen beginnen
  if (TNCT1 >= 1407)
  {
     ikanal=0;
     ipuls=0;
     TNCNT1 =0;
    }
  else
  {
  TCCR1B &= ~(1<<ICES1);    // fallende Flanke
  cli();
  csumme[ikanal] = ICR1;    // Pulslänge im Array speichern
  TCCR1B |= (1<<ICES1);     // steigende Flanke
  sei();
  ikanal++;             // Counter für nächsten Puls erhöhen
  }
      }
   else
      {
  ipuls = 1;  // Messung nach erstem Puls freigeben
      }
   }

Habe ich damit probiert, aber die Kanalzuordnung scheint dabei noch 
nicht zu passen.

von Oliver (Gast)


Lesenswert?

Mal ein Beispiel:

Timerclock 1ms, Normalmodus, Timer wird gestartet, also zählt der los:

0
1
2
3
...usw. bis
65535
0 (Überlauf)
1
2
3
...

Jetzt legst du an den ICP-Pin eine Rechteckfrequenz mit 100Hz an, d.h. 
alle 10ms kommt eine steigende Flanke. Interrupt Capture auf steigende 
Flanke, zur Vereinfachung schalten wir nicht um.
Der Zähler hat natürlich in der Zwischenzeit weitergezählt...
...
39
40
41
42 => hier kommt jetzt als Beispiel die erste steigende Flanke. Damit 
wir der Wert 42 nach ICP1 geschrieben. Im der jetzt folgenden ISR kannst 
du den Wert auslesen. Währenddessen zählt der Zähler, du ahnst es schon, 
fröhlich weiter:
43
44
45
46
47
48
59
50
51
52 => hier kommt jetzt die nächste Flanke: Also wird 52 ins ICP1 
geschrieben. Die Zeit zwischen zwei Flanken beträgt damit 52-42=10 
Timertakte.

Das geht jetzt immer so weiter, bis
...
65531
65532 => Flanke, ICP1 = 65532
65533
65534
65535
0 (Überlauf)
1
2
3
4
5
6 => Flanke, ICP1 = 6. Der Abstand beträgt 6-65532+65536 = 10.

Oliver

von Mapamann (Gast)


Lesenswert?

Ok, das habe ich gepeilt.

ich kann aber den Timer doch auch wieder null setzen oder muss ich den 
überlaufen lassen?

von Karl H. (kbuchegg)


Lesenswert?

Mapamann schrieb:
> Ok, das habe ich gepeilt.
>
> ich kann aber den Timer doch auch wieder null setzen oder muss ich den
> überlaufen lassen?


Können tust du schon. Nur wollen tut man nicht.
Der Timer soll in Ruhe einfach weiterzählen, möglichst ohne irgendeine 
Störung.

Um beim vom Oliver gebrachten Beispiel zu bleiben.

Timer zählt

0
1
2
3  <- jetz kommt die erste Flanke
4
5  <- jetzt reagiert dein Programm darauf un setzt den Timer auf 0
0
1
2
3
4
5
6
7  <- dem externen Signal war dein Timer auf 0 setzen völlig egal,
      das kommt trotzdem nach der vorgegebenen Zeitdauer

Ergebnis: Laut deiner Rechnerrei sind 7 Timerticks von einer Flanke zur 
nächsten vergangen. In Wirklichkeit waren es aber 10. Der Timer ist nur 
schon bis zur 3 gekommen, ehe dein Programm den Timer wieder auf 0 
gesetzt hat.

von Mapamann (Gast)


Lesenswert?

Hi...

probiere jetzt seit gestern, aber bekomme das in kein C Konstrukt rein.
Hat einer nen Tip bzw. Ansatz?

von Mapamann (Gast)


Lesenswert?

Mist, kein Bearbeiten Button...

Wollte noch Hinzufügen, das ich eben Probleme habe die lange Lücke bzw, 
den langen Impuls zu erkennen und somit die Kanäle richtig zu zu 
ordnen...

von Karl H. (kbuchegg)


Lesenswert?

Grundregel:
Wenn ein Programm bei dir nicht so richtig funktioniert und du Hilfe bei 
deinem Programm benötigst: Zeig das Programm.

von Karl H. (kbuchegg)


Lesenswert?

Jörg Wunsch schrieb:

> schaltest du dabei die jeweilige Flanke fürs input capture um.

Ich denke, dass braucht man gar nicht.

Das rc-Signal für einen Kanal geht immer von einer fallenden Flanke bis 
zur nächsten fallenden Flanke (oder umgekehrt, je nach System)

http://www.sprut.de/electronic/modell/allgemein/index.htm#1

Es reicht also völlig die Zeit zwischen 2 gleichartigen Flanken zu 
messen. Liegt diese Zeit über einem Grenzwert, dann ist die nächste 
Flanke diejenige mit der Kanal 0 beginnt.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Karl heinz Buchegger schrieb:

> Das rc-Signal für einen Kanal geht immer von einer fallenden Flanke bis
> zur nächsten fallenden Flanke (oder umgekehrt, je nach System)

Ich nahm bisher an, die Abstände der steigenden Flanken wäre von
Kanal zu Kanal immer gleich, und die Impulsbreite bestimmt den
Kanalwert.

OK, habe mir die Beschreibung bei Wikipedia nochmal durchgelesen,
stimmt, dann muss man nur eine Flanke detektieren.

von Karl H. (kbuchegg)


Lesenswert?

Jörg Wunsch schrieb:
> Karl heinz Buchegger schrieb:
>
>> Das rc-Signal für einen Kanal geht immer von einer fallenden Flanke bis
>> zur nächsten fallenden Flanke (oder umgekehrt, je nach System)
>
> Ich nahm bisher an, die Abstände der steigenden Flanken wäre von
> Kanal zu Kanal immer gleich, und die Impulsbreite bestimmt den
> Kanalwert.

Dachte ich eigentlich auch immer.
Aber wenn man sich das mal überlegt, ist es so wirklich einfacher.

Zur Ausswertung benötigt man nur ein Schieberegister.

Wir der lange Zeitslot erkannt, kommt eine 1 in das Schieberegister.
Mit jedem Puls im Datenstrom wird diese 1 weitergeschaltet. Durch die 
variable Zeitdauer von einem Puls zum nächsten entsteht dabei an jedem 
Ausgang des Schieberegisters automatisch ein 1 Puls in genau der 
zeitlichen Länge, die von einem Puls zum nächsten vergangen ist.

Würde man die benötigte Zeitdauer in der Pulsbreite selbst codieren, 
wäre das viel aufwändiger.

(Und jetzt ist mir auch schön langsam klar, warum Fernsteuerungsimpule 
eine minimale Länge von 1 ms haben. Man braucht ganz einfach eine untere 
Zeitgrenze für den 'Weiterschaltpuls'. Alles in allem ist das eigentlich 
ziemlich trickreich :-)

von Karl H. (kbuchegg)


Lesenswert?

Ich lehn mich jetzt mal aus dem Fenster. Mach ich ungern, weil ich das 
hier momentan nicht testen kann. Aber IMHO müsste das so funktionieren 
(oder zumindest dicht drann sein :-)
1
#include <inttypes.h>
2
#include <avr/interrupt.h>
3
#include <avr/io.h>
4
#define F_CPU 16000000UL     // Quarz mit 16 Mhz  
5
6
7
volatile uint16_t value[16];
8
volatile uint8_t  kanal = 255;
9
10
uint16_t prevCapture;
11
12
13
ISR( TIMER1_CAPT_vect )
14
{
15
  uint16_t thisCapture = ICR1;
16
  uint16_t time = thisCapture - prevCapture;
17
18
  if( kanal < 16 )
19
    value[kanal++] = time;
20
21
  if( time > 169 )       // Pulsdauer über 2.5 ms?
22
    kanal = 0;           // endlich: der lange Puls; der nächste Capture
23
                         // wird für Kanal 0 sein
24
25
  prevCapture = thisCapture;
26
}
27
28
int main(void)
29
{
30
  DDRD &= ~(1 << PD6);        // PD6 Input ICP
31
  PORTD |= (1<<PD6);          // Pull up on PD2
32
  DDRA = 0xff;                // PORT A komplett Output
33
  
34
  
35
  TIMSK |= (1<<TICIE1);       // Capture Interrupt enable
36
  TCCR1B |= (1<<CS12);        // Prescaler 256 Timer 1, Fallende Flanke Capture
37
38
  sei();                      // Globale Interrupts erlauben
39
40
41
  for(;;)
42
  {
43
    if( kanal < 16 )      // gibts gültige Werte?
44
    {
45
      if( value[5] < 90 )
46
        PORTA |= 1 << PA3;
47
      else
48
        PORTA &= ~( 1 << PA3 );
49
50
      if( value[6] < 90 )
51
        PORTA |= 1 << PA4;
52
      else
53
        PORTA &= ~( 1 << PA4 );
54
    }
55
  }
56
}

Worauf ich jetzt nicht speziell geachtet habe: Das beim erstmaligen 
Durchlauf durch die Kanäle keine ungültigen Werte ausgewertet werden. 
Die Zurdnung zu den Kanälen synchronisiert sich allerdings selbst. Nur 
beginnt die Hauptschleife schon das value Array auszuwerten, wenn der 
erste Kanal-0 Impuls erkannt wurde und noch keine gültigen Werte im 
value Array stehen. Beim nächsten Kanal-0 Impuls ist dieser Spuk dann 
allerdings vorbei und value enthält auf allen erkannten Kanälen gültige 
Werte.

von Oliver (Gast)


Lesenswert?

>Wollte noch Hinzufügen, das ich eben Probleme habe die lange Lücke bzw,
>den langen Impuls zu erkennen und somit die Kanäle richtig zu zu
>ordnen...

Nach dem Einschalten prüfst du jeden reinkommenden Impuls auf seine 
Länge. Ist der kürzer oder gleich 2ms, machst du gar nichts. Erst, wenn 
einer auftaucht, der (deutlich) länger ist als 2ms, hast du Kanal 1 
gefunden, und fängst mit der Auswertung an. 2ms enstpricht bei 10MHz 
CPU-Takt und Vorteiler 256 etwas über 78 Zählerschritten. Ich würde da 
auf mindestens 2,5ms testen, also auf 100 Zählerschritte.

Ab jetzt kommen der Reihe nach die Werte für die einzelnen Kanäle. 
Wiviele das sind, hängt vermutlich von deiner Fernsteuerung ab.

Nicht verkehrt ist es, nach jeder Impulsfolge erneut auf die lange Pause 
zu prüfen, und nur dann neue Werte für die Kanäle anzunehmen, wenn diese 
auch da war. Es könnte ja mal ein Impuls verloren gehen, dann käme deine 
Auswertung aus dem Tritt.

Oliver

von Mapamann (Gast)


Lesenswert?

Hi...

erstmal wieder Danke für die Hilfe.

Hab das Programm von Karl Heinz mal kompiliert und gebrannt, klappt aber 
nicht.Die LED's reagieren nicht auf meine Fernsteuerung.

Ist aber auch nicht schlimm, will es ja im Prinzip selbst hin bekommen. 
Habe eben nur Probleme das in eine C Syntax zu packen (mangels Erfahrung 
denke ich).

Muss aber jetzt erstmal los, kann also erst heut Nachmittag weiter 
machen.
Werde auf jeden Fall berichten.

von Karl H. (kbuchegg)


Lesenswert?

Mapamann schrieb:
> Hi...
>
> erstmal wieder Danke für die Hilfe.
>
> Hab das Programm von Karl Heinz mal kompiliert und gebrannt, klappt aber
> nicht.Die LED's reagieren nicht auf meine Fernsteuerung.

Ich hab noch einen Fehler nachträglich korrigiert.

War
    value[kanal] = time;

soll sein
    value[kanal++] = time;

Hast du diese Änderung mitbekommen?

Die Timerwerte hab ich nachgerechnet und angepasst. Bei 16Mhz 
Taktfrequenz und Vorteiler 256 zählt der Timer bei 2.5 ms bis ~170. 1.5 
ms (also Servomitte) müsste irgendwo bei 90 sein.

Wenn dein µC mit 16Mhz läuft, sollte das Timing daher stimmen.

Edit:
Ach ja, sehs gerade. Auf das value Array wird nicht atomar zugegriffen. 
Da das 16 Bit ist, sollte man das machen. Obwohl, bei den Werten ist das 
High Byte sowieso immer 0. Also könnte man value durchaus auch auf 
uint8_t bringen.

von Mapamann (Gast)


Angehängte Dateien:

Lesenswert?

Hi...

und erstmal wieder Danke Karl Heinz.

Habe im Anhang nochmal das aktuelle Programm.

Wieso ist value bei dir mir [16] Elementen deklariet?

Ich möchte doch "nur" 8 Kanäle + Startimpuls, also sollten doch 9 
reichen oder?

Bei der Initialierung von ikanal muss ich nur darauf achten, das der 
Wert größer als der höchste ikanal Wert ist, damit beim ersten Interrupt 
ikanal genullt wird?

Soweit klappt damit auch alles, außer das sich komischerweise in 
isumme[8] der erste Kanal der Fersteuerung befindet und bei Kanal 2 bis 
8 jeweils immer die Led an PA0, zusätzlich zu der jeweiligen Led, die 
den Kanal sybolisieren soll, mit angeht. Daran rätsel ich schon ne 
weile.

von Karl H. (kbuchegg)


Lesenswert?

Mapamann schrieb:
> Hi...
>
> und erstmal wieder Danke Karl Heinz.
>
> Habe im Anhang nochmal das aktuelle Programm.
>
> Wieso ist value bei dir mir [16] Elementen deklariet?

Weil ich mir beim definieren des Arrays noch nicht sicher war, wie ich 
ein Überlaufen verhindern werde. Und in solchen Fällen gebe ich gerne 
noch eine kleine Zugabe :-)

> Ich möchte doch "nur" 8 Kanäle + Startimpuls, also sollten doch 9
> reichen oder?

Yep.

> Bei der Initialierung von ikanal muss ich nur darauf achten, das der
> Wert größer als der höchste ikanal Wert ist, damit beim ersten Interrupt
> ikanal genullt wird?

Genauso das ist die Idee in meinem Code.
ikanal fängt mit einem großen Wert an. In der ISR wird der Wert nur dann 
abgespeichert, wenn ikanal im erlaubten Bereich ist. In diesen erlaubten 
Bereich kommt es, sobald der erste laaaange Puls vom RC-Eingang erkannt 
wird. Dadurch synchronisiert sich der ganze Klapperatismus selbsttätig 
auf den ersten Kanalimpuls

von Mapamann (Gast)


Lesenswert?

Gut,

also habe ich ja zumindest schon ein bisschen was gelernt bzw. 
verstanden.
Freut mich:).

Aber warum die LED an PA0 immer mit angeht bzw. Kanal 1 in isumme[8] 
liegt kannst du dir auch nicht erklären?

von Karl H. (kbuchegg)


Lesenswert?

Mapamann schrieb:
> Gut,
>
> also habe ich ja zumindest schon ein bisschen was gelernt bzw.
> verstanden.
> Freut mich:).
>
> Aber warum die LED an PA0 immer mit angeht bzw. Kanal 1 in isumme[8]
> liegt kannst du dir auch nicht erklären?

Ehrlich gesagt: nein

in isumme[8] müsste meinem Verständnis nach eigentlich die Länge des 
Synchronisierpulses liegen (wenn dein Sender 8 Kanäle sendet). Kanal 1 
müsste sich in isumme[0] wiederfinden.


Welche RC-Steuerung benutzt du?

von Karl H. (kbuchegg)


Lesenswert?

Ich überleg gerade was sich ändern würde, wenn in deinem Empfänger der 
Ruhepegel nicht high sondern low ist, und man auf die steigende Flanke 
synchonisieren müsst.

Du hast nicht zufällig ein Oszi, an dem du dir das Signal mal ansehen 
kannst?

von Mapa M. (mapamann)


Lesenswert?

Karl heinz Buchegger schrieb:

> Ehrlich gesagt: nein
>
> in isumme[8] müsste meinem Verständnis nach eigentlich die Länge des
> Synchronisierpulses liegen (wenn dein Sender 8 Kanäle sendet). Kanal 1
> müsste sich in isumme[0] wiederfinden.
>
>
> Welche RC-Steuerung benutzt du?

Das dachte ich mir eben auch.

Habe die Synchronisation gleich schon auf steigende Flanke umgestellt.
Ist aber trotzdem bei beiden das Selbe.

Sender sendet 8 Kanäle, weil ist erstens ne 8 Kanal Funke und zweitens 
hat's ja, als ich das selbe über den Int0 relisiert hab, wunderbar 
geklappt (auch steigende Flanke).

Habe ne Graupner MC 16/20, Empfänger Graupner C17

Umbau des C17 wegen dem Summensignal hier:

http://www.mikrokopter.de/ucwiki/c17?highlight=%28%28----%28-%2A%29%28%5Cr%29%3F%5Cn%29%28.%2A%29KategorieEmpf%C3%A4nger%29

>Du hast nicht zufällig ein Oszi, an dem du dir das Signal mal ansehen
>kannst?


Oszi leider (noch) keins :(

von Mapa M. (mapamann)


Lesenswert?

Hab jetzt seit gestern gesucht und probiert, aber konnte nicht wirklich 
herraus finden, wieso es nicht klappt.

Werd mich später nochmal dran machen, vielleicht finde ich da was.

Möchte mich jetzt auch mal an das ansteuern von Servos wagen.
Habe ja durch den Zeitstempel die Impulslängen der einzelnen Kanäle in 
isumme[] gespeichert.

Habe Timer 2 so initialisiert:

void RC_timer2_init(void)

  {
    TCCR2 |= (1<<WGM21) | (1<<COM20) | (1<<CS22) | (1<<CS21);
    //  Timer 2 CTC Mode, Toggle on Compare Match, 256 Prescaler
    TIMSK |= (1<<OCIE2) | (1<<TOIE2);
    // Timer 2 Compare Match und Overflow Interrupt enable
  }

Wie ein Servoimpuls aussehen muss weis ich.
Nur habe ich ein Problem dabei die Pause zu programmieren.
Timer 2 zählt ja nur bis 255 und bei einem Prescaler von 256 und 16MHz 
macht das ne max Auflösung von etwa 4ms.

Damit kann ich ja die 1-2ms machen, aber die Pause dann wohl nur wenn 
ich etwa 4 oder 5 Überläufe zulasse und dann wieder den Servoimpuls am 
OC2 Pin ausgebe.Wie verknüfte ich da am besten die Compare und Overflow 
ISR vom timer 2?

von Mapa M. (mapamann)


Angehängte Dateien:

Lesenswert?

Hi,

ich mal wieder:)

Nehme inzwischen die Auswertung wieder mit dem Int0 vor.
Da klappt alles wunderbar.
Habe's auch geschafft ein Servo mittels Timer1 im CTC Mode anzusteuern.
Klappt auch wunderbar.

Habe allerdings noch ein kleines problem.
Ensprechendes Servo soll dann später über einen Kippschalter eine feste 
Position anfahren.
Da soll aber jedoch nich ruckarig passieren, sobald der Schalter 
umgelegt wurde, sondern soll das Servo "langsam" diese Position 
anfahren.
Bin allerdings noch nicht ganz dahinter gestiegen wie ich das in C 
realisieren kann. Mit einem Delay in einer ISR arbeiten ist ja 
"bähhh"...soviel habe ich schon gelesen;)

im Anhang mal ein Versuch von mir das zu realisieren...aber es 
funktioniert irgendwie nicht(wenn, dann ist das sowieso erstmal nur der 
Fall für Impuls > 1,5ms)

Wenn Kanal in "Mittelstellung", dann solls im oberen if Zweig bleiben.
Wenn > Mittelstellung, gebe ich mir die Registerwerte für die jeweiligen 
Impule bzw. Pausen vor.
Aber so ganz klappt das eben noch nicht.

jemand nen Tip, wie ich so nen Soft drive realisieren kann?
Oder ist einfach nur meine selbst gewählte Auflösung (immer +0,1ms) zu 
groß?

Ach ja..hier noch der Kopf:

volatile unsigned char cSumSig[8];
volatile uint8_t iCounter=0,iValid=0, ipause=0, isoft=0;
volatile uint8_t isdrive=0;

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.