【IgH EtherCAT】如何利用 RTAI 提供的實時任務和調度機制來構建一個高精度、確定性的工業控制應用

SVG圖展示了系統的分層架構:

  • RTAI實時層

    :包含RT_TASK、信號量和定時器

  • EtherCAT Master層

    :主站、域、從站配置和PDO映射

  • EtherCAT網絡層

    :與實際硬件設備(EL3162模擬輸入、EL2004數字輸出)通信

關鍵特點:

  1. 實時性

    :使用RTAI確保2000Hz的精確定時

  2. 同步保護

    :通過信號量保護EtherCAT操作

  3. 狀態監控

    :定期檢查主站、域和從站狀態

  4. 雙向數據流

    :接收模擬輸入數據,發送數字輸出控制信號

這個系統典型用于工業自動化場景,需要高精度的實時控制和可靠的現場總線通信

這個序列圖詳細描繪了從模塊加載到卸載的整個生命周期中,Linux 內核、RTAI 子系統、驅動邏輯以及 EtherCAT 主站之間的動態交互。它特別突出了 RTAI 任務的創建、周期性執行以及與信號量的交互。

時序圖展示了EtherCAT RTAI程序的完整執行流程:

  • 模塊初始化階段

    :請求主站、創建域、配置從站、注冊PDO等

  • 循環執行階段

    :以2000Hz頻率進行實時數據交換

  • 模塊卸載階段

    :清理資源

這段使用了?RTAI (Real-Time Application Interface)?的 IgH EtherCAT Master 示例代碼。這是一個在內核空間運行的硬實時應用,通過 RTAI 提供的任務調度和定時器來實現高精度的周期性控制。

