输入捕获程序
代码来源工程:Intelligent_Agricultural_Equipment_Innovation_Competition(
Mycode/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) {
// 有效距离,进行避障或定位
}
*/三级滤波(去极值 + 幅值限制 + 斜率限制)
原始测距值抖动较大,工程中用三级滤波稳定输出:
- 去极值均值:窗口内排序,去掉最大/最小各 N 个后求平均
- 幅值限制:均值超出合理范围则保持上次有效值
- 斜率限制:单周期变化量超过阈值则限速跟随,防止突变
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 即为稳定输出
*/