PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Atmega8 mit 16Mhz betreiben.



hosti
17.05.2009, 10:28
Guten morgen.

Ich habe einen Atmega 8 den ich mit 16Mhz betreibe.
Das wäre aufhedenfall der Wunsch.



Mit Hilfe des Timers null löse ich alle 0.01ms einen Interrupt aus.


F_CPU = 16000000
timer = (256-F_CPU/8/100000)
TCCR0 |= (1<<CS01) | (!(1<<CS00)) | (!(1CS02));
TCNT0 = timer;
TIMSK |= (1<<TOIE0);
TIFR |= (1<<TOV0);

Setzte den Pin auf High
Anschliessen zähle ich pro Interrupt eine Variable hoch bis 150.
Setzte den Pin auf Low und lösche die Variable.
Das sollte mir ein Rechtecksignal von 1.5ms geben.

Leider stimmts nicht genau(Zeitlich), woran könnte das lieben.

Falsche Fusebit einstellungen?
Ich bin mir dort nicht ganz sicher wie man 16Mhz einstellt.

Danke fürs lesen und mögliche Tipps

EDIT:
Ich glaube mein ATMEGA läuft wirklich nicht auf 16Mhz.
Ich habe ihn jetzt so eingestellt.
16Mhz / 8(Prescaler) = 2Mhz

TCCR |= (1<<CS01);
Ich möchte alle 0.01ms einen Interupt
1s/0.00001 = 100000
2000000 / 100000 = 20
256 (8Bit)-20 = 236 (Vorladen)

TCNT0 = 236;

Wen ich nun den Pin für 150 Interupts auf High lege sollte das doch 1.5ms erben?
Ich kriege aber ein High von 2ms???

Oder mein Oszi stimmt überhaupt nicht mehr, was ich aber nicht glaube.

Besserwessi
17.05.2009, 13:16
Das ist mehr ein Software Problem. Die einstelling des Timers sollte nicht stimmen:
TCCR0 |= (1<<CS01) | (!(1<<CS00)) | (!(1CS02));

sollte TCCR0 = 255 setzen. Das wird wohl kaum gewollt sein.

Gock
17.05.2009, 13:33
Hi!
erstens:
"TCCR0 |= (1<<CS01) | (!(1<<CS00)) | (!(1CS02));"
Das führt nicht zum gewünschten Effekt, da Du mit einem "NOR" die Bits nicht 0 setzen kannst.
Du meinst zB
TCCR0 &= 0b11111010;
Ist aber egal, da die Biits von vornherein ohnehin 0 sind.
Das ist aber auch falsch, da Du einen Prescaler von 0 brauchst und das geht bei Timer 0 so:
TCCR0 &= 0b11111001;
Du willst also nach 150 Takten den T0OVF IRQ auslösen, also:
Timer = 256 - 150;
Das hier:
"timer = (256-F_CPU/8/100000);"
geht auch nicht, wenn es zeitkritisch ist, weil die Berechnung verhältnismäßig lange dauern kann. Sowas wenns geht vermeiden.
Durch 8 teilen sollte man auch eher mit >> 3.
Fusebits sollte man natürlich richtig einstellen, aber da Du nichts nähres beschreibst...
Gruß

hosti
17.05.2009, 15:14
Danke für eure Antworten. ich hätte nicht gedacht, das so viel nicht stimmt.
Dein mein Ausgangssignal stimmt fast, ich bin nur microsekunden von meinem 1.5ms entfernt.

mit timer = (256-16000000/8/100000)
und
TCNT0 = timer
lade ich doch meinen Timer mit 236 vor.
Damit ich alle 0.01ms einen Interupt kriege.

Wie müsste ich den Den Timer einstellen damit ich auf meine 0.01ms käme und wieso bin ich den jetzt nur etwas daneben?

Gock
17.05.2009, 15:42
Achso, Du willst mit einem Prescaler 8 auf 20...ich hatte da doch etwas von 150 gelesen und dann bräuchtest Du einen anderen TCNT0 Wert und Presaler 1. Hab da was korrigiert, aber so ginge es auch.
Wertest Du das hier
timer = (256-16000000/8/100000)
jedemal aus oder nur einmal am Anfang? Startest Du den Timer danach?
Und von welchem Typ ist eigentlich Timer???
Wenn Du den Timer nach der Berechnung startest, entsteht ein Fehler von der Dauer der Berechnungszeit.
Ich glaube diese wenigen Programmfragmente reichen nicht aus, um genaueres zu sagen.
Gruß