// Linux // 區域注釋:Linux 內核頭文件#include?<linux/module.h>?// 包含 Linux 內核模塊編程所需的核心頭文件#include?<linux/err.h>?// 包含內核錯誤處理相關的頭文件// RTAI // 區域注釋:RTAI 頭文件#include?<rtai_sched.h>?// 包含 RTAI 調度器相關的頭文件,如任務創建、周期性設置#include?<rtai_sem.h>?// 包含 RTAI 信號量相關的頭文件,用于同步// EtherCAT // 區域注釋:EtherCAT 頭文件#include?"../../include/ecrt.h"?// 包含 IgH EtherCAT 主站的實時接口頭文件/****************************************************************************/?// 分隔注釋// Module parameters // 區域注釋:模塊參數#define?FREQUENCY 2000?// task frequency in Hz // 宏定義:任務頻率為 2000 Hz#define?INHIBIT_TIME 20?// 宏定義:抑制時間為 20 (單位可能是微秒,用于回調函數)#define?TIMERTICKS (1000000000 / FREQUENCY)?// 宏定義:每個周期的納秒數// Optional features (comment to disable) // 區域注釋:可選特性 (注釋掉以禁用)#define?CONFIGURE_PDOS?// 宏定義:啟用 PDO 配置代碼塊#define?PFX?"ec_rtai_sample: "?// 宏定義:內核日志消息的前綴,方便調試/****************************************************************************/?// 分隔注釋// EtherCAT // 區域注釋:EtherCAT 相關全局變量static?ec_master_t?*master =?NULL;?// 聲明一個靜態的 EtherCAT 主站對象指針static?ec_master_state_t?master_state = {};?// 聲明一個靜態的主站狀態結構體變量static?ec_domain_t?*domain1 =?NULL;?// 聲明一個靜態的 EtherCAT 過程數據域指針static?ec_domain_state_t?domain1_state = {};?// 聲明一個靜態的域狀態結構體變量static?ec_slave_config_t?*sc_ana_in =?NULL;?// 聲明一個靜態的從站配置對象指針,用于模擬量輸入從站static?ec_slave_config_state_t?sc_ana_in_state = {};?// 聲明一個靜態的從站配置狀態結構體變量// RTAI // 區域注釋:RTAI 相關全局變量static?RT_TASK task;?// 聲明一個 RTAI 任務結構體變量static?SEM master_sem;?// 聲明一個 RTAI 信號量結構體變量static?cycles_t?t_last_cycle =?0, t_critical;?// 聲明 RTAI 的時間戳變量 (CPU周期數),用于高精度時間測量/****************************************************************************/?// 分隔注釋// process data // 區域注釋:過程數據static?uint8_t?*domain1_pd;?// process data memory // 聲明一個指向過程數據內存區域的指針#define?AnaInSlavePos ?0, 3?// 宏定義:模擬量輸入從站的位置#define?DigOutSlavePos 0, 2?// 宏定義:數字量輸出從站的位置#define?Beckhoff_EL2004 0x00000002, 0x07D43052?// 宏定義:倍福 EL2004 的廠商/產品ID#define?Beckhoff_EL3162 0x00000002, 0x0C5A3052?// 宏定義:倍福 EL3162 的廠商/產品IDstatic?unsigned?int?off_ana_in;?// offsets for PDO entries // 靜態無符號整型,存儲模擬量輸入值的偏移量static?unsigned?int?off_dig_out;?// 靜態無符號整型,存儲數字量輸出的偏移量const?static?ec_pdo_entry_reg_t?domain1_regs[] = {?// 定義一個靜態常量數組,用于注冊需要映射到域的PDO條目? ? {AnaInSlavePos, ?Beckhoff_EL3162,?0x3101,?2, &off_ana_in},?// 注冊 EL3162 的值 PDO? ? {DigOutSlavePos, Beckhoff_EL2004,?0x3001,?1, &off_dig_out},?// 注冊 EL2004 的輸出 PDO? ? {}?// 數組結束標志};static?unsigned?int?counter =?0;?// 靜態無符號整型,用作通用計數器static?unsigned?int?blink =?0;?// 靜態無符號整型,用作閃爍標志/****************************************************************************/?// 分隔注釋#ifdef?CONFIGURE_PDOS?// 如果定義了 CONFIGURE_PDOS 宏// 以下是為從站進行 SII (從站信息接口) 覆蓋配置所需的數據結構static?ec_pdo_entry_info_t?el3162_channel1[] = {?// 定義 EL3162 通道1的 PDO 條目信息? ? {0x3101,?1, ?8},?// status (狀態,8位)? ? {0x3101,?2,?16} ?// value (值,16位)};static?ec_pdo_entry_info_t?el3162_channel2[] = {?// 定義 EL3162 通道2的 PDO 條目信息? ? {0x3102,?1, ?8},?// status (狀態,8位)? ? {0x3102,?2,?16} ?// value (值,16位)};static?ec_pdo_info_t?el3162_pdos[] = {?// 定義 EL3162 的 PDO 信息 (將條目分組)? ? {0x1A00,?2, el3162_channel1},?// TxPDO 0x1A00? ? {0x1A01,?2, el3162_channel2} ?// TxPDO 0x1A01};static?ec_sync_info_t?el3162_syncs[] = {?// 定義 EL3162 的同步管理器配置? ? {2, EC_DIR_OUTPUT},?// SM2 是一個空的輸出 SM? ? {3, EC_DIR_INPUT,?2, el3162_pdos},?// SM3 是輸入 SM,關聯2個 PDO? ? {0xff}?// 結束標志};static?ec_pdo_entry_info_t?el2004_channels[] = {?// 定義 EL2004 的 PDO 條目信息? ? {0x3001,?1,?1},?// Value 1 (值1,1位)? ? {0x3001,?2,?1},?// Value 2 (值2,1位)? ? {0x3001,?3,?1},?// Value 3 (值3,1位)? ? {0x3001,?4,?1} ?// Value 4 (值4,1位)};static?ec_pdo_info_t?el2004_pdos[] = {?// 定義 EL2004 的 PDO 信息? ? {0x1600,?1, &el2004_channels[0]},?// RxPDO 0x1600? ? {0x1601,?1, &el2004_channels[1]},?// RxPDO 0x1601? ? {0x1602,?1, &el2004_channels[2]},?// RxPDO 0x1602? ? {0x1603,?1, &el2004_channels[3]} ?// RxPDO 0x1603};static?ec_sync_info_t?el2004_syncs[] = {?// 定義 EL2004 的同步管理器配置? ? {0, EC_DIR_OUTPUT,?4, el2004_pdos},?// SM0 是輸出 SM,關聯4個 PDO? ? {1, EC_DIR_INPUT},?// SM1 是一個空的輸入 SM? ? {0xff}?// 結束標志};#endif?// 結束 #ifdef/****************************************************************************/?// 分隔注釋void?check_domain1_state(void)?// 定義一個函數,用于檢查并打印域的狀態變化{? ??ec_domain_state_t?ds;?// 聲明一個局部的域狀態結構體變量? ??rt_sem_wait(&master_sem);?// 等待(獲取)RTAI信號量? ??ecrt_domain_state(domain1, &ds);?// 調用 ecrt API,獲取 domain1 的當前狀態? ??rt_sem_signal(&master_sem);?// 發送(釋放)RTAI信號量? ??if?(ds.working_counter != domain1_state.working_counter)?// 比較當前工作計數器(WC)與上次記錄的WC? ? ? ??printk(KERN_INFO PFX?"Domain1: WC %u.\n", ds.working_counter);?// 如果不一致,打印新的WC值? ??if?(ds.wc_state != domain1_state.wc_state)?// 比較當前工作計數器狀態與上次記錄的狀態? ? ? ??printk(KERN_INFO PFX?"Domain1: State %u.\n", ds.wc_state);?// 如果不一致,打印新的WC狀態? ? domain1_state = ds;?// 將當前狀態賦值給全局變量,用于下次比較}/****************************************************************************/?// 分隔注釋void?check_master_state(void)?// 定義一個函數,用于檢查并打印主站的狀態變化{? ??ec_master_state_t?ms;?// 聲明一個局部的主站狀態結構體變量? ??rt_sem_wait(&master_sem);?// 等待 RTAI 信號量? ??ecrt_master_state(master, &ms);?// 調用 ecrt API,獲取主站的當前狀態? ??rt_sem_signal(&master_sem);?// 釋放 RTAI 信號量? ??if?(ms.slaves_responding != master_state.slaves_responding)?// 比較當前響應的從站數量與上次記錄的數量? ? ? ??printk(KERN_INFO PFX?"%u slave(s).\n", ms.slaves_responding);?// 如果不一致,打印新的從站數量? ??if?(ms.al_states != master_state.al_states)?// 比較當前應用層(AL)狀態與上次記錄的狀態? ? ? ??printk(KERN_INFO PFX?"AL states: 0x%02X.\n", ms.al_states);?// 如果不一致,以十六進制格式打印新的AL狀態? ??if?(ms.link_up != master_state.link_up)?// 比較當前鏈路連接狀態與上次記錄的狀態? ? ? ??printk(KERN_INFO PFX?"Link is %s.\n", ms.link_up ??"up"?:?"down");?// 如果不一致,打印鏈路是 "up" 還是 "down"? ? master_state = ms;?// 將當前狀態賦值給全局變量,用于下次比較}/****************************************************************************/?// 分隔注釋void?check_slave_config_states(void)?// 定義一個函數,用于檢查并打印特定從站的配置狀態變化{? ??ec_slave_config_state_t?s;?// 聲明一個局部的從站配置狀態結構體變量? ??rt_sem_wait(&master_sem);?// 等待 RTAI 信號量? ??ecrt_slave_config_state(sc_ana_in, &s);?// 調用 ecrt API,獲取模擬量輸入從站的當前配置狀態? ??rt_sem_signal(&master_sem);?// 釋放 RTAI 信號量? ??if?(s.al_state != sc_ana_in_state.al_state)?// 比較當前應用層狀態與上次記錄的狀態? ? ? ??printk(KERN_INFO PFX?"AnaIn: State 0x%02X.\n", s.al_state);?// 如果不一致,打印新的AL狀態? ??if?(s.online != sc_ana_in_state.online)?// 比較當前在線狀態與上次記錄的狀態? ? ? ??printk(KERN_INFO PFX?"AnaIn: %s.\n", s.online ??"online"?:?"offline");?// 如果不一致,打印在線/離線狀態? ??if?(s.operational != sc_ana_in_state.operational)?// 比較當前操作狀態與上次記錄的狀態? ? ? ??printk(KERN_INFO PFX?"AnaIn: %soperational.\n",?// 如果不一致,打印是否進入操作狀態? ? ? ? ? ? ? ? s.operational ??""?:?"Not ");?// 根據 operational 的值打印 "operational" 或 "Not operational"? ? sc_ana_in_state = s;?// 將當前狀態賦值給全局變量,用于下次比較}/****************************************************************************/?// 分隔注釋void?run(long?data)?// RTAI 實時任務的主體函數{? ??while?(1) {?// 進入一個無限循環? ? ? ? t_last_cycle =?get_cycles();?// 使用 RTAI 函數獲取當前 CPU 周期數,記錄周期開始時間? ? ? ??// receive process data // 注釋:接收過程數據? ? ? ??rt_sem_wait(&master_sem);?// 獲取信號量? ? ? ??ecrt_master_receive(master);?// 從網絡接口接收數據幀? ? ? ??ecrt_domain_process(domain1);?// 處理域的數據? ? ? ??rt_sem_signal(&master_sem);?// 釋放信號量? ? ? ??// check process data state (optional) // 注釋:檢查過程數據狀態(可選)? ? ? ??check_domain1_state();?// 調用函數檢查并打印域的狀態變化? ? ? ??if?(counter) {?// 如果計數器不為0? ? ? ? ? ? counter--;?// 計數器減1? ? ? ? }?else?{?// do this at 1 Hz // 如果計數器為0 (即每秒執行一次)? ? ? ? ? ? counter = FREQUENCY;?// 重置計數器為頻率值 (2000)? ? ? ? ? ??// calculate new process data // 注釋:計算新的過程數據? ? ? ? ? ? blink = !blink;?// 對 blink 變量取反,實現閃爍邏輯? ? ? ? ? ??// check for master state (optional) // 注釋:檢查主站狀態(可選)? ? ? ? ? ??check_master_state();?// 調用函數檢查并打印主站的狀態變化? ? ? ? ? ??// check for islave configuration state(s) (optional) // 注釋:檢查從站配置狀態(可選)? ? ? ? ? ??check_slave_config_states();?// 調用函數檢查并打印特定從站的狀態變化? ? ? ? }? ? ? ??// write process data // 注釋:寫入過程數據? ? ? ??EC_WRITE_U8(domain1_pd + off_dig_out, blink ??0x06?:?0x09);?// 將數據寫入過程數據區,控制數字量輸出 (輸出 0b0110 或 0b1001)? ? ? ??rt_sem_wait(&master_sem);?// 獲取信號量? ? ? ??ecrt_domain_queue(domain1);?// 將要發送的域數據放入發送隊列? ? ? ??ecrt_master_send(master);?// 將數據幀發送到 EtherCAT 總線? ? ? ??rt_sem_signal(&master_sem);?// 釋放信號量? ? ? ??rt_task_wait_period();?// RTAI 函數:使當前任務睡眠,直到下一個周期性調度點? ? }}/****************************************************************************/?// 分隔注釋void?send_callback(void?*cb_data)?// 定義一個發送回調函數 (用于外部事件驅動模式){? ??ec_master_t?*m = (ec_master_t?*) cb_data;?// 將 void* 指針轉換為 master 指針? ??// too close to the next real time cycle: deny access... // 注釋:離下一個實時周期太近:拒絕訪問...? ??if?(get_cycles() - t_last_cycle <= t_critical) {?// 如果當前時間與上個周期開始時間的差值小于臨界值? ? ? ??rt_sem_wait(&master_sem);?// 獲取信號量? ? ? ??ecrt_master_send_ext(m);?// 調用擴展的發送函數? ? ? ??rt_sem_signal(&master_sem);?// 釋放信號量? ? }}/****************************************************************************/?// 分隔注釋void?receive_callback(void?*cb_data)?// 定義一個接收回調函數 (用于外部事件驅動模式){? ??ec_master_t?*m = (ec_master_t?*) cb_data;?// 將 void* 指針轉換為 master 指針? ??// too close to the next real time cycle: deny access... // 注釋:離下一個實時周期太近:拒絕訪問...? ??if?(get_cycles() - t_last_cycle <= t_critical) {?// 如果當前時間與上個周期開始時間的差值小于臨界值? ? ? ??rt_sem_wait(&master_sem);?// 獲取信號量? ? ? ??ecrt_master_receive(m);?// 調用接收函數? ? ? ??rt_sem_signal(&master_sem);?// 釋放信號量? ? }}/****************************************************************************/?// 分隔注釋int?__init?init_mod(void)?// 內核模塊的初始化函數{? ??int?ret =?-1;?// 聲明并初始化返回值? ? RTIME tick_period, requested_ticks, now;?// 聲明 RTAI 的時間變量#ifdef?CONFIGURE_PDOS?// 如果定義了 CONFIGURE_PDOS 宏? ??ec_slave_config_t?*sc;?// 聲明一個從站配置對象指針#endif?// 結束 #ifdef? ??printk(KERN_INFO PFX?"Starting...\n");?// 在內核日志中打印啟動信息? ??rt_sem_init(&master_sem,?1);?// 初始化 RTAI 信號量,初始值為1? ??// 計算一個臨界時間值,用于回調函數中判斷是否離下一個周期太近? ? t_critical = cpu_khz *?1000?/ FREQUENCY - cpu_khz * INHIBIT_TIME /?1000;? ? master =?ecrt_request_master(0);?// 請求索引為0的 EtherCAT 主站實例? ??if?(!master) {?// 檢查主站請求是否成功? ? ? ? ret = -EBUSY;?// 設置返回值為設備忙? ? ? ??printk(KERN_ERR PFX?"Requesting master 0 failed!\n");?// 打印錯誤信息? ? ? ??goto?out_return;?// 跳轉到返回處? ? }? ??ecrt_master_callbacks(master, send_callback, receive_callback, master);?// 注冊發送和接收的回調函數? ??printk(KERN_INFO PFX?"Registering domain...\n");?// 打印信息? ??if?(!(domain1 =?ecrt_master_create_domain(master))) {?// 在主站上創建一個過程數據域? ? ? ??printk(KERN_ERR PFX?"Domain creation failed!\n");?// 如果失敗,打印錯誤? ? ? ??goto?out_release_master;?// 跳轉到釋放主站處? ? }? ??if?(!(sc_ana_in =?ecrt_master_slave_config(?// 為模擬量輸入從站創建配置? ? ? ? ? ? ? ? ? ? master, AnaInSlavePos, Beckhoff_EL3162))) {? ? ? ??printk(KERN_ERR PFX?"Failed to get slave configuration.\n");?// 打印錯誤? ? ? ??goto?out_release_master;?// 跳轉到釋放主站處? ? }#ifdef?CONFIGURE_PDOS?// 如果定義了 CONFIGURE_PDOS 宏? ??printk(KERN_INFO PFX?"Configuring PDOs...\n");?// 打印信息? ??if?(ecrt_slave_config_pdos(sc_ana_in, EC_END, el3162_syncs)) {?// 為 EL3162 配置 PDO? ? ? ??printk(KERN_ERR PFX?"Failed to configure PDOs.\n");?// 打印錯誤? ? ? ??goto?out_release_master;?// 跳轉到釋放主站處? ? }? ??if?(!(sc =?ecrt_master_slave_config(master, DigOutSlavePos,?// 為數字量輸出從站創建配置? ? ? ? ? ? ? ? ? ? Beckhoff_EL2004))) {? ? ? ??printk(KERN_ERR PFX?"Failed to get slave configuration.\n");?// 打印錯誤? ? ? ??goto?out_release_master;?// 跳轉到釋放主站處? ? }? ??if?(ecrt_slave_config_pdos(sc, EC_END, el2004_syncs)) {?// 為 EL2004 配置 PDO? ? ? ??printk(KERN_ERR PFX?"Failed to configure PDOs.\n");?// 打印錯誤? ? ? ??goto?out_release_master;?// 跳轉到釋放主站處? ? }#endif?// 結束 #ifdef? ??printk(KERN_INFO PFX?"Registering PDO entries...\n");?// 打印信息? ??if?(ecrt_domain_reg_pdo_entry_list(domain1, domain1_regs)) {?// 將定義的 PDO 注冊列表注冊到域? ? ? ??printk(KERN_ERR PFX?"PDO entry registration failed!\n");?// 如果注冊失敗,打印錯誤? ? ? ??goto?out_release_master;?// 跳轉到釋放主站處? ? }? ??printk(KERN_INFO PFX?"Activating master...\n");?// 打印信息? ??if?(ecrt_master_activate(master)) {?// 激活主站,開始總線通信? ? ? ??printk(KERN_ERR PFX?"Failed to activate master!\n");?// 如果激活失敗,打印錯誤? ? ? ??goto?out_release_master;?// 跳轉到釋放主站處? ? }? ??// Get internal process data for domain // 注釋:獲取域的內部過程數據? ? domain1_pd =?ecrt_domain_data(domain1);?// 獲取域的過程數據內存區指針? ??printk(KERN_INFO PFX?"Starting cyclic sample thread...\n");?// 打印信息? ? requested_ticks =?nano2count(TIMERTICKS);?// 將周期納秒數轉換為 RTAI 的時鐘節拍數? ? tick_period =?start_rt_timer(requested_ticks);?// 啟動 RTAI 的實時定時器,并獲取實際的節拍周期? ??printk(KERN_INFO PFX?"RT timer started with %i/%i ticks.\n",?// 打印定時器信息? ? ? ? ? ?(int) tick_period, (int) requested_ticks);? ??// 初始化 RTAI 任務? ??if?(rt_task_init(&task, run,?0,?2000,?0,?1,?NULL)) {?// 參數:任務結構體, 函數, 參數, 棧大小, 優先級, 使用FPU, 信號? ? ? ??printk(KERN_ERR PFX?"Failed to init RTAI task!\n");?// 如果失敗,打印錯誤? ? ? ??goto?out_stop_timer;?// 跳轉到停止定時器處? ? }? ? now =?rt_get_time();?// 獲取當前 RTAI 時間? ??// 將任務設置為周期性任務? ??if?(rt_task_make_periodic(&task, now + tick_period, tick_period)) {?// 參數:任務, 首次啟動時間, 周期? ? ? ??printk(KERN_ERR PFX?"Failed to run RTAI task!\n");?// 如果失敗,打印錯誤? ? ? ??goto?out_stop_task;?// 跳轉到刪除任務處? ? }? ??printk(KERN_INFO PFX?"Initialized.\n");?// 打印初始化成功信息? ??return?0;?// 返回成功?out_stop_task:?// 清理標簽:刪除任務? ??rt_task_delete(&task);?// 刪除 RTAI 任務?out_stop_timer:?// 清理標簽:停止定時器? ??stop_rt_timer();?// 停止 RTAI 實時定時器?out_release_master:?// 清理標簽:釋放主站? ??printk(KERN_ERR PFX?"Releasing master...\n");?// 打印信息? ??ecrt_release_master(master);?// 釋放主站資源?out_return:?// 清理標簽:返回? ??rt_sem_delete(&master_sem);?// 刪除 RTAI 信號量? ??printk(KERN_ERR PFX?"Failed to load. Aborting.\n");?// 打印加載失敗信息? ??return?ret;?// 返回錯誤碼}/****************************************************************************/?// 分隔注釋void?__exit?cleanup_mod(void)?// 內核模塊的退出函數{? ??printk(KERN_INFO PFX?"Stopping...\n");?// 在內核日志中打印停止信息? ??rt_task_delete(&task);?// 刪除 RTAI 任務? ??stop_rt_timer();?// 停止 RTAI 實時定時器? ??ecrt_release_master(master);?// 釋放主站資源? ??rt_sem_delete(&master_sem);?// 刪除 RTAI 信號量? ??printk(KERN_INFO PFX?"Unloading.\n");?// 打印卸載完成信息}/****************************************************************************/?// 分隔注釋MODULE_LICENSE("GPL");?// 宏:聲明模塊的許可證為 GPLMODULE_AUTHOR("Florian Pose <fp@igh.de>");?// 宏:聲明模塊的作者MODULE_DESCRIPTION("EtherCAT RTAI sample module");?// 宏:聲明模塊的描述module_init(init_mod);?// 宏:將 init_mod 函數注冊為模塊的初始化函數module_exit(cleanup_mod);?// 宏:將 cleanup_mod 函數注冊為模塊的退出函數/****************************************************************************/

