Forum: Mikrocontroller und Digitale Elektronik timer interrupt attiny45


von Marc (Gast)


Lesenswert?

Hallo,
ich wollte einen Servo mit einem attiny45 und den timer zum laufen 
bringen. Da habe ich vorher allerdings schon probleme.
Zunächst mal mein Code
1
#define F_CPU 8000000UL
2
#include <avr/interrupt.h>
3
#include <avr/io.h>
4
5
void init();
6
void setup();
7
8
int zaehler;
9
10
11
int main(void) {
12
  init();
13
  setup();
14
  while(1) 
15
  {
16
  }
17
}
18
19
void init(){
20
  DDRB |= (1<<PB0);  //PB0 -> Ausgang  
21
  PORTB &= ~(1<<PB0);  // PB0 -> low 
22
  DDRB |= (1 << PB1);  // PB1 = Output
23
  PORTB &= ~(1<<PB1);  // PB1 -> low
24
  
25
} 
26
27
void setup(){
28
  TIMSK |= (1<<TOIE0);  // Interrupt beim Overflos auslösen
29
  TCCR0A |= (1 << COM0B1) | (1 << WGM01) | (1 << WGM00);   // COM0B1 -> Clear OC0A/OC0B on Compare Match,
30
                                  // set OC0A/OC0B at BOTTOM (non-inverting mode)
31
                                // WGM01/ WGM00 -> Mode 3 Fast PWM
32
  TCCR0B |= (1 << WGM02) | (1 << CS01); // prescale 8 -> 8000000/8/256 = 3906,25 Hz -> Interrupt alle 0.256ms (1000/3906)
33
  sei();    // Interrupts freigeben
34
}
35
36
ISR(TIM0_OVF_vect){
37
  zaehler++;
38
  if (zaehler>=1953){ //->0,5 Sekunden
39
    zaehler = 0; // 
40
  PORTB ^= 1<<PB0;
41
  }
42
}


zunächst wollte ich erst einmal testen,ob der den Interrupt macht und 
eine LED anschalten. Dazu sollte er alle 0,256ms den Interrupt auslösen 
und alle 0,5s  ms die LED toggeln. Diese bleibt allerdings aus.

Eine Warnung bekomme ich
../interrupt.c:38:1: warning: 'TIM0_OVF_vect' appears to be a misspelled 
signal handler [enabled by default]

damit kann ich aber irgendwie nichts Anfangen.

Wäre cool, wenn da mal jemand rüber gucken könnte, wo genau der Fehler 
ist.

Marc

von Marc (Gast)


Lesenswert?

Also die Warnung habe ich doch herausgefunden.
musste
1
ISR(TIMER0_OVF_vect){
heissen

von Peter II (Gast)


Lesenswert?

Marc schrieb:
> damit kann ich aber irgendwie nichts Anfangen.

der Name TIM0_OVF_vect wird falsch sein. Zur not mal in dem headerfile 
suche wir er richtig ist.

von M. K. (sylaina)


Lesenswert?

Richtig, du hast den falschen Namen genommen.
Noch ein Tipp: Im Moment arbeitest du mit dem Timer Overflow Interrupt. 
Schau dir mal den Timer Compare Match Interrupt an. Du kannst hier eine 
Variable sparen und dein Code würde kleiner werden:
1
ISR(TIMER0_COMPA_vect){
2
    PORTB ^= 1 << PB0;
3
}

wäre das einzige, was in deiner Interruptroutine steht. Du müsstest nur 
das Compare-Register entsprechend vorladen in der Initialisierung ;)

von Bretze (Gast)


Lesenswert?

Marc schrieb:
>
1
>   TCCR0B |= (1 << WGM02) | (1 << CS01); // prescale 8 -> 8000000/8/256 = 3906,25 Hz -> Interrupt alle 0.256ms (1000/3906)
2
>

Mit dem isses nicht mehr mode 3 und OCR0A müßte noch gesetzt werden. 
Eher wohl das WGM02 NICHT setzen, dann klappt auch der OVF Interrupt

von Marc (Gast)


Lesenswert?

Das mit der falschen Benennung hatte ich ja schon oben gefunden. aber 
danke noch mal.

