Forum: Mikrocontroller und Digitale Elektronik Mehrere HC-SR04 Sensoren auslesen


von Kevin B. (bucki)


Angehängte Dateien:

Lesenswert?

Hallo liebe Forumsgemeinde,

ich bin ein ziemlicher Anfänger was AVR Programmierung anbelangt und 
stehe gerade vor einem Problem, bei dem ich eure Hilfe erhoffe. Ich 
verwende eine Atmega88-PU als Slave für einen Rasenroboter, dieser soll 
6 verschiedene HC-SR04 Ultraschallsensoren nacheinander auswerten und 
danach außerdem die Drehzhal der beiden Antriebsmotoren erfassen und 
beide Infos an den Master (auch ein Atmega88-PU) via UART schicken, wenn 
dieser danach fragt. Kernproblem ist hier aber die Sensorauswertung. Der 
restliche Code muss erst noch geschrieben werden.

jetzt wollte ich diesen so verwenden, das jeder Triggereingang einen PIN 
vom Atmega geschenkt bekommt, die Echo Leitungen aber über Dioden und 
Pull Down Widerstände zusammengefasst werden. ( Da es ja nur 3 wirkliche 
externe Interrupts gibt, Pin Change wollte ich Zwecks des "was ist nun 
für ein Zustand?" nicht verwenden )

Randbedingung ist außerdem, dass INT0 und INT1 nicht verfügbar sind, da 
an diesen die Drehzahl über Inkrementalgeber für die Motoren ermittelt 
werden sollen.

Nun allgemein die Frage ob der Code so funktionieren könnte?
Und eher zwei speziellere die das alles schon scheitern lassen könnten:

1) Was passiert, wenn der Timer 1 bereits schon läuft ( um die Messung 
für Sensor x nach ca 35ms zu unterbrechen und den nächsten Sensor x+1 zu 
triggern ), nun aber durch den Analogkomparator ein weiterer Start des 
Timer 1 ausgelöst wird? ( dieser soll die steigende Flanke und fallende 
Flanke des Echos erfassen ) Beginnt dieser einfach erneut bei 0 zu 
zählen oder zählt er gemütlich weiter? Oder geht das an sich nicht?

2) Kann man, so wie ich es momentan versuche, während der Ausführung der 
ISR für den Analogkomparator durch Triggerung und auftreten einer 
steigenden Flanke an AIN0 den Komparator während der ISR die Triggerung 
auf eine fallende Flanke umstellen, sodass damit eine Zeitenmessung 
möglich ist?

Entschuldigung für die doofe Darstellung, ich weiß nur leider nicht wie 
ich den Code hier schöner einbinden könnte.
Ich hoffe die Pfiffigen unter euch können und wollen mir Amateur helfen. 
:D Schon einmal vielen Dank an euch!

Grüße
Kevin

von Dennis K. (scarfaceno1)


Lesenswert?

Hi,

es war nicht leicht deinen Ausführungen zu folgen, aber ich versuche mal 
mein bestes.

Deinen Code so anzusehen ist mir nicht wirklich gelungen. Warum hast du 
statt den ganzen png Dateien nicht einfach die Quellcode Datei 
angehängt?

1) Interrupts sind im laufenden Interrupt gesperrt es sei denn du gibst 
sie explizit frei. Tritt während eines laufenden Interrupts ein weiterer 
erfasst, wird dieser sofort bearbeitet, sobald der 1. abgearbeitet ist.

2) Hab ich nicht ganz verstanden, aber ich sag mal was. Du kannst doch 
wenn AIN0 auf eine steigende Flanke ausgelöst wurde einfach im Interrupt 
direkt umkonfigurieren, dass nun auf fallende Flanke regiert werden 
soll.

Falls du den Flankenwechsel schneller erwartest, als ihn der der 16MHz 
getaktete AVR verarbeiten kann musst du den Controller wechseln.
Halte ich aber für unwahrscheinlich.

von Kevin B. (bucki)


Lesenswert?

Erstmal danke dir Dennis! Wie binde ich das denn als Quellcode ein?
Ich probiere mal etwas:
1
/*
2
 * Logik Rasenroboter.c
3
 *
4
 * Created: 10.04.2017 14:25:59
5
 * Author : kbuckena
6
 */ 
