PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : [ERLEDIGT] SRF05 auslesen liefert nur Ergebnis 0



Torrentula
28.12.2011, 13:40
Hallo RNler!

Da ich mal wieder Plane einen SF05 einzusetzen habe ich ein Programm zum auslesen geschrieben, da das Programm aus einem anderen Thread nicht funktionierte (immer mal wieder Ergebnis 0).

Das Programm soll wie folgt funktionieren:

1. Messung wird per 12µs-Impuls an SRF05 ausgelöst
2. Externer Interrupt 0 wird auf steigende Flanke konfiguriert
3. INT0 wird zum ersten mal ausgelöst und startet den Timer, welcher selber alle 10µs einen Interrupt auslöst; gleichzeitig wird INT0 auf fallende Flanke konfiguriert
4. INT0 wird nun bei fallender Flanke ausgelöst und stoppt den Timer --> variable measurement_complete wird nun auf 1 gesetzt
5. Die Dauer des Impulses in µs wird durch 58 geteilt um Entfernung in cm zu erhalten und die Entfernung wird per RS232 an den PC gesendet

Ich erhalte jedoch immer eine 0 im Terminal. Wenn ich den Timer auf 10Hz umgestellt habe, haben sich einfach alle Werte aufaddiert.

Hier mein Code:


#ifndef F_CPU
#define F_CPU 16000000UL
#endif

#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include <stdlib.h>
#include "standards.h" // hier sind init_USART() und sendchar() definiert

#define SRFout PD7
#define SRFin PD2

volatile unsigned int microseconds = 0; // um die Pulslänge zu speichern
volatile uint8_t INT0_interrupt = 0; // wird benutzt um zwischen Pulbeginn und Pulsende zu entscheiden
volatile uint8_t measurement_complete = 0; // wird in der hauptschleife gepollt um Abschluss der Messung festzustellen

int main(void)
{
init_USART();

// Timer einstellen
TCCR0A = (1<<WGM01); // CTC Mode of Timer 0
// ((16000000 / 1024) / 100) -1 = 155
OCR0A = 155; // 155 steps = interrupt ca. alle 10µs

TIMSK0 |= (1<<OCIE0A); // Enable compare interrupt
//~~~~~~~~~~~~~~~~~

EIMSK |= (1<<INT0); // External interrupt mask register INT0 aktivieren

DDRD |= (1<<SRFout); // Auslösepin auf Ausgang
DDRD &= ~(1<<SRFin); // INT0 pin muss auch auf input stehen

// Variablen
uint16_t distance = 0;

unsigned char buffer[10];

while(1)
{
// Auslösen der Messung:
PORTD |= (1<<SRFout);
_delay_us(12); // 12µs Auslöse-Signal
PORTD &= ~(1<<SRFout);

// external interrupt 0 bei steigender flanke auslösen:
EICRA = (ISC01) | (1<<ISC00);

sei(); // alle interrupts aktivieren

while(measurement_complete != 1){
// auf Abschluss der Messung warten
}
measurement_complete = 0;

distance = microseconds / 58;

itoa(distance, buffer, 10);
sendUSART(buffer);
sendchar('\n');

distance = 0; // distance wieder zurücksetzen, sonst werden Messungen aufaddiert
microseconds = 0; // microseconds ebenfalls zurücksetzen
_delay_ms(1000);
}
}

ISR(INT0_vect){ // external interrupt 0

if(INT0_interrupt == 0){ // nur wenn noch nicht bei steigender Flanke ausgelöst wurde
TCCR0B |= (1<<CS02) | (1<<CS00); // Timer starten mit Prescaler 1024

// umschalten interrupt bei fallender Flanke:
EICRA = (1<<ISC01); // EICRA = External Interrup Control Register A

INT0_interrupt = 1; // da nun bei steigender Flanke bereits getriggert wurde
}
else{ // wenn bereits ein INT0 interrupt erfolgte
TCCR0B &= ~((1<<CS02) | (1<<CS00)); // Timer stoppen

cli(); // alle interrupts sperren

INT0_interrupt = 0; // Variable zurücksetzen
measurement_complete = 1; // Variable um in der Hauptschleife den Abschluss der Messung festzustellen
}
}

//gibt uns einen Interrupt ca. alle 10µs
ISR(TIMER0_COMPA_vect){
microseconds += 10;
}


MfG

