【正點原子】STM32MP257 同構多核架構下的 ADC 電壓采集與處理應用開發實戰

在嵌入式系統中,ADC模擬電壓的讀取是常見的需求。如何高效、并發、且可控地完成數據采集與處理?本篇文章通過雙線程分別綁定在 Linux 系統的不同 CPU 核心上,采集 /sys/bus/iio 接口的 ADC 原始值與縮放系數 scale,并在另一個核上計算真實電壓值,適用于高性能、低延遲的工業控制場景。

在這里插入圖片描述

正點原子 STM32MP257 同構多核架構下的 ADC 電壓采集與處理應用開發實戰

  • 一、背景介紹:為什么要用多核并發讀取ADC?
  • 二、系統架構與源碼解析
    • 1、數據采集線程(CPU0)
    • 2、數據處理線程(CPU1)
    • 3、兩線程同步機制
    • 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,將數據傳給處理線程
int val = read_sysfs_int(SYSFS_ADC_PATH);
float scale = read_sysfs_float(SYSFS_ADC_SCALE);
shared_data = val;
shared_scale = scale;

2、數據處理線程(CPU1)

  • 綁定在 CPU1
  • 阻塞等待數據更新信號
  • 計算真實電壓:voltage = val × scale × 0.001
  • 可拓展濾波、特征提取、閾值報警等算法處理
float voltage = val * 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
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <sched.h>#define SYSFS_ADC_PATH "/sys/bus/iio/devices/iio:device0/in_voltage15_raw"
#define SYSFS_ADC_SCALE "/sys/bus/iio/devices/iio:device0/in_voltage_scale"#define ACQ_INTERVAL_US 500000  // 500 msstatic int shared_data = 0;
static float shared_scale = 0.0f;
static int data_ready = 0;static pthread_mutex_t data_lock = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t data_cond = PTHREAD_COND_INITIALIZER;static void bind_thread_to_cpu(pthread_t tid, int cpu)
{cpu_set_t cpuset;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));}
}static int read_sysfs_int(const char *path)
{int fd = open(path, O_RDONLY);if (fd < 0) return -1;char buf[32];ssize_t len = read(fd, buf, sizeof(buf)-1);close(fd);if (len <= 0) return -1;buf[len] = '\0';return atoi(buf);
}static float read_sysfs_float(const char *path)
{int fd = open(path, O_RDONLY);if (fd < 0) return -1.0f;char buf[32];ssize_t len = read(fd, buf, sizeof(buf)-1);close(fd);if (len <= 0) return -1.0f;buf[len] = '\0';return atof(buf);
}static void *acquisition_thread(void *arg)
{pthread_t tid = pthread_self();bind_thread_to_cpu(tid, 0);printf("采集線程綁定到 CPU0\n");while (1) {int val = read_sysfs_int(SYSFS_ADC_PATH);float scale = read_sysfs_float(SYSFS_ADC_SCALE);if (val < 0 || scale <= 0) {perror("讀取ADC或Scale失敗");usleep(ACQ_INTERVAL_US);continue;}pthread_mutex_lock(&data_lock);shared_data = val;shared_scale = scale;data_ready = 1;pthread_cond_signal(&data_cond);pthread_mutex_unlock(&data_lock);printf("采集線程: 原始值=%d, scale=%.6f\n", val, scale);usleep(ACQ_INTERVAL_US);}return NULL;
}static void *processing_thread(void *arg)
{pthread_t tid = pthread_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);}int val = shared_data;float scale = shared_scale;data_ready = 0;pthread_mutex_unlock(&data_lock);float voltage = val * scale * 0.001;printf("處理線程: 處理 %d × %.6f x 0.001 = %.2f V\n", val, scale, voltage);}return NULL;
}int main(int argc, char *argv[])
{pthread_t tid_acq, tid_proc;int ret;ret = pthread_create(&tid_acq, NULL, acquisition_thread, NULL);if (ret) {fprintf(stderr, "創建采集線程失敗: %s\n", strerror(ret));return 1;}ret = pthread_create(&tid_proc, NULL, processing_thread, NULL);if (ret) {fprintf(stderr, "創建處理線程失敗: %s\n", strerror(ret));return 1;}pthread_join(tid_acq, NULL);pthread_join(tid_proc, NULL);return 0;
}

