PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : ATtiny2313 PWM/ Timer ausprogrammieren



Gareth
23.07.2011, 16:45
Ich beschäftige mich erst seit kurzem mit dem 2313 und verstehe immer noch nicht ganz welche Register man wie setzten muss um einen Timer zu intialisieren, diesem einen PWM-Port zuzuweisen etc. Zwar hab ich mir schon viele Seiten dazu durchgelesen, bekomme aber immer noch keine Ordnung in das TCC/TNC/OCR-Gewirr.
Die grundsätzlichen Zusammenhänge (also was CTC, den Prescaler und so angehen) hab ich schon verstanden, es fehlen mir nur gute/einfache Beispielprogramme anhand derer man einmal für jedes Timer/PWM-Feature einmal sieht wies angewendet wird.

Mit dem was ich bisher verstanden hab, habe ich jetzt mal versucht soein Konstrukt, was eigentlich alles die Hardware des Chips erledigen kann, "per Hand" selber zu schreiben.
Mein Ziel ist es (vorerst) alles soweit zu verstehen und auf den Chip zu kriegen, dass vier angeschlossene LEDs nacheinander an/aus faden.

Mein src sieht folgendermaßen aus (erstmal nur mit einer LED). Das Problem ist, dass ich nur ein kurzes Blitzen der LED sehe, dann eine Sek. Pause, dann wieder ein Blitzen. Was stimmt da nicht?


#define F_CPU 1000000UL

#include <avr/io.h>
#include <util/delay.h>

uint16_t table[256] = {0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3,
4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 6, 6, 6,
6, 7, 7, 7, 8, 8, 8, 9, 9, 10, 10, 10, 11,
11, 12, 12, 13, 13, 14, 15, 15, 16, 17, 17,
18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28,
29, 31, 32, 33, 35, 36, 38, 40, 41, 43, 45,
47, 49, 52, 54, 56, 59, 61, 64, 67, 70, 73,
76, 79, 83, 87, 91, 95, 99, 103, 108, 112,
117, 123, 128, 134, 140, 146, 152, 159, 166,
173, 181, 189, 197, 206, 215, 225, 235, 245,
256, 267, 279, 292, 304, 318, 332, 347, 362,
378, 395, 412, 431, 450, 470, 490, 512, 535,
558, 583, 609, 636, 664, 693, 724, 756, 790,
825, 861, 899, 939, 981, 1024, 1069, 1117,
1166, 1218, 1272, 1328, 1387, 1448, 1512,
1579, 1649, 1722, 1798, 1878, 1961, 2048,
2139, 2233, 2332, 2435, 2543, 2656, 2773,
2896, 3025, 3158, 3298, 3444, 3597, 3756,
3922, 4096, 4277, 4467, 4664, 4871, 5087,
5312, 5547, 5793, 6049, 6317, 6596, 6889,
7194, 7512, 7845, 8192, 8555, 8933, 9329,
9742, 10173, 10624, 11094, 11585, 12098,
12634, 13193, 13777, 14387, 15024, 15689,
16384, 17109, 17867, 18658, 19484, 20346,
21247, 22188, 23170, 24196, 25267, 26386,
27554, 28774, 30048, 31378, 32768, 34218,
35733, 37315, 38967, 40693, 42494, 44376,
46340, 48392, 50534, 52772, 55108, 57548,
60096, 62757, 65535};

int main (void)
{
// Pin 1 von Port A (PA1) als Ausgang schalten
DDRA = (1 << PA1);



int count=0;
int arrayindex;
int OCRzahl=table[0];
int ledstatus=0;//0 aus, 1 an

while(1){

for(arrayindex=0;arrayindex<256;arrayindex++)//alle 256 helligkeitsschritte
{

for(count=0;count<65540;count++)//entspricht einem timerdurchlauf
{
if(count==0)//am anfang die led einschalten
{
ledstatus=1;
PORTA |= (1 << PA1); //led an
}

if(ledstatus==1 && count > OCRzahl)//bei erreichen der OCRzahl die
{ //led ausschalten
ledstatus=0;
PORTA &= ~(1 << PA1); //led aus
}
}
_delay_ms(12);//warten bis die led das nächste mal aufblitzen soll

OCRzahl=table[arrayindex];//zeit nach der die led das nächste mal aus geht

}

_delay_ms(500);



//runterfaden
for(arrayindex=256;arrayindex>0;arrayindex--)//alle 256 helligkeitsschritte
{

for(count=0;count<65540;count++)//entspricht einem timerdurchlauf
{
if(count==0)//am anfang die led einschalten
{
ledstatus=1;
PORTA |= (1 << PA1); //led an
}

if(ledstatus==1 && count > OCRzahl)//bei erreichen der OCRzahl die
{ //led ausschalten
ledstatus=0;
PORTA &= ~(1 << PA1); //led aus
}
}
_delay_ms(12);//warten bis die led das nächste mal aufblitzen soll

OCRzahl=table[arrayindex];//zeit nach der die led das nächste mal aus geht

}

_delay_ms(500);





PORTA |= (1 << PA1); //nur um zu sehen ob überhaupt
_delay_ms(500); //irgentwas "lebt", falls große fehler im code sind
PORTA &= ~(1 << PA1);
_delay_ms(500);


}








}



