I'm trying to control this HC-SR4 Ultrasonic sensor by simply ON an LED when an object closer than 100cm is detected. I'm using TIM2 for the trigger signal (Pin PB10), and TIM4 to receive Echo signal (Pin PB6). and the LED is connected to Pin PB7. when I load the code below, the LED simply turns ON, whether there's an object or not, it's just ON.

#include <stdio.h>
#include "stm32l1xx.h"                  // Keil::Device:Startup

        //Initialize the timers variables.
    volatile int timespan = 0;      // Total pulse width
    volatile int lastcounter = 0;   // Timer counter value of the last event
    volatile int newcounter = 0;    // Timer counter value of the current event
    volatile int overflow = 0;      // Count the number of overflows

    void SetHSI(void);           
    void Delay(int);
    void GPIO_config(void);
    void TIM2_Trigger(void);
    void TIM4_Init(void);
    void TIM4_Echo_Read(void);
    void LED (void);


        int main(void){

            SetHSI();
            GPIO_config();
            TIM2_Trigger();
            TIM4_Init();


          while(1){

             TIM4_Echo_Read();
             LED();

             Delay(100);
      }
   }

void Delay(int x){
    //input milliseconds, delay that number of milliseconds
    int a,b;
    for(a=0; a<x; a++){
        for(b=0; b<1000; b++){
        }
    }
}

  //set HSI as SystemCoreClock (HSE is not populated on STM32L-Discovery board)//

void SetHSI(void) {

// Turn on HSI (16MHz)
RCC->CR |= RCC_CR_HSION;
// Wait until HSI is ready
while( (RCC->CR & RCC_CR_HSIRDY) == 0);
// Select HSI as system clock
RCC->CFGR &= ~RCC_CFGR_SW_HSI;
RCC->CFGR |= RCC_CFGR_SW_HSI;
while( (RCC->CFGR & RCC_CFGR_SWS)!=RCC_CFGR_SWS_HSI ); // Wait till HSI
}

// Configure GPIO Port B
void GPIO_config(void){

    RCC->AHBRSTR |= RCC_AHBRSTR_GPIOBRST;   // Reset GPIOB clock 
    RCC->AHBRSTR &= ~RCC_AHBRSTR_GPIOBRST;  // Clear Reset 
    RCC->AHBENR |= RCC_AHBENR_GPIOBEN;      // Enable GPIOB clock 

    //PB6 Echo Pin
    GPIOB->MODER   &=   ~(0x03 << 12);    // Clear bit 12 & 13 Alternate function mode 
    GPIOB->MODER   |=   (0x02 << 12);    // set as Alternate function mode 
    GPIOB->OSPEEDR &=   ~(0x03<< 12);   // 40 MHz  speed 
    GPIOB->OSPEEDR |=   (0x03<< 12);    // 40 MHz  speed 
    GPIOB->PUPDR &=         ~(0X3<<12); // NO PULL-UP PULL-DOWN 
    GPIOB->OTYPER &=        ~(1<<6);    // PUSH-PULL 
    GPIOB->AFR[0] &= ~GPIO_AFRL_AFRL6;  // Clear pin 6 for alternate function
    GPIOB->AFR[0] |=        0x2 << (4*6);   // set PB pin 6 as AF2 (TIM4_CH1) 

//PB10 Pluse Generating Pin
    GPIOB->MODER   &=   ~(0x03 << (2*10));  // Clear bit 12 & 13 Alternate function mode 
    GPIOB->MODER   |=   0x02 << (2*10);     // set as Alternate function mode 
    GPIOB->OSPEEDR &=   ~(0x03<< (2*10));   // 40 MHz  speed 
    GPIOB->OSPEEDR |=   0x03<< (2*10);      // 40 MHz  speed 
    GPIOB->PUPDR &=         ~(1<<10);       // NO PULL-UP PULL-DOWN 
    GPIOB->OTYPER &=        ~(1<<10);       // PUSH-PULL 
    GPIOB->AFR[1] |=        0x1 << (4*2);   // set PB pin 10 as AF1 (TIM2_CH3) 

//PB7 LED ON/OFF
    GPIOB->MODER   |=   GPIO_MODER_MODER7_0;   // General purpose output mode
  GPIOB->OSPEEDR |=   GPIO_OSPEEDER_OSPEEDR7;  // Max High speed 50MHz


}