@ Michael. Ich möchte ja später einen Servo-Motor ansteuern. 
funktioniert das dann mit dem Timer Compare Match Interrupt immer noch?

Werde ich mir aber mal angucken

Marc

von Marc (Gast)


Lesenswert?

@ Bretze
ja, auch das war falsch. nur WGM02 ist reserviert.

Habe jetzt
1
  TCCR0B |= (1 << WGM02) | (1 << WGM00) | (1 << CS01); // prescale 8 -> 8000000/8/256 = 3906,25 Hz -> Interrupt alle 0.256ms (1000/3906)
2
  OCR0A = 1; // Timer zählt 78 Takte = 20ms und springt danach wieder auf 0

Im datenblat steht bei der Tabelle für PWM-Mode (bei WGM02 und WGM00)
Update of
OCRx at

heisst das, dass der zähler immer bei den Wert anfängt, den ich da 
setze? also hier 1?

leuchten tut aber leider immer noch nichts.

Marc

von M. K. (sylaina)


Lesenswert?

Marc schrieb:
> @ Michael. Ich möchte ja später einen Servo-Motor ansteuern.
> funktioniert das dann mit dem Timer Compare Match Interrupt immer noch?

Klar, warum sollte es nicht funktionieren? Ich weiß ja nicht genau wie 
du den Motor steuern willst aber denk dran: Du hast zwei Timer und 
kannst beide benutzen. Das Timing muss nur stimmen. Wenn dein Tiny mit 8 
MHz läuft und du den einen Timer brauchst um PB0 alle 500 ms zu toggeln 
wirst du auch den Systemtakt runterteilen müssen um nicht irgendwo die 
Auslösungen in der ISR zu zählen. Die Idee mit dem "eigenen" Zähler ist 
zwar ganz nett aber kein schöner Stil. Daher sollte man immer schaun ob 
es nicht ohne einen solchen Zähler lösbar ist.
Oder, ich sags mal anders: Ich kenne viele Menschen die ihren µC gerne 
mit 16 MHz laufen lassen, dabei genügt es in ihrer Anwendung wenn der µC 
mit 100 kHz liefe.
Das ist eines der ersten Dinge, die du prüfen solltest: Wie schnell muss 
dein µC reagieren können? Das gibt dein Systemtakt vor und nichts 
anderes.

von Bretze (Gast)


Lesenswert?

Marc schrieb:
> @ Bretze
> ja, auch das war falsch. nur WGM02 ist reserviert.

??? WGM02 ist nicht reserviert!?

>
> Habe jetzt
>
>
1
  TCCR0B |= (1 << WGM02) | (1 << WGM00) | (1 << CS01); // prescale 
2
> 8 -> 8000000/8/256 = 3906,25 Hz -> Interrupt alle 0.256ms (1000/3906)
3
>   OCR0A = 1; // Timer zählt 78 Takte = 20ms und springt danach wieder 
4
> auf 0

WGM00 ist nicht im TCCR0B


>
> Im datenblat steht bei der Tabelle für PWM-Mode (bei WGM02 und WGM00)
> Update of
> OCRx at
>
> heisst das, dass der zähler immer bei den Wert anfängt, den ich da
> setze? also hier 1?

Nein. Es gibt die Tabelle mit den Modes und welche Bits dafür gesetzt 
werden müssen. In der Tabelle bedeutet "Update of OCRx ..", daß das 
Register (OCR0A, OCR0B)bei entsprechendem TCNT0-Stand aktualisiert wird. 
Das Programm kann jederzeit hineinschreiben, effektiv wird das dann aber 
erst zu den entsprechenden TCNT Ständen. Wird also gepuffert. Siehe 
entsprechende Erklärungen im Datenblatt. Vorteil zB bei Nutzung der 
HW-PWM Ausgänge um keine Glitches aufkommen zu lassen.

von Marc (Gast)


Lesenswert?

Ich habe da noch einmal eine Nachfrage.
Ich könnte ja die frequenz des Attiny langsamer machen.
Ist das richtig, dass ich den Internen Oszillator nur mit 8MHz oder 
6,4MHz schwingen lassen kann?
http://www.engbedded.com/fusecalc/

