AVR_ATMEGA_Interrupts&TIMER/COUNTER

Dejan

Neues Mitglied
09. Okt. 2014
4
0
0
Sprachen
Hi Leute :),

zu meinem Projekt kann ich mal die Aufgabenstellung festlegen die zu erfüllen habe.
Es geht drum, dass ich 2 Interrupts habe: der erste wird bei einer steigenden Flanke aktiviert und der zweite wird bei einer fallenden Flanke aktiviert. (an Pin D2)
Zischen steigender und fallender Flanke sollte ein Timer in 1µs anfangen zu Zählen und den Wert "ausgeben".
Die Ausgabe ist hier nur als printf Befehl angezeigt.

Im Voraus ich bin nicht besonders gut im AVR Programmieren ^^.

Aber hab mal ein Schema zum Programm versucht zu erstellen und würde mich freuen, wenn mir jemand weiterhelfen bzw. den weiteren Lösungsweg erklären könnte. :)

LG Dejan :)

____________________________
// Grundeinstellungen
SETBIT0(DDRD, 2); // Port D2 auf Dateneingang
SETBIT1(PORTD, 2); // PullUp aktivieren

// Flanke bei INT0
//Auf steigende Flanke
SETBIT1(MCUCR, ISC01);
SETBIT1(MCUCR, ISC00);

// Fallende Flanke bei INT1
//Auf fallende Flanke
SETBIT1(MCUCR, ISC11);
SETBIT0(MCUCR, ISC10);

SETBIT1(GICR, 6); // Ext. Int0 ein
SETBIT1(GICR, 7); // Ext. Int1 ein
SETBIT1(SREG, 7); // Global Interrupt Enable


void timer16init(void)
{
cli(); // disable global interrupts

// Timer1 auf Modus CTC (mode 4)
SETBIT0(TCCR1B, WGM13);
SETBIT1(TCCR1B, WGM12);
SETBIT0(TCCR1A, WGM11);
SETBIT0(TCCR1A, WGM10);

OCR1A = 12; // von CPU Takt 12000000 Hz auf 1µs Takt: Zeit= die wir wollen(0,000001s)/(1/CPU takt(12MHz)) = 12

// Aktiviert TOGGLE auf OC1A-Anschluss
SETBIT1(DDRD, 5); // OC1A-Anschluss auf Output
SETBIT1(TCCR1A, COM1A0); // OC1A-Anschluss TOGGLE
SETBIT0(TCCR1A, COM1A1); // wenn TCNT1 = OCR1A

sei(); // enable global interrupts:
}

ISR(INT0_vect) // Dieses Unterprogramm wird automatisch bei INT0 ausgeführt
{
TCCR1A = 0; // Zähler wird auf 0 gesetzt
TCCR1B = 0; // Zähler wird auf 0 gesetzt

timer16init(void)

//GTCCR &= ~(1 << TSM); // Zähler starten
}

ISR(INT1_vect) // Dieses Unterprogramm wird automatisch bei INT1 ausgeführt
{



prinf("Der Wert ist: %d µs",TCNT1) ; // Zähler wird ausgegeben / eh falsch




}
 
Da paßt einiges nicht, habe ich richtig verstanden: Du willst die Länge der Hi-Phase messen (us-genau)?
 
Wieviele µs können das maximal sein?
Welchen Controller verwendest Du?
Hast Du Dir mal das Datenblatt dieses Controllers angesehen - insbesondere das Kapitel zum Timer und dessen Register?
Generell bieten sich erstmal 2 Wege an:
entweder Du inkrementierst den Timer im µs-Takt, beginnst damit bei der steigenden INT-Flanke und wertest den Zähler bei der fallenden Flanke aus,
oder der Timer erzeugt jede µs einen Interrupt, in dessen ISR Du eine (hinreichend breite) Variable zu Fuß inkrementierst. Bei der steigenden INT-Flanke wird der Zähler (und ggf auch der Timer (inklusive IRQ-Flag)) zurückgesetzt, bei der fallenden wird deine Variable ausgewertet und im Hauptprogramm ausgegeben.