// CONFIGURE TIM2 FOR SENDING OUTPUT SIGNAL
void TIM2_Trigger(void){
    RCC->APB1ENR |= RCC_APB1ENR_TIM2EN; // ENABLE TIM2 CLOCK
    TIM2->PSC = 159;                    // SET APPROPRAIT PRESCALER TO SLOW DOWN THE CLOCK
    TIM2->ARR = 0XFFFF;         // SET MAX PULSE WIDTH OF 65536us FOR 16-BIT TIMER

    TIM2->CCMR2 |= TIM_CCMR2_OC3M_1 | TIM_CCMR2_OC3M_2; // 111: PWM mode 1 
    TIM2->CCMR2 |= TIM_CCMR2_OC3PE;         // CH3 Output Preload Enable
    TIM2->CR1 |= TIM_CR1_ARPE;              // Auto-reload Prelaod Enable
    TIM2->CCER |= TIM_CCER_CC3E;            // Enable Output for CH3
    TIM2->EGR |= TIM_EGR_UG;                // Force Update
    TIM2->SR &= ~TIM_SR_UIF;                // Clear the Update Flag
    TIM2->DIER |= TIM_DIER_UIE;             // Enable Interrupt on Update
    TIM2->CR1 &= ~TIM_CR1_DIR;              // Set upcounting counter direction
    TIM2->CCR3 &= ~(TIM_CCR3_CCR3);         // Clear CCR3 (Channel 3) 
    TIM2->CCR3 |= 0x1;                      // Load the register 
    TIM2->CR1 |= TIM_CR1_CEN;               // Enable the counter
}


// CONFIGURE TIM4 FOR RECEIVING INPUT SIGNAL
void TIM4_Init(void){
    RCC->APB1ENR |= RCC_APB1ENR_TIM4EN;         // ENABLE TIM4 CLOCK
    TIM4->PSC = 15;                             // SET APPROPRAIT PRESCALER TO SLOW DOWN THE CLOCK
    TIM4->ARR = 0xFFFF;                         // SET MAX PULSE WIDTH OF 65536us FOR 16-BIT TIMER
    TIM4->CCMR1 &= ~TIM_CCMR1_CC1S;             // CLEAR CAPTURE/COMPARE REGISTER
    TIM4->CCMR1 |= 0X1;                         // SELECT CH1 INPUTE CAPTURE 
    TIM4->CCMR1 &= ~TIM_CCMR1_IC1F;             // DISABLE DIGITAL FILTERING
    TIM4->CCER |= (1<<1 | 1<<3);                // SELECT BOTH RISING AND FALLING EDGE DETECTION CC1P & CC1NP
    TIM4->CCMR1 &= ~(TIM_CCMR1_IC1PSC);         // INPUT PRESCALER 0 TO CAPTURE EACH VALID EDGE
    TIM4->DIER |= TIM_DIER_UIE;                 // UPDATE INTERRUPT ENABLE
    TIM4->CCER |= TIM_CCER_CC1E;                // ENABLE COUNTER CAPTURE
    TIM4->DIER |= TIM_DIER_CC1IE;               // ENABLE CH1 CAPTURE/COMPARE INTERRUPT
    TIM4->CR1 |= TIM_CR1_CEN;                   // Enable the counter
    NVIC_SetPriority(TIM4_IRQn, 1);             // SET PRIORITY TO 1
    NVIC_EnableIRQ(TIM4_IRQn);                  //ENABLE TIM4 INTERRUPT IN NVIC


}

void TIM4_Echo_Read(void){

    if ((TIM4->SR & TIM_SR_UIF) != 0){          // Check the update event flag
        overflow++;                             // if UIF = 1, increment overflow counter
        TIM4->SR &= ~TIM_SR_UIF;                // clear UIF
    }
    if ((TIM4->SR & TIM_SR_CC1IF) != 0){        // Check capture event flag 
    newcounter = TIM4->CCR1;                    // read capture value, store as newcounter
    timespan = (newcounter - lastcounter)+(65536 * overflow); // calculate the total pulse width
    lastcounter = newcounter;                   // save the value of newcounter as lastcounter to be used for the next cycle
    overflow = 0;                               // clear overflow counter
    }

}