wollte das dann wie hier
http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial/Die_Timer_und_Z%C3%A4hler_des_AVR
in CTC Clear Timer on Compare Match (Auto Reload)
machen.dann würde ich die Frequezn auf 6,4MHz setzen , den Prescaler auf 
1024 und OCR0A = 125.
Dann käme ich genau auf 20 ms, die ich dann den overflow aufrufen würde 
und den pin auf high stellen würde.
mit den zweiten timer (Den ich mir noch angucken muss) würde ich den pin 
je nach sollwinkel des servos nach 1,5 bis 2,5 ms wieder zurücksetzen.

Marc

von Marc (Gast)


Lesenswert?

Direkt noch eine Frage hätte ich,
wie muss ich denn genau die fuses setzen? da sind ja jetzt vier mal 
6,4MHz angegeben.
Ist das egal welchen von denen ich nehme?

von Marc (Gast)


Lesenswert?

Eine letzte frage für heute noch.
Habe das Programm nach dem Beispiel nachgebaut. Folgendes ist anders bei 
mir:
- mein Attiny läuft mit 8MHz
- damit ich sehe, ob der das funktioniert, lasse ich den zähler in der 
ISR-   Routine  250 mal hochzählen um die led an und aus zu schalten. 
Später möchte ich (wie oben erwähnt) alle 20ms den pin für den servo auf 
high schalten

allerdings bleibt auch hier die LED
aus

[code]
#define F_CPU 8000000UL
#include <avr/interrupt.h>
#include <avr/io.h>

void init();
void setup();

int zaehler;


int main(void) {
  init();
  setup();
  while(1)
  {
  }
}

void init(){
  DDRB |= (1<<PB0);  //PB0 -> Ausgang
  PORTB &= ~(1<<PB0);  // PB0 -> low
  DDRB |= (1 << PB1);  // PB1 = Output
  PORTB &= ~(1<<PB1);  // PB1 -> low

}

void setup(){
  TCCR0A |= (1 << WGM01); // CTC Mode
  TCCR0B |= (1 << CS02); // prescale 256
  OCR0A = 125-1;
  TIMSK |= (1<<OCIE0A);
  sei();    // Interrupts freigeben
}

//ISR(TIMER0_OVF_vect){ // alle 4 ms
ISR(TIMER0_COMPA_vect){
  zaehler++;
  if (zaehler>=250){// -> 1s
    zaehler = 0;
  PORTB ^= 1<<PB0;
  }
}


Was ist denn jetzt noch verkehrt?

von Marc (Gast)


Lesenswert?

den ISR habe ich wie im Beispeil in
TIMER0_COMPA_vect
geändert.
allerdings keine Verbesserung

von Bretzel (Gast)


Lesenswert?

Marc schrieb:
> Was ist denn jetzt noch verkehrt?

Scheint mir alles richtig zu sein. Auch die Sekunde stimmt.

Wie ist die LED angeschlossen? Vorwiderstand? Wieviel Strom braucht die? 
Programmer insbesondere von PB0 abgezogen?

von M. K. (sylaina)


Lesenswert?

Auf alles gehe ich nicht ein, da musst du dich noch mal genau einlesen. 
Schau hier mal in die verschiedenen Artikel auf der Seite.

Zum Systemtakt

Nur mal so angenommen du verwendest den internen RC-Oszilator mit 8 MHz. 
Das kann man machen und ist nicht unbedingt verkehrt. Dann hast du einen 
Systemtakt-Prescaler, der kann den Takt des Taktgebers runterteilen und 
zwar um die Faktoren 2, 4, 8, 16, 32, 64, 256 und 1024. Das steht aber 
im Datenblatt. Nehmen wir mal an du nimmst den 256er dann läuft dein 
ATTiny mit 8/256 MHz.
Jetzt musst du dir den Timer anschauen. Der kann mit dem Systemtakt 
laufen oder auch den Systemtakt nochmal runter teilen um 8, 64, 256 oder 
1024. Wählst du da nun den 8er stellst du fest, dass du OCR0A mit 1953 
(genau: 1953,125 aber Kommastellen gehen ja nicht, wir bauen uns also 
einen Fehler ein) vorladen. Das passt da natürlich nicht rein, OCR0A ist 
ja nur 8 bit groß. Nun kannst du entweder den Prescaler für den 
Systemtakt ändern oder für den Timer. Der für den Systemtakt ist schon 
auf maximum, somit bleibt hier nur der für den Timer. Den auf 64 
geändert ergibt, dass du für OCR0A auf 244 (genau: 244,141) vorladen 
musst. Das passt rein, würde also gehen.
Für OCR0A gilt: Je größer der Wert desto kleiner der Fehler der durch 
die wegfallenden Nachkommastellen entsteht. Du könntest den Prescaler 
des Timers ja auch auf 1024 einstellen, OCR0A ist dann nur noch 15 
(genauer: 15,259).

