Kani-Coaching (C lernen mit Nomis)

Bevor ich weiter mache. Wollte ich erstmal mit den Umgang von Timern und Interrupts lernen.

Anfangen wollte ich mit den Timern. (Später dann zb. durch einen Timer-Overflow ausgelöste Interrupts)
Leider habe ich das mi den Timer noch nicht ganz im Griff.
Ich gehe nach diesem Tutorial.
http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial#Die_Timer.2FCounter_des_AVR
Ziel ist es eine PWM zu erstellen, die auf-/abzählt.
Regiser: TCNT1H, TCNT1L
In der PWM-Betriebsart wird das Register als Auf/Ab-Zähler verwendet, d.h. der Wert steigt zuerst von 0, bis er den Überlauf von 65535 auf 0 erreicht hat. Dann zählt das Register rückwärts wiederum bis 0.


CodeBox C
#include <avr/io.h>;

int main ()
{
DDRE = 0b11111111;

while(1)
{
TCCR1A |= (1<<COM1A1); // PWM-Modus auswählen: Invertierte PWM
TCCR1A &= ~(1<<COM1A0); // Nach dem Hochzählen OC1 auf 0

TCCR1A |= (1<<PWM11); // Hiermit sage ich dem Compiler...
TCCR1A |= (1<<PWM10); // ...das er eine 10bit PWM machen soll
}
}

Build started 27.3.2009 at 14:51:19
avr-gcc.exe -mmcu=atmega8515 -Wall -gdwarf-2 -Os -std=gnu99 -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums -MD -MP -MT pwm_timer.o -MF dep/pwm_timer.o.d -c ../pwm_timer.c
../pwm_timer.c:1:20: warning: extra tokens at end of #include directive
../pwm_timer.c: In function 'main':
../pwm_timer.c:12: error: 'PWM11' undeclared (first use in this function)
../pwm_timer.c:12: error: (Each undeclared identifier is reported only once
../pwm_timer.c:12: error: for each function it appears in.)
../pwm_timer.c:13: error: 'PWM10' undeclared (first use in this function)
make: *** [pwm_timer.o] Error 1
Build failed with 4 errors and 1 warnings...
 


CodeBox C


TCCR1A |= (1<<PWM11); // Hiermit sage ich dem Compiler...
TCCR1A |= (1<<PWM10); // ...das er eine 10bit PWM machen soll



Dem Compiler sagst du damit nicht dass er eine PWM machen soll, der ist nämlich dumm und übersetzt das nur in ausführbaren Code. Ich glaub übrigens du meinst die WGM Bits und nicht die PWM Bits. Außerdem konfiguriert man Timer, solange sie nicht zur Laufzeit geändert werden müssen, nicht die ganze Zeit sonden noch bevor der Hauptschleife.
 
Unterstellst du mir gerade ich könnte nicht lesen? Natürlich steht da WGM. (Im Datenblatt mein ich)
Vor Allem solltest du, wenn der Compiler sich über dein PWM beschwert, das nehmen was auch funktioniert, was in diesem Fall wohl WGM wäre.
 
Hallo Kani,

die Bezeichnung PWMnm ist veraltet, schau mal in das Datenblatt des Mega8515 im Kapitel Timer/Counter Register Description.

PWM10 -> WGM10
PWM11 -> WGM11


Gruß,
Dirk
 
Hi Kani,

Hallo Kani,

die Bezeichnung PWMnm ist veraltet, schau mal in das Datenblatt des Mega8515 im Kapitel Timer/Counter Register Description.

PWM10 -> WGM10
PWM11 -> WGM11
jaja :D das Problem hatte ich auch schon mit Befehlen ...
warum geht der denn nicht ? Verdammt ! Muß doch gehen ??
Ein Blick und es fällt einem wie Schuppen ... :rolleyes:
Die Bit- und Registernamen ändern sich auch mal. Das kann
schon passieren.

Gruß
Dino
 
Könnte mir mal jemand kurz erklären, wofür ich Interrupts genau gebrauchen kann?
 
Ich such schon selber nach....Nomis, irgendwie mag ich deinen Humor nicht ;-)
 
Der Erklärbär ... - heute Interrupts

Hi Kani,

Könnte mir mal jemand kurz erklären, wofür ich Interrupts genau gebrauchen kann?
naja übergeredet. Dann spiel ich mal wieder den Erklärbär :D

Also wie fange ich an ... ?

