Forum: Mikrocontroller und Digitale Elektronik SRV05 Ultraschallsensor


von Jens K. (mister232)


Lesenswert?

Ich versuche mich gerade daran einen Ultraschallsensor vom Typ SRV05 an 
einem ATmega328 zum laufen zu bringen. Folgender Code ist für das 
Handling des SRV05 zuständig:
1
/*
2
 * SRF05.c
3
 *
4
 * Created: 08.03.2016 11:36:49
5
 *  Author: Admin
6
 */ 
7
8
#include "SRF05.h"
9
#include "global.h"
10
#include "EDIP.h"
11
#include "uart.h"
12
#include <avr/io.h>
13
#include <util/delay.h>
14
#include <stdio.h>
15
#include <string.h>
16
17
18
#define TRIGGER PB1
19
#define MEASURE PD3
20
21
volatile unsigned int microseconds = 0; // to save pulse length
22
volatile uint8_t INT1_interrupt = 0; // to indicate whether an INT1 interrupt has already occurred
23
volatile uint8_t measurement_complete = 0; // is used to indicate that the measurement is complete
24
25
// Function to initialize the SRF05-Interface
26
void SRF05_init(void)
27
{
28
  // Setup timer 1
29
  TCCR1A = (1<<WGM12); // CTC Mode of Timer 1
30
  // (8000000 / 8 ) / 10 = 1000 -> Every 1ms
31
  OCR1A = 10 - 1; // Interrupt fires every 10us
32
  TIMSK1 |= (1<<OCIE1A); // Enable compare interrupt
33
  
34
  DDRB |= (1<<TRIGGER); // set trigger pin to output
35
  
36
  DDRD &= ~(1<<MEASURE); // set INT1 as input
37
  EICRA |= (ISC10) | (ISC11);
38
  EIMSK |= (1<<INT1);
39
}
40
41
42
// Check distance
43
void SRF05_Measure(void)
44
{
45
  uint16_t dist = 0;
46
  char distanceBuf[20];
47
      
48
  dist = 0; // Reset distance
49
  microseconds = 0; // Reset ms counter
50
  TCNT1 = 0; // Set timer 1 to 0
51
52
  // Trigger the SRF05 measurement
53
  PORTB |= (1<<TRIGGER);
54
  _delay_ms(1); // 1ms trigger signal
55
  PORTB &= ~(1<<TRIGGER);
56
  uart_send("Trigger\n\r", 9);
57
58
  while(measurement_complete != 1); // wait until measurement is complete
59
60
61
  measurement_complete = 0; // Reset
62
63
  dist = microseconds / 29  ;
64
65
  // Print distance to LCD
66
  sprintf(distanceBuf, "Distanz: %ucm", dist);
67
  LCD_clearLine(0x11);
68
  LCD_Puts(distanceBuf, strlen(distanceBuf), 0x00, 0x11, 'L');
69
}
70
71
72
// Interrupt for signal from SRF05
73
ISR(INT1_vect)
74
{
75
   // Only if there hasn't been triggered on a rising edge yet
76
  if(INT1_interrupt == 0)
77
  {
78
    TCCR1B |= (1<<CS11); // Start timer with prescaler 8
79
    EICRA |= (1<<ISC11); // Set INT1 to trigger in falling edge
80
    EICRA &= ~(1<<ISC10);
81
82
    INT1_interrupt = 1; // To indicate, that an interrupt has already occurred
83
  }
84
  else
85
  {
86
    TCCR1B &= ~(1<<CS11); // Stop Timer
87
88
    INT1_interrupt = 0; // Reset variable
89
    measurement_complete = 1; // Variable to indicate end of measurement
90
  }
91
92
  
93
  EIFR = (1<<INTF1);
94
}
95
96
97
//Interrupt every 10µs
98
ISR(TIMER1_COMPA_vect)
99
{
100
  microseconds += 10;
101
}

Leider funktioniert es nicht so richtig. Auf dem LA sehe ich, dass der 
SRV05 sein Startsignal bekommt und brav darauf antwortet. Der µC zeigt 
mir nur einmal 0cm an, beim zweiten Aufruf der Funktion sendet er das 
Startsignal und hängt sich auf. Auf dem LA sehe ich, dass aber auch die 
zweite antwort vom SRV05 korrekt gesendet wird

von N. G. (newgeneration) Benutzerseite


Lesenswert?

ein Tipp von mir:

der SRF05 gibt ja pro cm 58us high zurück, also: lass einen Timer alle 
58us ein hoch zählen (ab einfachsten im CTC-mode)

Ich würde die PCInts nehmen (nehme ich generell lieber). Diese reagieren 
auf jeden Flankenwechsel.
D.h. bei der Steigenden Flanke speicherst du dir den 58us-Timerwert ab.
Bei einer fallenden Flanke ziehst du den aktuellen Timerwert vom 
gespeicherten ab.
UND: das ist deine Distanz in Zentimetern.

Denke allerdings dran, dass wenn du den Mode mit nur einem 
Auslöse-/Echo-Pin nimmst, du vor dem Start-Signal die PCInts abschalten 
musst und danach wieder ein.