這是一個在?Linux 內核空間運行的、基于?RTAI (Real-Time Application Interface)?的硬實時 EtherCAT 主站示例模塊。它展示了如何利用 RTAI 提供的實時任務和調度機制來構建一個高精度、確定性的工業控制應用。

核心架構與功能:

  1. 內核模塊形態:與?tty.c?示例類似,這是一個通過?insmod?加載、rmmod?卸載的完整內核模塊,其生命周期由?module_init?和?module_exit?函數管理。

  2. 硬實時環境 (RTAI):此示例的核心是 RTAI。它不依賴 Linux 自身的調度器或定時器,而是使用 RTAI 提供的、運行在 Linux 內核之下的實時微內核服務。

  • RTAI 任務

    :通過?rt_task_init?創建一個名為?task?的 RTAI 實時任務,并指定其執行函數為?run

  • RTAI 調度與定時

    :通過?start_rt_timer?啟動 RTAI 的高精度實時定時器,并通過?rt_task_make_periodic?將?task?設置為由該定時器驅動的周期性任務。周期的精確性由 RTAI 內核保證,抖動(jitter)非常小。

  • 周期性執行

    run?函數中的?rt_task_wait_period()?會精確地阻塞任務,直到下一個由 RTAI 定時器確定的調度點到來。

  • 并發保護 - RTAI 信號量

    • 為了保護對共享 EtherCAT 主站資源的訪問,代碼使用了 RTAI 提供的信號量 (rt_sem_init,?rt_sem_wait,?rt_sem_signal)。

    • 在?run?函數和兩個回調函數中,所有對?ecrt_*?函數的調用都被信號量加鎖和解鎖,確保了在 RTAI 的多任務環境下的數據一致性和原子性。

  • 周期性任務 (run)

    • 這是 RTAI 實時任務的主體,在一個無限循環中運行。

    • 每個周期開始時,它執行標準的 EtherCAT I/O 流程:接收、處理、應用邏輯、發送。

    • 應用邏輯

      :包含一個簡單的 1Hz 閃爍邏輯,控制一個數字量輸出模塊。

    • 狀態監控

      :以較低頻率檢查并打印主站、域和從站的狀態。

    • 高精度時間戳

      :使用?get_cycles()?獲取 CPU 周期數,用于高精度的時間測量,這在?send/receive_callback?中用于防止與實時周期沖突。

  • 可選的回調機制

    • 代碼中注冊了?send_callback?和?receive_callback,這通常用于外部事件驅動的模式(例如,由網卡中斷直接觸發數據收發)。

    • 在這些回調函數中,通過比較當前 CPU 周期數與上一個實時周期開始的時間戳,來判斷是否離下一個實時周期太近。如果太近,就不執行操作,這是為了避免外部事件干擾到由 RTAI 定時器驅動的主實時循環的確定性。

  • 生命周期管理

    • 加載 (insmod)

      : 執行?init_mod。完成 EtherCAT 配置、初始化 RTAI 信號量、啟動 RTAI 實時定時器,并創建和啟動 RTAI 周期性任務。

    • 運行

      : RTAI 調度器周期性地喚醒?run?任務,執行硬實時控制循環。

    • 卸載 (rmmod)

      : 執行?cleanup_mod。安全地刪除 RTAI 任務,停止 RTAI 定時器,釋放 EtherCAT 主站資源,并刪除 RTAI 信號量。

    總結:此代碼是一個經典的硬實時內核空間 EtherCAT 控制器范例。它完全繞開了標準 Linux 的調度和定時機制,將所有實時關鍵操作都交由 RTAI 微內核處理,從而獲得了微秒級的、確定性的性能。這是在需要極高實時性保證的工業自動化和機器人控制等領域中常見的架構。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/92945.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/92945.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/92945.shtml

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

