PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Timer atmega 128



zrmba
17.01.2018, 11:59
Hallo zusammen,
Ich muss ein Programm schreiben, in dem ich mit einen Atmega 128 eine LED ansteuerung durch einen Timer durchführe.
Und zwar sollen die LED´s, die sich auf dem Atmega 128 befinden, angesteuert werden.
Die ungerade anzahl an LED´s also die erste dritte ... soll eine ms leuchten und die geraden 9 ms.
Ich habe mit dem programm schon angefangen, aber irgendetwas ist noch falsch.
Im Anhang ist der Entwurf des Programmes.
Ich hoffe, jemand hat einen Rat für mich.

Viele Grüße

Ceos
17.01.2018, 13:12
also bei deiner frage fehlen entscheidende infos zum helfen, aber die fragen stelle ich mal hinterher und konzentrieren wir uns auf deinen code

du hast den timer also initialisiert in irgend einem modus mit einem prescale wert und einem wert den du in den timer counter schreibst (könntest du den modus mal herleiten eventuell?)
zum einen solletst du den prescale wert ERST DANN ÄNDERN wenn dein timer initialisiert ist, denn sobald du den prescale wert jenseits 0 setzt, startet der timer SOFORT und du hast schon keinen zuverlässigen timer mehr

also MERKE: timer prescaler = 0 > timer gestoppt, timer prescaler != 0 > Timer läuft! Und änderungen am timer sollte man nur machen wenn er steht!

außerdem macht das preloaden des timer counter keinen sinn, sobald der timer überläuft wird er auf TOP zurückgesetzt und TOp entscheidet sich je nachdem welchen WGM du programmierst


aber ich geh mal nach dem datenblatt:
WGM modus 0 (update on TOP, TOP = 0xFFFF, normal up counting)
frequenz 62.5kHz
preload TCNT mit 3036

zeit zum überlauf = (TOP - 3036) * (1/62.5kHz) = 62499 * 16nS = 999,984mS

da scheint also schon was mit der berechnung falsch zu sein, das ist nicht mal annähernd 9mS

außerdem wäre ein ansatz bei dem du den timer immer neu programmierst nach jeder änderung sehr unpräzise da das umprogrammieren des timer eine undefinierte zeit braucht!


Vorgeschlagene Lösung daher:

programmier den timer auf einen WGM und einen prescaler damit er mit der kleinsten notwendigen zeiteinheit überläuft

angenommen 1mS auflösung reicht: WGM=14 (Fast PWM, TOP = ICR1 Register, normal up counting) und CS02 also Prescaler auf 256 => 62500Hz
ICR1 = 62500; (wenn TCNT = 62500 = TOP = ICR1 erreicht, klappt er auf 0 und macht einen Overflow Interrupt, somit einen overflow pro millisekunde)
und im interrupt lässt du dann eine timer variable hochzählen und kannst anhand des timer wert dann deine LEDs bedienen

Bei deiner beschreibung schreibst du trivial Atmega128, aber es gibt verschiedene derivate mit unterschiedlichen timern und andern peripherie funktionen, du solltest also wenn möglich den genauen atmega typen angeben!
Außerdem hat der Atmega garkeine LEDs draufm, ich gehe davon aus dass du das Board meinst auf dem der Stmega128 sitzt !?

zrmba
17.01.2018, 14:11
Hallo Ceos,

erstmal danke für die Fehlererkennung und den Lösungsvorschlag. :-)

Ist das so richtig?

Viele Grüße

- - - Aktualisiert - - -

Sorry, habe noch einen Fehler gefunden und behoben...

Ceos
17.01.2018, 14:35
öhm was solln das mit dem "!=" da, ich vermute mal einen typo oder soll das das pipe symbol (bitweises "oder") sein, das kannst du machen indem du AltGr + spitze Klammer drückst, unten links zwischen y und a?

WGM14 funktioniert so nicht, hast du das datenblatt von deinem controller nicht zur hand? da steht eigentlich eklärt wie du auf WGM 14 kommst!

http://ww1.microchip.com/downloads/en/DeviceDoc/doc2467.pdf einmal der link zum downloaden und dann runter zum 16-bit timer kapitel beginnend auf seite 111

die registererklärung und die WGM modes stehen ab seite 132

wichtig ist die bits zur kontrolle des WGM verteilen sich auf beide register TCCR1 A und B