Das Array soll 256 CTC-artige Abbruchzahlen enthalten. Das PWM-hochgezähle übernimmt die 65k-Schleife. Also wird die LED angeschaltet, die for-Schleife zählt los und bei erreichen der CTC-Zahl wird sie ausgeschaltet. Das, so war es gedacht, sollte ein kurzes Aufblitzen der LED erzeugen. Dann wird 12ms gewartet und es folgt das nächste, etwas längere Aufblitzen.
Die Zahlen im Array hab ich von einer anderen Seite mit Beispielprogramm. Die Werte wurden über eine bestimmte Formel aufgerechnet und sollen das faden möglichst linear aussehen lassen (das menschliche Auge funktioniert irgentwie mehr logarithmisch oder so).
Eigentlich finde ich von der Idee her alles richtig^^
Bisher hatte ich nur mit ARM-µCs zu tun. Aber weiter als bis zu den Interrupts sind wir in der Schule noch nicht gekommen. Timer hatten wir auch schon, aber das war mehr soeine Busy-Waiting-Geschichte.

sternst
23.07.2011, 18:12
Zwar hab ich mir schon viele Seiten dazu durchgelesen, ...
Die grundsätzlichen Zusammenhänge (also was CTC, den Prescaler und so angehen) hab ich schon verstanden, es fehlen mir nur gute/einfache Beispielprogramme anhand derer man einmal für jedes Timer/PWM-Feature einmal sieht wies angewendet wird.Verstehe ich jetzt irgendwie nicht. Nahezu jedes AVR-Timer-Tutorial, das mir bis jetzt unter die Augen gekommen ist, enthielt auch mehrere Code-Beispiele, meist mit vielen und ausführlichen Kommentaren und Erklärungen. Wenn das alles nicht deinen Ansprüchen genügt hat, was denn dann?



uint16_t table[256] =Diese Tabelle belegt 512 Bytes RAM. Schau mal in das Datenblatt des ATtiny2313, wie viel RAM der hat.

Gareth
24.07.2011, 17:04
Ich habs jetzt mal ganz anders versucht; so wie es mir als noch-PWM-Laien am logischsten zu lösen wäre.
Das funktioniert auch, nur ist es wohl sehr umständlich, man würde es nie so machen und komischerweise leuchten zwei der vier angeschlossenen LEDs heller als die anderen beiden...
Was da abläuft ist, dass alle vier nacheinander einmal kurz an/aus faden.

Ich hab ja schon einige Beispielprogramme gefunden, so ist es ja nicht; und mir auch die entsprechenden Kapitel in dem 200 Steiten starken Datenblatt durchgelesen, aber das ist englisch und es werden sehr viele Details erwähnt, die ich zwei Absätze weiter schon wieder vergessen hab.
Und in den Beispielprogrammen, die ich bisher gesehen hab, beschränkt man sich nie auf das Ausprobieren einer jeweiligen Funktion, sondern es hängt immer viel mehr Funktionalität mit dran und macht es mir sehr schwer das für mich wichtige rauszulesen.
Kennt einer einfach gehaltene Beispiele? Wo man sich z.B. nur darauf beschränkt einen Timer anzuschmeißen oder damit eine ISR anzusteuern. Nichtmal solche einfachen Sachen hab ich bisher auf die Reihe bekommen;(


#define F_CPU 1000000UL

#include <avr/io.h>
#include <util/delay.h>


int main (void)
{
// Pin 1 von Port A (PA1) als Ausgang schalten
DDRA = (1 << PA1);

DDRB = (1 << PB4);

DDRB = (1 << PB3);

DDRA = (1 << PA0);

int counter=0;
int state=1;
int i,x=0;
int nxt=0;


for (;;) {

//PORTA |= (1 << PA1); // bit im Portregister auf 1 setzen => LED leuchtet

if(nxt==0)
{
PORTA |= (1 << PA1);
}
if(nxt==1)
{
PORTB |= (1 << PB4);
}
if(nxt==2)
{
PORTB |= (1 << PB3);
}
if(nxt==3)
{
PORTA |= (1 << PA0);
}
for(i=0;i<counter;i++)
{
_delay_us(1);
}

//PORTA &= ~(1 << PA1); // bit im Portregister auf 0 setzen => LED aus


if(nxt==0)
{
PORTA &= ~(1 << PA1); // bit im Portregister auf 1 setzen => LED leuchtet
}
if(nxt==1)
{
PORTB &= ~(1 << PB4);
}
if(nxt==2)
{
PORTB &= ~(1 << PB3);
}
if(nxt==3)
{
PORTA &= ~(1 << PA0);
}


for(i=0;i<100-counter;i++)
{
_delay_us(1);
}
_delay_ms(1);

if(state==1)
{
x++;
if(x==8)
{ x=0;
counter+=2;
if(counter>10)counter+=4;
if(counter>40)counter+=6;
if(counter>60)counter+=4;}

if(counter>90)state=0;
}

if(state==0)
{
x++;
if(x==8)
{ x=0;
counter--;
if(counter>20)counter-=4;
if(counter>60)counter-=6;}
if(counter<5){state=1;nxt++;if(nxt==4)nxt=0;}
}

}
}

sternst
24.07.2011, 22:15
Kennt einer einfach gehaltene Beispiele? Wo man sich z.B. nur darauf beschränkt einen Timer anzuschmeißen oder damit eine ISR anzusteuern.http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial/Die_Timer_und_Zähler_des_AVR (http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial/Die_Timer_und_Z%C3%A4hler_des_AVR)
http://www.rn-wissen.de/index.php/Timer/Counter_(Avr) (http://www.rn-wissen.de/index.php/Timer/Counter_%28Avr%29)

oberallgeier
24.07.2011, 23:09
... Wo man sich z.B. nur darauf beschränkt einen Timer anzuschmeißen oder damit eine ISR anzusteuern ...Schade, für den 2313 hab ich so etwas grad nicht rumliegen. Aber die hier läuft auf mega168/20 MHz. Initialisierung des Timerinterrupts, in der ISR wird nur hochgezählt bis auf 1 Sekunde - und der Port PC5 jede Sekunde getoggelt.
Ich hoffe, dass mein C(äh) verständlich ist.


// ================================================== ===============================
// === Initialisierung fuer Timer2 mega168 / 20 MHz ======================
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 157
TCCR2B |= (1<<CS21); // Prescaler 1/8 / Clock <- CPU doc S 158
OCR2A = 124; // Preset 124 für 50µs bei 20Mhz
TIMSK2 |= (1<<OCIE2A); // Tmr/Cntr2 CompareA interrupt enabled
}
// ================================================== ===============================
// ================================================== ===============================