相關文章

7款熱門智能電視文件管理器橫向評測

7款智能電視文件管理器橫向評測 在智能電視和電視盒子日益普及的今天&#xff0c;一款好用的文件管理器能讓您的數字生活更加便捷。本文為您評測了7款廣受歡迎的TV版文件管理器&#xff0c;助您找到最適合自己的工具。 1. ES文件瀏覽器TV版 ES文件瀏覽器是一款廣受歡迎的多功能…

Python 類元編程(導入時和運行時比較)

導入時和運行時比較 為了正確地做元編程&#xff0c;你必須知道 Python 解釋器什么時候計算各個代碼 塊。Python 程序員會區分“導入時”和“運行時”&#xff0c;不過這兩個術語沒有嚴 格的定義&#xff0c;而且二者之間存在著灰色地帶。在導入時&#xff0c;解釋器會從上到 下…

[git diff] 對比檢查變更 | 提交前復審 | 版本回退

git diff git diff 是 Git 版本控制系統中用于比較文件差異的核心命令&#xff0c;可以顯示工作目錄、暫存區&#xff08;Index&#xff09;和倉庫歷史之間的變化。 通過對比不同版本或狀態的文件內容&#xff0c;幫助開發者理解代碼變更。 比較工作目錄與暫存區 運行以下命令查…