//KEIN PRESCALER SETZEN
TCCR1A = (1<<WGM11); //WGM11 und WGM10 sind in TCCR1A
TCCR1B = (1<<WGM12) | (1<<WGM13); //laut datenblatt WGM(14) = WGM11 + WGM12 + WGM13
TCNT = 0; //kann man sich sparen ist eh 0 nachm neustart und wir wollen nicht den counter manipulieren whrend der timer rennt
ICR1 = 62500; //das ist das TOP register im WGM14 und bestimmt wann der counter einen overflow macht (der timer zählt aufwärts, nicht wie bei pic abwärts by default)
TCCR1B |= (1<<CS02); //erst JETZT schalten wir den timer ein und fassen ihn auch nie wieder an


die ISR sollte dann pro millisekunde einmal aufgerufen werden wenn cih jetzt nichts verschwitzt habe

zrmba
17.01.2018, 14:53
ja vielen dank,
das konnte ich vom datenblatt jetzt aber so nicht richtig ablesen, dass WGM sich aus WGM 11 12 und 13 ergibt aber vielen dank :-)

oberallgeier
17.01.2018, 15:36
.. das konnte ich vom datenblatt jetzt aber so nicht richtig ablesen ..??

Grüß Dich

und willkommen im Forum. Mal ein Vorschlag: druck Dir dieses Blatt (Quellprogramm im ersten Posting) aus und lege es auf Wiedervorlage Mai 2018 mit den Anmerkung: Verstehe ich was hier geschieht?

Nur interressehalber: Du setzt zweimal den gleichen Portpin PB2 auf Ausgang - willst aber offenkundig mehrere Ausgänge schalten. Wer sagt dem Controller dass Du noch gerade und ungerade Ausgänge haben möchtest? Außerdem hat der mega128 (Atmel-8151J-8-bit AVR Microcontroller_Datasheet_Complete-09/201) im Register TCCR1B kein Bit namens CS02 !

Mal ein bisschen Timerspielerei.
Hier ein Code den ich als Allzwecktimer (Timer2 !!) in fast allen meinen Controllern habe - eigentlich eher nebenbei mit dem Zweck einen Heartbeat - LED eine Sekunde an, eine Sekunde aus - zu halten. Bei mancherlei Störungen des Programmablaufs (hängenbleiben und so) bleibt der heartbeat manchmal stehen oder ändert deutlich seinen Rythmus. Dieser Code dürfte sinngemäß, d.h. mit entsprechenden Änderungen auch beim mega128@16MHz verwendbar sein - dann z.B. OCR2A auf 99 setzten etc.


/* >>
Stand .. C5_Ronny-5_etc\RCiCo\Timerbeispiel.c
================================================== ============================= =
Target MCU : siehe main
Target Hardware : siehe main
Target cpu-frequ. : siehe main 20 MHz Quarz, vgl. Current Conf. Options
================================================== ============================= =
Enthaltene Routinen: Verschiedene Timer
void TC2TMR_init(void) ; // Init Tmr/Cntr2, => 20 kHz = 50 µs
ISR(TIMER2_COMPA_vect) ; //
================================================== ============================= =
*** Versionsgeschichte:
x00 17Jan18 1630
================================================== ============================= =
*** Aufgabenstellung : Softwarebeispiel Timer2 auf ATmega328
Software ist NICHT GETESTET. Keine Garantie für jegliche Ansprüche
================================================== ============================ */


// ================================================== =========================== =
// === Initialisierung fuer Timer2 mega328 = Heartbeat-/Boardzeit-Timer
// vgl. Datenblatt Atmel-42735B-ATmega328/P_Datasheet_Complete-11/2016
void TC2TMR_init(void) // Init Tmr/Cntr 2, 8-Bit auf 20 kHz = 50 µs
{ //
TCCR2A |= (1<<WGM21); // Timer im CTC-Mode, Top=OCR2A doc S 205
TCCR2B |= (1<<CS21); // Prescaler 1/8 / Clock <- CPU 206
OCR2A = 124; // Preset 124 für 50µs bei 20Mhz
TIMSK2 |= (1<<OCIE2A); // Tmr/Cntr2 CompareA interrupt enabled
IztLEDu = 20; // Zeit als uint8_t deklarieren ==>>
// bleibt bis 255 in der uint8-Grenze
IztLEDg = 180; // - dto -
DDRB = 0b11111111; // Port B alles auf Ausgang
PORTB = 0b01010101; // alternierende Startzustände auf PORTB
} // Ende
// ================================================== =========================== =


// ================================================== =========================== =
// === Stoppen Timer2 mega168
void TC2_stop(void) // Stoppe Tmr/Cntr 2
{ //
TCCR2A &= ~(1<<WGM21); // Timer WGM21 resetten
TIMSK2 &= ~(1<<OCIE2A); // Tmr/Cntr2 CompA interrupt DIS abled
} // Ende
// ================================================== =========================== =