// ================================================== ===============================
// === Nicht unterbrechbare ISR für timer2 ========================================
// Routine zählt hoch im Takt 20 kHz = 50 µs. Der Zählerwert wird von den ISR für
// EXT_INT0 und -INT1 ausgelesen und den Werten Iz_yseci zugewiesen ......
ISR(TIMER2_COMPA_vect) // Vektor 7
{
if (Izeit_1 < Izthrznt) //Interrupt-Timer = 1 ... 20 000 ... (1 sec blink)
{
Izeit_1 ++; // ###>>> Izeit_1 ist aktuell int16_t ==>>
// Izeit_1 bleibt bis 32000 in der int16-Grenze
} //
else
{
PORTC ^= (1<<PC5); // LED auf Port PC5/I2C/SCL toggeln
Izeit_1 = 1; // ansonsten: Rückstellen auf Eins
} // Ende if (Izeit_1 < Izthrznt)
return;
}
// ================================================== ===============================

Nachtrag: die Bezüge auf die Dokumentation sind etwas alt . . .

Gareth
25.07.2011, 13:32
Erstmal vielen Dank für die beiden Links. Über die Seite vom ersten bin ich sogar schonmal irgentwann gestolpert, aber wusste da noch nicht was man davon alles wissen sollte. Die zweite setzt bei ihren Beispielen schon viel verständnis voraus.
Das Einzige, was mir immer noch ein paar Schwierigkeiten bereitet ist die Nummerierung. Mal wird ein Register als TIMSK bezeichnet, aber bei der konkreten Implementierung muss man noch passende Index/Buchstaben dranhängen TIMSK2 und da hab ich noch nicht den vollen Durchblick. Sowas wie WGM12 oder COM1A1 hat mich bis gestern auch noch total verwirrt, weil ich mich immer gefragt hab, was da für Zahlen hinterstehen.
Jetzt bin ich die erste Seite aber man richtig durchgegangen und verstehe schon viel mehr.

Damit etwas bei mir im Gedächtnis hängenbleibt, mache ich es immer so, dass ich alles so kompakt wie möglich mit eigenen Worten nochmal aufschreibe und das hab ich hier auch mal versucht.
Was stimmt davon/was nicht; es sind auch noch ein paar Detailfragen drin:


8bit Timer:
Overflow-Mode:
TCCR0 - 8bit Timer: 5=>CPU-Takt/1024 4=>CPU/256 3=>CPU/64 2=>CPU/8 1=>CPU
TCNT0 - 8bit Counter: Zählt von 0 bis 255. Setzt bei Überlauf TOV0, TIFR-Flag (Interrupt)
??TIMSK |= (1<<TOIE0): Overflow Interrupt erlauben??
sei() - Interruptverarbeitung aktivieren

Def. der InterruptRoutine:
ISR (TIMER0_OVF_vect){} //wobei TIMER0_OVF_vect sie an Timer0 bindet
/* TCNT0 kann hier auf einen Wert > 0 gesetzt werden um die Zeit bis zum nächsten
Aufruf zu verkürzen */



Clear Timer on Compare Match CTC-Mode: Anstatt immer bis 255 zu zählen gibt man
in OCR0A eine Zahl vor, bis zu der gezählt werden soll.
TCCR0A = (1<<WGM01); // CTC Modus //welcher Wert ist WGM01?
TCCR0B |= (1<<CS01); // Prescaler 8 //warum ver-odert? reicht WGM01 bis in dieses Register?
//wozu kann TCCR0B ohne CTC benutzt werden?
OCR0A - Counter-Obergrenze setzten
??TIMSK |= (1<<OCIE0A);??
sei() - Interruptverarbeitung aktivieren

Def. der InterruptRoutine:
ISR (TIMER0_COMPA_vect){} //TIMER0_COMPA_vect legt fest dass sie bei einem Compare Match
//aufgerufen wird, da hier ein anderes Register-Flag benutzt wird?
/* Es gibt nicht Das Eine ISR-Auslösende Timer-Flag sondern mehrere verschiedene?? */



16bit Timer:
Zu einem 16bit Timer gehört immer eine PWM-Funktionalität und immer ein bestimmter PIN.
TCCRA1: 0=>PWM aus 1=>8bit PWM 2=>9bit PWM 3=>10bit PWM
OC1A - Zugehöriger PIN, der je nach Einstellung bei Compare Match gesetzt wird.

PWM aus:
TCCRA1 - 8bit Einstellungsregister: 64=>OC1A Toggle 128=>OC1A 0 192=>OC1A 1