In Anbetracht des Prozessortaktes wäre Weg 1 besser (Weg 2 würde bei 12MHz ca alle 10 Prozessortakte 'n IRQ haben wollen - allein der Sprung in die ISR braucht 6/7, zurück sinds nochmal 4 - da stolperst Du in den Interrupts). Für Weg 1 wäre allerdings ein Timer-Takt von 1MHz erforderlich - also zB 8MHz Systemtakt, und vor dem Timer dann einen 8ter Vorteiler (wobei es sinniger ist, den Timer erstmal mit möglichst kleinem Vorteiler laufen zu lassen (wenn die erforderliche Reichweite das zuläßt -> Start Synchronisation)).

Du manipulierst teilweise mehrere Bits in einem Register je einzeln nacheinander. Das ist ineffizient, und kann manchmal unerwünschte Effekte haben (die Bits werden halt einzeln und nacheinander wirksam). Sinniger und effizienter ist es, sie möglichst in einem Rutsch zu manipulieren. Jede "setze ein Bit"/"lösche ein Bit"-Anweisung kann durchaus 5 Prozessortakte kosten (wobei ggf das eine Bit schon greift, ein anderes nicht...)

Zu C (?) müssen allerdings andere was sagen...;)
 
Also ich verwende den ATMEGA 32 und habe mich auch schon das Datenblatt angesehn mit den jeweiligen Registern usw.

Die Aufgabe sollte sein, dass man einen Kondensatorwert misst;
Programm:
bei PD3 ein H-Signal haben soll und den dann kurz auf L fallen lässt. Dann sollte ein entsprechendes H-Signal von dem NE555 kommen auf den Pin PD2. Dort sollte ein Interrupt erst dann ausgelöst werden, wenn die fallende Flanke von dem NE555 Signal kommt.
Der Timer sollte bei PD3 auf L anfangen zu zählen, und bei derm Interrupt für die fallende Flanke stoppen.

Dann sollte dieser Lauf 3 mal in der SekunFoto 22.10.14 13 09 49.jpgFoto 22.10.14 13 09 57.jpgFoto 22.10.14 13 09 49.jpgFoto 22.10.14 13 09 57.jpgde sich aktualisieren.

Der Zeitwert der aus dem L-Signal und den Interrupt (td) hervor ergibt sollte Multiplext an 2 7-Segment-Anzeigen ausgegeben werden.
Der Kondensatorwert sollte von 00 nF bis 99 nF ausgegeben werden.

Berechnung des Kondensators:
td=1,1*R(10k)*C(?) --> sollte man auf C umformen und dann ausgeben
 
Sollte sich so'ne Kapazitätsmessung nicht einfacher mit 'nem sinnig gewählten Vorwiderstand zwischen Vcc und dem "Capacitor Under Test" und dem anderen Ende auf Gnd machen lassen? Das Netz zwischen CUT und dem Ladewiderstand auf Ain0 legen, an Ain1 einen sinnigen Spannungsteiler Vcc-=/=-Gnd. Mit Ain0 könnte man den CUT erstmal sicher (hinreichend lange) entladen (Strombegrenzungswiderstand erforderlich), Dann startet man den Timer und schaltet Ain0 auf den Comperator (also von Gnd auf tristate). CUT wird über den Ladewiderstand geladen, irgendwann ist die Spannung am CUT größer als die am Teiler, der Komperator kippt -> könnte automatisch ein Input-Capture bei Timer1 auslösen und 'n Interrupt anfordern (um das Capture auszulesen usw).

Dino? Gehts so?

