输入捕获程序

← 返回 MOC | ← 主页

代码来源工程:Intelligent_Agricultural_Equipment_Innovation_CompetitionMycode/SR04.c


单路输入捕获测距(轮询触发)

适用于单个 HC-SR04,TIM 预分频至 1 MHz(计数精度 1 μs),上升沿捕获起点、下降沿捕获终点,差值除以 57.5 得距离(cm)。

/* 捕获状态结构体 */
typedef struct {
    uint8_t  cap_state;    /* Bit7=完成, Bit6=已捕获上升沿, Bit[0:5]=溢出次数 */
    uint32_t cap_count;    /* 最终计数值(含溢出补偿) */
    uint16_t start_count;  /* 上升沿时刻 */
    uint16_t end_count;    /* 下降沿时刻 */
} ul_cap_struct;
 
static ul_cap_struct cap;  /* 每路传感器一个实例 */
 
/**
 * @brief 输入捕获 + 溢出中断回调(mark=0 捕获中断,mark=1 溢出中断)
 *        在 HAL_TIM_IC_CaptureCallback / HAL_TIM_PeriodElapsedCallback 中调用
 */
void SR04_cap_receive(ul_cap_struct *c, TIM_HandleTypeDef *htim,
                      uint32_t channel, uint8_t mark)
{
    if (mark == 1) {
        /* 溢出中断:若已捕获上升沿则累计溢出次数,超时则复位 */
        if (c->cap_state & 0x40) {
            if ((c->cap_state & 0x3F) == 0x3F)
                c->cap_state = 0;   /* 溢出次数饱和,放弃本次 */
            else
                c->cap_state++;
        }
        return;
    }
 
    if (c->cap_state & 0x80)
        return;  /* 上次数据未取走,跳过 */
 
    if (!(c->cap_state & 0x40)) {
        /* 上升沿:记录起点,切换为下降沿检测 */
        c->cap_state  = 0x40;
        c->start_count = HAL_TIM_ReadCapturedValue(htim, channel);
        __HAL_TIM_SET_CAPTUREPOLARITY(htim, channel, TIM_INPUTCHANNELPOLARITY_FALLING);
    } else {
        /* 下降沿:计算计数差,标记完成 */
        c->end_count = HAL_TIM_ReadCapturedValue(htim, channel);
        uint32_t overflow = c->cap_state & 0x3F;
        c->cap_count = overflow * 65536 + c->end_count - c->start_count;
        c->cap_state = 0x80;
        __HAL_TIM_SET_CAPTUREPOLARITY(htim, channel, TIM_INPUTCHANNELPOLARITY_RISING);
    }
}
 
/**
 * @brief 读取距离(cm),读后清除完成标志
 * @return 距离 cm,未就绪返回 -1
 */
float SR04_read_cm(ul_cap_struct *c)
{
    if (!(c->cap_state & 0x80))
        return -1.0f;
 
    float dist = (float)c->cap_count / 57.5f;
    if (dist > 255.0f) dist = 255.0f;
    if (dist < 0.0f)   dist = 0.0f;
 
    c->cap_state = 0;
    return dist;
}
 
/* 使用示例 */
/*
// 1. TIM2 预分频 = 83(84MHz 主频 → 1MHz 计数),周期 = 65535
//    CH1 配置为输入捕获,初始上升沿极性
HAL_TIM_IC_Start_IT(&htim2, TIM_CHANNEL_1);
__HAL_TIM_ENABLE_IT(&htim2, TIM_IT_UPDATE);
 
// 2. 触发:TRIG 拉低 40 μs 后拉高(≥10 μs 高电平即可)
HAL_GPIO_WritePin(TRIG_GPIO_Port, TRIG_Pin, GPIO_PIN_RESET);
delay_us(40);
HAL_GPIO_WritePin(TRIG_GPIO_Port, TRIG_Pin, GPIO_PIN_SET);
 
// 3. 中断回调
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim) {
    if (htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1)
        SR04_cap_receive(&cap, htim, TIM_CHANNEL_1, 0);
}
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) {
    SR04_cap_receive(&cap, htim, TIM_CHANNEL_1, 1);
}
 
// 4. 主循环读取
float dist = SR04_read_cm(&cap);
if (dist > 0) {
    // 使用 dist
}
*/

四路输入捕获测距(多路状态机)

四个方向(前/后/左/右)各一个 HC-SR04,共用同一个 TIM 的 4 个通道,每路独立维护一个 ul_cap_struct

/* 四路实例 */
static ul_cap_struct cap_F, cap_B, cap_L, cap_R;
 
/* 触发宏:先拉低 40 μs 再拉高 */
#define TRIG_F  do { HAL_GPIO_WritePin(GPIOD, GPIO_PIN_14, 0); delay_us(40); \
                     HAL_GPIO_WritePin(GPIOD, GPIO_PIN_14, 1); } while(0)
#define TRIG_B  do { HAL_GPIO_WritePin(GPIOD, GPIO_PIN_15, 0); delay_us(40); \
                     HAL_GPIO_WritePin(GPIOD, GPIO_PIN_15, 1); } while(0)