hosti
17.05.2009, 15:58
Ich habs jetzt neu so versucht geht aber auch nicht, irgendwas habe ich scheinbar nicht begriffen.


#include <avr/io.h> // I/O Port definitions
#include <avr/interrupt.h> // Interrupt macros

#define F_CPU 16000000

#define timer 96 //0.01ms
//

volatile int ms1 = 0;
volatile int ms2 = 0;
volatile int ms3 = 0;

void timer_init(void)
{
TCCR0 |= (1<<CS00); //Prescaler 1
TCNT0 = timer;
TIMSK |= (1<<TOIE0); //Interupts aktivieren
TIFR |= (1<<TOV0);

};

ISR(TIMER0_OVF_vect)
{

//Endausschlag LINKS
if(ms1>150)
PORTB &= ~(1<<PORTB1);
else
PORTB |= (1<<PORTB1);
if(ms1<2000)
ms1++;
else
ms1 = 0;

TCNT0 = timer;

//Endausschlag MITTE
if(ms2>150)
PORTB &= ~(1<<PORTB2);
else
PORTB |= (1<<PORTB2);
if(ms2<2000)
ms2++;
else
ms2 = 0;

//Endausschlag RECHTS
if(ms3>150)
PORTB &= ~(1<<PORTB3);
else
PORTB |= (1<<PORTB3);
if(ms3<2000)
ms3++;
else
ms3 = 0;
};

int main(void)
{

DDRB |= (1<<PORTB1) | (1<<PORTB2) | (1<<PORTB3); //B... AUSGANG
PORTB &= ~((1<<PORTB1) | (1<<PORTB2) | (1<<PORTB3)); //B.. Low

sei(); //GLOBALE INTERUPTS AKTIVIERT

timer_init(); //FUNKTIONSAUFRUF

}



Ziel des Codes ist es, mir die Ports für 1.5ms auf High zu ziehen und 18.5ms auf Low (Servomittelposition)

Mit dem Wert von 150 wollte ich auf die 1.5ms kommen.
Und dort wo jetzt 96 steht hatte ich die Berrechnung der timer variable

Gock
17.05.2009, 16:16
Das ist das ganze Programm?
Das geht aber ohne Compilerfehler nicht durch, oder?
Prinzipiell fehlt die Endlosschleife while(1) in main und der Rückgabewert return 0.
TCNT0 solltest Du am Anfang der ISR zurücksetzen, sonst bekommst Du 150mal die Rechendauer bis dorthin als Fehler. Größer Vergleiche sollte man vermeiden und lieber größergleich nehmen.
Dass Endsusschlag mitte und Rechts eigentlich das gleiche machen wie links ist Dir klar, oder?
Warum machst Du es DIr eigentlich so kompliziert. Es gibt viele Tutorials, wie man einen Servo an einen OC Ausgang eines AVRs aschlißt. der Rest sind wenige Zeilen Code, wenn man die OutputCompareEinheit nutzt.
Gruß

hosti
17.05.2009, 16:24
Vielen dank für deine Hilfe

Ja das ist alles und funktioniert. Ich habe das programm jetzt angepasst aber die High low stimmen noch immer nicht.
Die Kommentare kannst du ignorieren die sind falsch.

Betreffend Hardware PWM sagt jeder etwas anderes, die meisten sagen Hardware PWM sei nicht zu ansteuerung von Servos geeignet.

Ich verstehe nur nicht was da schief läuft.
Dies Fuses habe ich bei Quarz auf
Ext.Crysatl Res. HighFreq. Startup 16K +64ms
Und CKOPT ist drin

Gock
17.05.2009, 16:38
Den Post versstehe ich nicht.
Hast Du die Codeoptimierung eingeschaltet? Womit progst Du (AVRStudio?), welches Board hast Du und welchen Programmer?
Gruß

Besserwessi
17.05.2009, 16:42
Solche Ausdrücke wie

"timer = (256-F_CPU/8/100000);"

Sind an sich kein Problem. Da sind zwar divisionen drin, aber es sind alles Konstanten und die Berechung macht also schon der Compiler auf dem PC. Das einzige wo man drauf achten muß ist das Runden der zahlen, und das normalerweise mit integer Werten gerechent wird. Ggf. also noch ein L anhängen damit wenigsten Longint benutzt wird für die Rechnung.