PWM an:
TCCRA1: 128=>(beim Hochzählen) OC1A auf 0 (beim Runterzählen auf 1) 192=>OC1A 1 bzw. 0

PWM aus:
TCCR1B - zweites 8bit Einstellungsregister, das die eigentlichen Auslösezeitpunkte
festlegt bzw. Prescaler. (wie TCCR0 beim 8bit Timer)
5=>Hochzählen der OCR0A bei jedem CPU-Takt/1024 ten Takt 4=>CPU/256 3=>CPU/64 2=>CPU/8
1=>CPU/Takt 0=>angehalten (nachdem TCNT0 den Wert von OCR0A erreicht hat, folgt das
Auslösen der ISR bzw. PWM bedingter PIN umlegungen)
/* Die Höheren Bits in TCCR1B sind für Feineinstellungen und erstmal unwichtig */

TCNT1H/TCNT1L - Zwei 8bit Register, also zusammen 16bit, die die Funktion von TCNT0
beim 8bit Timer erfüllen und immer von 0 bis 65535 zählen. (TCNT1High->höherwertige Bits)

OCR1H/OCR1L - Zwei 8bit Register, die zusammen die 16bit Zahl darstellen, bei der ein
Compare Match (ISR oder PWM-PINumlegung) ausgelöst/durchgeführt wird. (erst in H schreiben)
/* Kann man auch eine 16bit Zahl in OCR1L stecken und die zu hohen Bits werden automatisch
in OCR1H gespeichert? /*

ICR1H/ICR1L - Bei PINflanke von außen wird eine andere TOP-Grenze gesetzt (unwichtig)

PWM an:
/* Wenn in TCCRA1 ein PWM-Mode ausgewählt wurde, wird nicht immer von 0 bis 65535 gezählt
sondern je nach Modus( 8bit,9bit,10bit) bis zu einer Grenze und dann wieder zurück. */
Wurde in TCCRA1 der 8bit PWM-Modus gewählt, wird in TCNT1H/TCNT1L von 0 bis 255 und zurück
gezählt. Beim 9bit PWM-Modus von 0 bis 511 und beim 10bit PWM-Modus bis 1023. Nur wenn
überhaupt ein Modus gewählt wurde wird, sobald der Zähler den Wert von OCR1H/OCR1L
erreicht, die PWM-Typische PINumlegung durchgeführt.(z.B. PIN PB3). Was genau mit diesem
PIN passiert wurde am Anfang in TCCRA1 festgelegt.



Was sowohl für den 8bit als auch für den 16bit Timer gilt:

TIMSK - Interrupt Mask: 128=>bei Überlauf von TCNT1H/TCNT1L, also dem Zähler des 16bit
Timers (Timer1) wird ein (Timer Overflow 1) Interrupt ausgelöst.
64=>wenn OCR1H/OCR1L den Wert von TCNT1H/TCNT1L erreicht, also wenn der Zähler von Timer1
den Grenzwert von Timer1 erreicht, wird ein (Compare Match) Interrupt ausgelöst.
8=>Es wird ein (Capture Event) Interrupt ausgelöst, wenn ein Signal an PIN PD6 anliegt.
2=>Bei einem Überlauf von TCNT0, dem Zähler des 8bit Timers (Timer0) wird ein (Timer
Overflow 0) Interrupt ausgelöst.
Mehrere Bits werden gesetzt, indem man sie nacheinander mit TIMSK verODERt.
/* Beim 8bit Register OCR0A hatte man eine neue Obergrenze (statt 255) gesetzt, keinen
Wert bis zu dem gezählt, ein Interrupt ausgelöst wird und dann weitergezählt wird, wie
beim 16bit Timer! Deshalb gibt es auch kein Bit in dieser TIMSK-Maske, das man setzt
um einen Interrupt bei Erreichen von OCR0A auszulösen!!! */

TIFR - Interrupt Flag Register: Beinhaltet die Flags, die die Timer setzten.

Gareth
25.07.2011, 13:34
Ach mist, weiter oben ist jetzt ein Bischen was durcheinander gekommen, weil ich erst nicht wusste, dass das OCR0A beim 16bit Timer durch zwei andere ersetzt wird.

Gareth
25.07.2011, 14:39
Das Beispielprogramm über PWM ganz unten auf http://www.rn-wissen.de/index.php/Timer/Counter_%28Avr%29
verwirrt mich jetzt grade nicht wenig.
Es wird ein 16bit Timer verwendet (muss ja, da der 8bit Timer nichts mit PWM zutun hat).
Dann wird bei Punkt 2. gesagt, dass die WGM10/12 Bits auf auf TCCR1A/TCCR1B verteilt sind (auf http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial/Die_Timer_und_Z%C3%A4hler_des_AVR steht das ja ganz anders), aber gut, das liegt dann wohl daran dass es ein anderer Chip ist.
Was ich dann aber gar nicht verstehe ist, wie dort OCR1A/OCR1B verwendet werden. Erstens dachte ich, dass die bei einem 16bit Timer OCR1H/OCR1L heißen, also H und L für High und Low, weil ja beide zusammen nur Eine große Zahl darstellen.
Da der 8bit PWM-Modus gewählt wurde steht da auch, dass nur bis 255 gezählt wird und der OCR-Wert also zwischen 0 und 255 liegen muss. Das heißt man darf eigentlich nur OCR1L benutzen, weil das erste Bit in OCR1H ja 256 ist.
Aber die in dem Beispiel setzten beide einfach auf 127, womit man doch eine zahl > 32k hat. Das ist um einiges größer als 255. Wo liegt mein Denkfehler?
Ganz oben wird außerdem gesagt, dass das PWM-Signal an PD5/OC1A und PD4/OC1B ausgegeben wird. Heißt das man hat an beiden PINs einfach das selbe Signal oder kommen da verschiedene an (wenn verschiedene ankommen, wo wurde dann gesagt wie der zweite sich bei Compare Match bzw. Überlauf verhalten soll?).