Lies dich unbedingt da mal genau ein.

von c-hater (Gast)


Lesenswert?

Marc schrieb:

> Ich könnte ja die frequenz des Attiny langsamer machen.
> Ist das richtig, dass ich den Internen Oszillator nur mit 8MHz oder
> 6,4MHz schwingen lassen kann?

Nein. Beim Tiny45 hast du eine reiche Auswahl möglicher interner Takte.

Grundsätzlich stehen erstmal zwei interne Quellen zur Verfügungen, ein 
128kHz-Oszillator und ein 8MHz-Oszillator. Der Vorteil des 
128kHz-Oszillators ist, daß er selber sehr wenig Strom benötigt, der 
Vorteil des 8MHz-Oszillators ist, daß er sich relativ genau auf eine 
Sollfrequenz in einem relativ weiten Frequenzbereich abgleichen läßt.

Zusätzlich ist es möglich, eine PLL zur effektiven Taktverdoppelung 
des 8MHz-Oszillators zu verwenden.

Zusätzlich kannst du alle bisher genannten Takte mittels CKDIV-Fuse 
auch noch durch 8 teilen lassen.

Schon rein nominell hast du also folgende interne Taktoptionen:

16MHz(*)
8MHz(*)
2MHz(*)
1MHz(*)
128kHz
16kHz

Die mit (*) gekennzeichneten Optionen basieren allesamt auf dem 
8MHz-Oszillator als Taktquelle und können deshalb darüber hinaus noch 
über die Kalibrierung dieses Oszillators "stufenlos" weiter variiert 
werden, +-25% des Nominalwertes sind auf jeden Fall ziemlich problemlos 
drin, mit Rücksicht auf bestimmte Randbedingungen auch mehr.

> http://www.engbedded.com/fusecalc/

Du mußt da schon den richtigen Tiny wählen, sonst rechnet das Teil 
natürlich nach dem bekannten Grundgesetz der Informatik "shit in->shit 
out" nur Quatsch aus. Viel besser ist aber, zuerst mal das Datenblatt zu 
lesen und zu verstehen, um einen Überblick darüber zu gewinnen, was 
eigentlich alles möglich ist. Das erleichtert nicht nur die Benutzung 
des Fusecalc ungemein, sondern versetzt dich auch in die Lage, dessen 
Ergebnisse zu überprüfen, BEVOR du dich durch eventuell falsche 
Fuse-Settings "aussperrst".

> wollte das dann wie hier
> 
http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial/Die_Timer_und_Z%C3%A4hler_des_AVR
> in CTC Clear Timer on Compare Match (Auto Reload)
> machen.

Das ist doch Quatsch. Du wolltest einen Servo ansteuern, dafür ist PWM 
die beste Option, nicht CTC. Und du brauchst da auch keine ISR dafür, 
das kann der Timer alles ganz alleine. Der einzige Nachteil ist, daß du 
ganz bestimmte Pins als Ausgänge verwenden mußt, weil der Timer sein 
selbstständig erzeugtes Signal nur an genau diesen speziellen Pins nach 
außen absondern kann. (Jedenfalls ist das bei den meisten AVRs noch so, 
bei einigen neueren aber schon nicht mehr, da kann man das Signal zu 
verschiedenen Pins "routen".)

Das A und O ist in jedem Fall: Lesen und Verstehen des Datenblatts!