Wenn der Timer 16 Bit PWM kann, dann geht Hardware PWM oft recht gut. Wenn die Taktfrequenz ungünstig liegt, dann hat man allerdings nicht besonders viel Auflösung. Bei 16 MHz kann man Prescaler 8 und einen normalen PWM mode (kein fast PWM) nehmen, dann kommt die Frequenz ganz gut hin.

Wenn man das nicht will, kann man wenigstens den Timer Direkt nutzen für die ganze Verzögerung.

hosti
17.05.2009, 16:44
Ich benutze myAVR Workpad und als Progammer mySmartUSB.
Board ist selbsthergestellt. Codeoptimierung hat Workpad nicht

Workpad motz beim von mir geposteten Code nicht.
Ich habe ihn dennoch so abgeändert wie du es vorgeschlagen hat.

Mit meinem Code(Auch mit dem der deine änderungen enthält) bekomme ich momentan ein ausgangssignal von 2ms High und ca 26.5ms Low.
Eigentlich sollte es 1.5ms high und 18.5ms Low sein.

Gock
17.05.2009, 17:14
@Besserwessi
Es war zu Beginn etwas verwirrend, denn Timer hätte auch eine variable sein können. BTW, mit Semikolon ist es keine Präprozessoranweisung... Aber natürlich hast Du Recht, denn jetzt ist es klarer.

@hosti
Kommentier mal alles unnötige aus der ISR. Die wird alle 163 Takte aufgerufen! Möglicherweise gehen hier ein paar IRQs verloren... Dass der Compiler keine Warnungen meldet, wundert mich.
Besser noch: wähle wie oben beschrieben einen Prescaler von 8 und ändere die TCNT0 Werte.
Gruß

hosti
17.05.2009, 17:37
Mein Code sieht nun wie folgt aus, aber das funktioniert gar nicht mehr...
Ich kriegs irgendwie echt nicht hin


#include <avr/io.h> // I/O Port definitions
#include <avr/interrupt.h> // Interrupt macros

#define F_CPU 16000000

#define timer 96 //0.01ms
//

volatile int ms1 = 0;
volatile int ms2 = 0;
volatile int ms3 = 0;

void timer_init(void)
{

TCCR0 |= (1<<CS01); //Prescaler 8
TCNT0 = timer;
TIMSK |= (1<<TOIE0); //Interupts aktivieren
TIFR |= (1<<TOV0);

};

ISR(TIMER0_OVF_vect)
{
TCNT0 = timer;
//Endausschlag LINKS
if(ms1>=150)
PORTB &= ~(1<<PORTB1);
else
PORTB |= (1<<PORTB1);
if(ms1<=2000)
ms1++;
else
ms1 = 0;

TCNT0 = timer;


if(ms2>=150)
PORTB &= ~(1<<PORTB2);
else
PORTB |= (1<<PORTB2);
if(ms2<=2000)
ms2++;
else
ms2 = 0;


if(ms3>=150)
PORTB &= ~(1<<PORTB3);
else
PORTB |= (1<<PORTB3);
if(ms3<=2000)
ms3++;
else
ms3 = 0;
};

int main(void)
{
sei();
timer_init();

DDRB |= (1<<PORTB1) | (1<<PORTB2) | (1<<PORTB3); //B... AUSGANG
PORTB &= ~((1<<PORTB1) | (1<<PORTB2) | (1<<PORTB3)); //B.. Low

//GLOBALE INTERUPTS AKTIVIERT
while(1)
{

}

}

Gock
17.05.2009, 18:09
Probier das:

#include <avr/io.h> // I/O Port definitions
#include <avr/interrupt.h> // Interrupt macros

#define F_CPU 16000000

#define timer 235 //0.01ms

volatile int ms1 = 0;
volatile int ms2 = 0;
volatile int ms3 = 0;

void timer_init(void){
TCNT0 = timer;
TIMSK |= (1<<TOIE0); //Interupts aktivieren
sei();
TCCR0 |= (1<<CS01); //Prescaler 8
}

ISR(TIMER0_OVF_vect){
TCNT0 = timer;
//Endausschlag LINKS
if(ms1>=150){
PORTB &= ~(1<<PORTB1);
if(ms1<=2000)
ms1++;
else
ms1 = 0;
}
else
PORTB |= (1<<PORTB1);
/*
if(ms2>=150)
PORTB &= ~(1<<PORTB2);
else
PORTB |= (1<<PORTB2);
if(ms2<=2000)
ms2++;
else
ms2 = 0;


if(ms3>=150)
PORTB &= ~(1<<PORTB3);
else
PORTB |= (1<<PORTB3);
if(ms3<=2000)
ms3++;
else
ms3 = 0;
*/
}