oberallgeier
25.07.2011, 15:00
... Schwierigkeiten bereitet ist die Nummerierung ...TIMSK2 ist das Timer/Counter Interrupt MasK Register für Timer2 - ist doch easy oder? Dementsprechend sind alle ähnlichen Register, Flags etc. durchnummeriert wie die zugehörigen Timer/Counter - wenn es mehrere von diesen Dingen gibt. Du müsstest die Zeit aufwenden, und Dir die Dokumentation durchlesen. Nicht alle dreihundert Seiten oder so - aber die Timer sollte man schon mal durchgehen. Selbst wenn man nicht perfekt Englisch spricht.

sternst
25.07.2011, 16:03
Es wird ein 16bit Timer verwendet (muss ja, da der 8bit Timer nichts mit PWM zutun hat).Der Satz in der Klammer macht keinen Sinn. Auch die 8-Bit-Timer haben PWM-Modi.


Was ich dann aber gar nicht verstehe ist, wie dort OCR1A/OCR1B verwendet werden. Erstens dachte ich, dass die bei einem 16bit Timer OCR1H/OCR1L heißen, also H und L für High und Low, weil ja beide zusammen nur Eine große Zahl darstellen.In C kannst du über OCR1A die beiden Register OCR1AH/OCR1AL als ein gemeinsames 16-Bit-Register ansprechen (entsprechend OCR1B und OCR1BH/OCR1BL). Und es ist auch sehr ratsam, das so zu machen, denn dann brauchst du dir über die Reihenfolge keine Gedanken machen (die H/L-Register können nämlich nicht in beliebiger Reihenfolge gelesen/geschrieben werden).


Aber die in dem Beispiel setzten beide einfach auf 127, womit man doch eine zahl > 32k hat. Das ist um einiges größer als 255. Wo liegt mein Denkfehler? Aus vorigem und diesen Zitat ergibt sich, dass du wohl irgendwie übersehen hast, dass es hier um 2 (bzw. 4) separate Register geht.


Ganz oben wird außerdem gesagt, dass das PWM-Signal an PD5/OC1A und PD4/OC1B ausgegeben wird. Heißt das man hat an beiden PINs einfach das selbe Signal oder kommen da verschiedene an (wenn verschiedene ankommen, wo wurde dann gesagt wie der zweite sich bei Compare Match bzw. Überlauf verhalten soll?). Das eine Signal ergibt sich aus OCR1A, das andere aus OCR1B. Es sind zwei PWM-Kanäle (A und B) in einem Timer.

Gareth
25.07.2011, 16:41
Ich beziehe mich jetzt mal nur auf: http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial/Die_Timer_und_Z%C3%A4hler_des_AVR
Da steht, so wie ich das verstehe (im Gegensatz zum 8bit Timer) bietet der 16bit Timer "Die PWM-Betriebsart zur Erzeugung eines pulsweitenmodulierten Ausgangssignals."
Und zur PWM steht in dem 8bit Timer-Abschnitt ja auch gar nichts. Gut zu wissen, dass es da doch welche gibt^^

Die Einstellungsregister für den 16bit Timer sind ja TCCR1A und TCCR1B. D.h. beide Register zum Einstellen des einen (zweiten) Timers. Und pro Timer hat man doch nur einen Zähler nämlich TCNT1H/TCNT1L.
Wenn es so ist, dass man aber Zwei OCR-Werte zum Vergleichen hat, wo wird denn festgelegt, was beim Compare Match des zweiten gemacht wird? Für einen der beiden hat man ja in TCCR1A/TCCR1B festgelegt, dass ein PIN auf 0,1 gesetzt,getoggled werden soll, wo sagt man das für den zweiten.
Oder gilt die eine Einstellung für beide Compare Matches und es wird in selber Weise der PIN auf 1,0 gesetzt, aber dann zu unterschiedlichen Zeitpunkten?
Auf der Seite wird dazu gar nichts gesagt und man denkt es gäbe nur einen OCR.

Und die andere Frage ist, wie man die PWM-Funktion beim 8bit Timer benutzt? Laut der Seite hat Timer0 nur ein TCCR0 für die Einstellungen (kein A oder B) und dort werden auch nur die ersten drei Bits benutzt, nämlich für den Prescaler.
Scheinbar fehlen auf der Seite viele Erklärungen.

Ich werde mir das Manual natürlich noch häufiger durchlesen, bis ichs eben verstanden hab, aber gibts vllt eine deutsche Seite, die das etwas vollständiger Erklärt?

Besserwessi
25.07.2011, 18:29
Die 8 Bit timer sind nicht alle gleich. Bei den neueren AVRs haben auch die 8 Bit timer oft 2 OCR Register und man kann entsprechend damit auch 2 PWM Kanäle erzeugen. Wie es geht ließt man am besten im Datenblatt nach, denn Timer0 und Timer2 sind ggf auch etwas verschieden.

Wie die PWM Pins reagieren kann man beim Tiny2313 im Register TCCR1A einstellen, für beide Kanäle getrennt. Der 2. Kanal fehlt nur im den Tutorial - keine Ahnung für welchen µC das ist.

