c - HC-SR4超声波传感器与STM32L1接口(interface)

标签 c embedded stm32 stm32ldiscovery

当检测到距离小于1m的物体时,我试图通过仅打开LED来控制HC-SR4超声波传感器。
我将TIM2用于触发信号(引脚PB10),并将TIM4用于接收回波信号(引脚PB6)。 LED连接到PB7引脚。
当我加载下面的代码时,LED只是点亮,无论是否有物体,它都点亮。

知道我哪里出错了吗?

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


// switch from HSE to HSI clock 16MHz
void HSI_config(){

RCC->CR |= RCC_CR_HSION; // Turn On HSI oscillator
RCC->CFGR |= RCC_CFGR_SW; // Select HSI clock
RCC->CFGR |= RCC_CFGR_SWS_HSI;
RCC->CR |= RCC_CR_HSIRDY; // wait for HSI stabilize
}

// Configure GPIO Port B
void GPIO_config(){

    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 << (2*6));     // Clear bit 12 & 13 Alternate function mode 
    GPIOB->MODER   |=   0x02 << (2*6);              // set as Alternate function mode 
    GPIOB->OSPEEDR &=   ~(0x03<< (2*6));            // 40 MHz  speed 
    GPIOB->OSPEEDR |=   0x03<< (2*6);               // 40 MHz  speed 
    GPIOB->PUPDR &=         ~(1<<6);                            // NO PULL-UP PULL-DOWN 
    GPIOB->OTYPER &=        ~(1<<6);                            // PUSH-PULL 
    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 TIM4 FOR RECEIVING INPUT SIGNAL
void TIM4_Enable(){
    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->CCER |= TIM_CCER_CC1E;                                // ENABLE COUNTER CAPTURE
    TIM4->DIER |= TIM_DIER_CC1IE;                               // ENABLE CH1 CAPTURE/COMPARE INTERRUPT
    NVIC_SetPriority(TIM4_IRQn, 1);                         // SET PRIORITY TO 1
    NVIC_EnableIRQ(TIM4_IRQn);                                  //ENABLE TIM4 INTERRUPT IN NVIC
}

// CONFIGURE TIM2 FOR SENDING OUTPUT SIGNAL
void TIM2_Enable(){
    RCC->APB1ENR |= RCC_APB1ENR_TIM2EN;                 // ENABLE TIM2 CLOCK
    TIM2->PSC = 15;                                                         // 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 downcounting counter direction
    TIM2->CR1 |= TIM_CR1_CEN;                                       // Enable Counter
}


    //Initialize the float variables.
    volatile uint8_t timespan = 0;                              // Total pulse width
    volatile uint8_t lastcounter = 0;                           // Timer counter value of the last event
    volatile uint8_t newcounter = 0;                            // Timer counter value of the current event
    volatile uint8_t overflow = 0;                              // Count the number of overflows
    volatile uint8_t PulseEnd = 0;                              // Declare end of pulse

void Echo_TIM4_IRQHandler(){

    if ((TIM4->SR & TIM_SR_UIF) != 0){                  // Check the update even flag
        overflow = overflow + 1;                                    // 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
    PulseEnd = 1;
}

}

void setSysTick(void){
    // ---------- SysTick timer (1ms) -------- //
    if (SysTick_Config(SystemCoreClock / 1000)) {
        // Capture error
        while (1){};
    }
}

    volatile uint32_t msTicks;      //counts 1ms timeTicks
void SysTick_Handler(void) {
    msTicks++;
}

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



    int main(void){

            float Distance = 0.0f;                                              // actual distance in cm
            int Pulsesent = 0;

    HSI_config();
    setSysTick();
    GPIO_config();
    TIM4_Enable();
    Echo_TIM4_IRQHandler();

while(1){

if (Pulsesent == 0) {
    (Pulsesent = 1);
        TIM2_Enable();
}

if(Pulsesent && PulseEnd){
            Pulsesent = 0;

        if(overflow == 1){
                timespan = 0;
            }
            else {
                Distance = (timespan / 58) ;
            }
            Delay(1);
        }   
    if (Distance <= 100){

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

        }   

    }   

}

最佳答案

您的代码是一团糟,而不仅仅是布局,使用全局变量的“逻辑”似乎完全是foobar。我认为您必须付出巨大的努力来整理代码的设计,并从经验丰富的嵌入式软件工程师那里获得建议。想想:这只是一小段代码,您无法使其正常工作,因此,以当前的开发方法来开发复杂的工作方式-您需要更改方法。