> mit den zweiten timer (Den ich mir noch angucken muss) würde ich den pin
> je nach sollwinkel des servos nach 1,5 bis 2,5 ms wieder zurücksetzen.

Das ist noch mehr Quatsch. Du brauchst immer nur einen Timer, selbst für 
dein nutzloses Software-Gefrickel.

Besser ist, wie gesagt, den Timer in einem PWM-Modus laufen zu lassen 
(Fast-PWM ist am einfachsten zu verstehen), dann macht er die ganze 
Arbeit alleine.

Den Timer versorgst du mit einem solchen Basistakt, daß der volle 
Zählumfang des Timers in ungefähr 20ms durchlaufen ist. Hier kommt es 
nicht besonders auf Genauigkeit an, wenn es nur 16 oder auch 24ms sind, 
geht das meist ganz genauso gut.

Den eigentlichen Stellwert des Servo erzeugst du dann über die 
OCRxn-Register des Timers. Darüber bestimmst du den Anteil der 
Gesamtzeit eines Zyklus, in dem das Ausgangssignal Low ist. Und der muß 
dann eben so ca. 1..2ms betragen.

von Walter S. (avatar)


Lesenswert?

Michael Köhler schrieb:
> Dann hast du einen
> Systemtakt-Prescaler, der kann den Takt des Taktgebers runterteilen und
> zwar um die Faktoren 2, 4, 8, 16, 32, 64, 256 und 1024. Das steht aber
> im Datenblatt. Nehmen wir mal an du nimmst den 256er dann läuft dein
> ATTiny mit 8/256 MHz.

: Bearbeitet durch User
von Stephan (Gast)


Lesenswert?

Erst einmal danke c-hater für deine ausführliche erklärung. Habe meinen 
Code noch einmal komplett überarbeitet. Jetzt benötige ich auch die 
ISR-Funktion nicht mehr. Allerdings habe ich noch eine kleine 
verständisschwierigkeit
1
#define F_CPU 8000000UL
2
#include <avr/interrupt.h>
3
#include <avr/io.h>
4
5
void init();
6
void setup();
7
8
9
int main(void) {
10
  init();
11
  setup();
12
  while(1) 
13
  {
14
  }
15
}
16
17
void init(){
18
  DDRB |= (1<<PB0);      //PB0 -> Ausgang  
19
  PORTB &= ~(1<<PB0);    // PB0 -> low 
20
  DDRB |= (1 << PB1);    // PB1 = Output
21
  PORTB &= ~(1<<PB1);    // PB1 -> low
22
  
23
} 
24
25
void setup(){
26
  TCCR0A |= (1 << COM0A1) | (1 << WGM01) | (1 << WGM00);  
27
    // (1 << COM0A1 -> set OC0A at BOTTOM (non-inverting mode)
28
  // (1 << WGM01) | (1 << WGM00) -> Fast PWM, TOP OCR0A (in TCCR0B)
29
30
  TCCR0B |= (1 << WGM02) | (1 << CS02) | (1 << CS00);  // prescale 1024
31
  OCR0A = 157;       // 1/(8MHz)*1024*157 = 20,096ms
32
  TIMSK |= (1 << OCIE0A);    // Compare Match Interrupt aktiviert
33
  sei();          // Interrupts freigeben
34
}

mit (1 << COM0A1) sage ich, dass er (wenn er bei null anfängt zu zählen) 
OC0A auf high setzen soll.
mit (1 << WGM02) (in TCCR0B) und (1 << WGM01) | (1 << WGM00) (in TCCR0A) 
sage ich, dass es FastPWM sein soll und der counter nur bis OCR0A zählen 
soll.
mit OCR0A=157 komme ich auf eine Periode von ziemlich genau 20ms.

mein Problem ist jetz, dass ich nicht weiss, womit ich sagen kann, dass 
er bei einem bestimmten Wert OC0A auf Low setzen soll.

Das einzige was ich gefunden habe, war das man dafür OCR0A verwendet. 
Wenn ich nur WGM01 und WGM00 setzen würde, hätte ich OCR0A frei. 
Allerdings finde ich dann keine gescheite Kombination um die 
Periodendauer nur in der nähe von 20 ms zu bekommen.