7
8
#include <avr/io.h>
9
#include <util/delay.h>
10
#include <stdio.h>
11
#include <stdlib.h>
12
#include <avr/interrupt.h>
13
#include <avr/iom88.h>
14
15
#ifndef 
16
#define F_CPU 16000000UL;    /* Zum Test noch auf 8000000UL*/
17
#endif
18
19
#define _ECHO_ PD6;
20
21
uint16_t Umdrehung=0;
22
int i=0;
23
int x, Hindernis;
24
uint16_t Dauer;
25
26
ISR(INT1){
27
    
28
  Umdrehung++; // Wenn steigende Flanke, dann eine Umdrehung erreicht
29
  
30
}
31
32
uint16_t Drehzahlerfassung(uint16_t *DRZ_1, uint16_t *DRZ_2, int *Richtung1, int *Richtung2){
33
  
34
  // Timer0 für diese Messung verwenden, außerdem mit Overflow um die 8 Bit "virtuell zu vergrößern"
35
    
36
  sei(); // Gloable Interrupts einschalten
37
   
38
  EICRA = (1<<ISC10) | (1<<ISC11); // steigende Flanke löst Interrupt an INT1 aus
39
  
40
  
41
}
42
43
int Sensorzuordnung(){
44
  
45
  switch (i)
46
  {
47
  // Sensor Vorn Rechts
48
  case 0: x= PB0;
49
    break;
50
  
51
  // Sensor Vorn Links  
52
  case 1: x=PB1;
53
    break;
54
  
55
  // Sensor Hinten Rechts  
56
  case 2: x=PB2;
57
    break;
58
  
59
  // Sensor Hinten Links  
60
  case 3: x=PB3;
61
    break;
62
  
63
  // Sensor Vorn Unten  
64
  case 4: x=PB4;
65
    break;
66
  
67
  // Sensor Hinten Unten  
68
  case 5: x=PB5;
69
    break;
70
    
71
  }
72
  
73
  return x; // anzusprechenden Sensor zurückgeben
74
}
75
76
int Gegenstandserkennung(int *Himmelsrichtung, int* Hindernis_da){
77
78
  Sensorzuordnung();
79
  
80
  ACSR = (1<<ACIE) | (1<<ACIC) ; // Enable Analog Komparator, bei Triggerevent Timer 1 auslösen
81
  ACSR = (1<<ACIS1) | (1<<ACIS0); // Analog Komparatorauf steigender Flanke triggern
82
  TCNT1 = 0; // Bei 0 loszäheln
83
  
84
  sei(); // Enable Globale Interrupt Flags
85
  
86
  while (i < 6 && Hindernis != 1) // solange nicht alle Sensoren durchgeschaut sind und kein Hindernis erkannt worden ist, Miss die Entfernung
87
  {
88
  
89
    PORTB &= ~(1<<x);      // Sensor Initialisierung
90
    _delay_us(3);
91
    PORTB = (1<<x);
92
    _delay_us(10);
93
    PORTB &= ~(1<<x);
94
    _delay_us(200);
95
  
96
    OCR1A = 8750; // 8750 Ticks sind bei 250kHz ca. 35ms, also der TOP Wert für kein Echo Empfangen
97
    TCCR1B = (1<<CS11) | (1<<CS10); // Prescaler Timer 1 auf 64 setzen --> 250kHz und Timer 1 starten 
98
    // _delay_ms(35); ????
99
    
100
  }
101
  
102
  cli();
103
  ACSR = (1<<ACD); // Den Analog Komparator wieder ausschalten
104
  i=0;
105
  
106
  *Himmelsrichtung = x;      // Hier von sehen ob Vorn oder Hinten oder Mäher angehoben
107
  *Hindernis_da = Hindernis;    // es ist ein Hindernis da
108
    
109
}
110
111
ISR(TIMER1_OVF_vect){ // Falls Zeit Überschritten und kein Echo, beginne wieder bei 0 zu zählen und mache beim nächsten Sensor weiter
112
  
113
  Dauer=0;
114
  TCNT1 = 0;
115
  TCCR1B &= ~(1<<CS11)      // um Timer 1 auszuschalten
116
  TCCR1B &= ~(1<<CS10)
117
  i++;              // zum nächsten Sensor schalten
118
119
}
120
121
ISR(ANALOG_COMP_vect){  // Wenn nun Triggerflanke eintrifft
122
  
123
  if (ACIS0==1) // Falls noch auf steigende Flanke gestellt
124
  {
125
    ACSR = (1<<ACIS1) | (0<<ACIS0); // Analog Komparator auf fallende Flanke triggern  
126
    TCNT1 = 0; // Trigger könnte ja kurz vor Overflow kommen, also Trigger "rücksetzen" und erst nach doppelter Dauer Overflow Interrupt auslösen
127
    
128
  }
129
  else{
130
    Dauer=TCNT1; // Dauer erhält hochgezählten Wert von Timer/Counter1
131
    Hindernis=1;
132
  }
133
  
134
}
135
136
int main(void)
137
{
138
  uint16_t DRZ_1, DRZ_2;
139
  int Himmelsrichtung, Hindernis_da, Richtung1, Richtung2;
140
  DDRB = (1<<PB0) | (1<<PB1) | (1<<PB2) | (1<<PB3) | (1<<PB4) | (1<<PB5); // Sensorausgänge setzen
141
  DDRD &= ~(1<<_ECHO_); // Echoeingang für Sensoren setzen
142
  DDRD = (0<<PD4) | (0<<PD5); // Richtungseingänge für Drehrichtung setzen 
143
  
144
  TCCR1B &= ~(1<<WGM13); // WGM13 auf 0 um Normal Mode, also keine PWM, auszuwählen ( Timer 1 )
145
  //TCCR0B &= ~(1<<WGM02); // WGM02 auf 0 um Normal Mode, also keine PWM, auszuwählen ( Timer 0 )
146
  TIMSK1 = (1<<OCIE1A); // Compare Match Interrupt für Timer 1 aktivieren
147
  //TIMSK0 = (1<<OCIE0A); // Compare Match Interrupt für Timer 0 aktivieren
148
  
149
  while (1)    
150
  {
151
    Gegenstandserkennung(&Himmelsrichtung, &Hindernis_da);  
152
    if (Hindernis_da==1) // Überhaupt Hindernis da?
153
    {
154
      float Entfernung = (343*Dauer*0.000004)/2; // ( 343 m/s * Timer1 * 64/16MHz )/2 , da Echo hin und zurück und wir nur Hinweg haben wollen == Ergebnis in Meter
155
      
156
      
157
      if (Entfernung<=0.08) // Hindernis 8cm vor mir? Wenn ja aber Hoppa und stehenbleiben!
158
      {
159
    
160
        if (Himmelsrichtung==0 || Himmelsrichtung==1)
161
        {
162
        
163
          //Sende via UART "Vorn"
164
        }
165
  
166
        if (Himmelsrichtung==2 || Himmelsrichtung==3)
167
        {
168
          //Sende via UART "Hinten"
169
        }
170
    
171
        if (Himmelsrichtung==4 || Himmelsrichtung==5)
172
        {
173
          // Sende via UART "Mähwerk aus"
174
        }
175
      }
176
    }
177
    else{
178
      
179
      Drehzahlerfassung(&DRZ_1, &DRZ_2, &Richtung1, &Richtung2);
180
      
181
    }
182
    
183
    
184
            // Drehzahl aktuell senden
185
            
186
    Hindernis=0;  // Hindernis auf 0 zurücksetzen wenn in Gegenrichtung gefahren
187
      
188
  }
189
}

