HC-SR04 Driver

Ein Ultraschallabstandssensor ist für mein aktuelles Projekt recht praktisch und den HC-SR04 hatte ich noch in der Bastelkiste liegen.

Der Ultraschallsensor funktioniert recht simpel: Auf eine fallende Flanke am Trigger-Eingang wird das Signal abgesetzt. Nach einer kurzen und definierten Verzögerung (Offset), wir der Echo-Ausgang auf High gesetzt, so lange bis das Echo des Ultraschalls zurück kehrt. Sollte das Signal verloren sein, z.B. bei zu weiten Entfernungen oder bei zu weichen Oberflächen, bleibt der Echo-Pin für 200 ms auf High. Dies signalisiert den Fehler.

Eigentlich ganz einfach. Einen fertigen Treiber dafür zu finden und zu übernehmen, sollte ja schnell gemacht sein. Die ersten google Treffer waren leider nicht überzeugen. Alles sehr kompliziert geschrieben. Zu viel Quellcode für zu wenig Funktion deutet darauf hin, dass der Sensor nicht verstanden wurde oder die Programmiererfahrung nicht ausreichend ist.

Nagut, also doch einen eigenen kleinen Treiber schreiben. Es sind ja auch nur meine Ansprüche an den Treiber und funktionieren tun sie hoffentlich alle.

Mein Projekt beruht auf einem CortexM4 (STM32F4) und mittlerweile habe ich mich ganz gut an den CubeMX von ST Microelectonics gewöhnt, wahrscheinlich weil einige Kinderkrankheiten mittlerweile beseitigt sind. CubeMX geniert zu einzelnen Peripheriebetandteilen Quellcode. Die GPIOs können zum Beispiel bequem per GUI ausgewählt werden, wie die Timer auch.

Zugegeben, auch deswegen ist mein Treiberquellcode schmal und enthält nicht die Initialisierung der Hardware. Die Initialisierung ist aber schnell erklärt.

CubeMX oder eigene Konfig (andere µC)

Einen Timer mit PWM-Funktion anlegen. Das Duty Cycle wird auf ca. 0,5 gesetzt (nicht kritisch) und der Timer muss aller 20 ms überlaufen. Praktisch ist es, wenn dabei ein Timer-Maximalwert von 20000 eingestellt werden kann. Das rechnet sich leichter (für Menschen), jedoch kann der Timer-Vorteiler nicht bei jedem Mikrocontroller so genau eingestellt werden. Über die 20 ms sollten aber genug Auflösung gegeben sein -> möglichst hoher Timer-Endwert.

Der PWM-Pin des Timers triggert das Echo. Die fallende Flanke muss beim Timeroverflow passieren. Evtl. muss dazu der Ausgang invertiert werden (im Timer Modul).

Der Overflow-Interrupt des Timers sollte Konfiguriert werden.

Der Echo-GPIO-Eingang sollte ein Interrupt bei fallender Flanke auslösen.

Der Quellcode

static volatile int hcsr04_timestamp = 0;
static volatile int overflow_flag = 0;

//timer PWM (DC 0,5) falling edge at overflow (trigger pin) -> 20 ms interval = 20000 digits
//and falling edge gpio interrupt (echo pin)
//ready to run
//if not, set it up in this function
void hcsr04_startMeasure(void)
{
 //start the timer, to measure the echo flight time right after the falling trigger edge
 __HAL_TIM_ENABLE_IT(&htim12, TIM_IT_UPDATE);
 HAL_TIM_PWM_Start(&htim12, TIM_CHANNEL_1);
}

//returns actual distance or if a error occurs 0 mm
uint32_t hcsr04_getLastDistance_mm(void)
{
 if(hcsr04_timestamp == 0) return 0; // timer overflow signal -> problem with measurement
 int ret = ((hcsr04_timestamp * 343) / 1000 - 154) / 2; //mm per 10 us - 154 mm offset -> double distance => / 2
 return ret < 0 ? 0 : ret > 2000 ? 0 : ret;
}

//callback, use it in timer overflow interrupt
void hcsr04_cb_timeroverflow(void)
{
 //check for sensor error (200 ms timeout)
 overflow_flag++;
}

