PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Interrupt geht vergessen?



ChRiZ
30.08.2007, 20:08
Hallo Zusammen!

Ich habe ein kleines Projekt, es geht um einen Motortreiber, der 2 Motoren steuert mit einem Atmega88.

Für die Positionierung benütze ich Hallsensoren, die an den Motoren angebracht sind.
Ein Hall implus kommt ca. alle 6ms (von jedem Motor), und zwei zähler zähen die Impulse von der ISR aus mit.
Nun ist die Positioniergenauigkeit sehr ungenau, nach einigen 180Grad bewegungen hält der Motor nicht mehr dort an wo er gestartet ist.
Das kann eigentlich nur passieren wenn er einen oder mehrere Hallsignale verpasst.
Die Hallsignale sehen sehr gut aus, die Flanken sind stark. Der Atmega ist konfiguriert auf die Fallende Flanke zu achten.

Der Atmega88 läuft auf 20.00 MHZ

nun die Frage:
gibt es im Atmega88 einen Interrupt speicher? oder gehen die Interrupts verloren für die es keine Zeit gibt?

Kann ich den verschiedenen Interrupts ein Gewicht oder eine Dringlichkeit zuweisen? Ich benutze int0, int1 und alle 3 HW timer, die intern auch ISR aufrufen, wobei die internen Timer nicht sooo wichtig sind.

Vielen Dank für eure Hilfe!!

fluchtpunkt
30.08.2007, 21:36
normalerweise gehen die Interrupts nicht verloren, zumindest wenn nur ein einziger kommt waehrrend du in ner ISR bist, wenn du da drin natuerlich unnoetig Zeit vertroedelst dann koennen auch mal 2 Interrupts auftreten und dann wird nur auf einen getriggert.

Ich hoffe also das deine ISRs moeglichst kurz sind.
(Extrembeispiel: In der ISR Sachen per UART oder ans LCD ausgeben).
In die IRSs sollte nur das kommen was wirklich notwendig ist, Ausgaben bspw. kann man ueber Flags regeln. Also im Interrupt Flag aktivieren, im normalen Code dann Flag auslesen und reagieren.

ChRiZ
05.09.2007, 21:35
hmm, die interrupts sind für die Regelung von 2 Motoren. Die ISR werden bei Fallenden Flanken der Hallsensoren (jeweils Hall1) der Motoren ausgelöst.
In den ISR wird legedlich in einer IF schlaufe ein PIN des Controllers abgefragt (ein zweites Hallsignal, für die Richtungserkennung).
in der IF wird dan entschieden ob der Winkel (int) increment oder decrementiert wird.

Wenn sich der Atmega nun in einer ISR befindet und ein zweiter Interrupt kommt?
Ich betreibe den Atmega mit 20MHZ mit einem externen Quarz (darf man dass? im datasheet ist diese Gescheindigkeit glaube ich nur für ext. Taktgeneratoren an OSC1..??) die Motoren drehen max. 10'000 U/min und haben jeweils 3 Hallsensooren..

wkrug
06.09.2007, 12:11
In den ISR wird legedlich in einer IF schlaufe ein PIN des Controllers abgefragt (ein zweites Hallsignal, für die Richtungserkennung).
in der IF wird dan entschieden ob der Winkel (int) increment oder decrementiert wird.
Würd ich so nicht machen.
Setz ein Flag in der Interruptroutine und frag im Hauptprogramm den zweiten Hallsensor (PIN) ab und entscheide dann die Richtung.