void LED (void){

    int Distance;               // actual distance in cm
    Distance = (timespan / 58);

    if (Distance <= 100){

        GPIOB->BSRRL = (1<<7);
    }
        else {
        GPIOB->BSRRH = (1<<7);

        }   
    }

Here are the findings when I run the debugger:

1/ Newcounter reads the CCR1 value. (e.g. 0X000000000001AD2E), timespan loads the value of the equation, and Distance load the value of equation (timespan/58).

2/ The value of Distance is always way more than 100, even if there is an object 50cm away. In general the values does not reflect the actual condition.

Not to mention that the LED is strangely ON all the time, although the above findings should result in OFF LED.

I can see the LED connected to the Echo pin (PB6) blinking, which I think it means a continues signal is being received.

Thoughts?

P.S I should've used float for Distance, however due to debugger issue I replaced it with int, till I get the issues above sorted out.

  • Additionally, I'm using a voltage divider ( 5V -> 3V) from the Echo pin to the input pin PB6 – lightworks Aug 8 '17 at 8:21
  • You don't say how your LED is connected so a first guess would be that making the pin high turns the LED on. However, it could be that you haven't set the pins up correctly and I'd [attempt to] switch the LED off at the start of your code, single step though and make sure it really does switch off. – DiBosco Aug 8 '17 at 8:31
  • Another thought is that you use way too many global variables. Passing values in and out of functions makes things much easier to read. – DiBosco Aug 8 '17 at 8:32
  • @DiBosco I did mention that LED is connected to PB7 (as part of discovery development board). furthermore, i tried in a separate code to blink the LED with BSRRL (Set) & BSRRH (reset) and it worked. most importantly, when I run the debugger, the LED never turns on even when completing the whole cycle. regarding the global variable, can you give me an example of how I can make this better? – lightworks Aug 8 '17 at 8:50
  • Telling me the LED is connected to PB7 means nothing though. I didn't know whether it was connected so its pin being high turned it on or off? That was the salient point. Is your separate code setting up the GPIO in exactly the same way? Did that also not work if you ran it with the debugger? – DiBosco Aug 8 '17 at 8:56

I would like to share the updated code, which actually works (no library needed):

#include <stdio.h>
#include "stm32l1xx.h"                  // Keil::Device:Startup

        //Initialize the timers variables.
    volatile int timespan = 0;                              // Total pulse width
    volatile int lastcounter = 0;                           // Timer counter value of the last event
    volatile int newcounter = 0;                            // Timer counter value of the current event
    volatile int overflow = 0;                              // Count the number of overflows


    void SysTick_Handler(void);
    void SetHSI(void);
    void LED_GPIO(void);    
    void TIM4_C1_Init(void);
    void TIM2_C3_Init(void);
    void TIM4_IRQHandler(void);
    void LED (void);

        void setSysTick(void){
    // ---------- SysTick timer (1ms) -------- //
    if (SysTick_Config(SystemCoreClock / 1000)) {
    while (1);  // Capture error
    }
}
    volatile uint32_t msTicks=0; //counts 1ms timeTicks 
    void SysTick_Handler(void) {
    msTicks++;
}

static void Delay(__IO uint32_t dlyTicks){ 
  uint32_t curTicks; 
    curTicks = msTicks;
    while ((msTicks - curTicks) < dlyTicks);
}

        int main(void){
          SysTick_Handler();
            setSysTick();
            SetHSI();                             
            LED_GPIO();

            TIM2_C3_Init();
            TIM4_C1_Init();
while(1){

    LED();

Delay(100);
    }
}

/*----------------------------------------------------------------------------
  set HSI as SystemCoreClock (HSE is not populated on STM32L-Discovery board)
 *----------------------------------------------------------------------------*/
