HC-SR4超声波传感器与STM32L1的接口
我试图通过简单地在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);
}
}
}
什么乱七八糟的你的代码,而不仅仅是你的布局,您使用全局变量的“逻辑”似乎是完全foobar的。我认为您必须付出重大努力来整理代码的设计,并从有经验的嵌入式软件工程师处获得建议。想一想:这只是一小部分代码,你无法得到它的工作,所以利用你目前的开发方法,你将如何得到复杂的工作 - 你需要改变你的方法。
至少有一个与您的代码直接相关的代码问题是,您在main()
中直接调用TIM4中断处理程序时,其中一个全局变量PulseEnd
被设置为副作用 - 为什么?也许是初始化全局变量?不要这样做! - 并且在此代码中的任何位置都不会重置。
之后,首次进入while(1)
环能立刻将PulseSent
1,测试当然是真的,你的代码立即认为回波已被检测,而timespan
可能仍然是0,肯定小于5800(你鸿沟timespan
由随机幻数58得到Distance
,然后比较Distance
到100来决定LED是否应该打开或关闭)。但是,这不是你唯一的问题,例如timespan
是uint8_t
,这意味着它是一个8位变量(类型名称中有一个提示),它只能在0-255范围内,但你试图填写timespan
来自两个计时器计数器之间的差异,这些计数器大概是16位,因为您在可能溢出的地方添加了65536.因此,基本上您的代码总是立即认为存在距离小于100个单位的回波距离,并且此外距离永远不会超过255/58(4位和1位),因此即使回声时序工作得更好,也不能超过100个,因此LED始终保持开启状态。
不知道为什么你有代码来处理overflow
main()
,而且你正在修改main和TIM4 isr溢出的事实是一个红色的标志,有可能发生神秘/瞬时/稀有/难以追踪/致命的问题,我注意到,当overflow==1
(但如果溢出> 1,并且不能溢出有效地为1,因为它正在增加TIM4中断代码?),那么Distance
不会重新计算,但随后的代码然后使用Distance
照亮/熄灭LED。这更多的逻辑问题的臭味。
newcounter
和lastcounter
也是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()中修改代码后,它仍然有机会在它接近真实硬件之前工作,正如你发现的那样,可能很难理解实际正在发生的事情。
感谢您的宝贵答案。我正在浏览你提到的每一个音符,我会分享我得到的任何进展。希望我会在最后分享一个操作代码。非常感谢 – lightworks
我在这里有一些疑惑,希望你能回复我的即将发表的评论。我还会回复一些关于我为什么写上面代码的特定部分的问题。关于数字58,实际上它在HC-SR04数据表中作为公式给出,其中范围可以通过发送触发信号和接收回波信号之间的时间间隔来计算。公式:美国/ 58 =厘米。 – lightworks
无论变量状态如何,寄存器GPIOB-> BSRRL只会将GPIOB位复位为1。 GPIOB-> BSRRL是32位寄存器的低16位。 当位为'1'时,高16位将设置GPIOB引脚。
所以要关闭LED,您需要类似于 GPIOB-> BSRRH =(1 < < 7);. (这会让Barny感到不安,非常具体,没有任何便携性,需要连接到芯片的硬件等知识。)。
好的。不要让我不高兴,交配。如果它必须是硬件特定的,那就是它必须的。主要的事情必须是正确的。你可能指的是神奇的数字咆哮。如果人们将15和58放入寄存器,那么应该如何维护。意味着不得不返回到数据表来找出为什么它的价值和如何改变价值,以及如何判断一个58是否与另一个58相同?这就是命名常量的用法,用一种方式定义它们,以显示它们的计算方式,更容易看出它们是否错误并更易于理解。 – barny
嘿,同意你的意见,我相信一个新手会看到你的观点并采纳它们。但是,我仍然一起编写一些代码来使事情发生,然后将这些发现纳入一些可以维护的事情中。 – BitSmith
@BitSmith很棒的笔记,谢谢 – lightworks
我想和大家分享更新的代码,它的实际工作(不需要库):
#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);
}
}
好像你应该能够确定您的传感器通过用万用表看它的工作。您应该能够看到您是否正在使用调试器正确地感知它(或输出调试数据)。您应该能够证明您可以正确控制LED。这三件事可以帮助你缩小你做错的地方。 –
在调试器中遍历代码。弄清楚什么是不像你期望的那样工作。 – kkrambo
时间跨度是否应该是uint8?你计算一些可能比255大的东西来放入它。你看过硬件,看看这些线路是否适当脉动?你有没有做过任何调试? – barny