修复arduino uno板的C代码 - 超声波模块HC-SR04

问题描述 投票:0回答:1

我最近编写了代码,旨在测量 arduino uno 超声波模块 HC-SR04 检测到的范围。然而,打印的范围值总是错误的。范围值从来都不准确,经常会跳动 3000 厘米到 5 厘米。

我非常感谢您对这个 DIY 项目的帮助。谢谢大家! :D

#include <avr/io.h>
#include <util/delay.h>
#include <stdio.h>
#include <avr/interrupt.h>

// Define macros for bit manipulation
#define bitSet(reg, n) (reg |= 1 << n)
#define bitRead(reg, n) (reg & (1 << n))
#define bitClear(reg, n) (reg &= ~(1 << n))

// Define constants for clock frequency and baud rate
#define FOSC 16000000
#define BAUD 9600
#define MYUBRR FOSC / 16 / BAUD - 1

// Define pin assignments
#define TRIG_PIN PB1
#define ECHO_PIN PB2

// Declare volatile variables for storing time and distance
volatile unsigned long startTime = 0;
volatile unsigned long endTime = 0;
volatile unsigned long duration = 0;
volatile int distance = 0;

// Function prototypes
void USART_Init(unsigned int ubrr);
void USART_Transmit(unsigned char data);
void txString(const char *pStr);

// Function to initialize USART communication
void USART_Init(unsigned int ubrr)
{
  // Set baud rate
  UBRR0H = (unsigned char)(ubrr >> 8);
  UBRR0L = (unsigned char)ubrr;

  // Enable receiver and transmitter
  UCSR0B = (1 << RXEN0) | (1 << TXEN0);

  // Set frame format: 8 data bits, 1 stop bit
  UCSR0C = (1 << UCSZ01) | (1 << UCSZ00);
}

// Function to transmit a single character over USART
void USART_Transmit(unsigned char data)
{
  // Wait for empty transmit buffer
  while (!(UCSR0A & (1 << UDRE0)))
    ;
  // Put data into buffer, sends the data
  UDR0 = data;
}

// Function to transmit a string of characters over USART
void txString(const char *pStr)
{
  while (*pStr != '\0')
  {
    USART_Transmit(*pStr);
    pStr++;
  }
}

// Interrupt service routine for Timer 1 capture event
ISR(TIMER1_CAPT_vect)
{
  if (bitRead(PINB, ECHO_PIN)) {
    startTime = ICR1;
  } else {
    endTime = ICR1;
    duration = endTime - startTime;
    distance = duration * 0.017 / 2;
  }
}

int main(void)
{
  // Initialize USART communication
  USART_Init(MYUBRR);

  // Set trigger pin as output and echo pin as input
  DDRB |= (1 << TRIG_PIN);
  DDRB &= ~(1 << ECHO_PIN);

  // Set timer 1 for input capture mode with noise canceler and prescaler of 8
  TCCR1B |= (1 << ICNC1) | (1 << ICES1) | (1 << CS11);

  // Enable input capture interrupt
  TIMSK1 |= (1 << ICIE1);

  // Enable global interrupts
  sei();

  // Main loop
  while (1)
  {
    // Trigger ultrasonic sensor
    PORTB |= (1 << TRIG_PIN);
    _delay_us(10);
    PORTB &= ~(1 << TRIG_PIN);

    // Transmit distance over USART
    char formattedDistance[20];
    sprintf(formattedDistance, "Distance: %d cm\n", distance);
    txString(formattedDistance);

    _delay_ms(500); // Wait for 1 second
  }
}

c math arduino-uno platformio
1个回答
0
投票

问题出在中断例程上:

ICR1
是一个16位寄存器。但你将它分配给
unsigned long
,它是 32 位的。如果您有计时器溢出,则持续时间测量不会自然溢出。

您必须将

startTime
endTime
duration
声明为
unsigned int
。最佳做法是使用
#include stdint.h
,以便您可以指定整数的大小,该大小可能因平台而异。

正如 Lundin 指出的,让 ISR 尽可能短,例如

#include "stdint.h"

uint16_t duration = 0;
uint16_t startTime = 0;
uint16_t endTime = 0;

// Interrupt service routine for Timer 1 capture event
ISR(TIMER1_CAPT_vect)
{
  if (bitRead(PINB, ECHO_PIN)) {
    startTime = ICR1;
  } else {
    endTime = ICR1;
    duration = endTime - startTime;
  }
}

然后在主循环中转换为距离。通常,在没有浮点单元 (FPU) 的 MCU 上,您需要使用整数算术进行计算。为了简单起见,您可以将它们保留为浮点算术,但请注意它们将花费更长的时间。

// Convert to distance
float twice_the_distance = (float)duration * 0.017f;
uint16_t distance = ((uint16_t)twice_the_distance) >> 1; // Divide by two is a simple shift operation

顺便说一句,当你想要测量时,我还会明确地重置、启动和停止计时器,这样你就可以确保在正确的时刻测量到时间。

© www.soinside.com 2019 - 2024. All rights reserved.