与您的症状直接相关的代码问题中,至少有一个问题是,在PulseEnd中直接调用TIM4中断处理程序时,全局变量之一main()被设置为副作用-为什么? ?也许要初始化全局变量?不要这样! -并且永远不会在此代码的任何地方重置。

然后,第一次进入while(1)循环,该循环立即将PulseSent设置为1,测试if (PulseSent && PulseEnd)当然是正确的,并且您的代码立即认为已检测到回声,而timespan可能仍为0,当然小于5800(将timespan除以随机的幻数58得到Distance,然后将Distance与100进行比较以决定LED应该亮还是灭)。但是,这不是唯一的问题,例如timespanuint8_t,这意味着它是一个8位变量(类型名称中有提示),只能在0-255范围内,但是您尝试使用两个计时器计数器(可能为16位)之间的差值来填充timespan,因为您会添加65536的可能溢出。因此,基本上,您的代码始终会立即认为存在小于100个单位距离的回声,此外距离永远不能超过255/58(4位),因此即使回声定时效果更好,也永远不能超过100,因此LED始终亮着。

不知道为什么有代码在overflow中处理main(),并且您正在同时修改main和TIM4 isr中的溢出是一个危险信号,闻到了潜在的神秘/短暂/稀有/不可追踪/致命的问题,我注意到,当overflow==1时(但不是如果溢出> 1,并且由于TIM4中断代码正在递增而不能有效地溢出为1时,则不是),则不会重新计算Distance,但随后的代码然后使用Distance点亮/熄灭LED。这冒出更多的逻辑问题。

newcounterlastcounter也是uint8_t,但是计数器是16位的,不是吗?值得检查。您是否在启用警告的情况下运行编译器,应该能够告诉您何时必须通过赋值将表达式转换为限制性更强的类型(例如C192警告?)。如果没有警告,则应打开警告,并应注意它们。实际上,打开-Wall或任何等效的keil,并检查每个警告,最好通过更正代码来消除它。如果您无法更改代码以消除奇怪的警告。每次添加大量代码时,请确保检查警告中的更改。当然,最容易看到从零警告到> 0警告的变化,因此(也应该是)消除所有警告的另一个动机。

看不到任何代码可以处理没有回声响应的情况(如果超声波传感器损坏或断开连接,也可能发生)。好像您的代码(一旦PulseEnd问题解决了),如果没有得到回声,就永远不会再试一次,不确定。

由于PulseEnd永远不会重置,因此您的代码将尽快将TIM2脉冲发送到超声波测距仪-很好,延迟了delay(1),我猜这是一个1ms的延迟-因此,取决于硬件,尚不清楚实际上会得到回声响应。如果您查看了传感器的输出信号,您将会看到。

我已经确定了魔术数字58的来源,并假设TIM4以10KHz运行,空气中的声音速度为340m / s,到目标的距离为1m(因此脉冲必须传播2m)将花费5.882ms。好像您要通过将整数四舍五入为58.82来嵌入1.4%的比例缩放错误(在100cm的目标范围内为1.4 cm),如果计算必须为整数(但这不是因为Distance被声明为浮动)然后使用59只是0.3%的缩放误差,但您可以在浮点数中进行微不足道的计算,并且误差很小。我猜想存储在PSC中的幻数15会设置计时器速度。我讨厌魔术数字。

无论如何,当/如果您真的必须像这样在复杂的舞蹈中使用全局变量,请开发一个与其他人坐下来回顾您的工作并在纸上写出变量如何在初始化main()之间相互作用的过程。和中断例程,并要严格意识到,对它们处理方式的任何更改都意味着您需要坐下来并在一张新纸上重新演练该舞蹈,这是指它以前的工作方式,并将其记录在上一张纸上。并尽最大努力开发单元测试,至少可以让您有一定程度的信心,即对main()中的代码进行更改后,仍然有机会在接近您发现的真实硬件之前工作。很难理解实际情况。

关于c - HC-SR4超声波传感器与STM32L1接口(interface),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45224559/

相关文章:

c - 在 C 中将变量记录到文件中

c - 将循环结构数据放入 C 中的文件中

embedded - 有关 ISR 期间中断的问题

c - 单周期乘法和硬件除法是什么意思?

linux - GNU ARM - 未找到 nano.specs

c - 使用线程的全双工客户端-服务器套接字

c - 如何使用一个终端调试 QEMU?

c - 安装AVR开发平台for mac时架构x86_64错误

c - 是否可以使用STM32生成CAN总线错误?

c++ - 混合 C/C++ 代码中的结构指针