Stell dir mal vor, du hast ein Programm, das irgendwelche Daten verarbeiten
und irgendwas regel soll. Das wurschtelt dann so vor sich hin und arbeitet
und regelt und so weiter. Aber nun möchtest Du natürlich auch ein paar
Daten über den PC abrufen oder die Konfiguration ändern.

Wie stellst Du es nun an, rauszukriegen, ob der PC irgendwas zu deinem
Prozessor gesendet hat oder ob er nichts gemacht hat. Ob er jetzt was
wissen oder verändern will oder nicht ??

Da gibt es 2 Methoden ... Polling und Interrupts.

1.) Polling
Da machst Du das Programm so, das es in der Hauptschleife immer mal
nachsieht, ob da was an der Schnittstelle vom PC angekommen ist.
Es geht also bei jedem Durchlauf in die Routinen für die Schnittstelle
und fragt die Eingaangsports ab.

2.) Interrupts
Da kümmert sich die Hardware um eine Meldung das da was angekommen
ist. Du schreibst das Hauptprogramm so, das es regelt und verarbeitet und
regelt und verarbeitet und mehr nicht. Wenn jetzt was vom PC ankommt,
dann landet das bei der Schnittstelle deines Prozessors im Eingangspuffer.
der Puffer merkt jetzt : Hey was angekommen - da muß jetzt was abgearbeitet
werden. Also unterbricht es dein normales Programm und ruft ein eigenes
nur für diesen Zweck geschriebenes Unterprogramm auf (die Interrupt
Service Routine - ISR). Die holt jetzt die Daten aus dem Puffer und legt sie
da ab, wo die Hauptroutine (die ja jetzt unterbrochen ist) sie verarbeiten
kann. Wenn diese ISR damit fertig ist, springt der Prozessor wieder an die
Stelle zurück, wo er das Hauptprogramm unterbrochen hat und macht an der
Stelle weiter mit dem Hauptprogramm.

Der Unterschied:

Das Polling ist sehr einfach zu programmieren, es verbrät aber auch entsprechend
viel Performance, wenn es dauernd aufgerufen wird obwohl es gerade nicht
gebraucht wird.

Bei Interrupt-Programmierung muß man einige Sachen beachten. Es ist
manchmal schon recht tricky. Dafür ist es besser für die Performance eines
Systems, da die Teile für zB Schnittstellen nur dann aufgerufen werden,
wenn sie wirklich gebraucht werden.

ISRs (Interrupt Service Routinen) :

Das sind grob gesehen relativ normale Unterprogramme/Subroutinen die mit
einem Call aufgerufen werden könnten. Sie werden aber nicht von einem
Programm aufgerufen sondern von der Hardware (dem Prozessor) aufgerufen.
Das geht über die Interrupt-Vektor-Tabelle am Anfang des Flashs. Sieh dir
mal den Anfang der Datenblätter an. Da steht die Tabelle drin und die Namen
der Vektoren.

Bei Interrupts mußt Du dir über eines im klaren sein. Du kannst NICHT
kontrollieren wann dich ein Interrupt aus dem Hauptprogramm reißt.
Wenn Du also für das Ansprechen einer Hardware ein genaues Timing
benötigst, dann solltest Du die Interrupts für diesen Zeitpunkt erst einmal
abschalten. Das hat natürlich auch zur Folge ... Wenn Du einen Interrupt
für eine Software-Uhr brauchst, der jede Sekunde deine Uhrenroutine
aufruft, dann geht die Uhr auf einnmal falsch, weil du den Interrupt ja
mal kurz abgeschaltet hast.

Das ist so als ob du gerade liest und auf einmal kommt deine Mutter rein:
Hey !!! Müll runterbringen !!! (das ist ein Interrupt) und nach dem
Müll runterbringen liest du weiter :D :D

Es ist also mit etwas Gehirnschmalz verbunden, alles in den richtigen
Zusammenhang zu bringen.

Ich hoffe jetzt mal, es hat ein wenig was erklärt.

Gruß
Dino
 
Noch was Interruptiges ;-)

noch was vergessen :D

Bei Interrupts gibt es wie im richtigen Leben auch verschiedene Prioritäten.
Ein Anpfiff von Vatern hat immer eine höhere Priorität als ein Anpfiff von
Muttern ;) :eek: :D

Es wird also erst das ausgeführt was der Vater erzählt hat und dann das
von der Mutter bevor man endlich weiterlesen kann.

Gruß
Dino
 