Nunja, aus irgendwelchen Gründen sollt ihr den Timer-IC nutzen. Aus irgendeinem Grund sollt ihr dazu zwingend den externen Interrupt nehmen (man könnte ja trotzdem das Input Capture Unit des Timers nutzen (ICP oder eben auch über den AC getriggert).
Egal.
Du hast den Weg ja selbst beschrieben:
den 555 triggern und sofort den Timer starten,
Trigger zurücknehmen.
Im Interrupt (fallende Flanke) den Timer (TCNT) auslesen, Timer anhalten und zurücksetzen -> Ergebnis anpassen, global ablegen, Fähnchen fürs HP setzen.
Wenn der Timer in µs inkrementiert, muß das Ergebnis nur noch durch 1,1 geteilt werden (eigentlich könnte man den Ladewiderstand etwas anpassen - 'n 100K parallel dazu ergäbe dann 1,1*9K909091=10K, wodurch gar nicht mehr zu rechnen wäre. Aber das dürft ihr vielleicht nicht.)
Ein 99nF sollte mit 1089µs erfaßt werden (beim eleganten Weg mit 990), der Timer würde nach gut 65,5ms überlaufen/einen IRQ erzeugen können. Oder mittels OutputCompareUnit auch früher. Dort könnte man dann die beiden extremfälle 00 und EE abarbeiten - unter ASM könnte das sogar in derselben ISR geschehen (unter C? kA).

555triggern->Timer Starten->trigger zurücknehmen sollte sinnigerweise 'ne Subroutine sein, diese soll also mit 3Hz aufgerufen werden. Das läßt sich sicher mit dem Multiplexing der beiden Ziffern kombinieren: ausgehend von 60Hz "Bildwiderholfrequenz" müßte die ISR die die Ziffern umschaltet mit 120Hz zuschlagen. In dieser ISR erzeugst Du nebenbei 'ne 3Hz Zeitbasis (Flag) fürs Hauptprogramm. (jede 40ste ISR)
Das Hauptprogramm pollt diese, und startet dann die Mess-Sub.
Außerdem wird im HP auf das Fähnchen aus der ISR gepollt (neues Ergebnis). Ist es gesetzt, wird es gelöscht und die Umrechnung ausgeführt (inklusive Aufbereitung der darzustellenden Segmentanzeige und der seriellen Ausgabe.)
 
Hi,

sieht mal wieder nach nem Schul-/Studium-/Azubiprojekt aus :rolleyes:

Mal wieder eine vorgefertigte Umgebung die man mit einem eigenen Programm verschlimmbessern darf :p:cool:

So wie LotadaC das beschrieben hat könnte man das machen.

Ich würde den folgenden Punkt allerdings anders machen ...
555triggern->Timer Starten->trigger zurücknehmen sollte sinnigerweise 'ne Subroutine sein, diese soll also mit 3Hz aufgerufen werden. Das läßt sich sicher mit dem Multiplexing der beiden Ziffern kombinieren: ausgehend von 60Hz "Bildwiderholfrequenz" müßte die ISR die die Ziffern umschaltet mit 120Hz zuschlagen. In dieser ISR erzeugst Du nebenbei 'ne 3Hz Zeitbasis (Flag) fürs Hauptprogramm. (jede 40ste ISR)
Das Hauptprogramm pollt diese, und startet dann die Mess-Sub.
Außerdem wird im HP auf das Fähnchen aus der ISR gepollt (neues Ergebnis). Ist es gesetzt, wird es gelöscht und die Umrechnung ausgeführt (inklusive Aufbereitung der darzustellenden Segmentanzeige und der seriellen Ausgabe.)

Damit könnten die Mess-ISR vom externen Interrupt und die Multiplex-ISR zeitlich kollidieren. Auch wenn man den Code innerhalb der ISRs sehr kurz hält. Wenn man das vermeiden will, dann erzeugt man das Multiplexing mit Wartezeiten im Hauptprogramm. Ist zwar nicht ästhetisch aber damit hat man nur noch die eine ISR die vom NE555 getriggert wird und das Meßergebnis dann sofort auslesen kann. Man muß dann nicht warten bis evtl eine grade laufende Multiplex-ISR fertig ist. Ob das Multiplexing nun genau und stabil mit einer bestimmten Frequenz läuft wird man wohl nicht wirklich auf dem Display sehen.

Ich würde erstmal nen Skelett zusammenbauen das Werte/Text auf der Seriellen ausgeben kann. Damit hat man seine erste Debugschnittstelle um interne Abläufe sehen zu können.

Danach im Hauptprogramm das Multiplexing zusammenbauen und die Anzeige beleben. Softwaremultiplexing ist da einfacher :p Für 25Hz Wiederholrate wären das 40ms Zykluszeit. Damit also 20ms für jede Stelle. Also ein 20ms Wait in die Hauptschleife und bei jedem Schleifendurchlauf eine der beiden Stellen bearbeiten.

Danach würde ich durch einen Schleifenzähler in der Hauptschleife die etwa 1/3sec (333ms) für die 3 Messungen pro Sekunde erzeugen.

Danach mal im Takt des Meßintervalls den Pin für den NE555 toggeln und eine ISR für den externen Interrupt zusammenbauen. Als Test könnte man in der ISR einfach mal was auf der seriellen ausgeben. Dann weiß man das alles funktioniert.

Und zum Abschluß den Timer für die Zeitmessung reinbauen. Und fertig.


Gruß
Dino
 
Habe in der Schule versucht bevor ich eure Tips gelesen habe deses Programm zu überarbeiten. Das wäre mein derzeitiges Ergebnis ^^.
Es sollte bis zu dem "Ausgeben" des Wertes der Zeit gehen bzw funktionieren. Habe leider noch nicht geschafft, dass es 3 mal in einer sek. sich aktualisiert.

Es sollte noch fehlen: Umrechnung auf C; 3 mal in der sek. aktualisiern; Multiplexen auf die 7-Segment-Anzeigen und Wert ausgeben.

Würde mich sehr freuen, wenn ihr euch dieses Programm genauer anschauen würdet und auf Fehler oder solches hinweisen würdet. :) Dank euch schon mal im voraus ! :)