Gibt es also noch ein Register, mit dem ich sagen kann, wann die PWM auf 
wieder auf LOW gesetzt wird?

von Walter S. (avatar)


Lesenswert?

wer A sagt muss auch B sagen ;-)

es gibt neben OCR0A auch OCR0B

: Bearbeitet durch User
von Marc (Gast)


Lesenswert?

Ich denke OCR0B ist für OC0B, also für einen andere Pin. So verstehe ich 
das zumindestens aus dem Datenblatt.
Ich müsste anstelle von OCR0A -> TCNT0 setzen. Dann würde er TCNT0 
wieder auf HIGH springen und bei OCR0A auf LOW
1
#define F_CPU 8000000UL
2
#include <avr/interrupt.h>
3
#include <avr/io.h>
4
#include <util/delay.h>
5
6
void init();
7
void setup();
8
9
volatile uint8_t i;
10
11
int main(void) {
12
  i = 0;
13
  init();
14
  setup();
15
  while(1) 
16
  {
17
    for (i=0; i<150; i++){
18
    OCR0B = i;
19
    _delay_ms(5);
20
  }
21
  for(i=150; i>0; i--){
22
    OCR0B = i;
23
    _delay_ms(5);
24
    
25
  }
26
  }
27
}
28
29
void init(){
30
  DDRB |= (1<<PB0);      //PB0 -> Ausgang  
31
  PORTB &= ~(1<<PB0);    // PB0 -> low 
32
  DDRB |= (1 << PB1);    // PB1 = Output
33
  PORTB &= ~(1<<PB1);    // PB1 -> low
34
  
35
} 
36
37
void setup(){
38
  TCCR0A |= (1 << COM0A1) | (1 << WGM01) | (1 << WGM00);  
39
    // (1 << COM0A1 -> set OC0A at BOTTOM (non-inverting mode)
40
  // (1 << WGM01) | (1 << WGM00) -> Fast PWM, TOP OCR0A (in TCCR0B)
41
42
  TCCR0B |= (1 << WGM02) | (1 << CS02) | (1 << CS00);  // prescale 1024
43
  TCNT0 = 157;
44
  OCR0A = 0;       // 1/(8MHz)*1024*157 = 20,096ms
45
  TIMSK |= (1 << OCIE0A) | (1 << TOIE0);    // Compare Interrupt erlauben
46
  sei();          // Interrupts freigeben
47
}

Da ich nicht weiss, ob das mit dem Servo funktioniert, probiere ich es 
erst mit der LED aus. Die sollte ja jetzt eigentlich von dunkel nach 
hell aufleuchten und anschließend wieder von hell nach dunkel.
Aber auch hier leuchtet nichts.

HAbe es auch mit OCR0B auprobiert gehabt
1
OCR0A = 157;
2
OCR0B = i;
aber auch hier tat sich nichts

MArc

von MWS (Gast)


Lesenswert?

Du erlaubst Overflow- Und Compare-Interrupts ohne passende ISRs, so wird 
das nix, das ergibt zyklische Resets. Außerdem sollte man beim 
erstmaligen Beschreiben eines Registers die Werte direkt zuweisen und 
nicht verodern.

von Marc (Gast)


Lesenswert?

Habe jetzt nur noch
1
TIMSK |= (1 << OCIE0A)
da stehen und im setup
[cdoe]OCR0A = 0; [/code]
aber auch so tut sich nichts.
wieso darf ich denn im setup nicht
[cdoe]OCR0A = i; [/code]
stehen haben? i habe ich doch vorher auf 0 gesetzt.

von Bretze (Gast)


Lesenswert?

Marc schrieb:
> Habe jetzt nur noch
> da stehen und im setup
>
1
TIMSK |= (1 << OCIE0A)

Wozu? Es gibt doch keine ISR dazu.

> [cdoe]OCR0A = 0; [/code]
> [cdoe]OCR0A = i; [/code]

Mit Kopieren und probieren wird das nichts. Abgesehen davon verstehe ich 
die Frage nicht. OCR0A darf durchaus mit Zuwesisung durch eine Variable 
gesetzt werden.

