sensor_sr04: add library for HC-SR04 ultrasonic range sensor
This commit is contained in:
parent
8526dc084b
commit
11f5bc9771
159
lib/sensor_sr04.c
Normal file
159
lib/sensor_sr04.c
Normal file
@ -0,0 +1,159 @@
|
||||
/** library to determine range using HC-SR04 ultrasonic range sensor
|
||||
* @file
|
||||
* @author King Kévin <kingkevin@cuvoodoo.info>
|
||||
* @copyright SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* @date 2020
|
||||
* @note peripherals used: timer @ref sensor_sr04_timer, GPIO @ref sensor_sr04_gpio
|
||||
*/
|
||||
/* standard libraries */
|
||||
#include <stdint.h> // standard integer types
|
||||
#include <stdlib.h> // general utilities
|
||||
#include <stdbool.h> // boolean utilities
|
||||
|
||||
/* STM32 (including CM3) libraries */
|
||||
#include <libopencmsis/core_cm3.h> // Cortex M3 utilities
|
||||
#include <libopencm3/stm32/rcc.h> // real-time control clock library
|
||||
#include <libopencm3/stm32/gpio.h> // general purpose input output library
|
||||
#include <libopencm3/stm32/timer.h> // timer utilities
|
||||
#include <libopencm3/cm3/nvic.h> // interrupt handler
|
||||
|
||||
/* own libraries */
|
||||
#include "global.h" // common methods
|
||||
#include "sensor_sr04.h" // own definitions
|
||||
|
||||
/** @defgroup sensor_sr04_timer timer used to measure echo response length
|
||||
* @{
|
||||
*/
|
||||
#define SENSOR_SR04_TIMER 11 /**< timer ID peripheral peripheral */
|
||||
#define SENSOR_SR04_CHANNEL 1 /**< timer channel used for echo input capture */
|
||||
#define SENSOR_SR04_AF GPIO_AF3 /**< timer alternate function for the capture channel */
|
||||
//PB9 TIM11_CH1
|
||||
/** @} */
|
||||
|
||||
/** @defgroup sensor_sr04_gpio GPIO used to trigger measurement and get echo
|
||||
* @{
|
||||
*/
|
||||
#define SENSOR_SR04_TRIGGER PB8 /**< GPIO used to trigger measurement (pulled up to 5V by HC-SR04 module) */
|
||||
#define SENSOR_SR04_ECHO PB9 /**< GPIO used to get echo (active high), must be an timer input capture */
|
||||
/** @} */
|
||||
|
||||
volatile uint16_t sensor_sr04_distance = 0;
|
||||
|
||||
/** if an echo has been received */
|
||||
static volatile bool sensor_sr04_echo = false;
|
||||
|
||||
void sensor_sr04_setup(void)
|
||||
{
|
||||
// setup trigger GPIO
|
||||
rcc_periph_clock_enable(GPIO_RCC(SENSOR_SR04_TRIGGER)); // enable clock for GPIO port domain
|
||||
gpio_mode_setup(GPIO_PORT(SENSOR_SR04_TRIGGER), GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO_PIN(SENSOR_SR04_TRIGGER)); // set pin as output
|
||||
gpio_set_output_options(GPIO_PORT(SENSOR_SR04_TRIGGER), GPIO_OTYPE_OD, GPIO_OSPEED_2MHZ, GPIO_PIN(SENSOR_SR04_TRIGGER)); // set pin output as open-drain
|
||||
gpio_clear(GPIO_PORT(SENSOR_SR04_TRIGGER), GPIO_PIN(SENSOR_SR04_TRIGGER)); // idle low
|
||||
|
||||
// setup echo GPIO
|
||||
rcc_periph_clock_enable(GPIO_RCC(SENSOR_SR04_ECHO)); // enable clock for GPIO port domain
|
||||
gpio_mode_setup(GPIO_PORT(SENSOR_SR04_ECHO), GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN(SENSOR_SR04_ECHO)); // set pin to alternate function input capture
|
||||
gpio_set_af(GPIO_PORT(SENSOR_SR04_ECHO), SENSOR_SR04_AF, GPIO_PIN(SENSOR_SR04_ECHO)); // set alternate function for input capture
|
||||
|
||||
// setup timer
|
||||
rcc_periph_clock_enable(RCC_TIM(SENSOR_SR04_TIMER)); // enable clock for timer peripheral
|
||||
rcc_periph_reset_pulse(RST_TIM(SENSOR_SR04_TIMER)); // reset timer peripheral to default
|
||||
// the timers use clock from APB1 or APB2 (see memory map to figure out), derivate from AHB
|
||||
// in case of STM32F401, AHB is max. 84 MHz, APB1 is max. AHB / 2 = 42 MHz but the clock is twice the speed thus 84 MHz, APB2 is max. AHB = 84 MHz
|
||||
// for simplicity we use AHB for the calculation, but in fact it could be more complicated
|
||||
timer_set_prescaler(TIM(SENSOR_SR04_TIMER), 36 - 1); // set prescaler so we can measure up to 28 ms (1.0 / (84E6 / 36) * 2**16 = 28.1 ms), this above the maximum distance 400 cm (400 cm * 58 us/cm ) = 23200 us, datasheet says the maximum is 25 ms)
|
||||
timer_set_mode(TIM(SENSOR_SR04_TIMER), TIM_CR1_CKD_CK_INT, TIM_CR1_CMS_EDGE, TIM_CR1_DIR_UP); // count up
|
||||
timer_enable_update_event(TIM(SENSOR_SR04_TIMER)); // use update event to catch the overflow
|
||||
timer_update_on_overflow(TIM(SENSOR_SR04_TIMER)); // only set update event on overflow
|
||||
timer_one_shot_mode(TIM(SENSOR_SR04_TIMER)); // stop running after overflow
|
||||
timer_set_period(TIM(SENSOR_SR04_TIMER), UINT16_MAX); // use full range
|
||||
|
||||
// setup input capture
|
||||
timer_ic_set_input(TIM(SENSOR_SR04_TIMER), TIM_IC(SENSOR_SR04_CHANNEL), TIM_IC_IN_TI(SENSOR_SR04_CHANNEL)); // configure ICx to use TIn
|
||||
timer_ic_set_filter(TIM(SENSOR_SR04_TIMER), TIM_IC(SENSOR_SR04_CHANNEL), TIM_IC_OFF); // use no filter input (precise timing needed)
|
||||
|
||||
timer_ic_set_prescaler(TIM(SENSOR_SR04_TIMER), TIM_IC(SENSOR_SR04_CHANNEL), TIM_IC_PSC_OFF); // don't use any prescaler since we want to capture every pulse
|
||||
timer_ic_enable(TIM(SENSOR_SR04_TIMER), TIM_IC(SENSOR_SR04_CHANNEL)); // enable input capture
|
||||
timer_clear_flag(TIM(SENSOR_SR04_TIMER), TIM_SR_UIF); // clear update flag
|
||||
timer_enable_irq(TIM(SENSOR_SR04_TIMER), TIM_DIER_UIE); // enable update interrupt for timer
|
||||
timer_clear_flag(TIM(SENSOR_SR04_TIMER), TIM_SR_CCIF(SENSOR_SR04_CHANNEL)); // clear input compare flag
|
||||
timer_enable_irq(TIM(SENSOR_SR04_TIMER), TIM_DIER_CCIE(SENSOR_SR04_CHANNEL)); // enable capture interrupt
|
||||
nvic_enable_irq(NVIC_TIM_IRQ(SENSOR_SR04_TIMER)); // catch interrupt in service routine
|
||||
}
|
||||
|
||||
void sensor_sr04_release(void)
|
||||
{
|
||||
gpio_mode_setup(GPIO_PORT(SENSOR_SR04_TRIGGER), GPIO_MODE_INPUT, GPIO_PUPD_NONE, GPIO_PIN(SENSOR_SR04_TRIGGER)); // release pin
|
||||
rcc_periph_reset_pulse(RST_TIM(SENSOR_SR04_TIMER)); // reset timer peripheral to default
|
||||
rcc_periph_clock_disable(RCC_TIM(SENSOR_SR04_TIMER)); // enable clock for timer peripheral
|
||||
}
|
||||
|
||||
void sensor_sr04_trigger(void)
|
||||
{
|
||||
// prepare timer
|
||||
timer_disable_counter(TIM(SENSOR_SR04_TIMER)); // disable timer to prepare it
|
||||
timer_clear_flag(TIM(SENSOR_SR04_TIMER), TIM_SR_UIF); // clear update flag
|
||||
timer_clear_flag(TIM(SENSOR_SR04_TIMER), TIM_SR_CCIF(SENSOR_SR04_CHANNEL)); // clear input compare flag
|
||||
timer_set_counter(TIM(SENSOR_SR04_TIMER), 0); // reset counter
|
||||
timer_ic_set_polarity(TIM(SENSOR_SR04_TIMER), TIM_IC(SENSOR_SR04_CHANNEL), TIM_IC_RISING); // capture on incoming echo (rising edge)
|
||||
sensor_sr04_echo = false; // echo has not been received yet
|
||||
|
||||
// send pulse to trigger measurement
|
||||
gpio_set(GPIO_PORT(SENSOR_SR04_TRIGGER), GPIO_PIN(SENSOR_SR04_TRIGGER)); // start pull
|
||||
sleep_us(10 + 1); // pulse duration
|
||||
gpio_clear(GPIO_PORT(SENSOR_SR04_TRIGGER), GPIO_PIN(SENSOR_SR04_TRIGGER)); // end pulse
|
||||
|
||||
// start timer to measure the echo (the rest is done in the ISR)
|
||||
timer_enable_counter(TIM(SENSOR_SR04_TIMER)); // start timer
|
||||
|
||||
/*
|
||||
while (!gpio_get(GPIO_PORT(SENSOR_SR04_ECHO), GPIO_PIN(SENSOR_SR04_ECHO)) && !timer_get_flag(TIM(SENSOR_SR04_TIMER), TIM_SR_UIF)); // wait for echo
|
||||
if (timer_get_flag(TIM(SENSOR_SR04_TIMER), TIM_SR_UIF)) { // timeout reached
|
||||
// timer is already disabled
|
||||
return 0; // did not get any echo
|
||||
}
|
||||
|
||||
// the echo should have been received, measure it
|
||||
timer_set_counter(TIM(SENSOR_SR04_TIMER), 0); // reset counter to measure echo
|
||||
while (gpio_get(GPIO_PORT(SENSOR_SR04_ECHO), GPIO_PIN(SENSOR_SR04_ECHO)) && !timer_get_flag(TIM(SENSOR_SR04_TIMER), TIM_SR_UIF)); // wait for end of echo
|
||||
if (timer_get_flag(TIM(SENSOR_SR04_TIMER), TIM_SR_UIF)) { // timeout reached
|
||||
// timer is already disabled
|
||||
return UINT16_MAX; // object out of range
|
||||
}
|
||||
|
||||
// convert to distance
|
||||
const uint16_t count = timer_get_counter(TIM(SENSOR_SR04_TIMER)); // get measured time
|
||||
timer_disable_counter(TIM(SENSOR_SR04_TIMER)); // stop timer (just to be clean)
|
||||
|
||||
// calculate distance in mm
|
||||
return (count * (TIM_PSC(TIM(SENSOR_SR04_TIMER)) + 1) * (343 / 2)) / (rcc_ahb_frequency / 1000);
|
||||
// TODO use input capture, and interrupts
|
||||
*/
|
||||
}
|
||||
|
||||
/** interrupt service routine called for timer */
|
||||
void TIM_ISR(SENSOR_SR04_TIMER)(void)
|
||||
{
|
||||
if (timer_get_flag(TIM(SENSOR_SR04_TIMER), TIM_SR_UIF)) { // timeout reached
|
||||
timer_clear_flag(TIM(SENSOR_SR04_TIMER), TIM_SR_UIF); // clear flag
|
||||
if (!sensor_sr04_echo) { // no echo has been received
|
||||
sensor_sr04_distance = 1; // provide measurement to user: object is probably too near
|
||||
} else {
|
||||
sensor_sr04_distance = UINT16_MAX; // provide measurement to user: object is probably too far
|
||||
}
|
||||
} else if (timer_get_flag(TIM(SENSOR_SR04_TIMER), TIM_SR_CCIF(SENSOR_SR04_CHANNEL))) { // edge detected on input capture
|
||||
timer_clear_flag(TIM(SENSOR_SR04_TIMER), TIM_SR_CCIF(SENSOR_SR04_CHANNEL)); // clear input compare flag
|
||||
if (!sensor_sr04_echo) { // echo is incoming
|
||||
timer_set_counter(TIM(SENSOR_SR04_TIMER), 0); // reset timer counter
|
||||
timer_ic_set_polarity(TIM(SENSOR_SR04_TIMER), TIM_IC(SENSOR_SR04_CHANNEL), TIM_IC_FALLING); // capture on end of echo (falling edge)
|
||||
sensor_sr04_echo = true; // remember echo is incoming
|
||||
} else { // end of echo
|
||||
timer_disable_counter(TIM(SENSOR_SR04_TIMER)); // disable counter now that measurement is complete
|
||||
const uint16_t count = TIM_CCR(SENSOR_SR04_TIMER,SENSOR_SR04_CHANNEL); // save captured bit timing (this clear also the flag)
|
||||
sensor_sr04_distance =(count * (TIM_PSC(TIM(SENSOR_SR04_TIMER)) + 1) * 343 / 2) / (rcc_ahb_frequency / 1000); // calculate distance and provide result to user
|
||||
}
|
||||
} else { // no other interrupt should occur
|
||||
while (true); // unhandled exception: wait for the watchdog to bite
|
||||
}
|
||||
}
|
||||
|
26
lib/sensor_sr04.h
Normal file
26
lib/sensor_sr04.h
Normal file
@ -0,0 +1,26 @@
|
||||
/** library to determine range using HC-SR04 ultrasonic range sensor
|
||||
* @file
|
||||
* @author King Kévin <kingkevin@cuvoodoo.info>
|
||||
* @copyright SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* @date 2020
|
||||
* @note peripherals used: timer @ref sensor_sr04_timer, GPIO @ref sensor_sr04_gpio
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
/** distance in mm once an echo has been received
|
||||
* @note distance set 1 when the object is too near (no echo received)
|
||||
* @note distance set UINT16_MAX when the object is too far (echo received to late)
|
||||
* @warning to be clear by user
|
||||
*/
|
||||
extern volatile uint16_t sensor_sr04_distance;
|
||||
|
||||
/** configure MCU peripherals to communicate with HC-SR04 sensor
|
||||
*/
|
||||
void sensor_sr04_setup(void);
|
||||
/** configure MCU peripherals used to communicate with HC-SR04 sensor
|
||||
*/
|
||||
void sensor_sr04_release(void);
|
||||
/** trigger measurement
|
||||
* @note the resulting measurement will be set in sensor_sr04_distance
|
||||
*/
|
||||
void sensor_sr04_trigger(void);
|
Loading…
Reference in New Issue
Block a user