Ich hoffe das hat so funktioniert!

Schneller erwarte ich den Flankenwechsel keinesfalls. Ich möchte ja die 
Sensoren auf 8cm Entfernung Triggern, selbst mit Prescaler von 256 käme 
ich da auf eine so kurze Entfernung, die der HC SR04 schon gar nicht 
mehr erfassen kann.

Zu 1) Also söllte es wohl so funktionieren oder?
Zu 2)Sorry für die blöde Umschreibung. Also folgendes Szenario: Echo 
kommt an, da der Komparator auf steigende Flanke gestellt ist und der 
Timer 1 bereits läuft ( wird direkt nach der Triggerung des jeweiligen 
Sensors gestartet, um nach spätestens 35ms die Messung abzubrechen ( da 
kein Objekt in der Nähe ) und zum nächsten Sensor überzugehen) würde 
dieser den Timer 1 erneut starten, da ja das ACIC Bit gesetzt ist. Jetzt 
die Frage: startet der Timer einfach erneut von 0 an zu zählen oder 
macht dieser einfach ganz normal weiter oooder will der µC am Liebsten 
gar nichts von beiden tun und verweigert seine Arbeit?
Nun weiter: Da jetzt eine Flanke kam wird die ISR für den Analog 
Komparator gestartet. Dieser schaut mit if(ACIS0==1), ob dieser noch auf 
die steigende Flanke eingestellt ist und wenn ja, soll dieser während 
der ISR auf die fallende Flanke umgestellt werden und den Timer mit 
TCNT1=0 von 0 an neu zählen lassen. Jetzt geht das ECHO auf LOW wodurch 
die ISR des Analog Komparators erneut ausgeführt wird, diesmal springt 
er aber in den else Zweig und übergibt den aktuellen Wert des Timers und 
setzt zusätzlich Hindernis=1, da sich etwas vor dem Sensor befindet.

