PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Problem mit externem Interrupt / Timer



Geimel
20.08.2007, 16:27
Hallo,
ich will für eine CarreraBahn eine Zeitmessanlage bauen. Dazu habe ich 2 Auslöser an der Bahn die an einen Mega8 als externer Interrupteingang angschlossen sind. Nun will ich jeweils nur die Rundenzeit und die Rundenanzahl anzeigen. Da der Kontakt jedoch für einige ms besteht, wird der Interrupt ja quasi mehrmals ausgelöst. Mein frage ist nun, kann ich wenn jeweils der entsprechende Interrupt 1 oder 2 ausgelöst wurde den für zB. 250ms sperren, mit nem Timer oder so? Die Zeit soll aber weiterzählen.
Hier ein einfacher Code der bis dahin funkioniert, jedoch mehrmals auslöst: (soll noch weiterentwickelt werden, wenn dieses Problem gelöst ist)

#include <avr/io.h>
#include <inttypes.h>
#include <avr/interrupt.h>
#define F_CPU 4000000UL // 4 MHz
#include <util/delay.h>
#include "lcd.c"
#include "lcd_hw.h"
#include <string.h>

uint16_t time,time1,time2;
int rd1,rd2;//Rundenanzahl
char zeichen,output[20+1];

void delay_ms(unsigned int ms)
/* delay for a minimum of <ms> */
{ while(ms){ _delay_ms(0.96);ms--;}}
static void int_to_ascii(uint16_t inum,char *outbuf,signed char decimalpoint_pos,signed char spacepadd){
signed char i,j;
char chbuf[8];
j=0;
while(inum>9 && j<7){
// zero is ascii 48:
chbuf[j]=(char)48+ inum-((inum/10)*10);
inum=inum/10;
j++;
if(decimalpoint_pos==j){
chbuf[j]='.';
j++;}
}
chbuf[j]=(char)48+inum; // most significant digit
decimalpoint_pos--;
while(j<decimalpoint_pos){
j++;
chbuf[j]='0';}
if (spacepadd && j > (decimalpoint_pos+2)){
spacepadd=0;
}
if(decimalpoint_pos==j){
j++;
chbuf[j]='.';
j++;
chbuf[j]='0'; // leading zero
}
if (spacepadd){
j++;
chbuf[j]=' '; // leading space padding: "9.50" becomes " 9.50"
}
// now reverse the order
i=0;
while(j>=0){
outbuf[i]=chbuf[j];
j--;
i++; }
outbuf[i]='\0';
}





ISR(INT0_vect) {lcd_gotoxy(0,0);lcd_puts("P1: ");// in Zeile 1 P1: schreiben
int_to_ascii(rd1,output,0,1);lcd_puts(output);//in Zeile 1 Rundenanzahl1 schreiben
int_to_ascii(time1,output,3,1);lcd_puts(output);ti me1=0;//im Zeile 1 Rundenzeit1 schreiben
rd1++;//Rundenanzahl erhöhen
}
ISR(INT1_vect) {lcd_gotoxy(0,1);lcd_puts("P2: ");
int_to_ascii(rd2,output,0,1);lcd_puts(output);
int_to_ascii(time2,output,3,1);lcd_puts(output);ti me2=0;
rd2++;

}




int main(void)
{ DDRD |= (1<<PD2); //Register D für PD2 (auf 1) als Ausgang geschaltet
DDRD |= (1<<PD3);
PORTD |= (1 << PD2);
PORTD |= (1 << PD3);
GICR |= (1<<6)|(1<<7); //INT0 als Interrupt aktivieren

lcd_init(LCD_DISP_ON);
lcd_puts(" Los gehts ");delay_ms(2750);lcd_clrscr();

time=0;time1=0;time2=0;rd1=0;rd2=0;
sei(); //Interups Global an

while (1) {
_delay_us(980);time1++;time2++;
//time++ //Gesamtzeit

//int_to_ascii(integer wert,output char,kommastelleverschieben wert,leerzeichen vor dem wert)
/*lcd_puts("out_buf");
lcd_puts("V ");
lcd_putc('[');
lcd_puts("lool");
lcd_gotoxy(0,1);*/
}
return(0);
}

askazo
20.08.2007, 17:24
Wenn Du die ISC-Bits im MCUCR-Register entsprechend einstellst, reagieren die Interrupts nicht mehr auf einen Pegel, sondern auf eine Flanke. Damit sollte Dein Problem zu beheben sein.

Gruß,
askazo

Geimel
21.08.2007, 13:56
habe die Bits gesetzt, nur muss ich ja den Kontakt der den Impuls gibt irgendwie entprellen, da ein schleifkontakt ja bei drüberfahren doch mehrmals auslösen kann. Deshalb möchte ich die Externen Interrupts für eine gewisse Zeit deaktivieren, d.h. immer den Interupt der ausgelöst wurde. Wenn eine gewisse Zeit vergangen ist, soll er wieder aktiviert werden. Dies wollte ich mit einem Overflow Timer machen, wenn eine gewisse anzahl von Overflows existiert und der Externe Interrupt auf "aus" steht, dann soll er wieder aktiviert werden.
Leider funktioniert dieses insofern nicht, das der externe Interrupt wenn der Kontakt prellt trotzdem auslöst.
Hier mein Quellcode:

#include <avr/io.h>
#include <inttypes.h>
#include <avr/interrupt.h>
#define F_CPU 4000000UL // 4 MHz
#include <util/delay.h>
#include "lcd.c"
#include "lcd_hw.h"
#include <string.h>

uint16_t time,time1,time2;
uint8_t isr1,isr0,overflow1,overflow2,rd1,rd2;//Rundenanzahl
char zeichen,output[20+1];