Danke Dino =)
Ich hoffe du bist mir nicht böse. Hatte es in der Zeit schon selber rausgefunden. Jetzt habe ich es aber gefestigt und bin bereit in die Praxis zu gehen. :p
 
Hi Kani,

Danke Dino =)
Ich hoffe du bist mir nicht böse. Hatte es in der Zeit schon selber rausgefunden. Jetzt habe ich es aber gefestigt und bin bereit in die Praxis zu gehen. :p
Ne , bin ich nicht. Ich erklär es ja noch mal etwas anders als du es irgendwo
gelesen hast. Das führt dann meißtens dazu, das man Stellen, die man falsch
verstanden hat nochmal genau nachliest und Stellen die man richtig verstanden
hat vom anderen noch einmal bestätigt bekommt.

Gruß
Dino
 
Könnte das mal bitte eine nachgucken?


CodeBox C
#include <avr/io.h>


int main (void)
{

TCCR0 &= ~(1<<CS02); // Diese drei Einstellungen
TCCR0 &= ~(1<<CS01); // sind für den CPU-Takt
TCCR0 |= (1<<CS00); // ohne Prescaler

TCCR0 &= ~(1<<WGM00); // Timer-Modus...
TCCR0 &= ~(1<<WGM01); // ...auf normal

TIFR |= (1<<TOV0); // Beim Überlauf => Interrupt auslösen

while(1)
{
sei(); // Interrupts aktivieren


}
}

Wie sage ich den Timer welcher Interrupt ausgelöst werden soll oder wie sage ich dem Interrupt, dass er auf diesen Timer reagieren soll?
 
Du musst die Hardware des AVRs(USART, TimerCounter, Interruptpin usw ) so einstellen dass sie bei dem gewünschten Ereigniss einen Interruptrequest an den Interruptcontroller sendet. Das machst du beispielsweise bei dem Timer0 wenn du den overflow Interrupt auslösen willst, indem du im "Timer Interrupt Mask Register" TIMSK das "Timer Overflow Interrupt Enable" TOIE0 bit setzt.
 
Hi Kani,

und das SEI gehört eigentlich noch zur Initialisierung vor die Schleife.
Damit die Schleife nicht als leer wegrationalisiert wird, ein NOP rein.

SEI heißt ausgeschrieben : Set Global Interrupt Flag
CLI heißt ausgeschrieben : Clear Global Interrupt Flag

Das ganze Interrupt-System ist wie ein Baum aufgebaut.
Das Global-Interrupt-Flag stopt oder startet alles
Dann gibt es Interrupt-Enable-Flags für einzelne Teilbereiche (UART,Timer,...)
Und dann noch weitere unterteilungen zB bei PinChange für die einzelnen Pins.

Gruß
Dino
 
Okay, danke =)


CodeBox c
#include <avr/io.h>
#include <avr/interrupt.h>


int main (void)
{
DDRD = 0b11111111;
PORTD = 0b11111111;

sei(); // Interrupts aktivieren

TCCR0 &= ~(1<<CS02); // Diese drei Einstellungen
TCCR0 &= ~(1<<CS01); // sind für den CPU-Takt
TCCR0 |= (1<<CS00); // ohne Prescaler

TCCR0 &= ~(1<<WGM00); // Timer-Modus...
TCCR0 &= ~(1<<WGM01); // ...auf normal

TIMSK |= (1<<TOIE0);


while(1)
{


ISR(TIMER0_OVF_vect)
{
PORTD ^= (1<<PD0);
}
}
}

Build started 27.3.2009 at 23:11:19
avr-gcc.exe -mmcu=atmega8515 -Wall -gdwarf-2 -Os -std=gnu99 -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums -MD -MP -MT PWM_mit_Timern_und_Interrupt.o -MF dep/PWM_mit_Timern_und_Interrupt.o.d -c ../PWM_mit_Timern_und_Interrupt.c
../PWM_mit_Timern_und_Interrupt.c: In function 'main':
../PWM_mit_Timern_und_Interrupt.c:26: warning: 'TIMER0_OVF_vect_7' appears to be a misspelled signal handler
../PWM_mit_Timern_und_Interrupt.c:26: error: static declaration of 'TIMER0_OVF_vect_7' follows non-static declaration
../PWM_mit_Timern_und_Interrupt.c:26: error: previous declaration of 'TIMER0_OVF_vect_7' was here
make: *** [PWM_mit_Timern_und_Interrupt.o] Error 1
Build failed with 2 errors and 1 warnings...
 

Ü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)