Timer Periodic Mode - SRF04

In this tutorial i will show how to use a SRF04 with the Tiva launchpad TM4C123 using the general purpose timers.

The SRF04 requires 1 digital output and a input ready to read a positive pulse length. In this example it will be used a digital input to start a timer so it's possible to read the pulse length in any pin.

The timer will be in periodic mode. There are better modes for this but this method will let you use the SRF04 in any GPIO. It should work with clones, just adapt the math if needed.

Understanding the pulse:

The SRF04 works by you first requesting a reading by sending a HIGH value with a digital output in the Trigger pin of the SRF04. After the reading is made, which depends on the distance measured, hence you needing to always monitor the input rising edge after you ask for a reading, a HIGH pulse will be returned in the SRF04 Echo pin. The length of that pulse is what tells you the distance measured.

Now let's code!

First the system clock of course, at 80 Mhz:

SysCtlClockSet(SYSCTL_SYSDIV_2_5|SYSCTL_USE_PLL|SYSCTL_OSC_MAIN|SYSCTL_XTAL_16MHZ);

Now we want the output for the Trigger pin:

SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA); SysCtlDelay(3); GPIOPinTypeGPIOOutput(GPIO_PORTA_BASE, GPIO_PIN_3);

Let's use the pin PA3.

Set the the Echo pin to be a interrupt with Both edges:

SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA); SysCtlDelay(3); GPIOPinTypeGPIOInput(GPIO_PORTA_BASE, GPIO_PIN_2); GPIOIntEnable(GPIO_PORTA_BASE, GPIO_PIN_2); GPIOIntTypeSet(GPIO_PORTA_BASE, GPIO_PIN_2,GPIO_BOTH_EDGES); GPIOIntRegister(GPIO_PORTA_BASE,inputInt);

Now setup the timer:

SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER2); SysCtlDelay(3); TimerConfigure(TIMER2_BASE, TIMER_CFG_PERIODIC_UP); TimerEnable(TIMER2_BASE,TIMER_A);

Simply set the timer to be in periodic mode. I use the full Timer2 for this, i don't divide it into timerA and B, so i have a 32bit timer. You call it TimerA in the parameters that needs a timer when it's not split up. The timer base is always Timerx being x the number of the timer.

The interrupt handler:

void inputInt(){ GPIOIntClear(GPIO_PORTA_BASE, GPIO_PIN_2); //clear interrupt flag if ( GPIOPinRead(GPIO_PORTA_BASE, GPIO_PIN_2) == GPIO_PIN_2){ HWREG(TIMER2_BASE + TIMER_O_TAV) = 0; //Loads value 0 into the timer. TimerEnable(TIMER2_BASE,TIMER_A); //start timer to recor echowait=1; } else{ TimerDisable(TIMER2_BASE,TIMER_A); //stop timer pulse = TimerValueGet(TIMER2_BASE,TIMER_A); //record value echowait=0; }}

In this handler you can see that i read the input pin state. That is important because it let's me know if it was a rising edge or a falling edge, if the pin is reading HIGH then a rising edge happened, if it's reading LOW then a falling edge happened. And why i need to know that you might ask?

Well when a rising edge happens the timer is loaded with 0 to reset the counting by the accessing the register here named TIMER_O_TAV. The timer is in periodic mode count up but it could be in one shot mode, it's basically how it is working as.

In case of a falling edge, you save the value of the timer counter, and that is what you need to know the distance measured by the srf04.

The variable "pulse" is a global volatile uint32_t

Now the math to get the distance from the values.

For that we first need the counter value to be converted to micro-seconds. The clock is at 80Mhz so each counter value is equal to 1/80Mhz = 12.5nS. To avoid big floats let's instead do 1.0/80.0 so now instead of getting 0.0000000125 we get 0.0125 (we still use a float for this part). Then just multiply that value by the counter value and get the pulse length in micro-seconds.

Now simply divide that pulse length by 58 and you will get the distance in cm.

pulse =(uint32_t)(temp * pulse); pulse = pulse / 58;

And with all this you should be able to use a SRF04 or any cheap clones with the Tiva. This method is not the most precise for pulse reading but it is enough for a SRF04 and it makes it possible to use it in any GPIO pin. Also it is non blocking, you can do other things while a reading is happening.

I hope you liked the tutorial and that it helped you. Any feedback is always appreciated.

Here is the final code with UART communication to give the distance through the serial monitor. Understanding the UART studio is beyond this tutorial, please check Tiva UART.

#define PART_TM4C123GH6PM#include <stdint.h>#include <stdbool.h>#include "stdlib.h"#include "inc/hw_ints.h"#include "inc/hw_memmap.h"#include "inc/hw_timer.h"#include "inc/hw_uart.h"#include "inc/hw_gpio.h"#include "inc/hw_pwm.h"#include "inc/hw_types.h"#include "driverlib/pin_map.h"#include "driverlib/timer.h"#include "driverlib/gpio.h"#include "driverlib/interrupt.h"#include "driverlib/rom.h"#include "driverlib/rom_map.h"#include "driverlib/sysctl.h"#include "driverlib/uart.h"#include "driverlib/udma.h"#include "driverlib/pwm.h"#include "driverlib/ssi.h"#include "driverlib/systick.h"#include "utils/uartstdio.c"#include <string.h>void inputInt();void Captureinit();void InitConsole(void);//This is to avoid doing the math everytime you do a readingconst double temp = 1.0/80.0;//Stores the pulse lengthvolatile uint32_t pulse=0;//Tells the main code if the a pulse is being read at the momentvolatile uint8_t echowait=0;int main(){ //Set system clock to 80Mhz SysCtlClockSet(SYSCTL_SYSDIV_2_5|SYSCTL_USE_PLL|SYSCTL_OSC_MAIN|SYSCTL_XTAL_16MHZ); //Configures the UART InitConsole(); //Configures the timer Captureinit(); //Configure Trigger pin SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA); SysCtlDelay(3); GPIOPinTypeGPIOOutput(GPIO_PORTA_BASE, GPIO_PIN_3); //Configure Echo pin SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA); SysCtlDelay(3); GPIOPinTypeGPIOInput(GPIO_PORTA_BASE, GPIO_PIN_2); GPIOIntEnable(GPIO_PORTA_BASE, GPIO_PIN_2); GPIOIntTypeSet(GPIO_PORTA_BASE, GPIO_PIN_2,GPIO_BOTH_EDGES); GPIOIntRegister(GPIO_PORTA_BASE,inputInt); while(1) { //Checks if a pulse read is in progress if(echowait != 1){ //Does the required pulse of 10uS GPIOPinWrite(GPIO_PORTA_BASE, GPIO_PIN_3, GPIO_PIN_3); SysCtlDelay(266); GPIOPinWrite(GPIO_PORTA_BASE, GPIO_PIN_3, ~GPIO_PIN_3); /* This makes the code wait for a reading to finish You can omit this part if you want the code to be non-blocking but reading is only ready when echowait=0. */ while(echowait != 0); //Converts the counter value to cm. pulse =(uint32_t)(temp * pulse); pulse = pulse / 58; //Prints out the distance measured. UARTprintf("distance = %2dcm \n" , pulse); } //wait about 10ms until the next reading. SysCtlDelay(400000); }}void inputInt(){ //Clear interrupt flag. Since we only enabled on this is enough GPIOIntClear(GPIO_PORTA_BASE, GPIO_PIN_2); /* If it's a rising edge then set he timer to 0 It's in periodic mode so it was in some random value */ if ( GPIOPinRead(GPIO_PORTA_BASE, GPIO_PIN_2) == GPIO_PIN_2){ HWREG(TIMER2_BASE + TIMER_O_TAV) = 0; //Loads value 0 into the timer. TimerEnable(TIMER2_BASE,TIMER_A); echowait=1; } /* If it's a falling edge that was detected, then get the value of the counter */ else{ pulse = TimerValueGet(TIMER2_BASE,TIMER_A); //record value TimerDisable(TIMER2_BASE,TIMER_A); echowait=0; }}void Captureinit(){ /* Set the timer to be periodic. */ SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER2); SysCtlDelay(3); TimerConfigure(TIMER2_BASE, TIMER_CFG_PERIODIC_UP); TimerEnable(TIMER2_BASE,TIMER_A);}void InitConsole(void){ // // Enable GPIO port A which is used for UART0 pins. // TODO: change this to whichever GPIO port you are using. // SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA); SysCtlDelay(3); // // Configure the pin muxing for UART0 functions on port A0 and A1. // This step is not necessary if your part does not support pin muxing. // TODO: change this to select the port/pin you are using. // GPIOPinConfigure(GPIO_PA0_U0RX); GPIOPinConfigure(GPIO_PA1_U0TX); // // Enable UART0 so that we can configure the clock. // SysCtlPeripheralEnable(SYSCTL_PERIPH_UART0); // // Use the internal 16MHz oscillator as the UART clock source. // UARTClockSourceSet(UART0_BASE, UART_CLOCK_PIOSC); // // Select the alternate (UART) function for these pins. // TODO: change this to select the port/pin you are using. // GPIOPinTypeUART(GPIO_PORTA_BASE, GPIO_PIN_0 | GPIO_PIN_1); // // Initialize the UART for console I/O. // UARTStdioConfig(0, 115200, 16000000);}