//callback, use it in pin interrupt (falling edge)
void hcsr04_cb_pin_fallingedge(void)
{
 if(overflow_flag > 1 || overflow_flag < 0)
 {
  //discard one more measure after 200 ms timeout
  overflow_flag = -1;
  hcsr04_timestamp = 0;
 } else {
  //save the value of the timer, to calculate the distance
  hcsr04_timestamp = (overflow_flag != 1) ? 0 : htim12.Instance->CNT;

  //reset overflow flag
  overflow_flag = 0;
 }
}

Die zwei Funktionen mit dem Namen cb, sollten bei den entsprechenden Interrupt-Funktionen aufgerufen werden.

Folgender Test-Quellcode list die Distanz aus:

//start timer
 hcsr04_startMeasure();
 while(1)
 {
 printf("Distance: %d\n", (int)hcsr04_getLastDistance_mm());
 HAL_Delay(200); //delay 200 ms
 }

Erklärung zum Treiber:

Sollte der Timer-Interrupt öfter auftreten als der GPIO-Interrupt , liegt eine Messstörung vor. Damit diese erkannt wird und 0 mm ausgegeben werden kann, zählt das overflow_flag die Überläufe. Außerdem hat sich herausgestellt, dass manchmal eine gültige aber falsche Messung direkt nach einem Overflow auftritt (auch auf dem Oszi gesehen). Das overflow_flag verwirft daher die erste Messung nach einem Overflow ebenfalls. Natürlich könnte man es auch so umschreiben, dass immer die letzte gültige Messung ausgegeben wird, jedoch bekommt dann das Anwendungsprogramm darüber keinen Hinweis. Bei einem längeren Signalverlust könnte das zu einem Problem werden, um welches sich nicht der Treiber sondern die Applikationsschicht kümmern muss.

Jede Mikrosekunde (Timer-Tick) bewegt sich der Schall bei 20 °C mit einer Geschwindigkeit von ca. 0,343 mm. Die Berechnung sorgt für eine rundungsfehlerfreie Ausgabe. Dazu muss man nur die Formel so umstellen, dass Divisionen möglichst spät und damit durch hohe Zahlen durchgeführt werden. Die 154 mm sind der Offset der durch die langsame Sensorreaktion entsteht (siehe Datenblatt).

Abschließend

Dadurch, dass kaum Hardwarespezifische Dinge im Quellcode getan werden, sollte dieser Treiber problemlos auf alle Mikrocontroller portiert werden können.

Dieser Beitrag wurde unter Allgemein veröffentlicht. Setze ein Lesezeichen auf den Permalink.

2 Antworten zu HC-SR04 Driver

  1. Pingback: Robot controlled by artificial neural network | Electronic Stuff

  2. Stefenzo schreibt:

    Hallo, ich teste den HC-SR04. was mir auffällt, der Sensor reagiert nicht unter allen Umständen auf die negativ Flanke. Ohne Reflexionsobjekt funktioniert alles ohne Probleme. Mit nahem Objekt funktioniert auch alles ohne Probleme. Aber mit einem Abstand von ca. 40cm (bisschen mehr oder weniger), reagiert der Sensor nur auf jede zweite Flanke. Den negativ Impuls zum Auslösen habe ich zwischen 20μS und 400μS Dauer geändert. Die Impulshäufigkeit zwischen 10 und einem Impuls pro Sekunde. Aber bei einem bestimmten Abstand löst der Echo Impuls nicht zuverlässig aus. Beobachten konnte ich das mit einem Logic Analyser. Bedeutet, meine Software am Arduino funktioniert zuverlässig. der auslöse Impuls (negativ Flanke) kommt, trotzdem antwortet der Sensor nicht immer mit einem Echo Impuls, der Echo Ausgang bleibt einfach aus.interessanterweise konnte ich das gleiche Verhalten mit zwei Sensoren von verschiedenen Herstellern feststellen. Ist das Verhalten jemandem bekannt?

    Gruß Stefenzo

Hinterlasse einen Kommentar