找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

1570

积分

1

好友

218

主题
发表于 4 小时前 | 查看: 2| 回复: 0

在高频采样场景下,仅依赖CPU进行SPI字节读写极易导致系统响应迟滞甚至卡死。对于32位高精度模数转换器(ADC)的数据采集,要实现高吞吐、低延迟与零丢包的目标,必须在内核层面进行深度优化,核心在于利用SPI控制器的直接内存访问(DMA)功能与优化中断处理机制。

在常见的SoC(如RK3568)中,SPI控制器通常配备专用DMA通道。启用DMA后,数据搬运流程如下:

  1. FPGA的DRDY引脚电平跳变,触发SoC的GPIO中断。
  2. 内核驱动向DMA控制器提交传输描述符,指令内容为“从SPI接收寄存器搬运32字节数据至内存地址A”。
  3. DMA引擎直接在系统总线上完成数据搬移,此过程无需CPU介入。
  4. 传输完成后,DMA控制器产生中断,通知驱动数据已在内存中准备就绪。

核心优化策略

1. 批量读取(Batching / Burst Mode)

若每采集一帧数据(如32字节)就触发一次DMA传输,中断频率仍然很高(例如10kHz采样率对应每秒1万次中断)。优化方案是在FPGA侧构建一个深度FIFO缓冲区(例如缓存10帧数据),FPGA在攒够10帧数据后才触发一次DRDY中断。驱动程序则通过一次DMA传输读取320字节数据,从而将中断频率降低一个数量级,大幅减少系统上下文切换开销。

2. 内存对齐与缓存一致性(DMA Coherent Mapping)

32位ADC对数据完整性要求极高。在ARM64等架构中,必须妥善处理CPU缓存与DMA访问内存之间的一致性问题。在驱动开发中,应使用 dma_alloc_coherent() 分配缓冲区,或在定义缓冲区时使用 ____cacheline_aligned 属性进行标记。这确保了DMA写入内存的数据能被CPU立刻读取到最新值,而非过时的缓存副本。

3. 线程化中断(Threaded IRQs)优先级调优

Linux内核的中断处理模型中,将耗时操作放在底半部(线程化中断)中是常见做法。为了进一步降低中断处理的延迟,可以将处理SPI数据的线程设置为实时(Real-time)调度策略与高优先级。

// 在驱动probe函数中,设置中断线程为SCHED_FIFO实时优先级
struct sched_param param = { .sched_priority = 90 };
sched_setscheduler(st->irq_thread, SCHED_FIFO, ¶m);

配置SoC设备树以开启DMA

在原厂设备树中,必须显式声明SPI控制器使用的DMA通道。

&spi0 {
    status = "okay";
    /* 指定使用的DMA控制器及请求线(通道) */
    dmas = <&dmac0 0>, <&dmac0 1>;
    dma-names = "tx", "rx";
    /* 关键参数:调整接收FIFO阈值以匹配DMA传输 */
    rx-sample-delay-ns = <10>;

    adc_aggregator@0 {
        compatible = "my_org,fpga-ltc2500-aggregator";
        reg = <0>;
        spi-max-frequency = <50000000>;
        /* 连接GPIO中断 */
        interrupt-parent = <&gpio3>;
        interrupts = <RK_PC2 IRQ_TYPE_EDGE_FALLING>;
    };
};

在IIO驱动中触发DMA传输

在IIO驱动的轮询函数(trigger handler)中,通过 spi_message 接口提交传输请求。当数据长度超过内核预设的DMA阈值且设备树已正确配置时,SPI控制器驱动会自动启用DMA模式。

static irqreturn_t fpga_adc_trigger_handler(int irq, void *p)
{
    struct iio_poll_func *pf = p;
    struct iio_dev *indio_dev = pf->indio_dev;
    struct fpga_adc_state *st = iio_priv(indio_dev);
    struct spi_transfer xfer = {
        .rx_buf = st->rx_buf, // 必须为Cacheline对齐的DMA缓冲区
        .len = 320,           // 批量读取长度,例如10帧*32字节
        .cs_change = 0,
    };

    /* spi_sync_transfer 会根据长度和配置自动选择PIO或DMA模式 */
    spi_sync_transfer(st->spi, &xfer, 1);

    iio_push_to_buffers_with_timestamp(indio_dev, st->rx_buf, pf->timestamp);
    iio_trigger_notify_done(indio_dev->trig);
    return IRQ_HANDLED;
}

性能验证与调试

完成内核优化后,需通过系统工具验证效果:

  • 查看中断负载:执行 cat /proc/interrupts。观察对应的SPI或GPIO中断计数增长是否符合预期频率(如批量后应为原始频率的1/10)。同时,通过 top 命令查看系统CPU使用率,若 si(软中断)或 sy(系统)占用率极低,表明DMA工作正常。
  • 检查DMA引擎状态:通过 cat /sys/kernel/debug/dmaengine/summary 查看DMA控制器(如dw-axi-dmac)的活动传输记录。
  • 分析延迟:使用 ftracetrace-cmd 工具,跟踪从硬件中断触发到 spi_sync_transfer 函数完成的完整路径耗时,以确认优化后的延迟是否符合要求。

通过上述内核驱动与系统调优手段,可以显著提升基于SPI接口的高速ADC数据采集系统的稳定性和数据完整性。




上一篇:微软CEO纳德拉亲抓AI产品开发,Copilot与Edge功能优化成重点
下一篇:快手P0级安全事故深度剖析:解析黑灰产业务层攻击技术路径与安全防护挑战
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2025-12-24 13:08 , Processed in 0.179237 second(s), 40 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2025 云栈社区.

快速回复 返回顶部 返回列表