int main(void){
DDRB |= (1<<PORTB1) | (1<<PORTB2) | (1<<PORTB3); //B... AUSGANG
PORTB &= ~((1<<PORTB1) | (1<<PORTB2) | (1<<PORTB3)); //B.. Low

//GLOBALE INTERUPTS AKTIVIERT
timer_init();
while(1){

}
return 0;
}

hosti
17.05.2009, 18:51
danke für deine bemühungen.
Mit dieser methode passiert leider garnichts. Der Pin wird einfach auf High gelegt(mehr oder weniger)

Gock
17.05.2009, 19:18
Ja sicher, hab ich wohl was Vergessen...

#include <avr/io.h> // I/O Port definitions
#include <avr/interrupt.h> // Interrupt macros

#define F_CPU 16000000

#define timer 238 //0.01ms

volatile int ms1 = 0;
volatile int ms2 = 0;
volatile int ms3 = 0;

void timer_init(void){
TCNT0 = timer;
TIMSK |= (1<<TOIE0); //Interupts aktivieren
sei();
TCCR0 |= (1<<CS01); //Prescaler 8
}

ISR(TIMER0_OVF_vect){
TCNT0 = timer;
//Endausschlag LINKS
if(ms1>=150){
PORTB &= ~(1<<PORTB1);
if(ms1<=3800)
ms1++;
else{
ms1 = 0;
}
}
else
PORTB |= (1<<PORTB1);
ms1++;
/*
if(ms2>=150)
PORTB &= ~(1<<PORTB2);
else
PORTB |= (1<<PORTB2);
if(ms2<=2000)
ms2++;
else
ms2 = 0;


if(ms3>=150)
PORTB &= ~(1<<PORTB3);
else
PORTB |= (1<<PORTB3);
if(ms3<=2000)
ms3++;
else
ms3 = 0;
*/
}

int main(void){
DDRB |= (1<<PORTB1) | (1<<PORTB2) | (1<<PORTB3); //B... AUSGANG
PORTB &= ~((1<<PORTB1) | (1<<PORTB2) | (1<<PORTB3)); //B.. Low

//GLOBALE INTERUPTS AKTIVIERT
timer_init();
while(1){

}
return 0;
}
Jetzt aber.
Die 3800 kannst Du noch etwas optimieren.
Gruß

PS Mit der OCUnit geht es wirklich besser.

hosti
17.05.2009, 19:53
Danke dir, das gibt mir ein gutes signal. Nicht perfekt aber kommt nahe hin.
Nur habe ich auch jetz wieder abweichungen zum theoretischen Zeit Wert.
Also ich meine wen ich in einer bestimmten Zeit Interupts abfange und mir dann berechne das ich meine Variable auf einen bestimmten Wert hochzählen muss um z.B. 1.5ms zu bekommen. Wieso habe ich den praktisch abweichungen?

EDIT:
Betreffen der OCUnit(HardwarePWM?)
Ich hab das schon einmal probiert aber nicht alle 3 PWM's des Atmega sauber zum laufen gekriegt.

hosti
18.05.2009, 16:56
Siehe obiges Edit:

Gock
18.05.2009, 18:52
Hi!
Ja, aber ich hatte gehofft, dass sie jetzt geringer sind. Das wäre zu prüfen.
Für solche Abweichungen kann es viele Möglichkeiten geben: IRQs werden übergangen, weil die ISR zu lange dauert, Timer werden nicht sofort initialisiert (auf gewünschten Wert gesetzt), Resets werden ständig ausgelöst, Quarz ist falsch gewählt oder fehlerhaft.
Für lange ISR sind natürlich der eigene Code verantwortlich aber es hängt auch am Compiler, wieviel Code dieser daraus macht. Daher fragte ich ja auch, ob Du die Optimierung eingeschaltet hast. Der GCC hat davon verschiedene. Probier doch einfach mal den.
Ansonsten ist Deine Lösung leider wirklich nicht zu empfehlen.
Wenn man eine PWM per Hardware hinbekommt, dann sind die anderen nur noch Makulatur, weil sie identisch sind. Gut, es gibt Unterschiede zwischen 8 und 16Bit Countern.
Gruß

hosti
21.05.2009, 09:15
Ich finde einfach komisch das der code auf einem Atmega644 mit 16Mhz perfekt funktioniert hat und auf dem atmega8 nicht mehr.
Klingt für mich einfach irgendwie nach falsch eingestelltem Quarz