sternst
25.07.2011, 23:47
Erst mal was Grundsätzliches:
Die Tutorials orientieren sich an einem konkreten Controller als Beispiel. Da die Tutorials meist schon älter sind, ist dann auch dieser "Referenz-Controller" schon älter. Im obigen Fall ist es der AT90S2313, und der ist nun wirklich schon steinalt. Die Tutorials sind also nur dafür gut, die Grundlagen und Prinzipien zu verstehen. Wenn es an die konkreten Details geht, gibt es für dich nur eine Referenz, nämlich das Datenblatt zu dem Controller, den du konkret verwenden willst. Die sind in Englisch, finde dich damit ab. Das Technik-Englisch in den Datenblättern ist einfach, und außerdem kommst du ums Englisch eh nicht drumherum, wenn du mit Elektronik basteln willst.


Wenn es so ist, dass man aber Zwei OCR-Werte zum Vergleichen hat, wo wird denn festgelegt, was beim Compare Match des zweiten gemacht wird? Für einen der beiden hat man ja in TCCR1A/TCCR1B festgelegt, dass ein PIN auf 0,1 gesetzt,getoggled werden soll, wo sagt man das für den zweiten. In den selben Registern. Wenn es zwei PWM-Kanäle gibt, dann gibt es in TCCR1A sowohl Bits, die das für Kanal A festlegen (COM1A1/COM1A0), als auch welche für Kanal B (COM1B1/COM1B0).


Auf der Seite wird dazu gar nichts gesagt und man denkt es gäbe nur einen OCR.

Und die andere Frage ist, wie man die PWM-Funktion beim 8bit Timer benutzt? Laut der Seite hat Timer0 nur ein TCCR0 für die Einstellungen (kein A oder B) und dort werden auch nur die ersten drei Bits benutzt, nämlich für den Prescaler.

Scheinbar fehlen auf der Seite viele Erklärungen.Nein, es fehlen keine Erklärungen, der AT90S2313 hat das nur alles noch nicht. Timer1 hat bei dem nur einen PWM-Kanal und Timer0 kann gar keine PWM.

Gareth
26.07.2011, 12:38
Ok ich hätte mir wohl gleich das Datenblatt vornehmen sollen^^ Das hab ich soweit verstanden:

8bit Timer:

TCCR0A - WGM00/WGM01: Geben den Timer Modus an (zusammen mit WGM02 aus TCCR0B)
COM0A1/COM0A0: Legen fest was mit dem PIN OC0A (je nach Modus) passieren soll.
COM0B1/COM0B0: Legen fest was mit dem PIN OC0B (je nach Modus) passieren soll.
TCCR0B - CS00/CS01/CS02: Setzten die Geschwindigkeit des Timers (den Prescaler)
WGM02: Gehört zu WGM0:1 aus TCCR0A

TCNT0 - Der 8bit Zählwert 0..255

OCR0A - 8bit Vergleichswert für pin OC0A.
OCR0B - 8bit Vergleichswert für pin OC0B.

TIMSK - Bestimmt ob bei einem jeweiligen Compare Match/Overflow außer dem pin-umlegen
auch noch ein Interrupt ausgelöst werden soll.
OCIE0A: Wenn gesetzt -> Interrupt bei Compare Match von OCR0A.
OCIE0B: Wenn gesetzt -> Interrupt bei Compare Match von OCR0B.
TOIE0: Wenn gesetzt -> Interrupt bei Overflow von TCNT0.

(TIFR - Enthält die Compare Match Flags bzw. das Overflow Flag)

/* Zum Verständnis: Es gibt beim Timer keinen Compare Match-losen Modus. Wenn man die
Möglichkeiten der Compare/Vergleich/Pinumlegungs-Modi nicht benutzen will, sondern
z.B. nur den "normalen" Timer der bis 255 zählt und dann eine ISR aufruft haben will,
lässt man die WGM0:2/OCR/COM-Bits einfach leer und startet den Timer einfach über
CS00/CS01/CS02 in TCCR0B und setzt das Overflow-Interrupt-Bit TOIE0 in TIMSK.

In diesem "normalen" Modus (WGM-Bits auf 0) einem der Vergleichswerte der OC-Bits einen
Wert gibt und über die COM0-Bits sagt, wie der pin beim Compare Match gesetzt werden
soll, passiert auch das. (pin als Ausgang schalten nicht vergessen)
(wenn gleichzeitig noch ein Compare Match Interrupt ausgelöst werden soll->TIMSK)

Im CTC-Modus wird das eine Compare-Register (OCR0A) benutzt um die Obergrenze von 255
auf einen beliebigen, kleineren Wert zu setzten, bis zu dem der Timer dann immer zählt.
Die Compare Match-Pinumlegungsmöglichkeiten hat man aber nach wie vor und OCR0B kann
man immer noch frei in diesem Bereich bewegen. Lediglich der Compare-Match-Interrupt
den OCR0A auslöst, findet gleichzeitig mit dem Overflow-Interrupt statt.

Zu dem PWM-Modi zählen der Fast PWM-Modus, in dem der Timer, wie im "normalen" Modus,
immer bis 255 zählt, aber im Gegensatz dazu wird ein OC-Bit hier nicht nur beim
Compare Match verändert, sondern noch ein zweites Mal beim Overflow.
(was in jedem Modus jeweils passiert wird über die COM-Bits festgelegt)

Der zweite PWM-Modus ist der Phase Correct Modus, in dem der Timer wieder von 0..255
zählt, dann jedoch keinen Overflow verursacht und wieder bei 0 anfängt, sondern
anschließend rückwärts bis 0 zurück zählt. Auch hier werden die Bits (wie im Fast PWM)
zweimal verändert. Einmal beim Compare Match, wenn der Zähler beim Hochzählen
vorbeikommt und das zweite Mal beim Runterzählen.
(einen Overflow Interrupt kann es also nicht geben)
Beide PWM-Modi kann man auch CTC-artig benutzen, also dass nicht bis 255 sondern bis
zu dem Wert gez#hlt wird, den man in OCR0A gesetzt hat. (WGM0:1:2 in Mode 5 bzw. 7) */




