編碼器軟解碼實(shí)戰(zhàn):從AB相原理到ERU中斷驅(qū)動(dòng))
做了幾年嵌入式接觸過不少編碼器應(yīng)用但第一次在英飛凌AURIX TC264上做旋轉(zhuǎn)編碼器讀取時(shí)還是被“方案選擇”這件事卡了一下午。AURIX不像STM32那樣有一個(gè)特別順手的定時(shí)器編碼器模式TC264的GPT12雖然支持旋轉(zhuǎn)計(jì)數(shù)器但引腳復(fù)用表一翻發(fā)現(xiàn)編碼器的A、B兩相已經(jīng)被其他功能占掉根本沒有硬件解碼通道可用。最后只能回到最原始也最可控的路線軟件解碼簡(jiǎn)稱軟解碼。這篇文章就把我這次在AURIX TC264上實(shí)現(xiàn)旋轉(zhuǎn)編碼器軟解碼的完整過程寫下來。從編碼器工作原理、為什么選軟解碼、計(jì)數(shù)邏輯設(shè)計(jì)、基于ERU外部中斷的驅(qū)動(dòng)代碼到速度換算、濾波、實(shí)際調(diào)試中踩過的坑全部整理成可直接參考的方案。適合正在用AURIX做電機(jī)、手輪、角度反饋、位置閉環(huán)的工程師也適合從STM32生態(tài)轉(zhuǎn)過來的朋友——你會(huì)發(fā)現(xiàn)只要理解了AB相本質(zhì)軟解碼其實(shí)比硬件模塊更靈活。1. 為什么選軟解碼AURIX平臺(tái)上的編碼器方案取舍1.1 AB相與Z相到底在表達(dá)什么增量式旋轉(zhuǎn)編碼器最核心的輸出就是A、B兩路方波。軸轉(zhuǎn)動(dòng)時(shí)A、B兩路會(huì)產(chǎn)生相位差為90°的脈沖正轉(zhuǎn)時(shí)A相超前B相90°反轉(zhuǎn)時(shí)B相超前A相90°。這個(gè)相位關(guān)系就是方向信息。編碼器每轉(zhuǎn)一圈還會(huì)輸出一個(gè)Z相信號(hào)通常是一窄脈沖用來做絕對(duì)位置的零點(diǎn)標(biāo)定。用生活化一點(diǎn)的語言來說A、B兩相信號(hào)很像馬路上兩個(gè)相隔固定距離的傳感器你判斷一輛車是往左開還是往右開靠的是兩個(gè)傳感器被觸發(fā)的先后順序。編碼器里的方向和位置信息本質(zhì)上也是這么來的。在實(shí)際選型中編碼器輸出形式大致有三種集電極開路OC、推挽輸出和差分線驅(qū)動(dòng)RS422。OC輸出需要外部上拉電阻AURIX內(nèi)部上拉可以作為兜底但更建議外部接2.2k到10k的上拉推挽輸出直接懟進(jìn)MCU即可差分輸出則要配AM26LV32這類差分接收芯片先轉(zhuǎn)成單端3.3V方波再進(jìn)GPIO。這個(gè)電氣接口問題雖然和軟件無關(guān)但接錯(cuò)了一定會(huì)在調(diào)試階段浪費(fèi)大量時(shí)間。1.2 STM32與AURIX解碼方案的現(xiàn)實(shí)差異寫過STM32的朋友都知道STM32的高級(jí)定時(shí)器/通用定時(shí)器自帶Encoder Mode把編碼器的A、B兩相接進(jìn)TI1和TI2硬件就能自動(dòng)完成方向判斷和加減計(jì)數(shù)CPU完全不參與。這個(gè)模式用起來確實(shí)舒服導(dǎo)致很多從ST轉(zhuǎn)過來的工程師第一反應(yīng)是去AURIX里找類似外設(shè)。但AURIX系列的情況不太一樣。TC2xx/TC3xx中GPT12模塊提供了旋轉(zhuǎn)計(jì)數(shù)器模式Rotary Counter Mode可以把A、B兩相分別接入T2IN和T3IN由硬件完成方向識(shí)別。聽起來很像STM32的編碼器模式但它對(duì)引腳復(fù)用有固定要求。我這次翻TC264的引腳復(fù)用表編碼器A、B兩相被分配到的引腳恰好不屬于GPT12候選引腳硬要接就得改板子。在項(xiàng)目進(jìn)度面前改板子顯然不現(xiàn)實(shí)。于是只剩下兩條路一是用GTM模塊定時(shí)采樣二是用ERU外部中斷配合GPIO電平判斷做軟解碼。GTM的方式適合低頻采樣場(chǎng)景但響應(yīng)延遲大ERU中斷方式實(shí)時(shí)性更好邏輯也更直觀。最終選了ERU中斷方案。1.3 軟解碼的邊界在哪里軟解碼并不是萬能的。它最大的約束是CPU占用率會(huì)隨著編碼器轉(zhuǎn)速線性上升。以2500線的編碼器為例軸轉(zhuǎn)速3000rpm時(shí)A相脈沖頻率為125kHz如果做兩倍頻意味著每秒鐘要被中斷250k次做四倍頻則是500k次。TC264主頻200MHz一次ISR從響應(yīng)到退出如果消耗100個(gè)周期那么500k次中斷就要50M周期占CPU的25%左右。如果系統(tǒng)里還有復(fù)雜的控制算法、通信協(xié)議棧這個(gè)占用率就很嚇人了。所以我的判斷是在中低速應(yīng)用、或者編碼器物理分辨率不高的場(chǎng)景下軟解碼完全夠用且極其靈活但在高轉(zhuǎn)速、高分辨率、CPU資源緊張的場(chǎng)合優(yōu)先考慮GPT12硬件旋轉(zhuǎn)計(jì)數(shù)器或GTM方案??辞暹@個(gè)邊界后面就不會(huì)被“軟解碼是不是不專業(yè)”這種問題困擾。2. 軟解碼核心原理方向判斷、倍頻與計(jì)數(shù)邏輯2.1 方向判斷一只引腳觸發(fā)另一只引腳定方向軟解碼的方向判斷非常直接。以A相作為中斷源為例將A相配置為上升沿和下降沿都觸發(fā)中斷。每次A相邊沿到來時(shí)讀取B相當(dāng)前電平A相上升沿到來時(shí)如果B相為低電平說明正轉(zhuǎn)計(jì)數(shù)值加1A相上升沿到來時(shí)如果B相為高電平說明反轉(zhuǎn)計(jì)數(shù)值減1A相下降沿到來時(shí)如果B相為高電平說明正轉(zhuǎn)計(jì)數(shù)值加1A相下降沿到來時(shí)如果B相為低電平說明反轉(zhuǎn)計(jì)數(shù)值減1。這個(gè)判斷邏輯的本質(zhì)就是借助AB兩相的相位關(guān)系在每次邊沿時(shí)刻用另一相電平作為方向標(biāo)志。很多資料上把這稱為“單相雙沿觸發(fā)”或“兩倍頻方案”因?yàn)榫幋a器每輸出一個(gè)完整脈沖周期A相會(huì)經(jīng)歷上升沿和下降沿各一次計(jì)數(shù)值變化兩次也就是脈沖頻率的兩倍。如果你用四倍頻方案就是把B相也接進(jìn)中斷A、B兩相四個(gè)邊沿都參與計(jì)數(shù)。這樣每轉(zhuǎn)一圈的計(jì)數(shù)個(gè)數(shù)是物理線數(shù)的四倍分辨率更高。2.2 兩倍頻與四倍頻的取舍邏輯兩倍頻只需要占用一個(gè)ERU中斷通道代碼簡(jiǎn)單CPU中斷次數(shù)少。四倍頻需要占用兩個(gè)中斷通道代碼多一個(gè)狀態(tài)判斷但分辨率翻倍。實(shí)際項(xiàng)目中怎么選我的建議是如果編碼器本身線數(shù)夠高比如2500線以上兩倍頻已經(jīng)能提供每轉(zhuǎn)5000個(gè)計(jì)數(shù)通常夠用如果編碼器只有100線、200線這種低分辨率又需要比較精細(xì)的位置控制那就上四倍頻。四倍頻的代價(jià)不僅是中斷次數(shù)翻倍還引入了更嚴(yán)格的時(shí)序要求——兩個(gè)相鄰中斷之間的間隔可能是幾十微秒甚至幾微秒ISR里絕對(duì)不能做任何耗時(shí)操作比如打印、浮點(diǎn)運(yùn)算、慢速Flash讀寫。下面這張表是我在實(shí)際選型時(shí)常用的對(duì)比思路方案中斷源每轉(zhuǎn)計(jì)數(shù)CPU壓力適用場(chǎng)景單相單邊沿A相上升沿P物理線數(shù)最小極低速、對(duì)分辨率無要求單相雙沿A相上升下降沿2P小大多數(shù)電機(jī)反饋、手輪四倍頻A相和B相全部邊沿4P大高分辨率位置環(huán)、低速高精場(chǎng)景2.3 計(jì)數(shù)頻率上限的快速估算公式軟解碼能承受的最高轉(zhuǎn)速可以用一個(gè)簡(jiǎn)單公式估算最高轉(zhuǎn)速rpm 允許的中斷頻率 /倍頻系數(shù) × 編碼器線數(shù) / 60舉個(gè)例子2500線編碼器兩倍頻如果設(shè)定單邊沿中斷最大頻率為200kHz那么最高轉(zhuǎn)速 200000 /2 × 2500 / 60 2400rpm如果編碼器線數(shù)降到500線同樣200kHz中斷頻率下最高轉(zhuǎn)速能到12000rpm。這個(gè)估算在項(xiàng)目選型階段非常有用先算清楚再動(dòng)手可以少走很多彎路。3. 在TC264上落地軟解碼驅(qū)動(dòng)完整實(shí)現(xiàn)3.1 開發(fā)環(huán)境與硬件接線我這邊用的是AURIX Development Studio iLLD庫(kù)芯片為TC264雙核TriCore主頻200MHz。AURIX Development Studio是英飛凌官方的免費(fèi)IDE基于Eclipse對(duì)個(gè)人開發(fā)者很友好工程向?qū)Ю锟梢灾苯由蒚C264的iLLD基礎(chǔ)工程。硬件接線很簡(jiǎn)單A相接到任意一個(gè)普通GPIO我這段代碼示例里用的是P10.2B相接到另一個(gè)GPIO示例用P10.3編碼器供電根據(jù)手冊(cè)接3.3V或5V注意TC264的GPIO不是所有引腳都支持5V耐受輸入最高電壓務(wù)必以數(shù)據(jù)手冊(cè)為準(zhǔn)最好統(tǒng)一用3.3V供電或者加電平轉(zhuǎn)換Z相如果用不到就先不接需要零點(diǎn)標(biāo)定時(shí)再接一個(gè)普通GPIO。這里特別提醒一句如果編碼器是OC輸出外部上拉電阻一定要加否則邊沿會(huì)變得很“拖沓”示波器上看是一堆毛刺軟解碼會(huì)誤計(jì)數(shù)到懷疑人生。3.2 驅(qū)動(dòng)模塊怎么組織軟解碼代碼我拆成兩個(gè)文件encoder_soft.h對(duì)外暴露初始化函數(shù)、計(jì)數(shù)讀取函數(shù)、速度計(jì)算函數(shù)encoder_soft.c實(shí)際GPIO配置、ERU中斷配置、ISR實(shí)現(xiàn)。這種模塊化的好處是后續(xù)切換到GPT12硬件方案時(shí)只要把encoder_soft.c里的底層實(shí)現(xiàn)換掉頭文件的接口可以保持不變上層控制代碼幾乎不用動(dòng)。對(duì)外接口設(shè)計(jì)成三個(gè)核心函數(shù)Encoder_Init初始化引腳、配置ERU中斷Encoder_GetCount讀取當(dāng)前累計(jì)計(jì)數(shù)值Encoder_CalculateSpeed按固定周期計(jì)算轉(zhuǎn)速內(nèi)部用計(jì)數(shù)差值換算出RPM。計(jì)數(shù)變量用volatile修飾因?yàn)樗谥袛嗬锉恍薷?、在主循環(huán)里被讀取交叉訪問必須防止編譯器優(yōu)化。有RTOS的話讀取時(shí)建議做臨界區(qū)保護(hù)或者直接讀兩次取值相等才算有效避免讀到撕裂值。3.3 初始化代碼GPIO與ERU中斷配置核心初始化代碼如下// encoder_soft.h #ifndef ENCODER_SOFT_H #define ENCODER_SOFT_H #include Ifx_Types.h #define ENC_A_PORT MODULE_P10 #define ENC_A_PIN IfxPort_Pin_2 #define ENC_B_PORT MODULE_P10 #define ENC_B_PIN IfxPort_Pin_3 // 編碼器物理線數(shù)根據(jù)實(shí)際編碼器修改 #define ENC_PULSES_PER_REV 2500 #define ENC_COUNTS_PER_REV (ENC_PULSES_PER_REV * 2) // 兩倍頻 // 最小邊沿間隔單位us用于軟件濾波 #define ENC_MIN_EDGE_INTERVAL_US 20 extern volatile sint32 g_encoderCount; void Encoder_Init(void); sint32 Encoder_GetCount(void); float32 Encoder_CalculateSpeed(uint16 periodMs); #endif// encoder_soft.c #include encoder_soft.h #include IfxPort.h #include IfxStm.h #include IfxSrc.h #include IfxScuEru.h volatile sint32 g_encoderCount 0; static uint32 g_lastEdgeTick 0; static sint32 g_lastCount 0; static sint32 g_calcDelta 0; // 中斷服務(wù)函數(shù)綁定到CPU0優(yōu)先級(jí)設(shè)為10 IFX_INTERRUPT(EncoderA_ISR, 0, 10); static void Encoder_EruConfig(void) { /* * 這里處理ERU通道配置 * 1. 把A相引腳對(duì)應(yīng)的ERU輸入通道選出來 * 2. 配置為上升沿下降沿都產(chǎn)生事件 * 3. 把事件輸出路由到對(duì)應(yīng)的中斷源。 * * 不同板卡的引腳映射不同ERU通道號(hào)、SRC編號(hào)需要按 * TC26x 用戶手冊(cè) ERU 章節(jié)和當(dāng)前工程的 IfxScuSrcId 枚舉來填。 * iLLD版本不同底層庫(kù)函數(shù)名會(huì)有差異但配置思路一致。 */ } void Encoder_Init(void) { // 1. 引腳配置輸入模式 內(nèi)部上拉 IfxPort_setPinMode(ENC_A_PORT, ENC_A_PIN, IfxPort_Mode_inputPullUp); IfxPort_setPinMode(ENC_B_PORT, ENC_B_PIN, IfxPort_Mode_inputPullUp); // 2. ERU事件配置 Encoder_EruConfig(); // 3. 中斷源注冊(cè)到CPU0 // 具體SRC編號(hào)以所選ERU通道為準(zhǔn)這里示意為IfxScuSrcId_eru0 IfxSrc_init(IfxSrc_getSrc(IfxScuSrcId_eru0), IfxSrc_Tos_cpu0, 10); IfxSrc_enable(IfxSrc_getSrc(IfxScuSrcId_eru0)); // 4. 清計(jì)數(shù) g_encoderCount 0; g_lastEdgeTick IfxStm_getLower(IfxStm_getTFromModule(MODULE_STM0)); } void EncoderA_ISR(void) { // 取當(dāng)前STM時(shí)間戳配合最小間隔做濾波 uint32 nowTick IfxStm_getLower(IfxStm_getTFromModule(MODULE_STM0)); // 軟件濾波間隔太短的邊沿認(rèn)為是抖動(dòng)直接忽略 uint32 deltaUs (nowTick - g_lastEdgeTick) / 100; // 以STM頻率100MHz為例 if (deltaUs ENC_MIN_EDGE_INTERVAL_US) { return; } g_lastEdgeTick nowTick; // 方向判斷A相邊沿到來時(shí)讀B相電平 if (IfxPort_getPinState(ENC_B_PORT, ENC_B_PIN) TRUE) { if (IfxPort_getPinState(ENC_A_PORT, ENC_A_PIN) TRUE) { // A相上升沿B相為高反轉(zhuǎn) g_encoderCount--; } else { // A相下降沿B相為高正轉(zhuǎn) g_encoderCount; } } else { if (IfxPort_getPinState(ENC_A_PORT, ENC_A_PIN) TRUE) { // A相上升沿B相為低正轉(zhuǎn) g_encoderCount; } else { // A相下降沿B相為低反轉(zhuǎn) g_encoderCount--; } } }這段代碼里最容易被忽略的是STM時(shí)間戳的頻率。我示例中以STM0頻率100MHz為例也就是1個(gè)tick等于10ns。如果實(shí)際工程里STM被配置成其他頻率deltaUs的計(jì)算除數(shù)要跟著改否則濾波時(shí)間會(huì)差一個(gè)數(shù)量級(jí)。這個(gè)“頻率核對(duì)”習(xí)慣建議在寫任何時(shí)間相關(guān)內(nèi)容時(shí)都先確認(rèn)一遍。3.4 速度計(jì)算與主循環(huán)調(diào)用ISR負(fù)責(zé)累加計(jì)數(shù)速度計(jì)算放到主循環(huán)或周期性任務(wù)里做不要在中斷里做。float32 Encoder_CalculateSpeed(uint16 periodMs) { sint32 currentCount g_encoderCount; sint32 delta currentCount - g_lastCount; g_lastCount currentCount; g_calcDelta delta; // 轉(zhuǎn)速 計(jì)數(shù)差值 / 每轉(zhuǎn)計(jì)數(shù) / 時(shí)間(分鐘) // periodMs換算成分鐘: periodMs / 60000 float32 rpm (float32)delta / (float32)ENC_COUNTS_PER_REV / ((float32)periodMs / 60000.0f); return rpm; }主循環(huán)里的典型用法while (1) { if (g_1msFlag) { g_1msFlag 0; // 每10ms計(jì)算一次轉(zhuǎn)速 float32 rpm Encoder_CalculateSpeed(10); // 位置直接用累計(jì)計(jì)數(shù) sint32 position Encoder_GetCount(); } }900行左右的完整驅(qū)動(dòng)代碼當(dāng)然不止這么點(diǎn)但核心邏輯就是這些。ERU通道配置屬于平臺(tái)相關(guān)代碼按自己的板卡和iLLD版本補(bǔ)全即可。ISR里的計(jì)數(shù)判斷邏輯是真正的“軟解碼靈魂”這部分可以直接照抄。4. 工程化細(xì)節(jié)濾波、速度換算與中斷優(yōu)先級(jí)4.1 抖動(dòng)濾波不能靠中斷里的延時(shí)旋轉(zhuǎn)編碼器的機(jī)械觸點(diǎn)、長(zhǎng)線傳輸、電機(jī)EMI都會(huì)在A、B信號(hào)上疊加毛刺。如果用示波器看正常邊沿應(yīng)該是干凈利落的跳變但干擾嚴(yán)重時(shí)會(huì)出現(xiàn)反復(fù)抖動(dòng)也就是在幾個(gè)微秒內(nèi)電平來回跳變好多次。軟解碼中最忌諱的做法是在ISR里調(diào)用延時(shí)函數(shù)去“等電平穩(wěn)定”。中斷里做延時(shí)不僅阻塞其他中斷還會(huì)讓CPU占用率暴漲。正確做法是用時(shí)間戳濾波記錄上一次有效邊沿的時(shí)刻如果這一次邊沿距離上次不足設(shè)定閾值就認(rèn)為它是抖動(dòng)直接丟棄。我代碼里用的ENC_MIN_EDGE_INTERVAL_US就是干這個(gè)的。閾值設(shè)置多大太小沒效果太大會(huì)把真實(shí)高速邊沿也濾掉。建議先從20us起步如果編碼器轉(zhuǎn)速高再往下調(diào)到5us如果現(xiàn)場(chǎng)干擾嚴(yán)重可以往上調(diào)到100us。這個(gè)值最終應(yīng)該是實(shí)驗(yàn)調(diào)出來的不是拍腦袋定的。另外硬件上的RC濾波也很重要。如果編碼器線和MCU距離超過30cm建議在A、B引腳對(duì)地各加一個(gè)100pF到1nF的小電容能有效抑制高頻毛刺。軟件濾波負(fù)責(zé)兜底硬件濾波負(fù)責(zé)根治兩者不沖突。4.2 從計(jì)數(shù)到物理量的換算編碼器計(jì)數(shù)的最終目的要么是位置要么是速度要么兩者都要。位置就是累計(jì)計(jì)數(shù)值本身但要注意溢出問題。TC264的sint32計(jì)數(shù)范圍約±21億按每轉(zhuǎn)5000計(jì)數(shù)計(jì)算足夠覆蓋幾十萬轉(zhuǎn)基本不用擔(dān)心溢出但如果用了64位計(jì)數(shù)訪問時(shí)要注意原子性。速度換算公式我在代碼里已經(jīng)寫了這里再展開舉個(gè)例子編碼器物理線數(shù)P 2500軟解碼兩倍頻每轉(zhuǎn)計(jì)數(shù) 5000速度計(jì)算周期 10ms在10ms內(nèi)讀到計(jì)數(shù)差值為50。那么轉(zhuǎn)速就是轉(zhuǎn)速 50 / 5000 / (10 / 60000) 60rpm也就是說每10ms計(jì)數(shù)值變化50對(duì)應(yīng)60rpm。這個(gè)例子里如果你用四倍頻每轉(zhuǎn)計(jì)數(shù)就變成10000同樣的50個(gè)計(jì)數(shù)只對(duì)應(yīng)30rpm。倍頻系數(shù)變了換算公式里分母一定要同步改否則速度值會(huì)翻倍或減半這也是調(diào)試時(shí)很容易犯的錯(cuò)。4.3 中斷優(yōu)先級(jí)與CPU核綁定的經(jīng)驗(yàn)AURIX是多核芯片TC264是雙核TC3xx甚至到六核。ERU中斷可以綁定到任意一個(gè)核但實(shí)際使用中我建議固定放在一個(gè)核上。最合理的方式是把編碼器中斷放到控制算法所在的那個(gè)核減少跨核數(shù)據(jù)同步的麻煩。優(yōu)先級(jí)方面編碼器中斷屬于硬實(shí)時(shí)事件優(yōu)先級(jí)應(yīng)該設(shè)置得比較高但不能高于系統(tǒng)時(shí)鐘節(jié)拍和緊急故障中斷。我這邊把編碼器中斷優(yōu)先級(jí)設(shè)為10系統(tǒng)節(jié)拍設(shè)為8串口等慢速外設(shè)設(shè)為30左右。原則是中斷服務(wù)頻率越高、實(shí)時(shí)性要求越嚴(yán)格優(yōu)先級(jí)越高但優(yōu)先級(jí)高的ISR盡量不要做耗時(shí)操作否則會(huì)餓死低優(yōu)先級(jí)中斷。AURIX的中斷優(yōu)先級(jí)和向量綁定在iLLD里主要通過IFX_INTERRUPT宏和IfxSrc_init完成。IFX_INTERRUPT(EncoderA_ISR, 0, 10)里面的0表示CPU010表示優(yōu)先級(jí)。如果工程里用了CPU1做主控記得把0改成1否則中斷會(huì)一直掛在CPU0上可能出現(xiàn)兩個(gè)核互相等待的隱性死鎖。5. 實(shí)際調(diào)試中踩過的坑5.1 方向反了不是改線而是確認(rèn)判斷邏輯第一次上電手動(dòng)轉(zhuǎn)編碼器串口打印的計(jì)數(shù)值跟預(yù)期方向完全相反。很多人第一反應(yīng)是A、B線接反了然后開始焊線。其實(shí)不用。軟解碼代碼里方向判斷只是一個(gè)if/else把正轉(zhuǎn)和反轉(zhuǎn)的加減操作對(duì)調(diào)一下即可。我習(xí)慣做成一個(gè)方向宏#define ENC_DIRECTION_NORMAL 1 #define ENC_DIRECTION_REVERSED (-1) #define ENC_DIRECTION ENC_DIRECTION_NORMALISR里把計(jì)數(shù)更新寫成g_encoderCount ENC_DIRECTION * 1;這樣后續(xù)系統(tǒng)集成時(shí)如果發(fā)現(xiàn)機(jī)械方向不匹配改一個(gè)宏定義就行完全不用動(dòng)硬件。這個(gè)習(xí)慣幫我省過好幾次返工。5.2 上電瞬間計(jì)數(shù)亂跳編碼器上電瞬間A、B引腳如果不確定MCU內(nèi)部上拉還沒建立穩(wěn)定外部編碼器可能也還沒初始化完成ISR會(huì)被一堆偽邊沿觸發(fā)計(jì)數(shù)值隨機(jī)亂跳。解決辦法是分階段啟用芯片上電后先把計(jì)數(shù)變量清零引腳配置成上拉輸入延時(shí)100ms到500ms等編碼器供電穩(wěn)定再使能ERU中斷。如果有Z相更穩(wěn)妥的做法是中斷全程使能但每次Z相到來時(shí)把計(jì)數(shù)值重置為預(yù)設(shè)的零點(diǎn)。這樣即使上電瞬間計(jì)入了一些亂數(shù)Z相一到就被修正了。5.3 高速時(shí)丟步低速時(shí)沒事這個(gè)問題排查起來最費(fèi)時(shí)間。低速轉(zhuǎn)正常高速轉(zhuǎn)數(shù)值偏差變大甚至停止時(shí)計(jì)數(shù)值和實(shí)際角度對(duì)不上。原因通常有三個(gè)第一濾波閾值太大。ENC_MIN_EDGE_INTERVAL_US設(shè)置成100us時(shí)等于要求編碼器輸出頻率不能超過10kHz超過的邊沿全被當(dāng)成抖動(dòng)扔掉了。把閾值調(diào)小或者干脆用示波器量最小邊沿間隔再設(shè)置。第二ERU只配置了單邊沿觸發(fā)。比如只在上沿中斷那A相的下沿就完全丟失高速時(shí)空占比變化會(huì)導(dǎo)致計(jì)數(shù)值不穩(wěn)。軟解碼的“軟”不代表可以少算必須確保配置的是“上升沿下降沿”雙沿觸發(fā)。第三ISR執(zhí)行時(shí)間太長(zhǎng)。比如在ISR里做了浮點(diǎn)運(yùn)算、打印日志、調(diào)用耗時(shí)庫(kù)函數(shù)導(dǎo)致下一個(gè)邊沿到來時(shí)MCU還在處理上一個(gè)邊沿自然就丟中斷了。ISR里只做整數(shù)加減和時(shí)間戳判斷其他全部丟到主循環(huán)。還有一次我遇到的情況比較特殊編碼器信號(hào)線太長(zhǎng)邊沿上疊加了反射振蕩10kHz以下看不到問題一跑到幾十kHz就瘋狂誤觸發(fā)。后來在A、B端各加了一個(gè)100pF電容再配合軟件濾波問題立刻消失。這類問題沒有示波器基本查不出來所以做編碼器調(diào)試示波器是剛需。6. 還能怎么擴(kuò)展硬件方案與TC3xx遷移6.1 引腳允許時(shí)試試GPT12硬件旋轉(zhuǎn)計(jì)數(shù)器如果項(xiàng)目還在方案階段引腳布局沒有定死我會(huì)建議優(yōu)先評(píng)估GPT12的Rotary Counter模式。這是TC264自帶的硬件正交解碼能力配置正確后A、B相方向判斷和計(jì)數(shù)由外設(shè)自動(dòng)完成CPU零負(fù)擔(dān)。iLLD里提供了現(xiàn)成的接口典型思路是把A相接T2INB相接T3IN初始化T2為旋轉(zhuǎn)計(jì)數(shù)器模式使能方向識(shí)別讀計(jì)數(shù)值直接用庫(kù)函數(shù)取回。具體接口名包括IfxGpt12_enableRotaryCounter()和IfxGpt12_getRotaryCounter()這些在AURIX Development Studio的iLLD庫(kù)中都能找到。硬件方案的優(yōu)點(diǎn)是穩(wěn)定性和CPU利用率高缺點(diǎn)是引腳復(fù)用受限。如果軟件方案已經(jīng)跑通硬件方案可以作為性能優(yōu)化選項(xiàng)保留到平臺(tái)遷移時(shí)再應(yīng)用。6.2 從TC2xx遷移到TC3xx要注意什么TC2xx和TC3xx在GPIO、ERU、SRC中斷體系上保持了很高的兼容性軟解碼代碼遷移成本不大。主要注意三點(diǎn)第一時(shí)鐘頻率。TC3xx的STM頻率和TC2xx可能不同ISR里的時(shí)間戳換算分母要重新確認(rèn)。第二中斷節(jié)點(diǎn)映射。TC3xx的中斷源編號(hào)和TC2xx并非完全一致ERU通道對(duì)應(yīng)的SRC枚舉要查最新的IfxScuSrcId定義。第三iLLD版本。AURIX Development Studio升級(jí)后部分庫(kù)函數(shù)簽名和枚舉名可能有調(diào)整直接復(fù)制舊代碼時(shí)編譯器會(huì)提醒這時(shí)對(duì)照當(dāng)前版本的庫(kù)頭文件修改即可。我個(gè)人的體會(huì)是軟解碼的本質(zhì)是“用代碼彌補(bǔ)硬件的靈活性不足”。它雖然在極限性能上不如專用硬件模塊但在中小轉(zhuǎn)速、多路編碼器、引腳復(fù)用受限的工程場(chǎng)景下非常實(shí)用。而且通過親手實(shí)現(xiàn)方向判斷和計(jì)數(shù)邏輯你對(duì)編碼器工作方式的理解會(huì)完全不一樣。如果你也是被硬件復(fù)用問題卡住才點(diǎn)開這篇文章希望這套方案能幫你把時(shí)間省下來留到真正難啃的控制算法上。