I create a new video explaining how to use the ranging sensor HC-SR04 on NuttX:
Month: August 2017
Creating a HC-SR04 driver for NuttX
In this post I will explain how I did the HC-SR04 driver for NuttX.
I think this is the first time I try to explain how to create a driver for NuttX, oh wait! It is not! The first time was this post: NuttX driver to control a single LED
But this time it will be different, instead of just posting the code here, I will try to explain how I came with the driver. So, let to get started.
First I searched for the HC-SR04 datasheet, it was easy to find:
Reading it I discovered that I need to send a pulse of 10uS in the pin Trig to start the conversion, pretty easy:
static int hcsr04_start_measuring(FAR struct hcsr04_dev_s *priv) { /* Configure the interruption */ priv->rising = true; priv->config->irq_setmode(priv->config, priv->rising); priv->config->irq_enable(priv->config, true); /* Send to 10uS trigger pulse */ priv->config->set_trigger(priv->config, true); usleep(10); priv->config->set_trigger(priv->config, false); return 0; }
After sending the Trigger pulse we will receive a pulse in the Echo pin with the width encoding the measured distance.
Because we need to measure width of a pulse the first idea that came to my mind was to use the Input Capture of STM32 Timer (I’m using the STM32F103-Minimum board), but I want this driver to be generic, then I decided to use an ordinary GPIO interrupt pin of STM32.
I can setup STM32 to detect level changing (rising edge and falling edge), but some microcontrollers don’t support it, you need to select rising edge or falling edge, not both at same time. We need to work around it because I need this driver to be generic enough to work on these “poor man” MCUs.
Then to get it working I need to implement a ping-pong approach: first setup the GPIO pin to detect rising edge of signal and inside the ISR (Interrupt Service Routine or just interrupt handler) it needs to change the pin configuration to detect interruption in the falling edge.
So at hcsr04_int_handler I did it:
if (priv->rising) { /* Get the clock ticks from the free running timer */ priv->time_start_pulse = priv->config->get_clock(priv->config); /* Now we need to wait for the falling edge interruption */ priv->rising = false; priv->config->irq_setmode(priv->config, priv->rising); priv->config->irq_enable(priv->config, true); } else { /* Get the clock ticks from the free running timer */ priv->time_finish_pulse = priv->config->get_clock(priv->config); /* Disable interruptions */ priv->config->irq_enable(priv->config, false); /* Convertion is done */ sem_post(&priv->conv_donesem); }
So now you got the idea how it works, we just need to understand the magic under the hood. These functions irq_enable(), irq_setmode(), get_clock(), etc, are in fact “function pointers” to board specific functions:
/* Interrupt configuration data structure */ struct hcsr04_config_s { CODE int (*irq_attach)(FAR struct hcsr04_config_s * state, xcpt_t isr, FAR void *arg); CODE void (*irq_enable)(FAR const struct hcsr04_config_s *state, bool enable); CODE void (*irq_clear)(FAR const struct hcsr04_config_s *state); CODE void (*irq_setmode)(FAR struct hcsr04_config_s *state, bool risemode); CODE void (*set_trigger)(FAR const struct hcsr04_config_s *state, bool on); CODE int64_t (*get_clock)(FAR const struct hcsr04_config_s *state); };
The real functions are at “boards/arm/stm32/common/src/stm32_hcsr04.c”
This way we can create a generic abstraction and let it work with any board/microcontroller.
As you probably already figured-out, it is the irq_setmode() function that defines if the GPIO pin (connected to Echo pin of HC-SR04 module) will detect rising edge signal or falling edge signal:
/* Setup the interruption mode: Rising or Falling */ static void hcsr04_irq_setmode(FAR struct hcsr04_config_s *state, bool rise_mode) { FAR struct stm32_hcsr04config_s *priv = (FAR struct stm32_hcsr04config_s *)state; if (rise_mode) { priv->rising = true; priv->falling = false; } else { priv->rising = false; priv->falling = true; } }
Ok, we just need the “rising” variable to store the current edge mode, because the “falling” variable always will be the inverse. But let us to use both for didactic reason. Now irq_enable() just call stm32_gpiosetevent() passing these edge parameters and enabling it (passing the driver’s interrupt handler) or disabling it (passing a NULL).
/* Enable or disable the GPIO interrupt */ static void hcsr04_irq_enable(FAR const struct hcsr04_config_s *state, bool enable) { FAR struct stm32_hcsr04config_s *priv = (FAR struct stm32_hcsr04config_s *)state; sinfo("%d\n", enable); (void)stm32_gpiosetevent(GPIO_HCSR04_INT, priv->rising, priv->falling, true, enable ? priv->isr : NULL, priv->arg); }
Finally the get_clock() just returns the current clock tick of a free running timer configured to run at 1 microsecond resolution. I just need to convert the value of returned “timespec” variable “ts” to microseconds, multiplying tv_sec by 1 million (1 second has 1000000 us) and dividing tv_nsec by 1000 (1us = 1000 ns), see:
/* Return the current Free Running clock tick */ static int64_t hcsr04_get_clock(FAR const struct hcsr04_config_s *state) { /* Get the time from free running timer */ stm32_freerun_counter(&g_freerun, &ts); /* Return time in microseconds */ return ((ts.tv_sec * 1000000) + (ts.tv_nsec / 1000)); }
So, I think you got the idea how this driver works. It is was easy to implement. You can read the complete code here: “nuttx/drivers/sensors/hc_sr04.c”
You can see this driver working on this video tutorial:
Compiling sigrok and pulseview
Install the dependencies:
$ sudo apt-get install automake libudev-dev checkinstall libglib2.0-dev libftdi-dev sdcc python3-dev qt-sdk libboost-system-dev libboost-thread-dev libboost-filesystem-dev libqt5svg5-dev libglibmm-2.4-dev libboost1.58-all-dev
Clone the repositories:
$ git clone git://sigrok.org/libsigrok
$ git clone git://sigrok.org/libsigrokdecode
$ git clone git://sigrok.org/sigrok-cli
$ git clone git://sigrok.org/pulseview
Compile them all:
$ cd libsigrok $ ./autogen.sh $ ./configure $ make $ sudo checkinstall $ cd libsigrokdecode $ ./autogen.sh $ ./configure $ make $ sudo checkinstall $ cd sigrok-cli $ ./autogen.sh $ ./configure $ make $ sudo checkinstall $ cd pulseview $ cmake . $ make $ sudo checkinstall $ git clone git://sigrok.org/sigrok-firmware-fx2lafw $ cd sigrok-firmware-fx2lafw $ ./autogen.sh $ ./configure $ make $ sudo make install
Reduzing Memory consumption: Smaller Vector Tables
I want to reduce the memory consumption to NuttX in the stm32f103-minimum board to get external SPI NOR Flash with SmartFS and USB Serial Console at same time.
After mount the SmartFS partition I will have only 808 bytes of RAM:
nsh> free total used free largest Mem: 17536 15352 2184 2184 nsh> mksmartfs /dev/smart0p1 nsh> free total used free largest Mem: 17536 15352 2184 2184 nsh> mount -t smartfs /dev/smart0p1 /mnt nsh> free total used free largest Mem: 17536 16728 808 808 nsh>
Analyzing which features are consumpting more RAM I notice the interruption vector table is the using more than 600 bytes:
$ arm-none-eabi-nm --print-size --size-sort --radix dec -C nuttx | grep ' [DdBb] ' | tail -20 536873088 00000008 B g_sigpendingirqaction 536873096 00000008 B g_sigpendingirqsignal 536873104 00000008 B g_sigpendingsignal 536872252 00000008 B g_waitingforsemaphore 536872280 00000008 B g_waitingforsignal 536873124 00000008 B g_wdactivelist 536873112 00000008 B g_wdfreelist 536872436 00000016 B g_hpwork 536873296 00000028 B g_spawn_parms 536871024 00000028 d g_spi1dev 536871860 00000028 b g_syslog_dev 536870912 00000092 d g_usart1priv 536871764 00000096 b g_wdpool 536872288 00000128 B g_pidhash 536873132 00000164 B g_mmheap 536871564 00000188 b g_idletcb 536871308 00000256 b g_usart1rxbuffer 536871052 00000256 b g_usart1txbuffer 536871888 00000328 b g_usbdev 536872452 00000608 B g_irqvector
Fortunately there is an option to reduce it:
http://www.nuttx.org/doku.php?id=wiki:howtos:smallvectors
Keep tuned!
Renaming multiples files on Linux
Today I rediscovered the rename command (yes I used it in the past and documented it here, but I forgot).
I needed to rename name *.cpp files to *.cxx, then I used:
$ rename 's/\.cpp$/\.cxx/' *.cpp
Very easy!
Source: https://www.tecmint.com/rename-multiple-files-in-linux/