Torrentula

Chypsylon
28.12.2011, 14:41
Ich hab mir deinen code jetzt nicht genauer angeschaut aber vielleicht hilft dir der code den ich mal für einen hc-sr04 (fake von srf04) geschrieben habe weiter. Die Trigger zeit musst du vermutlich ändern, der rest dürfte so funktionieren....


/******
atmega32@16MHZ
******/

#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include <stdlib.h>

#include <lcd.h>

volatile uint16_t timestamp_last = 0;
volatile uint16_t zeit = 0;

/*Messung starten*/
void trig(void)
{
PORTC |= (1<<PC5);//Trig high
_delay_us(12);
PORTC &= ~(1<<PC5);//TRIG auf low
}



ISR(TIMER1_CAPT_vect)
{
//Wenn steigende Flanke
if(TCCR1B & (1<<ICES1))
{
//Flankenerkennung auf fallend
TCCR1B ^= (1<<ICES1);
//aktuelen timer-wert speichern
timestamp_last = ICR1;
}
//fallende Flanke
else
{
//Flankenerkennung auf steigend
TCCR1B ^= (1<<ICES1);
//Laufzeit = aktueller timerwert - vorheriger timerwert
zeit = ICR1 - timestamp_last;
}

}


int main(void)
{
DDRC |= (1 << PC5);//Trig als Ausgang
PORTC &= ~(1<<PC5);//TRIG auf low

DDRD &= ~(1<<PD6);//Echo als Eingang
PORTD &= ~(1<<PD6);//ECHO pullup AUS

lcd_init(LCD_DISP_ON);

lcd_puts("US Test");
lcd_gotoxy(0,1);

//Timer konfigurieren
TCCR1A = 0; // normal mode, keine PWM Ausgänge
//Noise Canceler aktivieren, Flankenerkennung auf steigende, Prescaler auf 64
TCCR1B |= (1<<ICNC1) | (1<<ICES1) | (1<<CS11) |(1<<CS10);

//ICP Interrupt aktivieren
TIMSK |= (1<<TICIE1);

//Globale Interrupts aktivieren
sei();

while(1)
{
//Entfernung aus Laufzeit berechnen
zeit = (zeit*4)/58;

lcd_puts(" ");
lcd_gotoxy(0,1);
lcd_put_uint16(zeit);
lcd_gotoxy(0,1);

//Messung auslösen
trig();

_delay_ms(50); //mit dem start der nächsten messung warten bis signal zerstreut
}

return 0;
}

Torrentula
28.12.2011, 16:58
Ich wollte zu Beginn auch die Input Capture Unit benutzen, habe aber irgendwie das mit der ICU nicht ganz verstanden.

Mit welchem Systemtakt hast du gearbeitet, du stellst ja den Prescaler auf 64 ein.

MfG

Torrentula

Chypsylon
28.12.2011, 18:12
Mit welchem Systemtakt hast du gearbeitet, du stellst ja den Prescaler auf 64 ein.


mit 16MHZ.

Was ich vorher vergessen habe, damit du input capture benutzen kannst muss der Ausgang des US an den icp-pin angeschlossen sein. Beim atmega32 ist das PD6.
Welchen Controller verwendest du?

Torrentula
28.12.2011, 19:04
Ich verwende den ATmega644 bei dem ist es auch der PD6, vielen Dank für den Hinweis!

Der Code funzt prima danke!

Warum klappt das bei anderen immer nur bei mir nicht...

MfG

Torrentula

sternst
28.12.2011, 19:12
Bei einem ersten Überfliegen deines Codes sehe ich diverse Probleme:

1)
EICRA = (ISC01) | (1<<ISC00);Siehst du da nicht irgendwie einen entscheidenden Unterschied rechts und links vom Oder?

2)
cli(); // alle interrupts sperrenUnwirksam. An der Stelle sind die Interrupts eh gesperrt, und am Ende der ISR werden sie auf jeden Fall wieder zugelassen.

3)
Der Timer wird beim Neustart nicht auf Null gesetzt.

4) (und möglicherweise der Hauptgrund für das immer 0)
Du stellst den Timer auf 10 MILLIsekunden ein.

Torrentula
28.12.2011, 19:18
Danke sternst! Manchmal sieht man den Wald vor lauter Bäumen nicht...

Funktioniert jetzt prima.

MfG

Torrentula