Schau nochmal was MWS geschrieben hat und versuch anhand des Dateblattes 
zu verstehen, was da mit dem Timer passiert.

Hat die LED überhaupt schon mal Lebenszeichen von sich gegeben

von c-hater (Gast)


Lesenswert?

Stephan schrieb:

> mit (1 << COM0A1) sage ich, dass er (wenn er bei null anfängt zu zählen)
> OC0A auf high setzen soll.
> mit (1 << WGM02) (in TCCR0B) und (1 << WGM01) | (1 << WGM00) (in TCCR0A)
> sage ich, dass es FastPWM sein soll und der counter nur bis OCR0A zählen
> soll.
> mit OCR0A=157 komme ich auf eine Periode von ziemlich genau 20ms.
>
> mein Problem ist jetz, dass ich nicht weiss, womit ich sagen kann, dass
> er bei einem bestimmten Wert OC0A auf Low setzen soll.

Die Antwort ist einfach: garnicht. Da OCR0A im Mode 7 die Zykluszeit 
bestimmt, kann man damit KEIN PWM-Signal erzeugen. Dafür steht dann 
nur noch OCR0B zur Verfügung. Also mußt du nicht den OC0A-Ausgang 
freischalten und benutzen, sondern den OC0B-Ausgang und zur Steuerung 
der PWM auch das OCR0B-Register verwenden.

Alternativ kannst du den Timermode 3 verwenden, bei dem die Zykluszeit 
allein vom Zählumfang des Timers bestimmt wird. Da im relevanten Bereich 
aber nur die Prescaler 1024 und 256 zur Verfügung stehen, mußt du dann 
die PLL-Taktoption mit 16MHz benutzen. Dann kommst du mit Prescaler 256 
auf eine Zykluszeit von 16ms, was OK ist. Der Vorteil ist hier, daß du 
sowohl OCR0A als auch OCR0B als PWM-Kanal verwenden kannst, also zwei 
Servos steuern kannst. Außerdem hast du im relevanten Bereich mehr 
Schritte zur Verfügung, kannst die Servos also auch noch feiner 
ansteuern.

Um bei 8MHz Takt bleiben zu können, wäre eine weitere Alternative der 
Timermodus 1, also PhasecorrectPWM ohne Limit. Da bei Phasecorrect hoch- 
und runtergezählt wird, halbiert sich die effektiv die Frequenz. Damit 
kannst du Prescaler 1024 verwenden und kommst damit bei 8MHz auf 
ebenfalls 16ms Zykluszeit. Auch hier stehen dann zwei PWM-Kanäle zur 
Verfügung.

von c-hater (Gast)


Lesenswert?

c-hater schrieb:

> kannst du Prescaler 1024 verwenden und kommst damit bei 8MHz auf
> ebenfalls 16ms Zykluszeit.

Und wenn du glaubst, unbedingt auf 20ms Zykluszeit kommen zu müssen 
(mußt du nicht), dann kannst du in beiden Varianten den 8MHz-Oszillator 
knapp ca. 25% langsamer, also mit gut 6MHz laufen lassen. Um exakt 20ms 
zu erreichen, wäre der Wert 6,5536MHz. Der passende Kalibrierwert dafür 
läge so ungefähr bei 70.

von Marc (Gast)


Lesenswert?

Es will einfach nicht funktionieren.
1
void setup(){
2
  TCCR0A |= (1 << COM0A1) | (1 << WGM01) | (1 << WGM00);  
3
    // (1 << COM0A1 -> set OC0A at BOTTOM (non-inverting mode)
4
  // (1 << WGM01) | (1 << WGM00) -> Fast PWM, TOP 0xFF
5
6
  TCCR0B |=  (1 << CS02) | (1 << CS00);  // prescale 1024  1/(16MHz)*1024*256 = 16,38 ms
7
  OCR0A = 256-1; 
8
  sei();          // Interrupts freigeben
9
}

fuse
lfuse 61
hfuse DF
efuse FF

damit sollte er mit 16MHz sein.

Auch habe ich am anfang vom programm
1
#define F_CPU 16000000UL
geändert.