2.使用方法

將以上代碼編輯為 adc_app.c 文件,在 ubuntu 系統里使用以下命令交叉編譯為可執行文件即可:

source /opt/st/stm32mp2/5.0.3-snapshot/environment-setup-cortexa35-ostl-linux
${CC} -o adc_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.pswp.cn/pingmian/76532.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/76532.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/76532.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

電商用戶購物行為分析:基于K-Means聚類與分類驗證的完整流程

隨著電商行業的快速發展,用戶行為分析成為企業優化營銷策略、提升用戶體驗的重要手段。通過分析用戶的購物行為數據,企業可以挖掘出用戶群體的消費特征和行為模式,從而制定更加精準的營銷策略。本文將詳細介紹一個基于Python實現的電商用戶購物行為分析系統,涵蓋數據預處理…

AMGCL庫的Backends及使用示例

AMGCL庫的Backends及使用示例 AMGCL是一個用于解決大型稀疏線性方程組的C庫&#xff0c;它提供了多種后端(backends)實現&#xff0c;允許用戶根據不同的硬件和性能需求選擇合適的計算后端。 AMGCL支持的主要Backends 內置Backends: builtin - 默認的純C實現block - 支持塊狀…

Express中間件(Middleware)詳解:從零開始掌握(3)

實用中間件模式25例 1. 基礎增強模式 請求屬性擴展 function extendRequest() {return (req, res, next) > {req.getClientLanguage () > {return req.headers[accept-language]?.split(,)[0] || en;};next();}; } 響應時間頭 function responseTime() {return (r…

05--MQTT物聯網協議

一、MQTT的概念 MQTT 協議快速入門 2025&#xff1a;基礎知識和實用教程 | EMQ 1.MQTT&#xff08;Message Queuing Telemetry Transport&#xff09;是一種輕量級、基于發布-訂閱模式的消息傳輸協議&#xff0c;適用于資源受限的設備和低帶寬、高延遲或不穩定的網絡環境。它…

數據結構與算法——鏈表OJ題詳解(2)

文章目錄 一、前言二、OJ續享2.1相交鏈表2.2環形鏈表12.2環形鏈表2 三、總結 一、前言 哦了兄弟們&#xff0c;咱們上次在詳解鏈表OJ題的時候&#xff0c;有一部分OJ題呢up并沒有整理完&#xff0c;這一個星期呢&#xff0c;up也是在不斷的學習并且沉淀著&#xff0c;也是終于…

SQL Server AlwaysOn (SQL 查詢數據詳解及監控用途)

修正后的完整查詢 SELECT ar.replica_server_name AS [副本名稱],ar.availability_mode_desc AS [同步模式],DB_NAME(dbr.database_id) AS [數據庫名稱],dbr.database_state_desc AS [數據庫狀態],dbr.synchronization_state_desc AS [同步狀態],dbr.synchronization_health_d…

力扣熱題100刷題day63|49.字母異位詞分組

目錄 一、哈希表相關理論 二、思路 核心思路 三、相關題目 四、總結 一、哈希表相關理論 代碼隨想錄刷題day15|&#xff08;哈希表篇&#xff09;242.有效的字母異位詞、383.贖金信-CSDN博客 二、思路 首先&#xff0c;創建一個map集合&#xff0c;遍歷字符串數組&…

愛普生可編程晶振SG8201CJ和SG8200CJ在胃鏡機器人發揮重要作用

在醫療機器人技術高速發展的今天&#xff0c;胃鏡機器人作為胃腸道疾病診斷與治療的創新設備&#xff0c;正逐漸改變傳統診療模式。其復雜精密的系統需要精準的時間同步與穩定的信號輸出&#xff0c;胃鏡機器人是一種先進的醫療設備&#xff0c;用于無創性地檢查胃部疾病。與傳…

Ubuntu22環境下,Docker部署阿里FunASR的gpu版本

番外: 隨著deepseek的爆火,人工智能相關的開發變得異常火爆,相關的大模型開發很常見的agent智能體需要ASR語音識別的功能,阿里開源的FunASR幾乎是把一個商業的項目放給我們使用了。那么我們項目中的生產環境怎么部署gpu版本的語音識別服務呢?經過跟deepseek的一上午的極限…

圖解Java設計模式

1、設計模式面試題 2、設計模式的重要性 3、7大設計原則介紹 3.1、單一職責原則

transformers的 pipeline是什么:將模型加載、數據預處理、推理等步驟進行了封裝

transformers的 pipeline是什么:將模型加載、數據預處理、推理等步驟進行了封裝 pipe = pipeline("text-generation", model=model, tokenizer=tokenizer, max_new_tokens=50 )pipeline :這是 transformers 庫中一個非常實用的工具函數。它可以基于預訓練模型快速構…

jmeter插件安裝

1、下載 下載地址&#xff1a; Documentation :: JMeter-Plugins.org 然后復制到D:\apache-jmeter-5.6.3\lib\ext 復制后 2、重啟jmeter 在菜單【選項】找到“Plugins Manager” 在 Plugins Manager 界面上&#xff0c;點擊“Available Plugins”標簽頁&#xff0c;可以瀏覽所…

VSCode CMake調試CPP程序

文章目錄 1 安裝C與CMake插件2 配置CMakeLists.txt3 使用CMake編譯調試3.1 編譯3.2 調試 4 自定義構建調試參考 1 安裝C與CMake插件 C插件 CMake插件 2 配置CMakeLists.txt 編寫測試程序 #include<iostream>int main(int argc, char const *argv[]) {int a 1, b 2;i…

【前端】【css】flex布局詳解

Flex 布局&#xff08;Flexible Box Layout&#xff0c;彈性盒子布局&#xff09;是 CSS3 中的一種布局模式&#xff0c;用于在容器中更高效地分配空間并對齊內容&#xff0c;即使它們的大小是動態未知的。它非常適用于響應式設計。 一、Flex 布局的基本概念 1. 啟用 Flex 布局…

LEARNING DYNAMICS OF LLM FINETUNING【論文閱讀筆記】

LEARNING DYNAMICS OF LLM FINETUNING 一句話總結 作者將LLM的學習動力機制拆解成AKG三項&#xff0c;并分別觀察了SFT和DPO訓練過程中??正梯度信號??和??負梯度信號??的變化及其帶來的影響&#xff0c;并得到以下結論&#xff1a; ??SFT通過梯度相似性間接提升無關…

Mac 下載 PicGo 的踩坑指南

Mac 下載 PicGo 的踩坑指南 一、安裝問題 下載地址&#xff1a;https://github.com/Molunerfinn/PicGo/releases 下載之后直接安裝即可&#xff0c;此時打開會報錯&#xff1a;Picgo.app 文件已損壞&#xff0c;您應該將它移到廢紙簍。 這是因為 macOS 為了保護用戶不受惡意…

Element UI 設置 el-table-column 寬度 width 為百分比無效

問題描述&#xff1a; 想要每列寬度不同&#xff0c;不想使用 px 固定值&#xff0c;將 width 設置成百分比&#xff0c;但是每一列還是很窄 原因&#xff1a; el-table 組件會被 vue 解析成 html&#xff0c;vue 直接把百分號去掉把數值當做列寬來呈現&#xff0c;所以&#x…

第五篇:Python面向對象編程(OOP)深度教程

1. 類與對象 1.1 基本概念 ??類??是創建對象的藍圖,定義了對象的??屬性??(數據)和??方法??(行為)。??對象??是類的實例化實體,每個對象擁有獨立的屬性值和共享的類方法 ??示例??:定義Dog類 class Dog:species = "Canis familiaris" …

【數據結構】2.順序表實現通訊錄

文章目錄 一、通訊錄的要求二、通訊錄的具體實現0、 準備工作1、通訊錄的初始化2、通訊錄的銷毀3、通訊錄的展示4、通訊錄添加數據5、通訊錄刪除數據6、通訊錄的查找7、通訊錄的修改8、保存通訊錄數據到文件9、讀取文件內容到通訊錄 三、 通訊錄的完整實現 一、通訊錄的要求 通…

程序化廣告行業(79/89):技術革新與行業發展脈絡梳理

程序化廣告行業&#xff08;79/89&#xff09;&#xff1a;技術革新與行業發展脈絡梳理 大家好&#xff01;一直以來&#xff0c;我都熱衷于在技術領域不斷探索&#xff0c;也深知知識共享對于進步的重要性。寫這篇博客&#xff0c;就是希望能和大家一起深入研究程序化廣告行業…