将HC-SR4超声波传感器与STM32L1连接

时间:2017-07-20 20:25:38

标签: c embedded stm32 stm32ldiscovery

当检测到距离小于1米的物体时,我试图通过简单地打开LED来控制这个HC-SR4超声波传感器。 我使用TIM2作为触发信号(引脚PB10),TIM4接收Echo信号(引脚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);

        }   

    }   

}

2 个答案:

答案 0 :(得分:1)

你的代码多么糟糕,而不仅仅是你的布局,&#34;逻辑&#34;您使用全局变量似乎完全是foobar。我认为你必须付出一些努力来理清你的代码设计,从经验丰富的嵌入式软件工程师那里获得建议。想想:这是一个微不足道的代码,你不能让它工作,所以用你目前的方法开发你将如何得到一些复杂的工作 - 你需要改变你的方法。

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

然后,第一次进入PulseSent循环,立即将if (PulseSent && PulseEnd)设置为1,测试timespan当然是真的,并且您的代码会立即认为在{{{{{} 1}}可能仍为0,肯定小于5800(您将timespan除以随机幻数58以获得Distance,然后将Distance与100进行比较以确定LED是否应该是打开或关闭)。然而,这不是你唯一的问题,例如timespan是一个uint8_t,这意味着它是一个8位变量(在类型名称中有一个提示),它只能是在0-255的范围内,你尝试从两个定时器计数器之间填充timespan,这两个计时器计数器大概是16位,因为你可能会增加65536的溢出。所以基本上你的代码总是立即认为距离不到100个单位的回声,此外距离不能超过255/58(4和一点),所以即使回声时间更好,也不能超过100,所以LED永远在。

不知道为什么你有代码来处理overflow中的main(),并且你在主要和TIM4 isr中修改溢出的事实是一个红旗,有气味的气味对于神秘/瞬态/稀有/无法追踪/致命的问题,我注意到overflow==1时(但如果溢出&gt; 1则没有,并且由于TIM4中断代码增加而无法有效溢出1) ?)然后Distance不会重新计算,然后后续代码使用Distance来点亮/熄灭LED。这有很多逻辑问题。

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

看不到任何似乎处理没有回声响应的情况的代码(如果超声波传感器损坏或断开也会发生这种情况)。看起来像你的代码(一旦PulseEnd问题得到修复)如果它没有得到回声,就不会再试一次,不确定。

因为PulseEnd永远不会被重置,你的代码会尽可能快地将TIM2脉冲发送到超声波测距仪 - 好吧,延迟减慢(1),我估计是1ms延迟 - 所以取决于硬件它&# 39;不清楚它是否真的会得到回声响应。如果您查看了传感器的输出信号,您会看到。

我已经计算出你的幻数58来自哪里,并假设TIM4以10KHz运行,声速在空中340m / s,距离目标1m(因此脉冲必须行进2m)需要5.882ms。似乎你正在嵌入一个1.4%的缩放误差(在你的目标范围为100厘米时为1.4厘米),将58.82向下舍入到58,如果计算必须是整数(但它不是因为距离被声明为浮动)然后使用59只有0.3%的缩放误差,但你可以轻松地进行浮点计算并且误差非常小。我猜测存储在PSC中的幻数15设置了计时器速度。我讨厌神奇的数字。

无论如何,当你真的 在这样的复杂舞蹈中使用全局变量时,开发一个与其他人坐下来回顾你正在做什么并在一些纸上写出来的过程变量如何在初始化,main()和中断例程之间进行交互,并且严格意识到对其处理的任何改变意味着你需要坐下来在一张新纸上重新开始跳舞,指的是它如何工作这是写在以前的一点纸上。并且尽最大努力开发单元测试,至少可以让你有一定程度的信心,在更改你的main()中的代码后,它仍然有机会在它接近真正的硬件之前工作,正如你发现的那样,要理解实际发生的事情是非常困难的。

答案 1 :(得分:1)

无论变量状态如何,寄存器GPIOB-> BSRRL将仅复位'1'的GPIOB位。 GPIOB-&gt; BSRRL是32位寄存器的低16位。 当位为1时,高16位将设置GPIOB引脚。

因此,要关闭LED,您需要类似的东西 GPIOB-> BSRRH =(1 <7); (这会让Barny感到不安,非常具体,没有任何便携性,需要知道连接到芯片的硬件等等。)