Jump to content
Sign in to follow this  
GrumpyOldPizza

HC-SR04 Ultrasonic sensor, bare metal.

Recommended Posts

Perhaps this is usable for somebody else. It's a simple interrupt driven scheme to talk to 2 HC-SR04s. The trigger pulses are connected to PE2/PE3, while the echo pulses are connected to PD2/PD3. The echo pulses are measured via capture compare on WTIMER3AB. TIMER2A is used to send a trigger pulse every 50ms, interleaved of course between the 2 sensors. This results effectively in a 10Hz update rate.

 

The code does not include the interrupt enable logic in NVIC. Should be simple enough though to add that based upon the target environment. "SystemCoreClock" is 80000000 on my system (this is a public CMSIS variable ...).

 

The results of the pulse measurement are stored in lm4f120_sonar_left_pulse and lm4f120_sonar_right_pulse. One could compute the distance directly from those in the ISR, or perhaps like I do in my rover, send the measurements via a data-queue/fifo to the controlling task.  

 

The key difference to other approaches posted is that this one is using the hardware directly, and it's interrupted based, which means no big overhead in the main task (like waiting for the echo to come back).

 

A couple of quick comments regarding the distance calculation. Speed of sound is dependent upon the air temperature. So if there is another sensor in the system that gives a reasonable estimate of whether it's -30F vs. +100F, it might be worth it. If one wants to have reasonable precision, then one needs to use floating point arithmetic. With 32 bit integer arithmetic you either end up overflowing the intermediate calculations, or have to give up precision. Using the FPU in an ISR is not the best idea because of the stack space requirements though. The last detail worth mentioning is that if the measuring platform moves one needs to take that into consideration as it shortens or extends the echo pulse duration.

 

- Thomas

 

#include "lm4f120h5qr.h"
 
extern uint32_t SystemCoreClock;
 
volatile uint32_t lm4f120_sonar_left_pulse;
static volatile uint32_t lm4f120_sonar_left_time;
volatile uint32_t lm4f120_sonar_right_pulse;
static volatile uint32_t lm4f120_sonar_right_time;
 
void lm4f120_timer2a_interrupt(void)
{
    if (TIMER2_MIS_R & TIMER_MIS_TATOMIS)
    {
        TIMER2_ICR_R = TIMER_MIS_TATOMIS;
 
        /* Invert PE2/PE3 to trigger the next range ping.
         */
 
        GPIO_PORTE_DATA_R = (GPIO_PORTE_DATA_R ^ 0x0c);
    }
}
 
static void lm4f120_timer2a_init(void)
{
    /* Enable TIMER2 */
    SYSCTL_RCGCTIMER_R |= SYSCTL_RCGCTIMER_R2;
    SYSCTL_RCGCTIMER_R;
 
    /* Setup TIMER2A to generate a timout every 50ms (20Hz)
     */
    TIMER2_CTL_R   = TIMER2_CTL_R & ~(TIMER_CTL_TAPWML | TIMER_CTL_TAOTE | TIMER_CTL_TAEVENT_M | TIMER_CTL_TASTALL | TIMER_CTL_TAEN);
    TIMER2_CFG_R   = TIMER_CFG_16_BIT;
    TIMER2_TAMR_R  = TIMER_TAMR_TAMR_PERIOD;
    TIMER2_IMR_R   = (TIMER2_IMR_R & ~(TIMER_IMR_TAMIM | TIMER_IMR_CAEIM | TIMER_IMR_CAMIM)) | TIMER_IMR_TATOIM;
    TIMER2_ICR_R   = TIMER_ICR_TAMCINT | TIMER_ICR_CAECINT | TIMER_ICR_CAMCINT | TIMER_ICR_TATOCINT;
    TIMER2_TAILR_R = 50000;
    TIMER2_TAPR_R  = SystemCoreClock / 1000000;
    TIMER2_CTL_R   = TIMER2_CTL_R | TIMER_CTL_TAEN;
}
 
void lm4f120_wtimer3a_interrupt(void)
{
    uint32_t pulse;
 
    if (WTIMER3_MIS_R & TIMER_MIS_CAEMIS)
    {
        WTIMER3_ICR_R = TIMER_ICR_CAECINT;
 
        if (!(GPIO_PORTE_DATA_R & 0x04))
        {
            if (GPIO_PORTD_DATA_R & 0x04)
            {
                lm4f120_sonar_left_time = WTIMER3_TAR_R;
            }
            else
            {
                pulse = WTIMER3_TAR_R - lm4f120_sonar_left_time;
 
                if (pulse < 38 * (SystemCoreClock / 1000))
                {
                    lm4f120_sonar_left_pulse = pulse;
                }
                else
                {
                    lm4f120_sonar_left_pulse = 0;
                }
           }
        }
    }
}
 
void lm4f120_wtimer3b_interrupt(void)
{
    uint32_t pulse;
 
    if (WTIMER3_MIS_R & TIMER_MIS_CBEMIS)
    {
        WTIMER3_ICR_R = TIMER_ICR_CBECINT;
 
        if (!(GPIO_PORTE_DATA_R & 0x08))
        {
            if (GPIO_PORTD_DATA_R & 0x08)
            {
                lm4f120_sonar_right_time = WTIMER3_TBR_R;
            }
            else
            {
                pulse = WTIMER3_TBR_R - lm4f120_sonar_right_time;
 
                if (pulse < 38 * (SystemCoreClock / 1000))
                {
                    lm4f120_sonar_right_pulse = pulse;
                }
                else
                {
                    lm4f120_sonar_right_pulse = 0;
                }
            }
        }
    }
}
 