void main(void)
{
volatile uint16_t i;
volatile uint16_t ii;

// Grundeinstellungen
SETBIT0(DDRD, 2); // Port D2 auf Dateneingang
SETBIT1(DDRD, 3); // Port D3 auf Datenausgang

// Flanke bei INT0
//Auf fallende Flanke
SETBIT1(MCUCR, ISC01);
SETBIT0(MCUCR, ISC00);

SETBIT1(GICR, 6); // Ext. Int0 ein
SETBIT1(SREG, 7); // Global Interrupt Enable


void timer16init(void)
{
cli(); // disable global interrupts

// Timer1 auf Modus CTC (mode 4)
SETBIT0(TCCR1B, WGM13);
SETBIT1(TCCR1B, WGM12);
SETBIT0(TCCR1A, WGM11);
SETBIT0(TCCR1A, WGM10);

OCR1A = 12; // von CPU Takt 12000000 Hz auf 1µs Takt: Zeit= die wir wollen(0,000001s)/(1/CPU takt(12MHz)) = 12

// Aktiviert TOGGLE auf OC1A-Anschluss
SETBIT1(DDRD, 5); // OC1A-Anschluss auf Output
SETBIT1(TCCR1A, COM1A0); // OC1A-Anschluss TOGGLE
SETBIT0(TCCR1A, COM1A1); // wenn TCNT1 = OCR1A

sei(); // enable global interrupts:
}

ISR(INT0_vect) // Dieses Unterprogramm wird automatisch bei INT0 ausgeführt
{

SETBIT0(TCCR1B, CS12); //Stoppen des Timers
SETBIT0(TCCR1B, CS11);
SETBIT0(TCCR1B, CS10);

printf("Aktueller Wert des Timers: %u"CR, TCNT1);
}


while(1==1)
{

SETBIT1(PORTD, 3); // PD 2 H
SETBIT0(PORTD, 3); // PD 2 L

TCCR1A = 0; // Zähler wird auf 0 gesetzt
TCCR1B = 0; // Zähler wird auf 0 gesetzt

SETBIT1(PORTD, 3); // PD 2 H

} // ENDE DER MAIN
}

}
 
Wieviele µs können das maximal sein?
Welchen Controller verwendest Du?
Hast Du Dir mal das Datenblatt dieses Controllers angesehen - insbesondere das Kapitel zum Timer und dessen Register?
Generell bieten sich erstmal 2 Wege an:
entweder Du inkrementierst den Timer im µs-Takt, beginnst damit bei der steigenden INT-Flanke und wertest den Zähler bei der fallenden Flanke aus,
oder der Timer erzeugt jede µs einen Interrupt, in dessen ISR Du eine (hinreichend breite) Variable zu Fuß inkrementierst. Bei der steigenden INT-Flanke wird der Zähler (und ggf auch der Timer (inklusive IRQ-Flag)) zurückgesetzt, bei der fallenden wird deine Variable ausgewertet und im Hauptprogramm ausgegeben.