// ================================================== =========================== =
// === Nicht unterbrechbare ISR für timer2 => Heartbeat-/Boardzeit 20 kHz = 50 µs
ISR(TIMER2_COMPA_vect) // Vektor 8, 0x000E
// - - - - - - - - - - - - - - - -
{ //
if ( IztLEDu ) // Zähler für 1 ms, 1., 3., .. LEDs
{ //
IztLEDu --; //
} //
else // Eine Millisekunde ist voll
{ //
IztLEDu = 20; // Rückstellen
PORTB ^= (1<<PB0); // toggle LEDs
PORTB ^= (1<<PB2); //
PORTB ^= (1<<PB4); //
PORTB ^= (1<<PB6); //
} // Ende if ( IztLEDu )
//
if ( IztLEDg ) // Zähler für 9 ms, 2., 4., .. LEDs
{ //
IztLEDg --; //
} //
else // Neun Millisekunden sind voll
{ //
IztLEDg = 180; // Rückstellen
PORTB ^= (1<<PB1); // toggle LEDs
PORTB ^= (1<<PB3); //
PORTB ^= (1<<PB5); //
PORTB ^= (1<<PB7); //
}
return; // Ende ISR(TIMER2_COMPA_vect)
} //
// ================================================== =========================== =

// ================================================== =========================== =
// ===== ENDE Routinen tmrij
// ================================================== =========================== =

Den Timer verwende ich auch für andere, schnelle Vorgänge - siehe Code. Dieses Vorgehen ist NICHT optimal für das Schalten von lediglich zwei LEDgruppen - aber es zeigt Dir, wie ich halbwegs pflegbare Software schreibe (mal´n Kommentar ist wunderbar) und so LED-Schalterei nach DEINEM Vorschlag bewerkstelligen kann.

PS: natürlich kann man etliches im Code einfacher schreiben - aber so ist es (für nen Anfänger?) anschaulich.

PPS: LED-Blitze von ner tausendstel Sekunde sind NICHT als Einzelblitz sichtbar, auch schneller als ne hunderstel ist kaum empfindbar. Ich empfehle die Zeiten deutlich länger zu setzen.

zrmba
17.01.2018, 16:03
Hallo ,

ist CS02 nicht der 256 prescaler?
ich meine schon...

- - - Aktualisiert - - -

ach so ja ich muss dann halt einen anderen pin als zweiten ausgang nehmen..
aber stimmt das programm jetzt so? in der else anweisung ist halt ein befehl der alles invertiert...

- - - Aktualisiert - - -

viele grüße

oberallgeier
17.01.2018, 16:06
.. ist CS02 nicht der 256 prescaler? .. ich meine schon...Was steht denn in Deinem Datenblatt zum TCCR1B? Kennst Du überhaupt das Datenblatt - und diese Stelle? Besser mal selber lesen als es sich vorlesen lassen ?

zrmba
17.01.2018, 16:22
achso sind CS 10 11 und 12.. ich konnte jetzt aber nicht genau feststellen, welcher welchen wert hat...

- - - Aktualisiert - - -

kann das sein, dass die richtige lösung CS100 lautet??

- - - Aktualisiert - - -

ahne CS 12...?

Ceos
17.01.2018, 17:34
oh ähm ja :D klar timer1 .. dann isses natürlcih CS12 hab mich von dem typo hiniressen lassen ... immer dieses blöde copy&paste :D

in der doku steht ja CSn0 CSn1 und CSn2 wobei n für dne timer steht haha

zrmba
17.01.2018, 18:02
ja danke :-D
ich habe jetzt noch eine doofe frage aber was soll schon passieren..
also die 1 ms habe ich jetzt aber ich soll ja jede zweite LED 9 ms leuchten lassen.
muss ich dafür einen zweiten timer einfügen oder reicht dass mit diesem invertierenden befehl bei else? ist das programm denn sonst richtig??

oberallgeier
21.01.2018, 10:27
.. also die 1 ms habe ich jetzt aber ich soll ja jede zweite LED 9 ms leuchten lassen ..Und - bist Du schon weiter ?

zrmba
21.01.2018, 10:34
grüss dich,
Ich habe noch etwas am quellcode geändert.
Wenn ich das programm laufen lasse, funktioniert es immernoch nicht so wie gewünscht...

