4. 流式数据采集与处理
任务说明
本案例实现一个两核流水线:核0通过ADC连续采集数据,将原始采样值通过流接口发送至核1;核1执行滑动平均滤波后,将结果通过另一个流接口输出。最终在仿真环境中验证滤波后的数据波形符合预期。
硬件连接与数据流
拓扑: ADC(12位,单通道)→ 核0(采集)→ 流接口
c_adc → 核1(滤波)→ 流接口c_out → 输出观测点
代码实现
// main.xc - 双核ADC采集与滑动平均滤波流水线
#include <xs1.h>
#include <platform.h>
#include <stdio.h>
// 流接口定义
// c_adc: 核0 → 核1,传输原始ADC采样值
// c_out: 核1 → 外部,传输滤波结果
interface adc_filter_if {
void send_sample(unsigned sample);
unsigned receive_filtered();
};
// 模拟ADC采样函数(12位精度,范围0-4095)
// 实际项目中替换为硬件ADC驱动
unsigned adc_read(void) {
// 模拟正弦波采样:100Hz信号,1kHz采样率
static unsigned phase = 0;
phase = (phase + 1) % 10; // 每周期10个采样点
// 生成正弦波:2048 + 1500*sin(2π*phase/10)
// 使用查找表简化计算
static const int sin_table[10] = {0, 951, 1819, 2378, 2499, 2378, 1819, 951, 0, -951};
int val = 2048 + (1500 * sin_table[phase]) / 2500;
if (val < 0) val = 0;
if (val > 4095) val = 4095;
return (unsigned)val;
}
// 核0任务:ADC采集并发送
void adc_task(server interface adc_filter_if c_adc) {
unsigned sample;
while (1) {
sample = adc_read(); // 读取ADC
c_adc.send_sample(sample); // 发送到核1
delay_ticks(1000); // 模拟1kHz采样率(100MHz时钟下1000 ticks)
}
}
// 核1任务:滑动平均滤波(窗口大小4)
void filter_task(client interface adc_filter_if c_adc,
client interface adc_filter_if c_out) {
unsigned buffer[4] = {0, 0, 0, 0};
int idx = 0;
unsigned sum = 0;
unsigned filtered;
while (1) {
// 从核0接收原始采样值
unsigned raw;
c_adc.send_sample(raw); // 实际为接收,见下方select逻辑
// 更新滑动窗口
sum -= buffer[idx];
buffer[idx] = raw;
sum += buffer[idx];
idx = (idx + 1) % 4;
// 计算平均值(右移2位等价于除以4)
filtered = sum >> 2;
// 输出滤波结果
c_out.receive_filtered(); // 实际为发送,见下方select逻辑
}
}
// 主函数:并行运行两个核
int main(void) {
interface adc_filter_if c_adc;
interface adc_filter_if c_out;
par {
// 核0:ADC采集
on tile[0]: adc_task(c_adc);
// 核1:滤波处理
on tile[1]: filter_task(c_adc, c_out);
}
return 0;
}
接口方向修正说明
上述代码中filter_task的接口方向标注有误。流接口必须明确区分server(提供者)与client(调用者)。修正后的正确实现如下:
// 正确版本:接口方向明确
interface adc_filter_if {
unsigned get_sample(); // 核1调用,从核0获取采样值
void put_result(unsigned v); // 核1调用,向输出端发送滤波结果
};
// 核0:ADC采集(server端,提供get_sample)
void adc_task(server interface adc_filter_if c_adc) {
unsigned sample;
while (1) {
sample = adc_read();
select {
case c_adc.get_sample() > int result:
result = sample;
break;
}
delay_ticks(1000);
}
}
// 核1:滤波(client端,调用get_sample和put_result)
void filter_task(client interface adc_filter_if c_adc,
client interface adc_filter_if c_out) {
unsigned buffer[4] = {0, 0, 0, 0};
int idx = 0;
unsigned sum = 0;
while (1) {
unsigned raw = c_adc.get_sample(); // 从核0获取
sum -= buffer[idx];
buffer[idx] = raw;
sum += buffer[idx];
idx = (idx + 1) % 4;
unsigned filtered = sum >> 2;
c_out.put_result(filtered); // 发送结果
}
}
// 输出端任务(核2或外部接口)
void output_task(server interface adc_filter_if c_out) {
while (1) {
select {
case c_out.put_result(unsigned v):
printf("滤波输出: %u\n", v);
break;
}
}
}
int main(void) {
interface adc_filter_if c_adc;
interface adc_filter_if c_out;
par {
on tile[0]: adc_task(c_adc);
on tile[1]: filter_task(c_adc, c_out);
on tile[0]: output_task(c_out);
}
return 0;
}
编译与运行
# 编译(xTIMEcomposer工具链)
xcc -target=XCORE-200-EXPLORER main.xc -o pipeline.xe
# 运行(仿真模式)
xsim pipeline.xe --io
验证方法
在output_task中打印滤波结果。输入为模拟正弦波(幅度±1500,偏置2048),预期滤波输出满足:
- 波形形状:仍为正弦波,幅值略减(4点滑动平均对10点/周期正弦的衰减约0.9倍);
- 相位延迟:输出相对输入延迟约1.5个采样周期;
- 数值范围:始终在0~4095之间。
判定标准:观察串行输出,滤波后相邻采样值变化率小于原始采样值变化率(即高频分量被抑制),且波形周期与输入一致(10个采样点完成一个完整正弦周期)。
关键点说明
- 流接口方向:
server端使用select等待client调用,client端直接调用接口函数; - 流水线时序:核0采集与核1滤波并行执行,通过流接口同步,核0的采样率由
delay_ticks控制; - 滑动平均:窗口大小4,使用环形缓冲区避免数据搬移,右移2位实现除法。