In Anbetracht des Prozessortaktes wäre Weg 1 besser (Weg 2 würde bei 12MHz ca alle 10 Prozessortakte 'n IRQ haben wollen - allein der Sprung in die ISR braucht 6/7, zurück sinds nochmal 4 - da stolperst Du in den Interrupts). Für Weg 1 wäre allerdings ein Timer-Takt von 1MHz erforderlich - also zB 8MHz Systemtakt, und vor dem Timer dann einen 8ter Vorteiler (wobei es sinniger ist, den Timer erstmal mit möglichst kleinem Vorteiler laufen zu lassen (wenn die erforderliche Reichweite das zuläßt -> Start Synchronisation)).

Du manipulierst teilweise mehrere Bits in einem Register je einzeln nacheinander. Das ist ineffizient, und kann manchmal unerwünschte Effekte haben (die Bits werden halt einzeln und nacheinander wirksam). Sinniger und effizienter ist es, sie möglichst in einem Rutsch zu manipulieren. Jede "setze ein Bit"/"lösche ein Bit"-Anweisung kann durchaus 5 Prozessortakte kosten (wobei ggf das eine Bit schon greift, ein anderes nicht...)...
So, Du stellst den Timer auf CTC bis OCR1A=12, der läuft also alle 13 TimerTakte über. Wolltest Du sicher mit OCR1A=11 auf alle 12 TimerTakte stellen. Dann würde der Timer bei 12MHz Systemtakt jede US überlaufen, wenn Du den mit Vorteile 1 auch mal starten würdest. Der OC1A-Pin würde dann jedes mal Toggeln, sollten also dort 500kHz messbar sein.
Im externen Interrupt stoppst Du den Timer und gibst das Zählregister aus...

In der Hauptprogramm verwirfst Du immer wieder die Timerkonfiguration (Du setzt die Timercontrolregister auf 0), und läßt D3 zappeln (ich tippe mal auf 10-15 Takte für die Schleife, wäre dann etwa 1MHz an D3)

Das hat jetzt nicht viel mit der Aufgabe zu tun...

Und wie gesagt: im us-Takt Interrupts anzufordern packst Du zeitlich nicht...

...Damit könnten die Mess-ISR vom externen Interrupt und die Multiplex-ISR zeitlich kollidieren. Auch wenn man den Code innerhalb der ISRs sehr kurz hält...
Deswegen auch der Hinweis mit dem (ggf AC-getriggerten) Input Capture Event...
Btw hast Du noch nichts zum ersten Punkt gesagt (Ladezeit ohne NE555 direkt über den AC messen)...
 

Über uns

  • Makerconnect ist ein Forum, welches wir ausschließlich für einen Gedankenaustausch und als Diskussionsplattform für Interessierte bereitstellen, welche sich privat, durch das Studium oder beruflich mit Mikrocontroller- und Kleinstrechnersystemen beschäftigen wollen oder müssen ;-)
  • Dirk
  • Du bist noch kein Mitglied in unserer freundlichen Community? Werde Teil von uns und registriere dich in unserem Forum.
  •  Registriere dich

User Menu

 Kaffeezeit

  • Wir arbeiten hart daran sicherzustellen, dass unser Forum permanent online und schnell erreichbar ist, unsere Forensoftware auf dem aktuellsten Stand ist und der Server regelmäßig gewartet wird. Auch die Themen Datensicherheit und Datenschutz sind uns wichtig und hier sind wir auch ständig aktiv. Alles in allem, sorgen wir uns darum, dass alles Drumherum stimmt :-)

    Dir gefällt das Forum und unsere Arbeit und du möchtest uns unterstützen? Unterstütze uns durch deine Premium-Mitgliedschaft!
    Wir freuen uns auch über eine Spende für unsere Kaffeekasse :-)
    Vielen Dank! :ciao:


     Spende uns! (Paypal)