static void lm4f120_wtimer3ab_init(void)
{
    /* Enable WTIMER3 */
    SYSCTL_RCGCWTIMER_R |= SYSCTL_RCGCWTIMER_R3;
    SYSCTL_RCGCWTIMER_R;
 
    /* Enable GPIOD */
    SYSCTL_RCGCGPIO_R |= SYSCTL_RCGCGPIO_R3;
    SYSCTL_RCGCGPIO_R;
 
    /* Set PD2/PD3 to be inputs */
    GPIO_PORTD_DIR_R = (GPIO_PORTD_DIR_R & ~0x0c) | (0x00);
 
    /* Enable WTIMER3/AFSEL */
    GPIO_PORTD_AFSEL_R = (GPIO_PORTD_AFSEL_R & ~0x0c) | (0x0c);
 
    /* Select the proper WTIMER3 settings in PCTL */
    GPIO_PORTD_PCTL_R = ((GPIO_PORTD_PCTL_R & ~(GPIO_PCTL_PD2_M | GPIO_PCTL_PD3_M)) |
                         (GPIO_PCTL_PD2_WT3CCP0 | GPIO_PCTL_PD3_WT3CCP1));
        
    /* Enable 2MA drive to meet timings */
    GPIO_PORTD_DR2R_R = (GPIO_PORTD_DR8R_R & ~0x0c) | (0x0c);
 
    /* Enable Pulldown */
    GPIO_PORTD_ODR_R = (GPIO_PORTD_PUR_R & ~0x0c) | (0x00);
    GPIO_PORTD_PDR_R = (GPIO_PORTD_PDR_R & ~0x0c) | (0x0c);
    GPIO_PORTD_PUR_R = (GPIO_PORTD_PUR_R & ~0x0c) | (0x00);
 
    GPIO_PORTD_DEN_R = (GPIO_PORTD_DEN_R & ~0x0c) | (0x0c);
 
    /* Setup WTIMER3 to generate an EVENT on every edge for A/B.
     */
    WTIMER3_CTL_R   = 0;
    WTIMER3_CFG_R   = TIMER_CFG_16_BIT;  /* really 32bit for WTIMER */
    WTIMER3_TAMR_R  = TIMER_TAMR_TACDIR | TIMER_TAMR_TACMR | TIMER_TAMR_TAMR_CAP;    
    WTIMER3_TBMR_R  = TIMER_TBMR_TBCDIR | TIMER_TBMR_TBCMR | TIMER_TBMR_TBMR_CAP;    
    WTIMER3_IMR_R   = TIMER_IMR_CBEIM | TIMER_IMR_CAEIM;
    WTIMER3_ICR_R   = TIMER_ICR_CBECINT | TIMER_ICR_CAECINT;
    WTIMER3_TAILR_R = 0xffffffff;
    WTIMER3_TBILR_R = 0xffffffff;
    WTIMER3_TAV_R   = 0xffffffff;
    WTIMER3_TBV_R   = 0xffffffff;
    WTIMER3_CTL_R   = TIMER_CTL_TBEVENT_BOTH | TIMER_CTL_TBEN | TIMER_CTL_TAEVENT_BOTH | TIMER_CTL_TAEN;
}
 
void lm4f120_sonar_initialize(void)
{
    /* Enable GPIOE */
    SYSCTL_RCGCGPIO_R |= SYSCTL_RCGCGPIO_R4;
    SYSCTL_RCGCGPIO_R;
 
    /* Set PE2/PE3 to be outputs */
    GPIO_PORTE_DIR_R = (GPIO_PORTE_DIR_R & ~0x0c) | (0x0c);
 
    /* Disable PE2/PE3 AFSEL */
    GPIO_PORTE_AFSEL_R = (GPIO_PORTE_AFSEL_R & ~0x0c) | (0x00);
        
    /* Enable 2MA drive to meet timings */
    GPIO_PORTE_DR8R_R = (GPIO_PORTE_DR8R_R & ~0x0c) | (0x0c);
 
    /* Enable Pulldown */
    GPIO_PORTE_ODR_R = (GPIO_PORTE_PUR_R & ~0x0c) | (0x00);
    GPIO_PORTE_PDR_R = (GPIO_PORTE_PDR_R & ~0x0c) | (0x0c);
    GPIO_PORTE_PUR_R = (GPIO_PORTE_PUR_R & ~0x0c) | (0x00);
 
    /* Initialize PE2/PE3 to H/L */
    GPIO_PORTE_DATA_R = (GPIO_PORTE_DATA_R & ~0x0c) | 0x04;
 
    GPIO_PORTE_DEN_R = (GPIO_PORTE_DEN_R & ~0x0c) | (0x0c);
 
    lm4f120_wtimer3ab_init();
    lm4f120_timer2a_init();
}
 
 

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
Sign in to follow this  

×