Jimmybot
22.01.2018, 12:27
ISR (TIMER1_OVF_vect)
{

long timer_1 = 0 ; // Variable "Timer1" deklarieren
long timer_2 = 0 ; // Variable "Timer2" deklarieren

while (1)

{

timer_1 ++; // Vorladewert von 0 an inkrementieren

PORTB = 0xAA ; // ungerade LED´s werden für 1 ms beleuchtet

if (timer_1 = preload_value_1)

{

TIMSK = (1 << TOIE1); // 1 ms ist vergangen -> Interrupt einschalten

}


else {

timer_2 ++;

PORTB=0x55 ; // gerade LED´s werden für 9 ms beleuchtet

if (timer_2 = preload_value_2)
{
TIMSK = (1 << TOTIE1); // 9 ms sind vergangen-> Interrupt einschalten
}

}
}
}

warum hast du in dein Interrupt eine Endlosschleife?


int main (void)
{
DDRB |=(1<<PB2); // Output 1
DDRB |=(1<<PB3); // Output 2


sei ();



}

Dein Programm ist schneller beendet, als der Timer1 sein Interrupt auslöst.
Und du greifst gar nicht auf deine init_Timer1 Funktion zu. Also läuft dieser erst gar nicht an.

zrmba
22.01.2018, 18:20
ja ich habe eine endlosschleife benutzt, weil der wechsel zwischen 1 ms und 9 ms ewig stattfinden soll.
achso, ja wo kommt das sei() dann genau hin? :oops:

Jimmybot
22.01.2018, 20:42
Okay... ich erkläre es dir, aber ich sehe hier ein großes Wissensdefizit im Bereich Progammierung von Microcontroller in der Sprache C bzw. C++!
(Und ja... ich bin auch nicht der super Programmierer)

sei(); gibt hier die Interrupt global frei. Soll heißen, dass du den Controller erlaubst auf Interrupt zu reagieren. cli(); ist dem entsprechen die Sperrung von Interrupt.

Deine Endloschleife (oder auch Arbeitsschleife genannt) sollte in der Main Funktion sein (hinter der Initialisierung).
Interrupts sollten immer so kurz und knapp programmiert werden wie möglich, um die Unterbrechung vom Programm möglichst kurz zu halten (ist aber in deinem Fall egal).

von daher würde ich dein Code so strukuieren:


- Einbinden von Headerdatein

- Funktionen und Interruproutinen

-Hauptfunktion
+ Portrichtung konfigurieren
+ Timer initialisieren
+ Interrups global freigeben

+ Arbeitsschleife



Sry, dass ich meine Antwort möglichs oberflächlich halte. Aber ich lese aus deinem ersten Post herraus, dass du den Auftrag hast den Code zu entwickeln bzw. dir Gedanken dazu zu machen sollst. Wir sind alle bereit dir zu helfen, aber ich werde dir keinen fertigen Code anbieten.

Darf man hier fragen, wer dich dazu verdammt hat?

zrmba
22.01.2018, 22:00
ehm ich studiere elektrotechnik, und ja ich habe auch keine großartigen programmierkenntnisse, wir müssen aber auch nur sehr wenig programmieren.. :-D

- - - Aktualisiert - - -

der neue quellcode sieht dann bei mir so aus:

Jimmybot
23.01.2018, 07:36
Bitte poste dein nächsten Code hier im Forum "lesbar"... also beim antworten den Button erweitern (unten rechts) klicken und dann im Menü "Code einfügen" benutzen und deinen Code reinkopieren. Dann kann man auch schneller lesen, ohne erst was runterladen zu müssen.

Zu dein Code:

Ich sortiere ihn mal





#include <avr/io.h> // AVR headerfile, in dem die Ansteuerung der I/Os definiert wird
#define F_CPU 16000000UL // Definiert die CPU-Frequenz auf 16MHz (entspricht Frequenz des verwendeten Quarzes)
#include <util/delay.h> // AVR Headerfile, das die Delay-Funktion definiert
#include <avr/interrupt.h> // Interrupt Bibliothek

#define preload_value_1 62500 // 1. Vorladewert
#define preload_value_2 562500 // 2. Vorladewert

void init_timer_1 (void)
{
// hier den Timer1 initialisieren
}

ISR (TIMER1_OVF_vect)
{
// Hier dein Code um zwischen 1 ms und 9 ms zu unterscheiden
}

int main (void)
{
// Portrichtung konfigurieren
DDRB |=(1<<PB2); // Output 1
DDRB |=(1<<PB3); // Output 2

// Funktion aufrufen um den Timer1 zu initialisieren
init_Timer_1();

// Interrups global freigeben
sei();

// Arbeitsschleife
while(1)
{
}
}

Den Inhalt von deiner ISR und init-Funktion habe ich mal weg gelassen.

Aber mal eine andere Frage... du sollst die ungeraden LEDs 1ms und die geraden 9ms leuchten lassen...