Frage1: Wenn man den CTC-Modus benutzt, wird der TOP-Wert laut Datenblatt in OCR0A
erwartet. Heißt das, dass man nur noch OCR0B frei setzten kann?

Frage2: Im Datenblatt auf Seite 75 steht in der Tabelle zum Phase Correct PWM Mode, dass
einmal ORC0B ge-cleart wird, dann dass OCR0B gesetzt wird, überall wird von OCR0B geredet
dabei macht das doch gar keinen Sinn, weil es hier doch um den Port OC0B geht.
Haben die (perfekten;) Atmel-Leute hier etwa einen Fehler reingebaut und überall ein R
reingeschrieben, wo es nicht hin gehört?
In der Tabelle für OC0A steht das nämlich alles ohne R da.

Frage3: In Tabelle 40 auf Seite 75 des Datenblattes gibt es eine Spalte "Update of OCRx at"
Warum wird OCRx ge-updatet und nicht der zugehörige Port OCx. Ich dachte die OCR0A/OCR0B
Werte verändert nur der User, wie er sie braucht. Und wenn da tatsächlich die Output-Pins
gemeint sind, warum steht dann nicht in jeder Zeile Immediate.
Was würde es für einen Sinn machen den Pin, der z.B. im Mode1-Phase-Correct beim Hochzählen
gesetzt werden soll, erst beim Erreichen von TOP zu updaten/zu setzten. Das ist dann doch
ein ganz anderer Zeitpunkt. Und wenn anschließend runtergezählt wird, der Pin beim
Compare Match beim Runterzählen wieder gesetzt werden sollte, wird er es auch nicht,
sondern erst beim nächsten Erreichen von TOP? Das kann nicht sein.

sternst
26.07.2011, 14:17
Im CTC-Modus ...
Lediglich der Compare-Match-Interrupt den OCR0A auslöst, findet gleichzeitig mit dem Overflow-Interrupt statt.Nein, die Interrupts finden nicht gleichzeitig statt. Der Overflow-Interrupt passiert weiterhin beim Erreichen von MAX (nicht TOP). Da MAX im CTC-Modus ja aber gar nicht erreicht wird, gibt es auch keinen Overflow-Interrupt.


Der zweite PWM-Modus ist der Phase Correct Modus, ...
(einen Overflow Interrupt kann es also nicht geben)Doch gibt es, und zwar immer dann, wenn er wieder bei 0 angekommen ist.

Die Spalte "TOV Flag Set on" in Tabelle 40 sagt dir, wann jeweils ein Overflow-Interrupt ausgelöst wird.



Frage1: Wenn man den CTC-Modus benutzt, wird der TOP-Wert laut Datenblatt in OCR0A
erwartet. Heißt das, dass man nur noch OCR0B frei setzten kann?Du kannst schon beide frei setzen und es wird auch bei beiden ein Compare-Match ausgelöst, nur dass halt OCR0A zusätzlich als TOP fungiert. Du kannst so den TOP-Wert bei laufenden Timer ändern.


Frage2: Im Datenblatt auf Seite 75 steht in der Tabelle zum Phase Correct PWM Mode, dass
einmal ORC0B ge-cleart wird, dann dass OCR0B gesetzt wird, überall wird von OCR0B geredet
dabei macht das doch gar keinen Sinn, weil es hier doch um den Port OC0B geht.
Haben die (perfekten:wink: Atmel-Leute hier etwa einen Fehler reingebaut und überall ein R
reingeschrieben, wo es nicht hin gehört?
In der Tabelle für OC0A steht das nämlich alles ohne R da.Glückwunsch, du hast gerade festgestellt, dass auch die Atmel-Leute nur Menschen sind. ;-)
Dir werden im Laufe der Zeit noch mehr Fehler auffallen.


Frage3: In Tabelle 40 auf Seite 75 des Datenblattes gibt es eine Spalte "Update of OCRx at"
Warum wird OCRx ge-updatet und nicht der zugehörige Port OCx. Ich dachte die OCR0A/OCR0B
Werte verändert nur der User, wie er sie braucht. Und wenn da tatsächlich die Output-Pins
gemeint sind, warum steht dann nicht in jeder Zeile Immediate.In den Erklärungen zum Timer im Datenblatt hast du entweder einen Abschnitt übersprungen, oder gründlich missverstanden.
Immer wenn du in irgendeines der OCRx-Register schreibst, landet der Wert nicht wirklich in dem Register, sondern wird in einem versteckten namenlosen Register "zwischengelagert". Erst wenn der Timer einen bestimmten Wert erreicht, wird der Inhalt aus dem Zwischenregister in das eigentliche Register geschrieben. Die Spalte in der Tabelle gibt an, wann das bei welchem Timer-Mode passiert.

