Interfaz de sensor ultrasónico HC-SR04 con STM32L

Estoy tratando de controlar este sensor ultrasónico HC-SR4 simplemente ENCENDIENDO un LED cuando se detecta un objeto a menos de 100 cm. Estoy usando TIM2 para la señal de activación (Pin PB10) y TIM4 para recibir la señal de eco (Pin PB6). y el LED está conectado al Pin PB7. cuando cargo el código a continuación, el LED simplemente se ENCIENDE, ya sea que haya un objeto o no, simplemente está ENCENDIDO.

#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);

        }   
    }

Aquí están los resultados cuando ejecuto el depurador:

1/ Newcounter lee el valor CCR1. (por ejemplo, 0X000000000001AD2E), intervalo de tiempo carga el valor de la ecuación y Distancia carga el valor de la ecuación (intervalo de tiempo/58).

2/ El valor de Distancia siempre es mucho más de 100, incluso si hay un objeto a 50 cm de distancia. En general, los valores no reflejan la condición real.

Sin mencionar que el LED está extrañamente ENCENDIDO todo el tiempo, aunque los hallazgos anteriores deberían dar como resultado un LED APAGADO.

Puedo ver el LED conectado al pin Echo (PB6) parpadeando, lo que creo que significa que se está recibiendo una señal continua.

¿Pensamientos?

PD: Debería haber usado float para Distancia, sin embargo, debido a un problema con el depurador, lo reemplacé con int, hasta que resolví los problemas anteriores.

Además, estoy usando un divisor de voltaje (5V -> 3V) desde el pin Echo hasta el pin de entrada PB6
No dice cómo está conectado su LED, por lo que una primera suposición sería que hacer que el pin sea alto enciende el LED. Sin embargo, podría ser que no haya configurado los pines correctamente y [intentaría] apagar el LED al comienzo de su código, un solo paso y asegurarse de que realmente se apague.
Otro pensamiento es que usas demasiadas variables globales. Pasar valores dentro y fuera de las funciones hace que las cosas sean mucho más fáciles de leer.
@DiBosco Mencioné que el LED está conectado a PB7 (como parte de la placa de desarrollo de descubrimiento). además, probé en un código separado para hacer parpadear el LED con BSRRL (Establecer) y BSRRH (reiniciar) y funcionó. lo más importante, cuando ejecuto el depurador, el LED nunca se enciende, incluso cuando se completa el ciclo completo. con respecto a la variable global, ¿puede darme un ejemplo de cómo puedo mejorar esto?
Sin embargo, decirme que el LED está conectado a PB7 no significa nada. No sabía si estaba conectado, por lo que su pin estaba alto y lo encendía o apagaba. Ese fue el punto sobresaliente. ¿Su código separado configura el GPIO exactamente de la misma manera? ¿Eso tampoco funcionó si lo ejecutó con el depurador?
Hay montones y montones de artículos y sugerencias sobre cómo evitar las variables globales. Uno de los muchos, muchos ejemplos está aquí: stackoverflow.com/questions/29348663/… Una vez que lo domines, no sabrás cómo te las arreglaste con tantos globales confusos.
@DiBosco sí, funcionó la misma configuración, que es: GPIOB->MODER |= GPIO_MODER_MODER7_0; GPIOB->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR7; y luego si (Distancia <= 100){ GPIOB->BSRRL = (1<<7); } más { GPIOB->BSRRH = (1<<7);
Un poco más sobre los globales. Realmente fui un poco cortante contigo, lo siento. No hay una respuesta rápida y fácil, tendrá que leer y practicar, pero una forma de comenzar es pasando valores de parámetros dentro y fuera de las funciones. Incluso esa regla básica en la que dice que solo usaré variables que se pasan a las funciones y locales dentro de esa función realmente lo ayudará mucho.
Bien, entonces usar ese software muy básico que solo enciende y apaga un LED, ¿ funciona con el depurador? Solo trato de llegar al fondo de dónde podría estar exactamente este problema.
@DiBosco sí funciona, lo probé con el depurador y no hubo problemas
De acuerdo, entonces, si coloca una función en el software problemático que, antes de ingresar al ciclo infinito principal, luego de un solo paso, ¿enciende y apaga los LED?
@DiBosco cuando entra en el bucle infinito y justo después de ejecutar GPIOB->BSRRL = (1<<7); el LED se enciende. luego entra en retardo para provocar el parpadeo, se apaga justo después de ejecutar GPIOB->BSRRH = (1<<7);. luego otra vez un breve retraso... luego se repite
OK, bien y suponiendo que acaba de poner eso en el código que no funciona, eso significa que la parte de control de LED del software es correcta. Entonces, si elimina esa función de prueba, configure el programa en ejecución e interrumpa la función LED en el si (Distancia <= 100), ¿qué sucede cuando realiza un solo paso? ¿Estás 100% seguro de que Distancia es lo que crees que debería ser?
@DiBosco Creo que aquí hay una confusión. Estaba hablando de la prueba de parpadeo del LED cuando dije que el LED se enciende. Si estamos hablando del código problemático anterior, el LED nunca se enciende porque el valor de Distancia SIEMPRE es> 100. y salta directamente a la declaración 'else{' que restablece el Pin LED. Creo que el problema es obtener valores poco realistas en 'Distancia'. tenga en cuenta que los valores pueden alcanzar hasta 800 cm y tal vez más, sabiendo que el sensor ultrasónico solo puede detectar hasta 400 cm. realmente me mata..
@DiBosco, las variables globales son para interactuar con la rutina del servicio de interrupción. Dado que no se llama desde el hilo principal de ejecución, sino que el hardware lo "llama" de manera efectiva, no es posible pasarle variables u obtener un valor de retorno como una subrutina ordinaria. Algunos de los globales podrían ser simplemente locales estáticos, pero un programa en C como este se compilará plano de todos modos, por lo que no es una gran diferencia. Sin embargo, no marcar como "volátiles" a los que se usan solo dentro de una sola ejecución del ISR permitiría que algo así newcounterse mantuviera en un registro.
@Chris, no hay una rutina de servicio de interrupción. Todo se hace con encuestas.
@DiBosco: parece haber sido diseñado para ser una rutina de servicio de interrupción, y en la respuesta del autor de la pregunta a continuación es una rutina de servicio de interrupción, pero tiene razón, en la versión de la pregunta fue encuestada.

Respuestas (1)

Me gustaría compartir el código actualizado, que realmente funciona (no se necesita biblioteca):

#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);

        }   
    }
Entonces, ¿todo se redujo a usar int en lugar de float?
@DiBosco No exactamente, la razón principal por la que no funcionó fue por escribir un nombre de función incorrecto al habilitar la interrupción de NVIC una vez que NVIC_SetPriority(TIM4_IRQn, 1); NVIC_EnableIRQ(TIM4_IRQn);el nombre de la función coincidió, funcionó. La razón por la que cambié a floates intque STM32L1 no tiene FPU. Sin embargo, intenté ejecutarlo después de escribir floaten lugar de int, y realmente funcionó, pero con una respuesta un poco lenta, y el Distancevalor no apareció tan bien mientras ejecutaba el depurador. Por lo tanto, se utiliza inten su lugar