【數據可視化-85】海底撈門店數據分析與可視化:Python + pyecharts打造炫酷暗黑主題大屏

&#x1f9d1; 博主簡介&#xff1a;曾任某智慧城市類企業算法總監&#xff0c;目前在美國市場的物流公司從事高級算法工程師一職&#xff0c;深耕人工智能領域&#xff0c;精通python數據挖掘、可視化、機器學習等&#xff0c;發表過AI相關的專利并多次在AI類比賽中獲獎。CSDN…

物聯網之小白調試網關設備

小伙伴們&#xff0c;你們好呀&#xff01;我是老寇&#xff01;跟我一起學習調試網關設備 相信搞過物聯網的朋友&#xff0c;對網關設備非常熟悉&#xff0c;本人以小白的視角&#xff0c;手把手教你調試網關設備&#xff01; 工作中使用的是Ubuntu操作系統&#xff0c;因此&a…

Node.js特訓專欄-實戰進階:22. Docker容器化部署

?? 歡迎來到 Node.js 實戰專欄!在這里,每一行代碼都是解鎖高性能應用的鑰匙,讓我們一起開啟 Node.js 的奇妙開發之旅! Node.js 特訓專欄主頁 專欄內容規劃詳情 我將從Docker容器化部署的基礎概念入手,介紹Node.js應用容器化的步驟,包括創建Dockerfile、構建鏡像、運行…