Eigentlich habe ich jetzt gedacht, dass ich mit 1<<COM0A -> OC0A bei 
null auf High gesetzt wird und sobald OCR0A erreicht ist wird OC0A auf 
Low gesetzt. Daher habe ich OCR0A auf 255. Also eigentlich sollte jetzt 
die ganze zeit an sein. Das teste ich wieder mit der LED. aber diese 
Bleibt aus. Auch wenn ich OCR0A=0 setze, passiert nichts. Auch wenn ich 
den Servo anschließe tut sich gar nichts.
In der while-schleife in der main-funktion habe ich nichts stehen.
Die LED nicht kaputt. Das habe ich schon getestet

von c-hater (Gast)


Lesenswert?

Marc schrieb:
> Es will einfach nicht funktionieren.

>
1
> void setup(){
2
>   TCCR0A |= (1 << COM0A1) | (1 << WGM01) | (1 << WGM00);
3
>     // (1 << COM0A1 -> set OC0A at BOTTOM (non-inverting mode)
4
>   // (1 << WGM01) | (1 << WGM00) -> Fast PWM, TOP 0xFF
5
> 
6
>   TCCR0B |=  (1 << CS02) | (1 << CS00);  // prescale 1024 
7
> 1/(16MHz)*1024*256 = 16,38 ms
8
>   OCR0A = 256-1;
9
>   sei();          // Interrupts freigeben
10
> }

Das ist korrekt, soweit es die Initialisierung des Timers betrifft. Es 
fehlt aber noch eine Sache: Du mußt den OC0A-Pin (also PB0) auf Ausgang 
schalten.

> fuse
> lfuse 61
> hfuse DF
> efuse FF
>
> damit sollte er mit 16MHz sein.

Nein, das ergibt 2MHz.

von Marc (Gast)


Lesenswert?

den PB0 setze ich in meiner init auf ausgang
1
void init(){
2
  DDRB |= (1<<PB0);      //PB0 -> Ausgang  
3
  PORTB &= ~(1<<PB0);    // PB0 -> low 
4
  DDRB |= (1 << PB1);    // PB1 = Output
5
  PORTB &= ~(1<<PB1);    // PB1 -> low
6
}

aber danke für den Hinweis.

mit den fuses muss ich dann auch noch mal angucken. das habe ich ja 
anscheinend auch falsch verstanden

von Marc (Gast)


Lesenswert?

Ich habe noch einmal eine Nachfrage.
Die lfuse habe ich auf 0x6E gesetzt. jetzt habe ich das Problem, dass 
ich den code nicht mehr überspielen kann.
im command-window gebe ich ein
avrdude -c STK500v2 -P COM5 -p t45 -B 9 -U flash:w:servo.hex

als fehlermeldung bekomme ich
avrdude: stk500v2_command<>: command failed
ardude: initialization failed, rc=-1

habe gelesen, dass es an der bitclock (-B) liegen kann. diesen habe ich 
einmal auf 1 gesetzt und hoch bis auf 250. Aber diese Fehlermeldung 
kommt immer wieder.

Davor habe ich die Programme immer mit den oben genannten Befehl 
überspielt. Also muss ich wohl mit den lfuse etwas falsch gemacht haben.

von MWS (Gast)


Lesenswert?

Marc schrieb:
> Die lfuse habe ich auf 0x6E gesetzt.

http://www.engbedded.com/fusecalc

Da sollte also jetzt ein externer Quarz dran sein. Ist das nicht der 
Fall, ist kein Takt vorhanden = vorübergehend tot.

Entweder Quarz dranhängen, oder zum Programmieren an CLKI/XTAL1 einen 
Takt zuführen.

von Marc (Gast)


Lesenswert?

ach sch.. zum heulen.
dann muss ich das erst mal zur Seite legen und später weiter machen. 
Habe auf http://www.klaus-leidinger.de/mp/Mikrocontroller/index.html
auch eine Anleitung gefunden, womit ich das wieder richten kann (hoffe 
ich zumindestens).
Trotzdem möchte ich mich hier schon einmal bei euch allen bedanken. Ohne 
euch (speziell c-hater aber auch alle anderen) hätte ich es nicht so 
weit geschafft.
Besten dank
Marc

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.