Gareth
27.07.2011, 14:55
Ja das stimmt, ich hab einen wichtigen Abschnitt wirklich eben erst gelesen.
Also das "Update of OCRx at" gibt es, weil man den OCRx-Wert in manchen Fällen
double-buffered hat. Wenn man OCR1A als TOP-Wert in einem Modus benutzt und ihn im
laufenden Programm ändern will, weil man eine andere Frequenz braucht etc. und man ihn
kleiner machen will, aber der Zähler (TCNT) grade über der neuen TOP-Grenze ist, wäre die
Grenze nach dem Umsetzten weg und er würde bis FFFF zählen. Das double-buffered bedeutet,
dass es eine Kopie von der OCR1A gibt, die der Zähler als TOP benutzt und wenn man
OCR1A ändert, hat man TOP damit noch gar nicht verändert. Die Zahl in OCR1A wird erst beim
nächsten Erreichen von "Update of OCRx at" in die (wichtigere) Kopie kopiert.

(Und wenn man beim 16bit Timer ICR1 statt OCR1A benutzt und ihn ändert kann es aber doch
passieren, dass man dem Zähler für den aktuellen Durchlauf die Obergrenze wegnimmt und er
bis FFFF durchläuft, weil die ICR1 nicht double-buffered ist)

Dann werden TOV und der Compare Match Interrupt von OCR1A nur beide gleichzeitig im Mode 7
ausgelöst?

Ich hab mir mal die iotn2313.h angeguckt.
Hier gibt es einige gleichwertige Benennungen, was man vielleicht wissen sollte.

16bit Timer:
OCR1 == OCR1A für den 16bit Zugriff
OCR1L == OCR1AL für den 8bit Zugriff auf den niederwertigen Teil
OCR1H == OCR1AH für den 8bit Zugriff auf den höherwertigen Teil

Für den B-Ausgang gibts hingegen keine Schreibweise ohne das B
OCR1B für den 16bit Zugriff
OCR1BL für den 8bit Zugriff auf den niederwertigen Teil
OCR1BH für den 8bit Zugriff auf den höherwertigen Teil

Auf TCNT kann ebenfalls über einen 16bit oder zwei 8bit Zugriffe geschrieben werden
TCNT1 für den 16bit Zugriff
TCNT1L für den 8bit Zugriff auf den niederwertigen Teil
TCNT1H für den 8bit Zugriff auf den höherwertigen Teil

Ebenso gilt für das ICR
ICR1 für den 16bit Zugriff
ICR1L für den 8bit Zugriff auf den niederwertigen Teil
ICR1H für den 8bit Zugriff auf den höherwertigen Teil

TCCR1A/TCCR1B/TCCR1C 8bit

8bit Timer:
Keine Überraschungen. Es gibt nur die 8bit-Register..
OCR0A 8bit
OCR0B 8bit
TCNT0 8bit

TCCR0A/TCCR0B 8bit


Inzwischen hab ichs hinbekommen Timer0 zu starten und damit eine LED zum faden zu bringen, und zwar so:

#define F_CPU 1000000UL
#include <avr/io.h>
#include <util/delay.h>

int main (void)
{
/* Fast PWM an Timer0. Benutzt werden soll nur OCR0A. Die LED/der Port OC0A soll beim
Erreichen von OCR0A aktiviert weden und beim Overflow wieder aus gehen.
In der while(1)-Schleife wird immer 12ms gewartet und dann OCR0A um eins erhöht.
(Bzw. wenn grade angefadet werden soll, um eins verringert)
*/

TCCR0A = (1 << COM0A1) | (1 << COM0A0) | (1 << WGM00) | (1 << WGM01);
//Set on Compare Match, clear on Overflow Fast-PWM (Mode 3 Tab.40 S.75)

/* ist doch zu langsam
TCCR0B = (1 << CS02) | (1 << CS00);
//Prescaler auf CPU/1024 -> langsamer gehts nicht (Tab.41 S. 77)
*/

TCCR0B = (1 << CS01);
//Prescaler auf CPU/8 -> perfekt^^

OCR0A = 1; //Startwert, pendelt dann zwischen 0..255

DDRB = (1 << PB2); //pin PB2 auf Ausgang (das ist OC0A)

uint8_t direction=0; //0->OCR0A raufzählen 1-> OCR0A runterzählen

while(1){
_delay_ms(12);

if(direction==0){
OCR0A += 1;
if(OCR0A==254)
direction=1;
}else
if(direction==1){
OCR0A -= 1;
if(OCR0A==1)
direction=0;
}
}
}


Eigentlich gar nicht so schwer wenn man denn ein bisschen Durchblick hat. Als nächstes werd ich versuchen den Verlauf angenehmer zu Timen, dann muss ich Transistoren finden, die schon bei Versorgung mit Pinspannung (wenn der mit 2 AA Batterien betrieben wird) ohne Widerstand alles durchlassen. (Damit ich steuern welche LED das PWM-Signal bekommt) Und anschließend, wie das Ganze mit einem kleinen Motor funktioniert. Denn mein Plan ist es ein kleines Elektroauto zum Fahren und Leuchten zu bringen.

Wieso liest man überall, dass Phasen-Korrekte PWM am Besten für eine Motorsteuerung sein soll?
Im Prinzip macht doch eine Fast-PWM das Selbe, nur das diese wohl schneller umschalten kann (da man bei
der Phasen Korrekten ja nur eine Umschaltung pro Timerdurchlauf hat und bei der Fast PWM zwei).