eclipse嵌入式編譯速度慢

eclipse 嵌入式 編譯 速度慢 同一個項目&#xff0c;eclipse編譯速度越來越慢&#xff0c;一開始幾秒鐘編譯完&#xff0c;后面要10分鐘。只需要將以下兩個程序卸載重新安裝即可。

編譯Android版本可用的高版本iproute2

背景&#xff1a; Android自帶的iproute2 太老&#xff0c;很多指令格式不支持 直接基于Android源碼&#xff0c;替換源碼下iproute2 代碼編譯新版&#xff0c;報錯太多&#xff0c;于是改用Android NDK工具編譯 環境&#xff1a; android-ndk-r25c-linux.zip 下載鏈接&am…

JavaScript的fetch函數的用法

基本語法fetch函數用于發起網絡請求&#xff0c;返回一個Promise對象。基本語法如下&#xff1a;fetch(url, options).then(response > response.json()).then(data > console.log(data)).catch(error > console.error(Error:, error));GET請求發起一個簡單的GET請求&…

Json和XML文件相互轉化

目錄 一.XML轉Json文件 示例&#xff1a;將XML轉換為JSON 依賴準備 Java代碼示例 代碼詳細講解 二.Json轉XML文件 示例&#xff1a;將JSON轉換為XML 依賴準備 Java代碼示例 代碼詳細講解 關鍵代碼解析 將JSON轉換為XML 寫入文件 示例輸入與輸出 三.具有相同功能的…