Danach wird alles dem Hauptprogramm übergeben, welches die Entfernung 
ausrechnet und bei einer Entfernung <= 8cm diese Information + die 
Richtung des Objektes, also Vorn oder Hinten, via UART an den Master 
schickt, welcher nun einen Bremsvorgang einleitet.

Ich hoffe diese Beschreibung war etwas ausführlicher und hilft dir das 
ganze besser zu verstehen. Die Farge besteht nur noch, ob das ganze so 
funktioniert, wie ich es mir denke bzw geschrieben habe.

Für weitere Hilfe würde ich mich weiter bedanken! :)

Grüße
Kevin

von Dennis K. (scarfaceno1)


Lesenswert?

Zu 1)

Ja das mit den Interrupts kann so funktionieren.

Zu 2)

Eine weitere Frage. Wenn du den Analog Comparator nutzt musst du ja 
beide Pins beschalten (AIN0 und AIN1). Ich denke es wäre einfacher über 
PCINT irgendeinen PIN zu nehmen und über EICRA (DB Seite 73) auf 
Interrupt bei "any logical change" zu konfigurieren.

ACIC zählt ja nur hoch wenn auch Ereignisse am Analog Comparator 
auftreten. Wenn also die Flanke auf High wechelt zählt er einmal und das 
nächste mal wenn du ihn umkonfiguriert hast und er die fallende Flanke 
zählt. dein Ergebnis ist immer 2. Egal wie lange das dauert.

Ich würde es so machen:
Messung starten an SR04 - 1
Timer starten
mit einem PCINT erfassen (kann auf steigend, fallend und any gesetzt 
werden)
Wert aus Timer holen
Wert speichern
Timer stoppen
Messung starten an SR04 - 2
...
...
...

Ich benutze den PCINT eigentlich sehr gerne:

ISR(PCINT0_vect) 
// PC0 -> PCINT8
{
    char x=0,y=0;
    temp_old = temp;
    test();
    x = temp;
    x &= 4;
    y = temp_old;
    y &= 4;
    if (!(x == y)) {

        if (x)
        {
            start0 = TCNT1;
        }
        if (!x)
        {
            stop0 = TCNT1;
            steer = stop0 - start0;
        }
    }
}

char test (void) {
    int_mask = PCMSK0;
    temp = PINB;
    temp &= int_mask;
    return temp;
}

Ein kleiner Code Ausschnitt um am PCINT festzustellen welcher Pin 
betätigt wurde macht die Funktion test().

Im Interrupt werte erfasse ich den Zählerstand des Timers und 
subtrahiere den Startwert vom Endwert (fallende - steigende Flanke).

Ich finde das recht simpel.
Kannst du dir ja mal überlegen.

von Kevin B. (bucki)


Lesenswert?

Hallo Bastelgemeinde,

Jetzt versuche ich gerade mehrere ( 6 Stück ) parallel laufen zu lassen. 
Die Auswertung eines Sensors über UART mit dem Atmega88p läuft 1A ( ohne 
Interrupts, nur über Pinzustände ). Nun frage ich mich aber, ob es einen 
"Pinsparenden" Weg gibt die Sensoren zusammen zu schalten?

Getriggert werden muss einzeln ( oder vielleicht mit Schieberegister 
möglich ), jetzt habe ich aber versucht die ECHO Ausgänge zusammen zu 
schalten. Direkt zusammen kommt nur 0 cm raus, mit Dioden kommen 
wiederum nur schwankende und sehr hohe Werte heraus ( ca. doppelt so 
viel ). Anzumerken sei hierbei, dass beim anstecken des zweiten Sensors 
bei beiden Varianten immer noch nur Sensor 1 getriggert und ausgewertet 
wurde. Also nur das physische Anschließen des zweiten Sensors hat die 
Werte schon so sehr verändert.

Kennt jemand einen einfachen Weg, wodurch ich die ECHO Ausgänge 
zusammenschalten kann?

LG,
Kevin

von Patrick J. (ho-bit-hun-ter)


Lesenswert?

Hi

Zur Not per Spannungsfolger, Inverter, OR-Gatter, Opto-Koppler, ... to 
be continue ... und per Dioden entkoppelt.


MfG

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.