正点原子 STM32MP257 同构多核架构下的 ADC 电压采集与处理应用开发实战
在嵌入式系统中ADC模拟电压的读取是常见的需求。如何高效、并发、且可控地完成数据采集与处理本篇文章通过双线程分别绑定在 Linux 系统的不同 CPU 核心上采集 /sys/bus/iio 接口的 ADC 原始值与缩放系数 scale并在另一个核上计算真实电压值适用于高性能、低延迟的工业控制场景。正点原子STM32MP257开发板 ARM嵌入式Linux异核A35M33 AI工控正点原子 STM32MP257 同构多核架构下的 ADC 电压采集与处理应用开发实战一、背景介绍为什么要用多核并发读取ADC二、系统架构与源码解析1、数据采集线程CPU02、数据处理线程CPU13、两线程同步机制4、完整代码及使用方法1.完整代码展示2.使用方法三、应用场景与实际部署建议1、工业自动化控制2、边缘AI与信号预处理3、多任务实时系统调度四、测试效果与输出示例五、总结与拓展建议一、背景介绍为什么要用多核并发读取ADC在嵌入式 Linux 平台如 STM32MP257、i.MX93 等中我们常使用工业级 ADC 进行传感器数据采集。通过内核 IIO 子系统用户可以在 /sys/bus/iio/devices/iio:deviceX/ 目录下读取原始电压值和电压缩放因子scale从而计算出真实电压。而本项目的设计目标是实现采集线程 处理线程分核运行充分利用 A核多核系统的资源提高数据采集实时性降低主线程阻塞风险。二、系统架构与源码解析该项目通过两个线程分别运行在 CPU0 和 CPU1线程间通过互斥锁和条件变量进行数据同步1、数据采集线程CPU0绑定在 CPU0定时读取原始 ADC值/sys/bus/iio/devices/iio:device0/in_voltage15_raw缩放系数/sys/bus/iio/devices/iio:device0/in_voltage_scale通过共享内存区 shared_data 和 shared_scale将数据传给处理线程intvalread_sysfs_int(SYSFS_ADC_PATH);floatscaleread_sysfs_float(SYSFS_ADC_SCALE);shared_dataval;shared_scalescale;2、数据处理线程CPU1绑定在 CPU1阻塞等待数据更新信号计算真实电压voltage val × scale × 0.001可拓展滤波、特征提取、阈值报警等算法处理floatvoltageval*scale*0.001;printf(处理线程: 处理 %d × %.6f x 0.001 %.2f V\n,val,scale,voltage);3、两线程同步机制使用 pthread_mutex_t 和 pthread_cond_t 进行数据同步确保线程安全。data_ready 标志位控制数据更新通知。4、完整代码及使用方法1.完整代码展示#define_GNU_SOURCE#includestdio.h#includestdlib.h#includeunistd.h#includepthread.h#includefcntl.h#includestring.h#includeerrno.h#includesched.h#defineSYSFS_ADC_PATH/sys/bus/iio/devices/iio:device0/in_voltage15_raw#defineSYSFS_ADC_SCALE/sys/bus/iio/devices/iio:device0/in_voltage_scale#defineACQ_INTERVAL_US500000// 500 msstaticintshared_data0;staticfloatshared_scale0.0f;staticintdata_ready0;staticpthread_mutex_tdata_lockPTHREAD_MUTEX_INITIALIZER;staticpthread_cond_tdata_condPTHREAD_COND_INITIALIZER;staticvoidbind_thread_to_cpu(pthread_ttid,intcpu){cpu_set_tcpuset;CPU_ZERO(cpuset);CPU_SET(cpu,cpuset);if(pthread_setaffinity_np(tid,sizeof(cpuset),cpuset)!0){fprintf(stderr,警告无法将线程绑定到 CPU%d: %s\n,cpu,strerror(errno));}}staticintread_sysfs_int(constchar*path){intfdopen(path,O_RDONLY);if(fd0)return-1;charbuf[32];ssize_tlenread(fd,buf,sizeof(buf)-1);close(fd);if(len0)return-1;buf[len]\0;returnatoi(buf);}staticfloatread_sysfs_float(constchar*path){intfdopen(path,O_RDONLY);if(fd0)return-1.0f;charbuf[32];ssize_tlenread(fd,buf,sizeof(buf)-1);close(fd);if(len0)return-1.0f;buf[len]\0;returnatof(buf);}staticvoid*acquisition_thread(void*arg){pthread_ttidpthread_self();bind_thread_to_cpu(tid,0);printf(采集线程绑定到 CPU0\n);while(1){intvalread_sysfs_int(SYSFS_ADC_PATH);floatscaleread_sysfs_float(SYSFS_ADC_SCALE);if(val0||scale0){perror(读取ADC或Scale失败);usleep(ACQ_INTERVAL_US);continue;}pthread_mutex_lock(data_lock);shared_dataval;shared_scalescale;data_ready1;pthread_cond_signal(data_cond);pthread_mutex_unlock(data_lock);printf(采集线程: 原始值%d, scale%.6f\n,val,scale);usleep(ACQ_INTERVAL_US);}returnNULL;}staticvoid*processing_thread(void*arg){pthread_ttidpthread_self();bind_thread_to_cpu(tid,1);printf(处理线程绑定到 CPU1\n);while(1){pthread_mutex_lock(data_lock);while(!data_ready){pthread_cond_wait(data_cond,data_lock);}intvalshared_data;floatscaleshared_scale;data_ready0;pthread_mutex_unlock(data_lock);floatvoltageval*scale*0.001;printf(处理线程: 处理 %d × %.6f x 0.001 %.2f V\n,val,scale,voltage);}returnNULL;}intmain(intargc,char*argv[]){pthread_ttid_acq,tid_proc;intret;retpthread_create(tid_acq,NULL,acquisition_thread,NULL);if(ret){fprintf(stderr,创建采集线程失败: %s\n,strerror(ret));return1;}retpthread_create(tid_proc,NULL,processing_thread,NULL);if(ret){fprintf(stderr,创建处理线程失败: %s\n,strerror(ret));return1;}pthread_join(tid_acq,NULL);pthread_join(tid_proc,NULL);return0;}2.使用方法将以上代码编辑为adc_app.c文件在 ubuntu 系统里使用以下命令交叉编译为可执行文件即可source/opt/st/stm32mp2/5.0.3-snapshot/environment-setup-cortexa35-ostl-linux${CC}-oadc_app adc_app.c最终生成的adc_app文件就是我们需要放到STM32MP257文件系统里的可执行文件。注意事项在STM32MP257的百度资料网盘里已经提供了交叉编译工具链的安装脚本文件路径是“STM32MP257开发板\05、开发工具\01、出厂系统交叉编译器”请大家可以自行去下载使用。atk-image-openstlinux-weston-stm32mp2.rootfs-x86_64-toolchain-5.0.3-snapshot-20250115-v1.0三、应用场景与实际部署建议本方案适用于以下典型场景1、工业自动化控制实时读取传感器电压信号如压力、温湿度、光强等多核处理确保主线程响应不中断电压计算后可直接用于闭环 PID 控制逻辑2、边缘AI与信号预处理采集模拟数据后可直接进行数字滤波、傅里叶变换等前处理数据处理线程也可通过 RPMsg 发送到 Cortex-M33 协处理核做进一步处理3、多任务实时系统调度多核绑定可防止线程“漂移”适用于带有调度器的 RT-PREEMPT 系统强化线程的确定性和性能隔离四、测试效果与输出示例运行后终端将周期性打印如下信息说明in_voltage15_raw 4095 表示ADC原始数值scale 0.439453 mV/LSB 是 ADC 的电压精度最终电压 4095* 0.439453 * 0.001 ≈ 1.8V执行 adc_app 可执行文件后我们用 ssh 打开 STM32MP257 的新终端用以下指令可以查看 这个例程的调用 cpu 使用情况top-H-p$(pidof adc_app)通过终端显示的消息可以看到 adc_app 主线程在 CPU1 里使用采集数据 和 处理数据的线程 分别在 CPU0 和 CPU1 里分别使用。五、总结与拓展建议通过绑定线程至特定 CPU 核心并使用条件变量进行线程同步我们实现了一个低延迟、高稳定性的 ADC 电压采集处理方案。它可轻松适配到任意支持 Linux 的 ARM 多核平台推荐用于工业控制、信号处理、边缘AI等高实时场景。后续可以拓展将数据通过 Socket/UDP/CanOpen 发送写入共享内存供 GUI 使用增加多通道采集与 Cortex-M 核通信RPMsg如果你也在做 STM32MP257 / i.MX93 / RK3588 等平台的异构核协同处理不妨试试这种方案有问题欢迎评论区一起探讨交流
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2606896.html
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!