Alle wieviel ms soll sich der Vorgang wiederholen?


Wie wills du das Ergebnis überprüfen? Denn 1ms und 9 ms sieht man nicht bzw. kaum. Hast du ein Oszilloskop um deine Arbeit zu prüfen?

Ceos
23.01.2018, 07:49
okay grundlegendes(sorry wenn ich gerade nciht feinfühliger formulieren kann):

deine ISR() hat nichts in der main() zu suchen und das mit der enfdlosschleife war jetzt ncith ganz verkehrt aber dir fehlt definitiv ein wenig grundlagenwissen

dein controller arbeitet den code der main schleife ab, es sei denn ein interrupt tritt auf der abgearbeitet werden kann/muss

wenn der controller das ende der main erreicht ist er in einem sog, undefiniertem zustand, sowas sollte grundsätzlich vermieden werden ist aber bei neuern controllern nicht mehr wirklich relevant da die compiler schon sorge dafür tragen dass der controller in einem definierten sicheren zustand bleibt

also merke: niemals die main verlassen, immer eine while(TRUE) endlosschleife in der main() in der sich wiederholender code abgearbeitet wird

du möchtest LEDs in festen zeitintervallen laufen lassen, das geht mit ISR auf 2 arten:

Version 1: dein timer läuft in einem festen zeitintervall über (1mS) und jedesmal im OVF interrupt zählst du eine variable timer hoch (timer++ in der ISR, mehr nicht), in der main() schleife fragst du diese timer variable ab und reagierst entsprechend und steuerst deine LEDS (z.B. wenn (timer < 9) dann LED1 an, wenn (timer < 18 ) LED2 an und timer = 0 zum zurücksetzen des timers)

Version 2: die elegantere lösung ist es die LED steuerung gleich innerhalb der ISR zu machen, also in der ISR dann so z.B.: wenn (timer < 9) dann LED1 an und timer++, wenn (timer < 18 ) LED2 an und timer = 0 zum zurücksetzen des timers

Version 2 ist eleganter denn die while(true) in der main() kann leer bleiben und andere aufgaben übernehmen, aber bedenke dass eine ISR immer dazuführt dass der code der main an einer quasi beliebigen stelle unterbrochen werden kann und so vielleicht auch mal dein if in der main eine variable prüft, die im nachfolgenden prozessortakt schon wieder geändert wird udn der inhalt deiner if bedingugn eventuell ungültig wird!

also merke ISRs sind elegant um aufgaben die von externen triggern (zeit ist in dem kontext der trigger) abhängig sind zu verarbeiten, sollten aber immer so geschrieben werden dass der code in der main davon während der ausführung nicht behindert werden kann

solltest du in der main mal trotzdem ein KURZES stück code ausführen dass mit der variablen der ISR interagieren muss kannst du kurz vorher ein cli(); und danach ein sei machen also z.B:
cli();
<deine bedingung und der code der abhängig ist von der variable>
sei();

um alle interrupts kurzzeitig abzuschalten, aber dann kann es passieren dass deine timings nciht eganz exakt passen weil die ISR eventuell ein kleines stück verzögert werden dadurch


ich weis das war jetzt viel theorie auf einmal aber du brauchst mal ein wenig verständnis, weil dein code im moment eher danach aussieht als ob du rumrätselst was wohin gehört

----------------------------
EDIT: blöde auto smilies XD

zrmba
23.01.2018, 08:51
@ jimmybot : ja richtig, ich soll die zeiten mit einem oszilloskop überprüfen.
ja die ungeraden LED´s sollen 1ms leuchten und die geraden 9ms und das soll sich sofort abwechseln.

- - - Aktualisiert - - -

danke auch für die korrektur da habe ich ein paar sachen falsch gemacht stimmt.

- - - Aktualisiert - - -

auch danke an ceos :-)
ja das stimmt wohl, dass ich eher rumrätsel, ich habe mich, wie man sieht, auch noch nicht großartig mit der programmierung von microcontrollern beschäftigt, aber die neue if anweisungen für die beiden led ansteuerungen kann ich so gut nachvollziehen, danke.

oberallgeier
23.01.2018, 09:50
.. dass ich eher rumrätsel, ich habe mich, wie man sieht, auch noch nicht großartig mit der programmierung von microcontrollern beschäftigt ..Nur für mein Verständnis: was missfällt Dir denn bei meinem Codebeispiel (https://www.roboternetz.de/community/threads/71530-Timer-atmega-128?p=641914&viewfull=1#post641914) von oben? Nur damit ich mich nicht wieder einmische, wenns eh unnötig ist.