void SetHSI(void) {

// Turn on HSI (16MHz)
RCC->CR |= RCC_CR_HSION;
// Wait until HSI is ready
while( (RCC->CR & RCC_CR_HSIRDY) == 0);
// Select HSI as system clock
RCC->CFGR &= ~RCC_CFGR_SW_HSI;
RCC->CFGR |= RCC_CFGR_SW_HSI;
while( (RCC->CFGR & RCC_CFGR_SWS)!=RCC_CFGR_SWS_HSI ); // Wait till HSI
}

// Configure GPIO Port B
void LED_GPIO(void){


    RCC->AHBENR |= RCC_AHBENR_GPIOBEN;        // Enable GPIOB clock 

//PB7 LED ON/OFF
    GPIOB->MODER   |=   GPIO_MODER_MODER7_0;     // General purpose output mode
  GPIOB->OSPEEDR |=   GPIO_OSPEEDER_OSPEEDR7;  // Max High speed 50MHz


}

// CONFIGURE TIM2 FOR SENDING OUTPUT SIGNAL
void TIM2_C3_Init(void){

    RCC->AHBENR |= RCC_AHBENR_GPIOBEN;        // Enable GPIOB clock 

//PB10 Pluse Generating Pin
    GPIOB->MODER   &=   ~(0x03 << (2*10));     // Clear bit 12 & 13 Alternate function mode 
    GPIOB->MODER   |=   0x02 << (2*10);                 // set as Alternate function mode 
    GPIOB->OSPEEDR &=   ~(0x03<< (2*10));           // 40 MHz  speed 
    GPIOB->OSPEEDR |=   0x03<< (2*10);              // 40 MHz  speed 
    GPIOB->PUPDR &=         ~(1<<10);                           // NO PULL-UP PULL-DOWN 
    GPIOB->OTYPER &=        ~(1<<10);                           // PUSH-PULL 
    GPIOB->AFR[1] |=        0x1 << (4*2);                   // set PB pin 10 as AF1 (TIM2_CH3)

    RCC->APB1ENR |= RCC_APB1ENR_TIM2EN;                 // ENABLE TIM2 CLOCK
    TIM2->PSC = 159;                                                        // SET APPROPRAIT PRESCALER TO SLOW DOWN THE CLOCK
    TIM2->ARR = 0XFFFF;                                                 // SET MAX PULSE WIDTH OF 65536us FOR 16-BIT TIMER
    TIM2->CR1 |= TIM_CR1_DIR;                                       // Set downcounting counter direction
    TIM2->CCMR2 &= ~(TIM_CCMR2_OC3M);                       // Clear OC3M (Channel 3)
  TIM2->CCMR2 |= TIM_CCMR2_OC3M_1 | TIM_CCMR2_OC3M_2;
    TIM2->CCMR2 |= TIM_CCMR2_OC3PE;                         // CH3 Output Preload Enable
    TIM2->CR1 |= TIM_CR1_ARPE;                                  // Auto-reload Prelaod Enable
    TIM2->CCER |= TIM_CCER_CC3E;                                // Enable Output for CH3
    TIM2->EGR |= TIM_EGR_UG;                                        // Force Update
    TIM2->SR &= ~TIM_SR_UIF;                                        // Clear the Update Flag
    TIM2->DIER |= TIM_DIER_UIE;                                 // Enable Interrupt on Update
    TIM2->CCR3 &= ~(TIM_CCR3_CCR3);                     // Clear CCR3 (Channel 3) 
    TIM2->CCR3 |= 0x1;                                            // Load the register 
    TIM2->CR1 |= TIM_CR1_CEN;                           // Enable the counter
}