#define TRIG_L  do { HAL_GPIO_WritePin(GPIOD, GPIO_PIN_10, 0); delay_us(40); \
                     HAL_GPIO_WritePin(GPIOD, GPIO_PIN_10, 1); } while(0)
#define TRIG_R  do { HAL_GPIO_WritePin(GPIOD, GPIO_PIN_11, 0); delay_us(40); \
                     HAL_GPIO_WritePin(GPIOD, GPIO_PIN_11, 1); } while(0)
 
/* 输入捕获中断 */
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
    if (htim->Instance != TIM2) return;
    if (htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1) SR04_cap_receive(&cap_F, htim, TIM_CHANNEL_1, 0);
    if (htim->Channel == HAL_TIM_ACTIVE_CHANNEL_2) SR04_cap_receive(&cap_B, htim, TIM_CHANNEL_2, 0);
    if (htim->Channel == HAL_TIM_ACTIVE_CHANNEL_3) SR04_cap_receive(&cap_L, htim, TIM_CHANNEL_3, 0);
    if (htim->Channel == HAL_TIM_ACTIVE_CHANNEL_4) SR04_cap_receive(&cap_R, htim, TIM_CHANNEL_4, 0);
}
 
/* 溢出中断 */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
    if (htim->Instance != TIM2) return;
    SR04_cap_receive(&cap_F, htim, TIM_CHANNEL_1, 1);
    SR04_cap_receive(&cap_B, htim, TIM_CHANNEL_2, 1);
    SR04_cap_receive(&cap_L, htim, TIM_CHANNEL_3, 1);
    SR04_cap_receive(&cap_R, htim, TIM_CHANNEL_4, 1);
}
 
/* 使用示例 */
/*
// 控制任务中按需触发并读取:
TRIG_F;
float dist_f = SR04_read_cm(&cap_F);
if (dist_f > 1.0f && dist_f < 255.0f) {
    // 有效距离,进行避障或定位
}
*/

三级滤波(去极值 + 幅值限制 + 斜率限制)

原始测距值抖动较大,工程中用三级滤波稳定输出:

  1. 去极值均值:窗口内排序,去掉最大/最小各 N 个后求平均
  2. 幅值限制:均值超出合理范围则保持上次有效值
  3. 斜率限制:单周期变化量超过阈值则限速跟随,防止突变
typedef struct {
    uint8_t  count;         /* 已填入数据数 */
    uint8_t  complete;      /* 缓冲区已满标志 */
    float    last_raw;      /* 上次原始值 */
    float    last_valid;    /* 上次有效输出 */
    float    buf[32];       /* 历史数据缓冲 */
} UltrasonicFilter_t;
 
/**
 * @brief 三级滤波
 *
 * @param f            滤波器状态
 * @param new_val      本次原始测距值(cm)
 * @param win          滤波窗口大小(建议 7~10)
 * @param remove_n     去极值个数(每端去掉 remove_n 个)
 * @param max_amp      幅值上限(cm),超出则保持上次有效值
 * @param max_slope    单周期最大变化量(cm),超出则限速跟随
 * @param out          输出滤波后的距离(cm)
 */
void ultrasonic_filter(UltrasonicFilter_t *f, float new_val,
                       uint8_t win, uint8_t remove_n,
                       float max_amp, float max_slope,
                       float *out)
{
    /* 1. 填入缓冲 */
    f->buf[f->count % win] = new_val;
    f->count++;
    if (f->count >= win) f->complete = 1;
 
    uint8_t n = f->complete ? win : f->count;
 
    /* 2. 冒泡排序(小窗口,开销可接受) */
    float tmp[32];
    for (uint8_t i = 0; i < n; i++) tmp[i] = f->buf[i];
    for (uint8_t i = 0; i < n - 1; i++)
        for (uint8_t j = 0; j < n - 1 - i; j++)
            if (tmp[j] > tmp[j+1]) { float t = tmp[j]; tmp[j] = tmp[j+1]; tmp[j+1] = t; }
 
    /* 3. 去极值求均值 */
    float sum = 0;
    uint8_t valid_n = n - 2 * remove_n;
    if (valid_n < 1) valid_n = 1;
    for (uint8_t i = remove_n; i < n - remove_n; i++) sum += tmp[i];
    float avg = sum / valid_n;
 
    /* 4. 幅值限制 */
    if (avg > max_amp) { *out = f->last_valid; return; }
 
    /* 5. 斜率限制 */
    float diff = avg - f->last_valid;
    if (diff >  max_slope) avg = f->last_valid + max_slope;
    if (diff < -max_slope) avg = f->last_valid - max_slope;
 
    f->last_valid = avg;
    *out = avg;
}
 
/* 使用示例 */
/*
static UltrasonicFilter_t filt_F = {0};
float dist_filtered;
 
// 每次读到新原始值后调用:
ultrasonic_filter(&filt_F, SR04_read_cm(&cap_F),
                  7, 1, 255.0f, 30.0f, &dist_filtered);
// dist_filtered 即为稳定输出
*/