void delay_ms(unsigned int ms)
/* delay for a minimum of <ms> */
{ while(ms){ _delay_ms(0.96);ms--;}}
static void int_to_ascii(uint16_t inum,char *outbuf,signed char decimalpoint_pos,signed char spacepadd){
signed char i,j;
char chbuf[8];
j=0;
while(inum>9 && j<7){
// zero is ascii 48:
chbuf[j]=(char)48+ inum-((inum/10)*10);
inum=inum/10;
j++;
if(decimalpoint_pos==j){
chbuf[j]='.';
j++;}
}
chbuf[j]=(char)48+inum; // most significant digit
decimalpoint_pos--;
while(j<decimalpoint_pos){
j++;
chbuf[j]='0';}
if (spacepadd && j > (decimalpoint_pos+2)){
spacepadd=0;
}
if(decimalpoint_pos==j){
j++;
chbuf[j]='.';
j++;
chbuf[j]='0'; // leading zero
}
if (spacepadd){
j++;
chbuf[j]=' '; // leading space padding: "9.50" becomes " 9.50"
}
// now reverse the order
i=0;
while(j>=0){
outbuf[i]=chbuf[j];
j--;
i++; }
outbuf[i]='\0';
}

ISR(TIMER0_OVF_vect) /*Interrupt Timer0 Overflow*/

{
if(isr0==0){overflow1++;if(overflow1==50){GICR|=GI CR|(1<<6);overflow1=0;isr0=1;}};//wenn genügend zeit vergangen ist Interrupts wieder anschalten
if(isr1==0){overflow2++;if(overflow2==50){GICR|=GI CR|(1<<7);overflow2=0;isr1=1;}};
}



ISR(INT0_vect) {lcd_gotoxy(0,0);lcd_puts("P1: ");// in Zeile 1 P1: schreiben
int_to_ascii(rd1,output,0,1);lcd_puts(output);//in Zeile 1 Rundenanzahl1 schreiben
int_to_ascii(time1,output,3,1);lcd_puts(output);ti me1=0;//im Zeile 1 Rundenzeit1 schreiben
rd1++;//Rundenanzahl erhöhen
isr0=0;GICR |=(1<<7);//externen interrupt 0 deaktivieren
}
ISR(INT1_vect) {lcd_gotoxy(0,1);lcd_puts("P2: ");
int_to_ascii(rd2,output,0,1);lcd_puts(output);
int_to_ascii(time2,output,3,1);lcd_puts(output);ti me2=0;
rd2++;
isr1=0;GICR |=(1<<6);
}




int main(void)
{ DDRD |= (1<<PD2); //Register D für PD2 (auf 1) als Ausgang geschaltet
DDRD |= (1<<PD3);
PORTD |= (1 << PD2);
PORTD |= (1 << PD3);
GICR |= (1<<6)|(1<<7); //INT0,INT1 als Interrupt aktivieren
MCUCR|=(1<<ISC11)|(1<<ISC10)|(1<<ISC00)|(1<<ISC01);//Auf steigende Flanke initialisieren

TCCR0 |= (1<<CS00) | (1<<CS02); /*Prescaling /1024*/
TIMSK |= (1<<TOIE0); /*Overflowinterrupt aktivieren*/

lcd_init(LCD_DISP_ON);
lcd_puts(" Los gehts ");delay_ms(2750);lcd_clrscr();

time=0;time1=0;time2=0;rd1=0;rd2=0;
sei(); //Interups Global an

while (1) {
_delay_us(980);
cli();time1++;time2++;sei();
//time++ //Gesamtzeit

//int_to_ascii(integer wert,output char,kommastelleverschieben wert,leerzeichen vor dem wert)
/*lcd_puts("out_buf");
lcd_puts("V ");
lcd_putc('[');
lcd_puts("lool");
lcd_gotoxy(0,1);*/
}
return(0);
}

askazo
21.08.2007, 14:43
Wenn Du das Prellen verhindern willst, musst Du die Interrupts in der ISR gleich als erstes deaktivieren. Das ganze Umrechnen und aufs LCD schreiben dauert ja im Vergleich zum Prellen ziemlich lange.

Gruß,
askazo

Geimel
21.08.2007, 14:51
okhabe ich gamacht, macht aber anscheinend keinen Unterschied. Geht das erstmal prinzipiell mit dem deaktivieren und besonders das reaktivieren so?
also das GICR|=GICR|(1<<6);habe ich mir mal aus dem Hut gezaubert,
weiß aber nicht ob das so geht und zulässig ist.

askazo
21.08.2007, 15:22
Achso, da habe ich eben gar nicht so drauf geachtet.... :rolleyes:
Mit
GICR |= (1<<6)
aktivierst Du den Interrupt.
Um ihn zu deaktivieren, müsstest Du
GICR &= ~(1<<6)
schreiben.

Was mir noch aufgefallen ist:
Warum verwendest Du für die Zeitmessung keinen Timer? Das ist doch die Paradeanwendung für einen Timer. Und viel genauer als mit delay.

askazo

manu_f
21.08.2007, 15:24
Hallo,

vllt ne blöde Idee aber hast du schon mal cli(); probiert?

Gruß
Manu

Geimel
21.08.2007, 15:30
mit cli deaktiviere ich ja global die interrupts, dann werden ja beide externen interrupts lahmgelegt. Ich möchte aber nur jeweils einen ausschalten, da der anderen noch für die zeitmessung des anderen Kontakts benötigt wird. Wenn jetzt der zweite externe Interrupt ausgelöst werden soll und der erste ist noch im Entprellungszeitraum, dann soll das ja auch gehen. Mit cli() geht das meines Wissens nach nicht.