2017-07-20 111 views
-2

我試圖通過簡單地在LED上檢測到距離小於1m的物體來控制HC-SR4超聲波傳感器。 我使用TIM2作爲觸發信號(引腳PB10),TIM4接收回聲信號(引腳PB6)。 LED連接到引腳PB7。 當我加載下面的代碼時,只需打開LED指示燈,無論有沒有物體,它都只是ON。HC-SR4超聲波傳感器與STM32L1的接口

任何想法我哪裏出錯了?

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

     } 

    } 

} 
+0

好像你應該能夠確定您的傳感器通過用萬用表看它的工作。您應該能夠看到您是否正在使用調試器正確地感知它(或輸出調試數據)。您應該能夠證明您可以正確控制LED。這三件事可以幫助你縮小你做錯的地方。 –

+0

在調試器中遍歷代碼。弄清楚什麼是不像你期望的那樣工作。 – kkrambo

+1

時間跨度是否應該是uint8?你計算一些可能比255大的東西來放入它。你看過硬件,看看這些線路是否適當脈動?你有沒有做過任何調試? – barny

回答

1

什麼亂七八糟的你的代碼,而不僅僅是你的佈局,您使用全局變量的「邏輯」似乎是完全foobar的。我認爲您必須付出重大努力來整理代碼的設計,並從有經驗的嵌入式軟件工程師處獲得建議。想一想:這只是一小部分代碼,你無法得到它的工作,所以利用你目前的開發方法,你將如何得到複雜的工作 - 你需要改變你的方法。

至少有一個與您的代碼直接相關的代碼問題是,您在main()中直接調用TIM4中斷處理程序時,其中一個全局變量PulseEnd被設置爲副作用 - 爲什麼?也許是初始化全局變量?不要這樣做! - 並且在此代碼中的任何位置都不會重置

之後,首次進入while(1)環能立刻將PulseSent 1,測試​​當然是真的,你的代碼立即認爲回波已被檢測,而timespan可能仍然是0,肯定小於5800(你鴻溝timespan由隨機幻數58得到Distance,然後比較Distance到100來決定LED是否應該打開或關閉)。但是,這不是你唯一的問題,例如timespanuint8_t,這意味着它是一個8位變量(類型名稱中有一個提示),它只能在0-255範圍內,但你試圖填寫timespan來自兩個計時器計數器之間的差異,這些計數器大概是16位,因爲您在可能溢出的地方添加了65536.因此,基本上您的代碼總是立即認爲存在距離小於100個單位的回波距離,並且此外距離永遠不會超過255/58(4位和1位),因此即使回聲時序工作得更好,也不能超過100個,因此LED始終保持開啓狀態。

不知道爲什麼你有代碼來處理overflowmain(),而且你正在修改main和TIM4 isr溢出的事實是一個紅色的標誌,有可能發生神祕/瞬時/稀有/難以追蹤/致命的問題,我注意到,當overflow==1(但如果溢出> 1,並且不能溢出有效地爲1,因爲它正在增加TIM4中斷代碼?),那麼Distance不會重新計算,但隨後的代碼然後使用Distance照亮/熄滅LED。這更多的邏輯問題的臭味。

newcounterlastcounter也是uint8_t,但是計數器是16位的,不是嗎?值得檢查。你是否啓動了帶有警告的編譯器,你應該能夠讓它告訴你何時必須通過賦值將表達式轉換爲更具限制性的類型(例如C192警告?)。如果你沒有警告,你應該注意,你應該注意它們。實際上,打開-Wall或任何keil等價物,並檢查每一個警告,最好通過糾正你的代碼來消除它。如果你不能改變你的代碼來消除奇怪的警告。每次添加大量代碼時,請確保檢查警告中的更改。當然,從零警告到> 0警告是最容易的,所以這是(或者應該是)消除所有警告的另一個動機。

看不到任何代碼似乎處理的情況下,從來沒有回聲響應(也可能發生,如果超聲波傳感器損壞或斷開)。看起來像你的代碼(一旦PulseEnd問題得到解決)將不會再次嘗試,如果它沒有得到回聲,不知道。

由於PulseEnd永遠不會被重置,您的代碼正在儘可能快地向超聲波測距儀發送TIM2脈衝 - 好了,延遲了一個延遲(1),我認爲延遲爲1ms - 因此取決於硬件不清楚它是否真的會得到回聲響應。如果你看過傳感器的輸出信號,你會看到。

我已經計算出你的魔法數字58來自哪裏,並且假設TIM4以10KHz的速度運行,空氣中的聲速爲340m/s,距目標1m的距離(因此脈衝必須運行2m) 5.882ms。好像你正在嵌入一個1.4%的縮放誤差(即在目標範圍100釐米處爲1.4釐米),如果計算必須是整數(但不是因爲「距離」被聲明爲浮動),將58.82舍入爲58,那麼使用59只有0.3%的縮放誤差,但你可以在浮點上做平凡的計算,並且有很小的誤差。我猜測存儲在PSC中的幻數15設定了定時器的速度。我討厭魔法數字。

無論如何,當/如果你真的在這樣一個複雜的舞蹈使用全局變量,開發坐下來與別人查看你在做什麼,寫出來的一些紙張如何變量相互作用的過程在初始化,main()和中斷例程之間,並且嚴格地意識到,對它們的處理的任何改變意味着你需要坐下來並且在一張新紙上再次跳舞,參考它如何用於寫入的工作放在前面的紙上。並且盡你最大的努力開發單元測試,至少讓你有一定程度的信心,在main()中修改代碼後,它仍然有機會在它接近真實硬件之前工作,正如你發現的那樣,可能很難理解實際正在發生的事情。

+0

感謝您的寶貴答案。我正在瀏覽你提到的每一個音符,我會分享我得到的任何進展。希望我會在最後分享一個操作代碼。非常感謝 – lightworks

+0

我在這裏有一些疑惑,希望你能回覆我的即將發表的評論。我還會回覆一些關於我爲什麼寫上面代碼的特定部分的問題。關於數字58,實際上它在HC-SR04數據表中作爲公式給出,其中範圍可以通過發送觸發信號和接收回波信號之間的時間間隔來計算。公式:美國/ 58 =釐米。 – lightworks

1

無論變量狀態如何,寄存器GPIOB-> BSRRL只會將GPIOB位復位爲1。 GPIOB-> BSRRL是32位寄存器的低16位。 當位爲'1'時,高16位將設置GPIOB引腳。

所以要關閉LED,您需要類似於 GPIOB-> BSRRH =(1 < < 7);. (這會讓Barny感到不安,非常具體,沒有任何便攜性,需要連接到芯片的硬件等知識。)

+0

好的。不要讓我不高興,交配。如果它必須是硬件特定的,那就是它必須的。主要的事情必須是正確的。你可能指的是神奇的數字咆哮。如果人們將15和58放入寄存器,那麼應​​該如何維護。意味着不得不返回到數據表來找出爲什麼它的價值和如何改變價值,以及如何判斷一個58是否與另一個58相同?這就是命名常量的用法,用一種方式定義它們,以顯示它們的計算方式,更容易看出它們是否錯誤並更易於理解。 – barny

+0

嘿,同意你的意見,我相信一個新手會看到你的觀點並採納它們。但是,我仍然一起編寫一些代碼來使事情發生,然後將這些發現納入一些可以維護的事情中。 – BitSmith

+0

@BitSmith很棒的筆記,謝謝 – lightworks

0

我想和大家分享更新的代碼,它的實際工作(不需要庫):

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

     } 
    } 
相關問題