Zu deinem Code:
warum nutzt du Interrupts, wenn du sowieso auf das Ergebnis wartest?
Dann könnte man einfach pollen und dein Problem würde wahrscheinlich 
einfacher zu lösen sein.
1
//liefert die Entfernung zum Objekt in cm
2
void srf05_get_distance(void) {
3
    PORT_START_PIN |= (1 << START_PIN);
4
    _delay_ms(1);
5
    PORT_START_PIN |= (1 << START_PIN);
6
    while (! (PIN_MEASURE_PIN & (1 << MEASURE_PIN))) ; //auf steigende Flanke warten
7
    uint16_t starttime = currentTick58us(); //den aktuellen Timer-wert speichern
8
    while (PIN_MEASURE_PIN & (1 << MEASURE_PIN)) ; //auf fallendeFlanke warten
9
    return currentTick58us() - starttime; //das ist dann schon die Distanz
10
}

von Jens K. (mister232)


Lesenswert?

Vielen Dank. Werde ich gleich mal ausprobieren. Ich habe gerade gemerkt, 
dass ich überall SRV05 statt SRF05 geschrieben habe...ups :-)

von Jens K. (mister232)


Lesenswert?

Habe den COde jetzt mal wie folgt abgeändert:
1
/*
2
 * SRF05.c
3
 *
4
 * Created: 08.03.2016 11:36:49
5
 *  Author: Admin
6
 */ 
7
8
#include "SRF05.h"
9
#include "global.h"
10
#include "EDIP.h"
11
#include "uart.h"
12
#include <avr/io.h>
13
#include <util/delay.h>
14
#include <stdio.h>
15
#include <string.h>
16
17
18
#define TRIGGER PB1
19
#define MEASURE PD3
20
21
uint16_t timerCNT = 0;
22
23
// Function to initialize the SRF05-Interface
24
void SRF05_init(void)
25
{
26
  // Setup timer 1
27
  TCNT1 = 65478; // Preload
28
  TCCR1B |= (1<<CS11); // Prescaler 8
29
  TIMSK1 |= (1<<TOIE1); // Enable overflow interrupt
30
  
31
  DDRB |= (1<<TRIGGER); // set trigger pin to output
32
}
33
34
35
// Task, which periodically checks the distance
36
void SRF05_Measure(void)
37
{
38
  uint16_t dist = 0;
39
  char distanceBuf[20];
40
      
41
  dist = 0; // Reset distance
42
43
  // Trigger the SRF05 measurement
44
  PORTB |= (1<<TRIGGER);
45
  _delay_ms(1); // 1ms trigger signal
46
  PORTB &= ~(1<<TRIGGER);
47
48
49
  while(!(PIND & (1<<MEASURE))); // Wait for rising edge
50
  timerCNT = 0; // Reset timer 1
51
  while(PIND & (1<<MEASURE)); // Wait for falling edge
52
    
53
  dist = timerCNT;
54
55
  // Print distance to LCD
56
  sprintf(distanceBuf, "Distanz: %dcm", dist);
57
  LCD_clearLine(0x32);
58
  LCD_Puts(distanceBuf, strlen(distanceBuf), 0x00, 0x32, 'L');
59
}
60
61
62
//Interrupt every 58µs
63
ISR(TIMER1_OVF_vect)
64
{
65
  timerCNT++;
66
}

Jetzt funktioniert die Messung zwar dauerhaft, aber leider kommt immer 
nur 0cm raus. Ein test zeigt, dass er in der Wartezeit garnicht 
hochzählt.

von Tom (Gast)


Lesenswert?

Jens K. schrieb:
> Ein test zeigt, dass er in der Wartezeit garnicht
> hochzählt.

Ich sollte mal einen Bot programmieren, der immer diesen Link postet ;)
https://www.mikrocontroller.net/articles/AVR-GCC-Tutorial#Datenaustausch_mit_Interrupt-Routinen

von N. G. (newgeneration) Benutzerseite


Lesenswert?

Hallo nochmal,

dein Timer stimmt leider noch nicht.

Jens K. schrieb:
> uint16_t timerCNT = 0;

volatile fehlt, sonst kriegt der Compiler das ändern in der ISR nicht 
mit, bzw. er optimiert es, da er denkt, dass die Variable nie geändert 
wird.

Jens K. schrieb:
> // Setup timer 1
>   TCNT1 = 65478; // Preload
>   TCCR1B |= (1<<CS11); // Prescaler 8
>   TIMSK1 |= (1<<TOIE1); // Enable overflow interrupt

der Preload ist zwar gut gemeint, gilt aber leider nur bis zum ersten 
Overflow, ab da zählt der Timer wieder schön von 0 hoch.

Jens K. schrieb:
> //Interrupt every 58µs
> ISR(TIMER1_OVF_vect)
> {
>   timerCNT++;
> }
Du müsstest dann hier auch einen Preload machen.
Aber warum das? Setze dich mal mit dem CTC mode auseinander.

Dieser setzt den Timer beim erreichen der ISR automatisch auf 0 zurück.
Ein Beispiel:
1
TIMSK5 = (1 << OCIE5A);
2
TCCR5B = (1 << CS51) | (1 << WGM52);  //prescaler 8, CTC
3
OCR5A = 58 * 2; //58us@16MHz

Aber eigentlich sollte mit einem richtigen Timer der Code klappen

von Jens K. (mister232)


Lesenswert?

Super, jetzt klappt es. Vielen Dank!

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.