Wenn sich der Atmega nun in einer ISR befindet und ein zweiter Interrupt kommt?
...Dann wird dieses zweite Interrupt Flag gesetzt. Die dazugehörige Routine wird aber erst nach Beendigung der laufenden Interruptroutine eingeleitet.
Treten in dieser Zeit mehrere Interrupts auf werden diese nach der Reihenfolge der Interruptvektortabelle abgearbeitet.
Alternativ kannst Du auch die Interruptabarbeitung mit "SEI" am Anfang einer Interruptroutine freigeben, das ist allerdings nicht ohne Fallstricke und sollte nur in absoluten Ausnahmefällen eingesetzt werden.
Ich hab das bis jetzt nur ein einziges mal bei der Abarbeitung einer 1wire Schnittstelle mit Drehzahlmesser gemacht.
Die Interruptroutinen sollten auf jeden Fall sehr schnell abgearbeitet werden können und keinesfalls sollte auf irgendwelche Portpins gewartet werden müssen (die vielleicht nie den geforderten Zustand annehmen).

pctoaster
06.09.2007, 12:53
Hallo,
Wenn der Hallsensor normale Inkrementalgebersignale liefert, dann guck mal hier:
https://www.roboternetz.de/phpBB2/viewtopic.php?t=33628
Da habe ich mal ein Codebeispiel für die Auswertung eines Impulsgebers mittels IRQ hineingestellt. Der IRQ dauert hier < 20uS.
Niemals in einem IRQ auf irgend etwas warten!!
Ports abfragen, kurze Berechnung mit PortAlt für Richtung, PortNeu in PortAlt speichern und dann nichts wie raus.

Gruß
pctoaster

fluchtpunkt
06.09.2007, 13:17
...Dann wird dieses zweite Interrupt Flag gesetzt. Die dazugehörige Routine wird aber erst nach Beendigung der laufenden Interruptroutine eingeleitet.
wann wird eigentlich das Interrupt Flag fuer die aktuell zu bearbeitende ISR geloescht? Beim Betreten der ISR oder erst beim Verlassen?


Treten in dieser Zeit mehrere Interrupts auf werden diese nach der Reihenfolge der Interruptvektortabelle abgearbeitet.
mehrere verschiedene sollte man hier wohl noch einmal betonen. Wenn man in einer ISR ist und dabei 10mal INT0 kommt, bekommt man nur einen davon mit.

pctoaster
06.09.2007, 13:41
Bereits beim Auslösen eines IRQ wird das Global Interrupt Enable Flag gelöscht und beim RTI wieder zurückgesetzt. Zumindest ist das beim AVR so.

Gruß
pctoaster

fluchtpunkt
06.09.2007, 13:52
das meinte ich nicht, ich meinte die einzelnen Interrupt Flag Register (TIFR, GIFR) usw. Aber beim Suchen nach den Registernamen bin ich selbst auf die Antwort gestossen.

OCF0 is cleared by hardware when executing the
corresponding interrupt handling vector.

Gock
06.09.2007, 15:12
Es kommt natürlich nicht nur auf die Länge der Ausführung in den Hall-ISRs an, sondern auch auf die restlichen. Wenn zb während des T0 IRQs INT0 und INT1 kommen, dann geht einer verloren. Deine Hall-ISR mit 3Halls x 167Umdr/s x 2Motoren = 1000 IRQs pro sekunde + restliche IRQs sind nicht so wenig, weshalb es nicht unwahrscheinlich ist, wenn es alle paar tausend IRQs zu einer Uberlagerung kommt.
Deswegen schließe ich mich allen an --> nur Flags setzen, wenn möglich!
Gruß

ChRiZ
06.09.2007, 20:31
Vielen Dank für euere Antworten!
Ich werde die If bedingung nun aus der ISR nehmen.

zum verständniss, ich warte nicht auf einen PIN, sodern frage nur den status 1x ab.

was meint ihr mit Flag setzten? einen Port Toggeln? oder eine Variable verändern?


viele Grüsse
Chris

fluchtpunkt
06.09.2007, 22:34
wenn du wirklich nur einen Pin abfragst und mit dem Ergebnis keine komplizierten Rechnungen ausfuerhst kannste das auch drin lassen. Dann ist dein Problem ein anderes, zB ein anderer Interrupt der viel Zeit klaut. Am besten mal Code posten.

Ein Flag ist meist ein Bit in einer Variable:


volatile unsigned char flags;
#define f_isr_flag 0x01
#define f_timer 0x02
#define f_timer1 0x04
// weiter gehts mit 0x08, 0x10, 0x20, 0x40, 0x80
// anhand des f_ am Anfang sieht man sehr schnell das es sich hierbei um ein Flag handelt.

// flag setzen, zB in deiner ISR:
flags |= f_isr_flag;

// flag loeschen
flags &= ~f_isr_flag;

// flag pruefen und anschliessend loeschen
if ( flags & f_isr_flag )
{
flags &= ~f_isr_flag;
// code der mal in der ISR war.
}


kannst auch fuer jedes flag ne eigene variable nehmen. und dann einfach immer 0 oder 1 zuweisen. volatile ist wichtig wenn flags in ISRs genutzt werden

ChRiZ
07.09.2007, 08:49
ok ich werde mal beides Ausprobieren.

mein ISR Code:




volatile int winkel1, winkel2;

ISR(SIG_INTERRUPT0){
if (PINC & (1<<PINC2)){winkel1--; /*LED(2,1);LED(3,0);*/}
else if ( !(PINC & (1<<PINC2)) ){ winkel1++;/* LED(2,0); LED(3,1);*/}
}

ISR(SIG_INTERRUPT1){
if (PINC & (1<<PINC3)){winkel2--; /*LED(2,1);LED(3,0);*/}
else if ( !(PINC & (1<<PINC3)) ){winkel2++;/* LED(2,0); LED(3,1);*/}
}

fluchtpunkt
07.09.2007, 09:01
das kannste direkt so stehen lassen, da verursacht die ganze Flagsache sicher noch mehr Overhead als das.

aber das else if (...) is auch irgendwie sinnlos oder?
Wenn der Pin nicht 1 ist dann ist er mit Sicherheit 0, muss man eigentlich nicht nochmal pruefen. Problematisch wirds wenn nach dem ersten if der Pin 0 ist und du im zweiten if nochmal pruefst und der Pin dann ploetzlich 1 is weil er zwischendurch getoggelt ist. dann wird dein winkel garnicht geaendert.
Aber keine Ahnung, vielleicht soll das ja genau so sein.

ChRiZ
07.09.2007, 09:16
nein, das elseif macht eigentlich schon keinen Sinn, ich habe aber beobachtet das die Genauigkeit etwas zunimmt, bzw. der Fehler nicht nur in eine Richtung driftet..

ich probiers aber nochmals ohne else if... :)
vorher hatte ich die winkel variablen nicht volatile , sondern nur int... und es scheint schon besser zu funktionieren...
was macht das volatile denn genau??

pctoaster
07.09.2007, 11:51
Volatile teilt dem Compiler mit, daß er diese Variable im Ram speichern soll. Ansonsten könnte er das in ein Register speichern und dir IRQ Routine würde von einer Änderung dieser Variable nichts mitbekommen oder umgekehrt.
Ich weiß immer noch nicht, welche Signale Dein Hallencoder liefert. Soll das ein normales Ausgangssignal eines Incrementalencoders sein ? Wenn ja, kann das was Du da machst, auf keinen Fall funktionieren.
So sieht ein normales Signal eines Encoders aus:
Wahrheitstabelle Encoder

Encoder1alt Encoder1neu Encoder2alt Encoder2neu Richtung
1 1 1 1 Ungültig
1 1 1 0 „++“
1 1 0 1 „--“
1 1 0 0 Ungültig
1 0 1 1 „--“
1 0 1 0 Ungültig
1 0 0 1 Ungültig
1 0 0 0 „++“
0 1 1 1 „++“
0 1 1 0 Ungültig
0 1 0 1 Ungültig
0 1 0 0 „--“
0 0 1 1 Ungültig
0 0 1 0 „--“
0 0 0 1 „++“
0 0 0 0 Ungültig