Python科學計算與可視化領域工具TVTK、Mayavi、Mlab、Traits(附視頻教程)

概述 TVTK、Mayavi、Mlab 和 Traits 都是 Python 科學計算與可視化領域中緊密相關的工具&#xff0c;它們常被結合使用來處理和展示三維數據。視頻教程&#xff1a;https://pan.quark.cn/s/f73e875225ca 1. TVTK TVTK&#xff08;Traits-based Visualization Toolkit&#xff0…

SQL INSERT INTO SELECT 詳解

SQL INSERT INTO SELECT 詳解 引言 SQL(Structured Query Language)是數據庫操作的基礎語言,廣泛用于各種關系型數據庫管理系統中。在SQL中,INSERT INTO SELECT 是一個強大的功能,它允許用戶從一個表中選取數據,并直接將這些數據插入到另一個表中。本文將詳細講解 SQL …

python速成學習路線

第一部分&#xff1a;核心基礎&#xff08;語法與工具&#xff09; 目標&#xff1a;掌握 Python 的基本語法規則、數據處理方式和開發工具 核心內容&#xff1a; 環境搭建 安裝Python 3.x版本&#xff08;推薦3.10&#xff09;配置開發工具&#xff08;如PyCharm、VS Code或…

自然語言處理的實際應用