// CONFIGURE TIM4 FOR RECEIVING INPUT SIGNAL
void TIM4_C1_Init(void){
    RCC->AHBENR |= RCC_AHBENR_GPIOBEN;        // Enable GPIOB clock 
    GPIOB->MODER   &=   ~(0x03 << 12);     // Clear bit 12 & 13 Alternate function mode 
    GPIOB->MODER   |=   (0x02 << 12);               // set as Alternate function mode 
    GPIOB->OSPEEDR &=   ~(0x03<< 12);           // 40 MHz  speed 
    GPIOB->OSPEEDR |=   (0x03<< 12);                // 40 MHz  speed 
    GPIOB->PUPDR &=         ~(0X3<<12);                         // NO PULL-UP PULL-DOWN 
    GPIOB->OTYPER &=        ~(1<<6);                            // PUSH-PULL 
    GPIOB->AFR[0] &= ~GPIO_AFRL_AFRL6;  // Clear pin 6 for alternate function
    GPIOB->AFR[0] |=        0x2 << (4*6);                   // set PB pin 6 as AF2 (TIM4_CH1) 

    RCC->APB1ENR |= RCC_APB1ENR_TIM4EN;                 // ENABLE TIM4 CLOCK
    TIM4->PSC = 15;                                     // SET APPROPRAIT PRESCALER TO SLOW DOWN THE CLOCK                                              
    TIM4->CCMR1 &= ~TIM_CCMR1_CC1S;                         // CLEAR CAPTURE/COMPARE REGISTER
    TIM4->CCMR1 |= 0X1;                                                 // SELECT CH1 INPUTE CAPTURE 
    TIM4->CCMR1 &= ~TIM_CCMR1_IC1F;                         // DISABLE DIGITAL FILTERING
    TIM4->CCER |= (1<<1 | 1<<3);                                // SELECT BOTH RISING AND FALLING EDGE DETECTION CC1P & CC1NP
    TIM4->CCMR1 &= ~(TIM_CCMR1_IC1PSC);                 // INPUT PRESCALER 0 TO CAPTURE EACH VALID EDGE
    TIM4->CCER |= TIM_CCER_CC1E;                                // ENABLE COUNTER CAPTURE
    TIM4->DIER |= TIM_DIER_CC1IE;                               // ENABLE CH1 CAPTURE/COMPARE INTERRUPT
    TIM4->DIER |= TIM_DIER_CC1DE;   
    TIM4->DIER |= TIM_DIER_UIE;                                 // UPDATE INTERRUPT ENABLE
    TIM4->CR1 &= ~TIM_CR1_DIR;                                      // Set downcounting counter direction
    TIM4->CR1 |= TIM_CR1_CEN;                                       // Enable the counter
    NVIC_SetPriority(TIM4_IRQn, 1);                         // SET PRIORITY TO 1
    NVIC_EnableIRQ(TIM4_IRQn);                                  //ENABLE TIM4 INTERRUPT IN NVIC


}

void TIM4_IRQHandler(void){

    if ((TIM4->SR & TIM_SR_UIF) != 0){                  // Check the update event flag
        overflow++;                                 // if UIF = 1, increment overflow counter
        TIM4->SR &= ~TIM_SR_UIF;                                    // clear UIF
    }
    if ((TIM4->SR & TIM_SR_CC1IF) != 0){                // Check capture event flag 
    newcounter = TIM4->CCR1;                                        // read capture value, store as newcounter
    timespan = (newcounter - lastcounter)+(65536 * overflow);   // calculate the total pulse width
    lastcounter = newcounter;                               // save the value of newcounter as lastcounter to be used for the next cycle
    overflow = 0;                                                       // clear overflow counter
    }

}

void LED (void){

    float Distance;                                             // actual distance in cm
    Distance = (timespan / 58.0);

    if (Distance > 0.0 && Distance <= 100.0){

        GPIOB->BSRRL = (1<<7);
    }
        else {
        GPIOB->BSRRH = (1<<7);

        }   
    }
  • So it was all down to using int rather than float? – DiBosco Aug 14 '17 at 8:46
  • @DiBosco Not exactly, the main reason why it didn't work was because of writing a wrong function name when enabling the NVIC interrupt in NVIC_SetPriority(TIM4_IRQn, 1); NVIC_EnableIRQ(TIM4_IRQn); once the function name was matched, it worked. The reason why I changed the float to int is that STM32L1 doesn't have a FPU. However, I did try to run it after writing float instead of int, and it actually worked, but with a bit of slow response, and the Distance value didn't show up as well while running the debugger. Hence, used intinstead – lightworks Aug 15 '17 at 5:11

Your Answer

 

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.