Encoder1 und Encoder2 sind dabei die beiden Encoderausgänge.
Darauf habe ich mein Programm aufgebaut und es funktioniert hervorragend.

Gruß
pctoaster

ChRiZ
07.09.2007, 21:25
ciao pctoaster!

ich habe keinen Encoder auf dem Motor, nur drei Hallsignale mit denen dieser auch gleich gesteuert wird.
Hinter dem Motor hat es eine grosse Untersetzung, für die Genauigkeit reicht mir ein Hallsignal vollkommen.
Damit ereiche ich, das ich jede Umdrehung (360 Grad) des Motors mitzählen kann. das Reicht mir völlig für meine Genauigkeit! (Auflösung nach dem Getriebe ist ca. 0.1Grad!! (ohne Spiel))

Mit dem zweiten Hallsignal (alle 3 sind jeweils 120Grad verschoben) kann ich nun die Richtung feststellen in der sich der Rotot dreht.
Ist Hall 1 && hall2 On dan Links lauf, Hall1 On hall2 Off = Rechtslauf.

Hall1 löst nun den Interrupt aus, dort wird eine Variable Winkel angepasst.

es funktioniert eigentlich auch, nur vergisst der CPU eine Menge Signale :( das System ist nach wenigen umdrehungen (vor allem bei grössere geschwindigkeit der Motoren) ungenau.

pctoaster
07.09.2007, 22:06
An der Dauer des IRQ liegt es mit Sicherheit nicht. Bist Du Dir sicher, daß die Signale sauber sind (ohne Prellen ect.) ?
Hast Du den IRQ sicher so eingestellt, daß er auf Flanke triggert ? Oder konkret: was steht in ISC10 und ISC 11 ?
Sieht das jetzt so aus ?:



volatile int winkel1, winkel2;

ISR(SIG_INTERRUPT0){
if (PINC & (1<<PINC2)){winkel1--; /*LED(2,1);LED(3,0);*/}
else { winkel1++;/* LED(2,0); LED(3,1);*/}
}

ISR(SIG_INTERRUPT1){
if (PINC & (1<<PINC3)){winkel2--; /*LED(2,1);LED(3,0);*/}
else {winkel2++;/* LED(2,0); LED(3,1);*/}
}


Änderst Du winkel1 oder winkel2 ausserhalb des ISR ?

ChRiZ
08.09.2007, 19:34
Jop ISR's sehen nun genau so aus... :)

ic10 und 11 wie auch 01 und 00 sind auf sinkende flanke getriggert.
die Signale sehen mit dem KO sehr sauber aus, die Flanken sind fast 90 Grad! Ich probierte auch schon einen Mittelwert des Portstatus zu nehmen, ändert aber nix :(
ich werde am montag mal den rest des Codes posten...

Wenn die PID geregelten Motoren ohne I und ohne D laufen, mit einem schwachen P funktioniert alles einwandfrei. Die Motoren drehen dann auch ziemlich langsam. (ca. 800U/min) somit denke ich das es ein Timing problem ist.
Meine PID Regelschlaufen, werden alle 25ms. aufgerufen, vielleicht ist das etwas zu oft...

Ich bin gespannt was Ihr zum code des Reglers sagen werdet... aber erst am Montag, es handelt sich hier um eine Diplomarbeit, und ich habe alles in der Schule gebunkert :)

ChRiZ
12.09.2007, 21:14
Hallo Zusammen!
so, ich bin wieder ein stück weiter gekommen, konnte die Genauigkeit aber noch nicht genau Messen.

hier der Code:

#include <inttypes.h>
#include <avr/interrupt.h>

#include "twislave.h"
//#include <util/delay.h> /* definiert _delay_ms() ab avr-libc Version 1.2.0 */

#include "L6229.h"
#include "ADIO.h"

#define MAX_Power 150 //MAX Power, 120 Max, 250 min
#define I2CAdresse 12 //MAX Power, 120 Max, 250 min
#define MaxStallTime 200 //MAX Power, 120 Max, 250 min
#define MAX_I 2000

#define time_on 150// 3
#define time_off time_on-50//125 ca. 12ms Periodendauer

#define Ta 15 //=1/Periodendauer;

volatile uint8_t regler1,regler2;
volatile uint8_t speed1,speed2;
volatile uint8_t dir1,dir2;

volatile int sollWert1,sollWert2;
volatile int sollWert1_eff,sollWert2_eff;

volatile float q1,q2;

volatile unsigned int strom2,strom1;
volatile uint8_t verzoegerung;

volatile int16_t stall;
volatile float e1,e2,e1alt,e2alt;
volatile int16_t winkel1, winkel2;
volatile float kp1,kp2,ki1,ki2,kd1,kd2; //p,i,d-Anteile
volatile float isum1,isum2;
volatile uint8_t modus1,modus2; //Modus in dem sich der Regler befindet, Modus = 0 Strompeak suchen, Modus = 1 normalbetrieb

ISR(SIG_INTERRUPT0){
if (PINC & (1<<PINC2)){winkel1--; /*LED(2,1);LED(3,0);*/}
else{winkel1++;/* LED(2,0); LED(3,1); if ( !(PINC & (1<<PINC2)) )*/}
}

ISR(SIG_INTERRUPT1){
if (PINC & (1<<PINC3)){winkel2--; /*LED(2,1);LED(3,0);*/}
else{winkel2++;/* LED(2,0); LED(3,1); if ( !(PINC & (1<<PINC3)) )*/}
}

void stromUeberwachen(){
strom2 = ReadChannel(1);
strom1 = ReadChannel(0);

if(strom1>110 || strom2>105){ LED(1,1);stall++;}
else {LED(1,0); stall=0;}

if(stall>MaxStallTime){enable(0,0);enable(1,0); regler1=0;regler2=0; } //Strombegrenzer
}


void reglerMot1(){

e1=(sollWert1-winkel1)/2000.0f;

isum1+=e1/200.0f;

if(e1<0){dir1 = 1; e1*=-1;} else dir1 = 0;

fwd_rev(0,dir1);

q1 = kp1 * e1; //P-Anteil
q1 += isum1*ki1*Ta; //I-Antei
q1 += (e1 - e1alt)/Ta * kd1; //D Anteil

e1alt=e1;

if(q1<0.0f)q1*=-1;
if(q1>1.0f)q1=1.0f;//0 = 5%

q1=(1.0f-q1*1.0f)*255.0f;

if(q1>255) OCR1A = 255; //Anti Wind-Up
else if(q1<MAX_Power) OCR1A = MAX_Power; //Anti Wind-Up
else OCR1A =q2;

if(e1<=0.001 && e1 >=-0.001){ enable(0,0);isum1=0; } else enable(0,1);
}

void reglerMot2(){
e2=(sollWert2-winkel2)/2000.0f;

isum2+=e2/200.0f;

if(e2<0){dir2 = 1; e2*=-1;} else dir2 = 0;

fwd_rev(1,dir2);

q2 = kp2 * e2; //P-Anteil
q2 += isum2*ki2*Ta; //I-Antei
q2 += (e2 - e2alt)/Ta * kd2; //D Anteil

e2alt=e2;

// if(isum2>1 || isum2<-1) LED(2,1); else LED(2,0);

if(q2<0.0f)q2*=-1;
if(q2>1.0f)q2=1.0f;//0 = 5%

q2=(1.0f-q2*1.0f)*255.0f;

if(q2>255) OCR1B = 255; //Anti Wind-Up
else if(q2<MAX_Power) OCR1B = MAX_Power; //Anti Wind-Up
else OCR1B =q2;

if(e2<=0.001 && e2 >=-0.001){ enable(1,0);isum2=0; } else enable(1,1);
}

void mainSchlaufe(){

switch(rxbuffer[0]){
case 1:
regler1=0;
enable(0,0);
rxbuffer[0] = 0;
break;

case 4:
enable(0,0);
if(rxbuffer[1]==1)kp1=rxbuffer[2];
if(rxbuffer[1]==2)ki1=rxbuffer[2];
if(rxbuffer[1]==3)kd1=rxbuffer[2];
rxbuffer[0] = 0;
break;

case 7:
sollWert1=(((rxbuffer[2])<<7) & 0b11111110000000); sollWert1+=rxbuffer[1]&0b00000001111111;
regler1=1;
rxbuffer[0] = 0;
break;

case 9:
winkel1=(((rxbuffer[2])<<7) & 0b11111110000000); winkel1+=rxbuffer[1]&0b00000001111111;
sollWert1=winkel1;
rxbuffer[0] = 0;
break;

}


switch(rxbuffer[3]){
case 1:
regler2=0;
enable(1,0);
rxbuffer[3] = 0;
break;

case 4:
enable(1,0);
if(rxbuffer[4]==1)kp2=rxbuffer[5];
if(rxbuffer[4]==2)ki2=rxbuffer[5];
if(rxbuffer[4]==3)kd2=rxbuffer[5];
rxbuffer[3] = 0;

break;

case 7:
sollWert2=(((rxbuffer[5])<<7) & 0b11111110000000); sollWert2+=rxbuffer[4]&0b00000001111111;
regler2=1;
rxbuffer[3] = 0;
break;

case 9:
winkel2=(((rxbuffer[5])<<7) & 0b11111110000000); winkel2+=rxbuffer[4]&0b00000001111111;
sollWert2=winkel2;
rxbuffer[3] = 0;
break;
}
}

// ISR zum auffangen der Interrupts:
SIGNAL(TIMER2_COMPA_vect)
{

if (OCR2A == time_off){ // lange geschlafen, jetzt ausgang aktivieren
OCR2A = time_on;
// Einschaltdauer einstellen
}
else{
if(verzoegerung >= 2){ //6 = ca. 70ms Abtastzeit 2 ca. 20ms
if(regler1==1)reglerMot1(); //Linker Oberschenkel gliedL[2]
if(regler2==1)reglerMot2(); //Linker Unterschenkel gliedL[1]
stromUeberwachen();
verzoegerung=0;
}

mainSchlaufe();
verzoegerung++;

PORTD &= ~(1<<PD4);
OCR2A = time_off; // Wecker stellen
}
}

void init_Ports(){
DDRB = 0xFF;
DDRC = 0xFF;
DDRD = 0xFF; //Alles Ausgang

DDRD &= ~(1 << DDD2); //Hallsensoren als Eingänge
DDRD &= ~(1 << DDD3); //Hallsensoren als Eingänge
DDRC &= ~(1 << DDC2); //Hallsensoren als Eingänge
DDRC &= ~(1 << DDC3); //Hallsensoren als Eingänge

PORTD |= (1<<PD2); /* internen Pull-Up an PD2 aktivieren */
PORTD |= (1<<PD3); /* internen Pull-Up an PD3 aktivieren */
PORTC |= (1<<PC2); /* internen Pull-Up an PC2 aktivieren */
PORTC |= (1<<PC3); /* internen Pull-Up an PC2 aktivieren */

EICRA = (1<<ISC01 | 0<<ISC00 | 1<<ISC11 | 0<<ISC10); //Auf sinkende Flanke von Hall1 achten
EIMSK = (1<<INT1 | 1<<INT0 ); //ISR Aktivieren

TCCR2A = (1<<WGM21);
TCCR2B = (1<<CS22) | (1<<CS21) | (1<<CS20); //prescaler 1024, 8 bit count, 1 count = 8ms, 125 count = 1000ms
OCR2A = time_off;
TIMSK2 = (1<<OCIE2A);
}



int main (void){
init_Ports(); //PORT & Timer init
l6229_init();

stall = 0;

sollWert1=0; sollWert2=0;
kp1=10; kp2=10;
ki1=2; ki2=2;
kd1=2; kd2=2;

regler1=regler2=0;
winkel1=winkel2=0;
dir1 = 0;dir2 = 1;
isum2=isum1=0.0f;
q1=q2=0;
e1=e2=0;
modus1=1;modus2=1;
strom2=0;strom1=0;

verzoegerung=0;

fwd_rev(0,dir1);fwd_rev(1,dir2);
bremse(1,1); bremse(0,1);

init_twi_slave(I2CAdresse);
LED(2,1);

for(;;){
//txbuffer[0] = 12; txbuffer[1] = 13; txbuffer[2] = 14; txbuffer[3] = 15;
}

}


ADIO.h


uint16_t ReadChannel(uint8_t mux)
{
uint8_t i;
uint16_t result;

ADCSRA = (1<<ADEN) | (1<<ADPS1) | (1<<ADPS0); // Frequenzvorteiler
// setzen auf 8 (1) und ADC aktivieren (1)

ADMUX = mux; // Kanal waehlen
ADMUX |= (1<<REFS1) | (1<<REFS0); // interne Referenzspannung nutzen

/* nach Aktivieren des ADC wird ein "Dummy-Readout" empfohlen, man liest
also einen Wert und verwirft diesen, um den ADC "warmlaufen zu lassen" */
ADCSRA |= (1<<ADSC); // eine ADC-Wandlung
while ( ADCSRA & (1<<ADSC) ) {
; // auf Abschluss der Konvertierung warten
}
result = ADCW; // ADCW muss einmal gelesen werden,
// sonst wird Ergebnis der nächsten Wandlung
// nicht übernommen.

/* Eigentliche Messung - Mittelwert aus 4 aufeinanderfolgenden Wandlungen */
result = 0;
for( i=0; i<4; i++ )
{
ADCSRA |= (1<<ADSC); // eine Wandlung "single conversion"
while ( ADCSRA & (1<<ADSC) ) {
; // auf Abschluss der Konvertierung warten
}
result += ADCW; // Wandlungsergebnisse aufaddieren
}
ADCSRA &= ~(1<<ADEN); // ADC deaktivieren (2)

result /= 4; // Summe durch vier teilen = arithm. Mittelwert

return result;
}



L6229.c


/*L6229.c

Motortreiber Funktionen

*/

#include "L6229.h"
#include <avr/io.h>

void pwm_init (void) { // Initialisierung:
TCCR1A = (1<<WGM10) | (1<<COM1A1) | (1<<COM1A0) |(1<<COM1B1) | (1<<COM1B0);
TCCR1B = (1<<CS11) | (1<<WGM12);

OCR1B = 100;
OCR1A = 100;
}

void calc(void){

#define BIT_Bremse_1 4;
#define BIT_Enable_1 0;
#define BIT_FWD_REV_1 1;

#define BIT_Bremse_2 3;
#define BIT_Enable_2 6;
#define BIT_FWD_REV_2 2;

/*
Bremse2 = PD4
Enable2 = PD1
Fwd_Rev2 = PB5
*/

if(Motor[0].bremse==0) PORTB |= (1 << PB3);
if(Motor[0].bremse==1) PORTB &= ~(1 << PB3);

if(Motor[0].enable==1) PORTD |= (1 << PD0);
if(Motor[0].enable==0) PORTD &= ~(1 << PD0);

if(Motor[0].fwd_rev==1) PORTB |= (1 << PB0);
if(Motor[0].fwd_rev==0) PORTB &= ~(1 << PB0);

if(Motor[1].bremse==0) PORTB |= (1 << PB4);
if(Motor[1].bremse==1) PORTB &= ~(1 << PB4);

if(Motor[1].enable==1) PORTD |= (1 << PD1);
if(Motor[1].enable==0) PORTD &= ~(1 << PD1);

if(Motor[1].fwd_rev==1) PORTB |= (1 << PB5);
if(Motor[1].fwd_rev==0) PORTB &= ~(1 << PB5);
}

void l6229_init(){

Motor[0].bremse = 0;
Motor[0].fwd_rev= 0;
Motor[0].enable = 0;

Motor[1].bremse = 0;
Motor[1].fwd_rev= 0;
Motor[1].enable = 0;

pwm_init();
calc();
}


void enable(int mot_nr,int value){ //EN bit
if(value==1)Motor[mot_nr].enable=1;
if(value==0)Motor[mot_nr].enable=0;

calc(); //Bits setzten
}

void fwd_rev(int mot_nr,int value){ //FWD REV Bit
Motor[mot_nr].enable=0;
if(value==1)Motor[mot_nr].fwd_rev=1;
if(value==0)Motor[mot_nr].fwd_rev=0;

calc();

}

void bremse(int mot_nr,int value){ //brake bit
if(value==0)Motor[mot_nr].bremse=1;
if(value==1)Motor[mot_nr].bremse=0;
calc();
}


void LED(int nr, int value){
if (value>=1){
switch (nr){
case 1: PORTD |= (1 << PD5); break;
case 2: PORTD |= (1 << PD6); break;
case 3: PORTD |= (1 << PD7); break;
}
}

else{
switch (nr){
case 1: PORTD &= ~(1 << PD5); break;
case 2: PORTD &= ~(1 << PD6); break;
case 3: PORTD &= ~(1 << PD7); break;
}
}
}






beschreibung:
Es werden 2 PWM Timer gestartet OR1A und OCR1B diese PWM werden Tiefpassgefiltert und als Analgospannung dem Motorentreiber l6229 von 0-5volt gegeben.
5 Volt ist volle Geschwindigkeit (ca. OCR1A/B=80), 0 Volt = minimalgeschwindigkeit (OCR1A/B=255;)

in der ADIO.h werden Analogwandlungen gemacht, um den Strom an den Motoren zu messen.

Ich habe die Abtastrate der PID-Regler auf ca. 20ms eingestellt.

Das AVR Studio meldet 99.5% voll in region Text.
Kann mir jemand sagen wie ich die Routinen etwas Optimeiren könnte?
[/code]

pctoaster
24.09.2007, 12:18
Hallo,
war in Urlaub, deshalb die späte Antwort.
Ohne jetzt sehr genau hinschauen zu müssen, kann ich Dir jetzt genau sagen, was da schiefläuft:
Du ruft deine mainloop in einem IRQ auf. NIEMALS tut man so etwas.
Und zwar aus folgenden Grund: Falls während Deines TIMER2_COMPA_vect (der ja sehr lange dauert) Dein Flanken IRQ 2x kommt, kann dieser 2. IRQ nicht ausgewertet werden. Es wird nur immer der nächste anstehende IRQ gespeichert.

Also lass diesem ganzen SIGNAL(TIMER2_COMPA_vect)
weg und lass diesen Inhalt in einer Endlosloop laufen.

Wenn Du diese Dinge zeitgesteuert laufen lassen willst:
Setze in SIGNAL(TIMER2_COMPA_vect) nur ein Flag und rufe in einer Endlos loop abhängig von diesem Flag Deinen ganzen Kram, was Du da im Timer IRQ hast, in der int main(void) auf (Flag dort wieder löschen).

Also:
SIGNAL(TIMER2_COMPA_vect) {
Flag = 1;
}

int main (void {
if (flag=1) {
main blabla
flag = 0;
}

und weiter....

}

Gruss
pctoaster

ChRiZ
24.09.2007, 22:46
he he ich hoffe das du schöne Ferien hattest!
Vielen Dank für deine Antwort! Ich werde das morgen ausprobieren!!

Gretz ChRiZ

ps: du meintest sicher if(flag) oder if(flag==1) :)

pctoaster
24.09.2007, 23:27
ja, das (flag==1) war etwas schlampig geschrieben, aber Du weißt ja wie ich das meine ;-)

Gruß
pctoaster