在這個信息爆炸的時代&#xff0c;我們每天都在與文字、語音打交道 —— 發送消息、查詢信息、使用智能助手…… 這些看似平常的互動背后&#xff0c;都離不開一項關鍵技術的支撐&#xff1a;自然語言處理&#xff08;NLP&#xff09;。作為人工智能的重要分支&#xff0c;NLP …

Docker實戰:為項目打造即開即用的寶塔LNMP環境

Docker實戰&#xff1a;為項目打造即開即用的寶塔LNMP環境背景一、準備基礎鏡像二、啟動配置容器&#xff08;關鍵步驟&#xff09;三、容器內環境配置&#xff08;逐步執行&#xff09;1. 基礎環境搭建2. 安裝Systemd&#xff08;寶塔依賴&#xff09;3. 安裝寶塔面板&#xf…

.net\c#web、小程序、安卓開發之基于asp.net家用汽車銷售管理系統的設計與實現

.net\c#web、小程序、安卓開發之基于asp.net家用汽車銷售管理系統的設計與實現

藥房智能盤庫系統:基于CV與時間序列預測的庫存革命

> 在醫療資源日益緊張的今天,**全國78%的藥房仍依賴人工盤庫**,平均每100家藥房每年因庫存問題損失超50萬元。當計算機視覺遇見時間序列預測,一場藥房庫存管理的智能化革命正在悄然發生。 --- ### 一、傳統藥房庫存的三大痛點與破局思路 #### 致命痛點分析 1. **人工…

【互動屏幕】解析雙屏聯動在數字展廳中的應用與價值

雙屏聯動 https://www.bmcyzs.com/ 作為現代展廳設計中的重要技術手段&#xff0c;通過兩塊或多塊屏幕的協同工作&#xff0c;實現了信息的動態展示與交互體驗的提升。在展廳環境中&#xff0c;雙屏聯動軟件能夠將觸摸屏與大屏幕無縫連接&#xff0c;使觀眾通過簡單的操作即可控…

clickhouse基礎概念及集群部署

一. 簡述&#xff1a; ClickHouse 是一款高性能列式存儲數據庫&#xff0c;專為海量數據的實時分析場景設計。它以極致的查詢速度、高效的存儲利用率和強大的并行處理能力著稱&#xff0c;廣泛應用于日志分析、用戶行為分析、業務監控等大數據分析領域。1. 核心特性&#xff1a…

低版本 IntelliJ IDEA 使用高版本 JDK 語言特性的問題

現實問題&#xff1a; 目前最新的 IntelliJ IDEA 已經不支持在 Win7 環境上安裝了&#xff0c;如果企業內開發環境仍然是 Win7&#xff0c;就會導致很多問題。 比如當前 IDEA 版本為 2023.1&#xff0c;最大支持 JDK17&#xff0c;如何正常使用 JDK21 的新特性呢&#xff1f;比…