Guten Abend!
Ich versuche gerade ein C Programm zu schreiben, in dem brauche ich den
Analog Comparator Interrupt sowie den Timer 1 Interrupt, das Problem
ist, der Timer 1 Interrupt wird ca. alle 25µs aufgerufen, und der Analog
Comparator Interrupt ca. alle 500µs.
Das Problem ist, wenn während des Interrupts vom Analog Comparator der
Timer Interrupt auslöst, geht mir ein Timer Interrupt verloren, und ich
weiß nicht wie ich das ändern kann. Denn in meinem Timer Interrupt wird
jedesmal ein Portpin getoggelt, und demnach wenn er ein Toggle verliert,
ist plötzlich aus 10% duty cycle 90% duty cycle geworden.
Weiß jemand was abhilfe schafft?
Mit freundlichen Gruß
Was passiert denn alles in den ISR? (also wie viel Rechenzeit wird dort
gebraucht?)
Eventuell könntest du in der Konparator-ISR die Interrupts wieder
freigeben, damit die Timer-ISR die Konparator-ISR unterbrechen kann.
Sowas ist allerdings mit Vorsicht zu genießen, falls der
Konparator-Interrupt doch mal öfter ausgelöst wird und sich dann selbst
unterbricht.
Also in dem Komparatorinterrupt wird ein switch case ausgeführt. mit 6
cases.
Im Timer Interrupt ist es etwas Umfangreicher undzwar, gibts 2 große if
Abfragen ( ob die variable die getoggelt wird 1 oder 0 ist ) und in
beiden Fällen wird wieder ein switch case mit 6 cases ausgeführt.
>Denn in meinem Timer Interrupt wird>jedesmal ein Portpin getoggelt, und demnach wenn er ein Toggle verliert,>ist plötzlich aus 10% duty cycle 90% duty cycle geworden.
Schon mal nach Output-Compare (OC...) geguckt?
Da macht der Timer das Pintogglen je nach Einstellung ganz von alleine
in Hardware. Da geht nichts verloren. Geht aber auch nur mit bestimmten
Portpins.
>Im Timer Interrupt ist es etwas Umfangreicher undzwar, gibts 2 große if>Abfragen ( ob die variable die getoggelt wird 1 oder 0 ist ) und in>beiden Fällen wird wieder ein switch case mit 6 cases ausgeführt.
Hatte ich überlesen...
Zeig mal deinen Code, und das Rumgestocher hat ein Ende.
B. L. schrieb:
> Hab nen AtMega8 µC der läuft auf 8MHz internen RC Oszillator.> Das Problem ist, ich brauche eine variable die 8000 mal pro Sekunde> umschaltet. Und das kann ich ja nicht mit dem Output Compare machen.
D.h. du hast minimal 1000 Takte Zeit, ehe ein Timer Interrupt verloren
geht
Das ist eine Menge Holz! Was machst du in deinem Compare Interrupt, dass
sich das nicht ausgeht?
> Also in dem Komparatorinterrupt wird ein switch case> ausgeführt. mit 6 cases.
Am switch wirds ja nicht liegen. Was passiert ín den cases?
Beschreibe nicht deine Code! Zeige ihn!
Also momentan ist in meinem Timer Interrupt ja nur der Toggle des
Ausgangspins, also liegts hauptsächlich an dem Komparatorinterrupt der
so aussieht:
1
charzahl=0;
2
3
ISR(ANA_COMP_vect)
4
5
{
6
7
switch(zahl)
8
9
{case0:ACSR=0b00001010;//fallende flanke
10
MotorStep(1);
11
ADMUX=5;//phase C rückkopplung
12
break;
13
case1:ACSR=0b00001011;//steigende flanke
14
MotorStep(2);
15
ADMUX=0;//phase B rückkopplung
16
break;
17
case2:ACSR=0b00001010;//fallende flanke
18
MotorStep(3);
19
ADMUX=1;//phase A rückkopplung
20
break;
21
case3:ACSR=0b00001011;//steigende flanke
22
MotorStep(4);
23
ADMUX=5;//phase C rückkopplung
24
break;
25
case4:ACSR=0b00001010;//fallende flanke
26
MotorStep(5);
27
ADMUX=0;//phase B rückkopplung
28
break;
29
case5:ACSR=0b00001011;//steigende flanke
30
MotorStep(6);
31
ADMUX=1;//phase A rückkopplung
32
break;
33
}
34
35
zahl++;
36
if(zahl==6)
37
zahl=0;
38
39
}
Also wird in dem Interrupt noch die Funktion "Motorstep()" aufgerufen
die wiefolgt aussieht:
1
voidMotorStep(unsignedcharStufe)
2
{
3
switch(Stufe)
4
{
5
case1:phaseAClr;
6
phaseBSet;
7
phaseCTri;
8
break;
9
10
case2:phaseAClr;
11
phaseBTri;
12
phaseCSet;
13
break;
14
15
case3:phaseATri;
16
phaseBClr;
17
phaseCSet;
18
break;
19
20
case4:phaseASet;
21
phaseBClr;
22
phaseCTri;
23
break;
24
25
case5:phaseASet;
26
phaseBTri;
27
phaseCClr;
28
break;
29
30
case6:phaseATri;
31
phaseBSet;
32
phaseCClr;
33
break;
34
}
35
}
Die Funtion ruft nun wiederum defines auf:
1
#define phaseASet PORTD |= (1 << PD0);\
2
PORTD |= (1 << PD1);
3
#define phaseAClr PORTD &= ~(1 << PD1);\
4
PORTD &= ~(1 << PD0);
5
#define phaseATri PORTD |= (1 << PD0);\
6
PORTD &= ~(1 << PD1);
7
#define phaseBSet PORTD |= (1 << PD2);\
8
PORTD |= (1 << PD3);
9
#define phaseBClr PORTD &= ~(1 << PD3);\
10
PORTD &= ~(1 << PD2);
11
#define phaseBTri PORTD |= (1 << PD2);\
12
PORTD &= ~(1 << PD3);
13
#define phaseCSet PORTD |= (1 << PD4);\
14
PORTD |= (1 << PD5);
15
#define phaseCClr PORTD &= ~(1 << PD5);\
16
PORTD &= ~(1 << PD4);
17
#define phaseCTri PORTD |= (1 << PD4);\
18
PORTD &= ~(1 << PD5);
Also man sieht schon der Ablauf des Komparator Interrupts dauert sehr
lang, aber ich wüsste keine Verbesserung.
Vielleicht könntet ihr mir sagen, wie ich das besser angehe?
MfG
Verzichte mal in der ISR auf die Funktionsaufrufe von Motorstep und
schreib das zutreffende direkt rein, also z.B.
ISR case 0:
phaseAClr;
phaseBSet;
phaseCTri;
break;
usw
könnte schon einiges an Zeit sparen, da kein Funktionsaufruf und kein
weiteres switch.
Gib halt am Anfang der Comparator-Interrupt-Routine die Interrupts
wieder frei. Dann kann der Timer-Interrupt den Comparator unterbrechen.
Musst halt nur sicher sein, dass es keine Ueberlaeufe gibt oder den Code
manuell mit einem Flag schuetzen.
Hallo!
Peter, also ich habe die Interrupt nun am Anfang der Komparator ISR
wieder freigegeben, nun funktioniert es so, ohne dass er einzelne Timer
Interrupt "verliert"
Nur wie meinst du, dass ich sicher gehen muss das kein Überlauf
stattfindet?
MfG
OPEL = OhnePowerEwigLetzter.
Dein Problem besteht wntweder in der Leistungfähigkeit des Prozessors,
deinen anforderungen oder der Umsetzung/effizienz.
Lade doch mal das ganze Programm mit beschreibung hoch.
Ich sähe folgende Lösung für das Problem.
ISR für Comparator Ereigniss:
Abschalten des Comparator Interupts
Setzen des Global Interrupt Enable (SEI)
Abarbeiten der Comparator ISR
Wiederanschalten des Comparator Interupts
Rücksprung
Dadurch kann die Comparator ISR nicht überlaufen und du verlierst keine
Timer Interrupts.
Ein ganz anderes Problem ist jedoch wenn deine Timer ISR schon zu lang
für die Zeit zwischen zwei Timer Interrupts ist.
Dann gibt es nur zwei Sachen:
- Schnellere CPU
- Optimieren,optimieren optimieren
cu
Hauke
Hauke:
Ich verstehe den Sinn, von Abschalten und Rücksprung nicht, es ändert
sich doch nichts an meinem jetzigen Programm? Denn irgendwie scheint es
so als springst du aus der ISR raus und gleich wieder rein? Oder habe
ich gerade einen enormen Denkfehler?
MfG
Ich würde als erstes diese CASE-Konstruktion rauswerfen und das über ein
banales Konstantenarray machen:
1
PORTD=PortDValue[Stufe]
wobei Du ins Array PortDValue schlicht die sechs Werte hinschreibst, auf
die der Port bei Stufe x (x = 0, 1, 2, 3, 4, 5) gesetzt werden soll. Das
reicht doch völlig aus.
>Hab nen AtMega8 µC der läuft auf 8MHz internen RC Oszillator.>ich brauche eine variable die 8000 mal pro Sekunde umschaltet.> TCCR1B |= (0 << CS12) | (0 << CS11) | (1 << CS10); //CK> ICR1=255; //TOP Wert
Wie passt das zusammen? 8 MHz/(8000/s) sind 1000. Prescaler 1 mit
TOP-Wert 255 wäre knapp viermal so schnell.
0.
Optimierung beim Compiler eingeschaltet ?
1.
switch(Stufen)
{
case 1:phaseAClr;
phaseBSet;
phaseCTri;
break;
Hier kann man doch bestimmt eleganter mit einem Bitmuster/Zustand
arbeiten.
Es wird doch nur PORTD manipuliert.
Eine Zustandstabelle sollte da helfen.
Ev. lässt sich da noch was vereinfachen.
2.
Char als Flag ? Da ist eine unit8_t schöner, oder ?
(Geschmack wie auch immer)
3.
switch(zahl)
{ case 0: ACSR = 0b00001010; //fallende flanke
Stufe=1;
ADMUX=5; //phase C rückkopplung
break;
Das ist wirklich blöd.... !!!!!
Erst bildest Du den Zustand in "Stufe" ab,
um ihn später in dem Timerinterrupt in die Wirklichkeit zu übertragen...
Speichere doch das entsprechende Bitmuster in Stufe oder nenne es
Zustand und setzte den einfach in dem Timerinterrupt.
Du sprst auf jeden fall ein Case-Konstrukt.
3.1 Auf Deutsch :
Soll heissen Speichere das entsprechende Bitmuster zum Manipulieren von
PORTD bereits in Deinem Komparator-Interrupt.
Deine Bitmuster kannst Du ja noch als define in deinem Programm lassen.
Den Toggle kannst Du global als volatile "exportieren",
oder Du setzt immer auch das komplement.
Bei zwei möglichkeiten ist das nicht so schlimm.
Ich habe deinen Code nur überflogen.
Mir stellt sich folgende Frage:
Passiert auf dem Port D noch mehr als nur die Motoransteuerung?
Sonst könnte man die Zuweisungen in ein Array packen und einfach den
Inhalt des jeweiligen Feldeintrags in Port D kopieren.
Damit wären diverse Abfragen einfach raus.
Wenn noch mehr auf Port D passieren soll, dann müsste man die übrigen
Bist in einer/weiteren Variablen zwischenspeichern und diese dann alle
miteinander verodern, bevor man Port D beschreibt.
Damit wäre die Switch-Case-Abfrage schon mal etwas kürzer...
Notfalls (da es ja unterschiedliche Switch-Case-Abfragen sind/zu sein
scheinen), macht man das Array entsprechend größer, so dass man ein Mal
die Werte für var und ein Mal für ~var hat.
Die Umschaltung der Analog-Converter-Flanke sollte man auch durch durch
ein einfaches Togglen des untersten Bits erreichen können.
So wie ich das sehe, wird die Flankenrichtung bei jedem ISR-Aufruf
geändert.
Entweder kann man direkt das ACSR ändern, oder man braucht noch eine
Hilfsvariable:
Das Togglen würde so aussehen: ACSR ^= 1; (Hier darf auch gerne der
Bit-Name mit der (1<<xy)-Schreibweise eingesetzt werden.
Wenn sich im ACSR noch andere Bits ändern, muss man auf eine
Hilfsvariable zurückgreifen, die man genauso behandelt und dann ins ACSR
schreibt.
Durch das Einsetzen von Arrays braucht man zwar mehr Speicher, ist dafür
aber (wesentlich) schneller.
j=sqrt(-1) schrieb:
> 0.> Optimierung beim Compiler eingeschaltet ?
-Os ist eingeschaltet.
> 2.> Char als Flag ? Da ist eine unit8_t schöner, oder ?> (Geschmack wie auch immer)
Habs jetzt auf uint8_t!
> 3.>> switch(zahl)>> { case 0: ACSR = 0b00001010; //fallende flanke> Stufe=1;> ADMUX=5; //phase C rückkopplung> break;>>> Das ist wirklich blöd.... !!!!!>> Erst bildest Du den Zustand in "Stufe" ab,> um ihn später in dem Timerinterrupt in die Wirklichkeit zu übertragen...>> Speichere doch das entsprechende Bitmuster in Stufe oder nenne es> Zustand und setzte den einfach in dem Timerinterrupt.>> Du sprst auf jeden fall ein Case-Konstrukt.
Ich versteh nicht wie du das meinst, ich muss es deshalb im Interrupt in
die Wirklichkeit übertragen, da im Interrupt der Ausgang noch getaktet
wird zusätzlich.
Hier ist jetzt mein geänderter Code, sieht einiges eleganter aus:
1
#include<avr/io.h>
2
#include<avr\interrupt.h>
3
4
voidHochfahren(void);
5
voidTimer_io_init(void);
6
7
charzahl=0;
8
uint8_tStufe=0;
9
charvar=0;
10
constuint8_tPBvals[6]={0b00011100,
11
0b00110100,
12
0b00110001,
13
0b00010011,
14
0b00000111,
15
0b00001101
16
};
17
18
constuint8_tPBvalc[6]={0b00010011,
19
0b00000111,
20
0b00001101,
21
0b00011100,
22
0b00110100,
23
0b00110001
24
};
25
26
ISR(TIMER1_COMPA_vect)
27
{var=~var;
28
if(var)
29
PORTB=PBvals[Stufe];
30
else
31
PORTB=PBvalc[Stufe];
32
}
33
34
ISR(ANA_COMP_vect)
35
36
{sei();
37
38
switch(zahl)
39
40
{case0:ACSR=0b00001010;//fallende flanke
41
Stufe=0;
42
ADMUX=5;//phase C rückkopplung
43
break;
44
case1:ACSR=0b00001011;//steigende flanke
45
Stufe=1;
46
ADMUX=0;//phase B rückkopplung
47
break;
48
case2:ACSR=0b00001010;//fallende flanke
49
Stufe=2;
50
ADMUX=1;//phase A rückkopplung
51
break;
52
case3:ACSR=0b00001011;//steigende flanke
53
Stufe=3;
54
ADMUX=5;//phase C rückkopplung
55
break;
56
case4:ACSR=0b00001010;//fallende flanke
57
Stufe=4;
58
ADMUX=0;//phase B rückkopplung
59
break;
60
case5:ACSR=0b00001011;//steigende flanke
61
Stufe=5;
62
ADMUX=1;//phase A rückkopplung
63
break;
64
}
65
66
zahl++;
67
if(zahl==6)
68
zahl=0;
69
70
}
71
72
73
74
75
76
intmain()
77
{Timer_io_init();
78
79
DDRB=0x3F;//Port D0 - D5 als Eingang
80
81
SFIOR|=(1<<ACME);//Multiplexer aktiv
82
83
ACSR=0b00001011;// komparator aktivieren mit steigender flanke und interrupt aktiviert
84
ADMUX=1;//phase A rückkopplung
85
86
87
Hochfahren();
88
89
sei();
90
91
while(1)
92
{
93
}
94
95
96
}
97
98
voidTimer_io_init(void)
99
{
100
101
TCCR1A|=(1<<WGM11)|(0<<WGM10);
102
TCCR1B|=(1<<WGM13)|(0<<WGM12);// phase correct mit ICR1 als TOP
Dann würde ich dasselbe gleich auch mit dem zweiten CASE-Konstrukt
machen.
> switch(zahl)>> { case 0: ACSR = 0b00001010; //fallende flanke> Stufe=0;> ADMUX=5; //phase C rückkopplung> break;> case 1: ACSR = 0b00001011; //steigende flanke> Stufe=1;> ADMUX=0; //phase B rückkopplung> break;> .............> .............> .............
kannst Du ersetzen durch
1
Stufe=zahl
2
ACSR=ACSR_DATA[Stufe]
3
ADMUX=ADMUX_DATA[Stufe]
und in den Konstantenarrays ACSR_DATA[6] und ADMUX_DATA[6] die Daten
reinschreiben - fertig.
>var=~var;> if(var)> PORTB=PBvals[Stufe];> else> PORTB=PBvalc[Stufe];
Was soll die Umschaltung über var bezwecken?
In Assembler kannst Du die Sache übrigens mit genau 15 Taktzyklen
erledigen, sofern die Stufe-Variable in einem eigenen Register s
vorgehalten wird (ohne Berücksichtigung Deiner var-Umschaltung):
Gastofatz schrieb:
> Dann würde ich dasselbe gleich auch mit dem zweiten CASE-Konstrukt> machen.>>> switch(zahl)>>>> { case 0: ACSR = 0b00001010; //fallende flanke>> Stufe=0;>> ADMUX=5; //phase C rückkopplung>> break;>> case 1: ACSR = 0b00001011; //steigende flanke>> Stufe=1;>> ADMUX=0; //phase B rückkopplung>> break;>> .............>> .............>> .............>> kannst Du ersetzen durch> Stufe = zahl> ACSR = ACSR_DATA[Stufe]> ADMUX = ADMUX_DATA[Stufe]>> und in den Konstantenarrays ACSR_DATA[6] und ADMUX_DATA[6] die Daten> reinschreiben - fertig.
Welche Geschwindigkeitssteigerung würde das bringen?
Das vorgeschlagene ACSR ^=1; Funktioniert nicht!
>>var=~var;>> if(var)>> PORTB=PBvals[Stufe];>> else>> PORTB=PBvalc[Stufe];>> Was soll die Umschaltung über var bezwecken?
Das soll nur dazu da sein, dass ich entweder 0 oder 1 in der var
variable habe, denn wenn ich jetzte 9:10 habe möchte ich gerne 9 Teile
lang PBvals[] haben und 1 Teil PBvalc[]
weißt du wie ich mein
> In Assembler kannst Du die Sache übrigens mit genau 15 Taktzyklen> erledigen, sofern die Stufe-Variable in einem eigenen Register s> vorgehalten wird (ohne Berücksichtigung Deiner var-Umschaltung):> TimerInterrupt:> in SREGSave, SREG ; 1 cycle>> ldi ZL, Low (PORTDATA) ; 1 cycle> ldi ZH, High(PORTDATA) ; 1 cycle> add ZL, s ; 1 cycle> ld r0, Z ; 2 cycles> out PORTD, r0 ; 1 cycle>> subi s, 1 ; 1 cycle> brsh StufeNotNeg ; 2 cycles> ldi s, 6-1>> StufeNotNeg:> out SREG, SREGSave ; 1 cycle>> reti ; 4 cycles
Naja Assembler, hab ich absolut 0 Ahnung leider
Ich vermute, da die Komparator Interrupt routine ja ständig vom Timer
Interrupt unterbrochen wird, das das Toggeln nicht funktioniert, da
irgendwas nicht past daraufhin?
MfG
B. L. schrieb:
> Nur wie meinst du, dass ich sicher gehen muss das kein Überlauf stattfindet?
Wenn du den Interrupt wieder freigibst und deine Interruptroutine so
lange dauert, dass der Interrupt schon wieder auftritt, bevor du fertig
bist, hast du ein Problem. Klar, oder? Wenn du dir also bei einer deiner
Interruptroutinen nicht sicher bist, ob sie schnell genug ist, kannst du
an deren Anfang einfach ein Flag pruefen, das signalisiert, dass der
letzte Aufruf noch laeuft.
volatile bool IsRunning = false;
void InterruptRoutine( void)
{
if( IsRunning == true) return;
IsRunning = true;
DoSomething();
IsRunning = false;
}
So in der Art. Ein klitzekleines Fenster ist natuerlich am Ende der
Routine noch, wo die Routine noch akive, das Flag aber schon geloescht
ist...
> Naja Assembler, hab ich absolut 0 Ahnung leider
Hallo!
Jungfrau und schwanger geht nicht gleichzeitig (von historischen
Ausnahmen abgesehen).
So ist es auch mit der Programmiererei! Entweder bequem per Hochsprache
ODER effizient. Beides geht nicht.
Zumindest die meisten Hochsprachen bieten deshalb den Weg,
Assemblerteile direkt einzubinden. Das erfordert aber immer sowohl
genaue Kenntnis des Prozessors, als auch des entsprechenden
Hochsprachen-Compilers.
Ich habe schon oft bei meinen Lösungen in ..C, ..Forth, ..Pascal oder
gar ..Basic hinterher den Eindruck gehabt, daß ich wahrscheinlich mit
meinem bevorzugten Programmieren in Assembler schneller, effizienter und
auch fehlerfreier zum Ziel gekommen wäre...
Vielleicht könntest Du dich mit dem Gedanken -Assembler- mal anfreunden?
Hmm, dass mit Assembler Programmiertes effizienter ist, wusste ich
schon, aber nunja Assembler scheint mir ein wenig seeeehr kompliziert zu
sein.
Hier mein jetziges Programm, es funktioniert ... so lala kann man sagen:
1
#include<avr/io.h>
2
#include<avr\interrupt.h>
3
4
voidHochfahren(void);
5
voidTimer_io_init(void);
6
7
charzahl=0;
8
uint8_tStufe=0;
9
charvar=0;
10
constuint8_tPBvals[6]={0b00011100,
11
0b00110100,
12
0b00110001,
13
0b00010011,
14
0b00000111,
15
0b00001101
16
};
17
18
constuint8_tPBvalc[6]={0b00011101,
19
0b00110101,
20
0b00110101,
21
0b00010111,
22
0b00010111,
23
0b00011101
24
};
25
constuint8_tACSR_DATA[6]={0b00001010,
26
0b00001011,
27
0b00001010,
28
0b00001011,
29
0b00001010,
30
0b00001011
31
};
32
constuint8_tADMUX_DATA[6]={0b00000101,
33
0b00000000,
34
0b00000001,
35
0b00000101,
36
0b00000000,
37
0b00000001
38
};
39
40
ISR(TIMER1_COMPA_vect)
41
{var=~var;
42
if(var)
43
PORTB=PBvals[Stufe];
44
else
45
PORTB=PBvalc[Stufe];
46
}
47
48
ISR(ANA_COMP_vect)
49
50
{sei();
51
Stufe=zahl;
52
ACSR=ACSR_DATA[Stufe];
53
ADMUX=ADMUX_DATA[Stufe];
54
55
zahl++;
56
if(zahl==6)
57
zahl=0;
58
59
}
60
61
62
63
64
65
intmain()
66
{Timer_io_init();
67
68
DDRB=0x3F;//Port B0 - B5 als Eingang
69
70
SFIOR|=(1<<ACME);//Multiplexer aktiv
71
72
ACSR=0b00001011;// komparator aktivieren mit steigender flanke und interrupt aktiviert
73
ADMUX=1;//phase A rückkopplung
74
75
76
Hochfahren();
77
78
sei();
79
80
while(1)
81
{
82
}
83
84
85
}
86
87
voidTimer_io_init(void)
88
{
89
90
TCCR1A|=(1<<WGM11)|(0<<WGM10);
91
TCCR1B|=(1<<WGM13)|(0<<WGM12);// phase correct mit ICR1 als TOP
@B.L.
Mein Vorschlag war so gemeint:
Normaler Ablauf:
Compare ISR wird aus gelöst: (Alle weiteren werden Interrups
unterbunden)
Compare ISR wird abgearbeitet (Timer INT wird evt. "vergessen")
Rücksprung aus ISR (RETI) (Interrupts werden wieder aktiviert)
1. Verbesserung:
Compare ISR wird aus gelöst: (Alle weiteren werden Interrups
unterbunden)
Setzen des Global Interrupt Enable (SEI)
Compare ISR wird abgearbeitet (Timer INT kann ausgelöst werden)
(während der Abarbeitung kann aber auch ein weiterer Compare INT
ausgelößt werden)
(Der Compare INT überfährt sich sozusagen selbst =8( )
Rücksprung aus Subroutine (RET) (Interrupts bleiben aktiviert)
Meine Verbesserung:
Compare ISR wird aus gelöst: (Alle weiteren werden Interrups
unterbunden)
Abschalten des Comparator Interupts
Setzen des Global Interrupt Enable (SEI)
Abarbeiten der Comparator ISR
(Timer INT kann ausgelöst werden)
(Compare INT kann NICHT ausgelößt werden da abgeschaltet)
(Der Compare INT kann sich also NICHT selbst überfahren)
Wiederanschalten des Comparator Interupts
Rücksprung
Wenn man ganz sicher gehen will:
Compare ISR wird aus gelöst: (Alle weiteren werden Interrups
unterbunden)
Abschalten des Comparator Interupts
Setzen des Global Interrupt Enable (SEI)
Abarbeiten der Comparator ISR
Löschen des Global Interrupt Enable (CLI)
Wiederanschalten des Comparator Interupts
Rücksprung aus ISR (RETI) (Interrupts werden wieder aktiviert)
>> und in den Konstantenarrays ACSR_DATA[6] und ADMUX_DATA[6] die Daten>> reinschreiben - fertig.>Welche Geschwindigkeitssteigerung würde das bringen?
Es steigert eher die Geschwindgkeit, mit der Du Dein Projekt
fertigstellst, weil man kompakten Code leichter überblickt und versteht.
Die dadurch gesparte Zeit kannst Du z. B. zum Assemblerlernen nutzen,
anschließend damit die Timer-ISR handoptimieren und dann wird die Sache
perfekt :-)
>Das soll nur dazu da sein, dass ich entweder 0 oder 1 in der var>variable habe, denn wenn ich jetzte 9:10 habe möchte ich gerne 9 Teile>lang PBvals[] haben und 1 Teil PBvalc[]>weißt du wie ich mein
Äh... ehrlich gesagt: So ganz noch nicht.
>Assembler scheint mir ein wenig seeeehr kompliziert zu sein.
Der Code mag wirr und abschreckend aussehen, aber wenn Du Dich in
kleinen Schritten damit vertraut machst, wirst Du feststellen, dass es
gar nicht so wild ist. Was Route_66 im vorletzten Satz schreibt, kann
ich übrigens bestens nachempfinden.
Nops werden nicht wegoptimiert, das hat auch absolut nichts mit den
besprochenen Problem zu tun.
Hauke:
Das Problem ist, der Comparator Interrupt ist sehr wichtig, da die Gegen
EMK vom Motor einen Comparator Interrupt auslöst, und falls der nicht
auslöst, kann es sein, das der Motor stoppt, und dabei kurzschlusstrom
zieht.
Das ist das hauptsächliche Problem, denn irgendwie habe ich das gefühlt,
dass der Comparator Interrupt nicht richtig ausgeführt wird, da er ja
öfters von den Timern unterbrochen wird!
MfG
Nur mal so als Tip:
Wenn man die Forenregeln lesen würde, würden sich auch mal mehr Leute
den Code anschauen.
Ich verbrenne mir jedenfalls nicht den Finger beim Scrollen.
Was ist denn so schwer daran, nen Anhang zu senden?
Peter
Also das ganze soll ja mal ungefähr sowas werden:
http://www.mikrokopter.de/ucwiki/BrushlessCtrlhttp://mikrocontroller.cco-ev.de/mikrosvn/BL-Ctrl/
Er hat auch die Software zur verfügung gestellt, aber die Blicke ich
nicht durch mit den paar hundert Zeilen nur für PWM
Am einfachsten wäre es doch wenn man einen Hardware PWM hätte und die 6
Ausgänge die ich benutze mit dieser Hardware PWM zu verunden oder?
Es geht einfach darum, dass so wie hier das Programm jetzt arbeitet, der
Kommutierungszeitpunkt für den Motor nicht mehr exakt past, da der Timer
Interrupt den Komparatorinterrupt "unterbricht".
So wie ich das sehe, wurde in dem Mikrokopter Projekt die Hardware PWM
Pins verwendet, aber nur für 3 Kanäle richtig?
Hieße das mit einem AT90PWM2 oder wie die alle heißen, könnte ich alle 6
Kanäle per HardwarePWM so verwenden wie ich es bräuchte. Oder ist das
absoluter schwachsinn und es würde überhaupt nicht funktionieren?
MfG
Ich bin draufgekommen, dass es keinen Sinn hat das mit Software PWM zu
machen, also bleibt mir nur HardwarePWM übrig.
Aber ich habe ein neues Problem, kann vielleicht jemand kurz
drüberschauen:
Funktionierende ( unelegante ) Methode:
1
#include<avr/io.h>
2
3
#define phaseASet PORTD |= (1 << PD0);\
4
PORTD |= (1 << PD1);
5
//Am=0 Ap=1
6
#define phaseAClr PORTD &= ~(1 << PD1);\
7
PORTD &= ~(1 << PD0);
8
//Ap=0 Am=1
9
#define phaseATri PORTD |= (1 << PD0);\
10
PORTD &= ~(1 << PD1);
11
//Am=0 Ap=0
12
#define phaseBSet PORTD |= (1 << PD2);\
13
PORTD |= (1 << PD3);
14
//Bm=0 Bp=1
15
#define phaseBClr PORTD &= ~(1 << PD3);\
16
PORTD &= ~(1 << PD2);
17
//Bp=0 Bm=1
18
#define phaseBTri PORTD |= (1 << PD2);\
19
PORTD &= ~(1 << PD3);
20
//Bm=0 Bp=0
21
#define phaseCSet PORTD |= (1 << PD4);\
22
PORTD |= (1 << PD5);
23
//Cm=0 Cp=1
24
#define phaseCClr PORTD &= ~(1 << PD5);\
25
PORTD &= ~(1 << PD4);
26
//Cp=0 Cm=1
27
#define phaseCTri PORTD |= (1 << PD4);\
28
PORTD &= ~(1 << PD5);
29
//Cm=0 Cp=0
30
31
voidMotorStep(unsignedchar);
32
33
34
intmain()
35
{DDRD=0x3F;
36
SFIOR|=(1<<ACME);//multiplexer aktiv
37
ACSR=0x00;// komparator aktivieren
38
39
while(1)
40
{MotorStep(1);
41
ADMUX=5;
42
while(bit_is_set(ACSR,ACO))
43
{}
44
45
MotorStep(2);
46
ADMUX=1;
47
while(bit_is_clear(ACSR,ACO))
48
{}
49
50
MotorStep(3);
51
ADMUX=0;
52
while(bit_is_set(ACSR,ACO))
53
{}
54
55
MotorStep(4);
56
ADMUX=5;
57
while(bit_is_clear(ACSR,ACO))
58
{}
59
60
MotorStep(5);
61
ADMUX=1;
62
while(bit_is_set(ACSR,ACO))
63
{}
64
65
MotorStep(6);
66
ADMUX=0;
67
while(bit_is_clear(ACSR,ACO))
68
{}
69
70
}
71
72
73
}
74
75
76
voidMotorStep(unsignedcharStufe)
77
{
78
switch(Stufe)
79
{case1:phaseAClr;
80
phaseBSet;
81
phaseCTri;
82
break;
83
case2:phaseAClr;
84
phaseBTri;
85
phaseCSet;
86
break;
87
case3:phaseATri;
88
phaseBClr;
89
phaseCSet;
90
break;
91
case4:phaseASet;
92
phaseBClr;
93
phaseCTri;
94
break;
95
case5:phaseASet;
96
phaseBTri;
97
phaseCClr;
98
break;
99
case6:phaseATri;
100
phaseBSet;
101
phaseCClr;
102
break;
103
}
104
}
Nicht Funktionierende ( elegante ) Methode:
1
#include<avr/io.h>
2
#include<avr\interrupt.h>
3
4
5
#define phaseASet PORTD |= (1 << PD0);\
6
PORTD |= (1 << PD1);
7
//Am=0 Ap=1
8
#define phaseAClr PORTD &= ~(1 << PD1);\
9
PORTD &= ~(1 << PD0);
10
//Ap=0 Am=1
11
#define phaseATri PORTD |= (1 << PD0);\
12
PORTD &= ~(1 << PD1);
13
//Am=0 Ap=0
14
#define phaseBSet PORTD |= (1 << PD2);\
15
PORTD |= (1 << PD3);
16
//Bm=0 Bp=1
17
#define phaseBClr PORTD &= ~(1 << PD3);\
18
PORTD &= ~(1 << PD2);
19
//Bp=0 Bm=1
20
#define phaseBTri PORTD |= (1 << PD2);\
21
PORTD &= ~(1 << PD3);
22
//Bm=0 Bp=0
23
#define phaseCSet PORTD |= (1 << PD4);\
24
PORTD |= (1 << PD5);
25
//Cm=0 Cp=1
26
#define phaseCClr PORTD &= ~(1 << PD5);\
27
PORTD &= ~(1 << PD4);
28
//Cp=0 Cm=1
29
#define phaseCTri PORTD |= (1 << PD4);\
30
PORTD &= ~(1 << PD5);
31
//Cm=0 Cp=0
32
33
34
voidMotorStep(uint8_t);
35
uint8_tzahl=0;
36
37
ISR(ANA_COMP_vect)
38
39
{
40
41
switch(zahl)
42
43
{case0:ACSR=0b00001010;//fallende flanke
44
MotorStep(1);
45
ADMUX=5;//phase C rückkopplung
46
break;
47
case1:ACSR=0b00001011;//steigende flanke
48
MotorStep(2);
49
ADMUX=1;//phase B rückkopplung
50
break;
51
case2:ACSR=0b00001010;//fallende flanke
52
MotorStep(3);
53
ADMUX=0;//phase A rückkopplung
54
break;
55
case3:ACSR=0b00001011;//steigende flanke
56
MotorStep(4);
57
ADMUX=5;//phase C rückkopplung
58
break;
59
case4:ACSR=0b00001010;//fallende flanke
60
MotorStep(5);
61
ADMUX=1;//phase B rückkopplung
62
break;
63
case5:ACSR=0b00001011;//steigende flanke
64
MotorStep(6);
65
ADMUX=ß;//phase A rückkopplung
66
break;
67
}
68
69
zahl++;
70
if(zahl==6)
71
zahl=0;
72
73
}
74
75
intmain()
76
{
77
DDRD=0x3F;//Port D0 - D5 als Eingang
78
SFIOR|=(1<<ACME);//multiplexer aktiv
79
ACSR=0b00001011;// komparator aktivieren mit steigender flanke und interrupt aktiviert
80
ADMUX=0;//phase A rückkopplung
81
82
sei();
83
while(1)
84
{
85
}
86
}
87
88
89
voidMotorStep(uint8_tStufe)
90
{
91
switch(Stufe)
92
{case1:phaseAClr;
93
phaseBSet;
94
phaseCTri;
95
break;
96
case2:phaseAClr;
97
phaseBTri;
98
phaseCSet;
99
break;
100
case3:phaseATri;
101
phaseBClr;
102
phaseCSet;
103
break;
104
case4:phaseASet;
105
phaseBClr;
106
phaseCTri;
107
break;
108
case5:phaseASet;
109
phaseBTri;
110
phaseCClr;
111
break;
112
case6:phaseATri;
113
phaseBSet;
114
phaseCClr;
115
break;
116
}
117
}
Kann mir jemand sagen was daran anders ist?
Ich sitze seit knapp 10 Stunden dran, und komm auf den Fehler nicht
drauf!
MfG