動(dòng)28BYJ-48步進(jìn)電機(jī):從相序到定時(shí)器中斷的工程實(shí)踐)
簡(jiǎn)介這份資源面向使用STM32單片機(jī)驅(qū)動(dòng)28BYJ-48步進(jìn)電機(jī)的初學(xué)者與嵌入式開(kāi)發(fā)者提供一套經(jīng)過(guò)實(shí)測(cè)可用的控制代碼支持角度調(diào)節(jié)與正反轉(zhuǎn)控制適合學(xué)習(xí)步進(jìn)電機(jī)驅(qū)動(dòng)原理、定時(shí)器PWM輸出與GPIO時(shí)序控制等基礎(chǔ)實(shí)驗(yàn)場(chǎng)景。壓縮包共120個(gè)文件約2.39MB以31個(gè)c源文件與32個(gè)h頭文件為核心涵蓋stm32f10x_tim、rcc、usart等標(biāo)準(zhǔn)外設(shè)庫(kù)模塊另含Keil工程文件uvprojx、uvoptx、編譯中間文件及hex燒錄文件工程結(jié)構(gòu)完整可直接打開(kāi)編譯與下載驗(yàn)證。該資源已有4316人學(xué)習(xí)下載具備一定的參考基礎(chǔ)。讀者可從中獲得一套可直接運(yùn)行的電機(jī)控制工程模板便于對(duì)照理解步進(jìn)電機(jī)相序驅(qū)動(dòng)、轉(zhuǎn)速與角度參數(shù)調(diào)整方法并在此基礎(chǔ)上快速移植到自己的STM32項(xiàng)目中減少?gòu)牧愦罱üこ痰臅r(shí)間成本。1. 從一顆“抖一下”的電機(jī)說(shuō)起手頭有一塊 STM32 最小系統(tǒng)板一個(gè) 28BYJ-48 步進(jìn)電機(jī)外加一塊 ULN2003 驅(qū)動(dòng)板這是很多人入門(mén)嵌入式控制時(shí)最先湊齊的一套硬件。它便宜、好買(mǎi)、接線簡(jiǎn)單但真正上手就會(huì)發(fā)現(xiàn)電機(jī)要么原地抖兩下不轉(zhuǎn)要么轉(zhuǎn)起來(lái)嗡嗡響、丟步、發(fā)熱甚至把驅(qū)動(dòng)板上的芯片燙得不敢碰。STM32 控制 28BYJ-48 步進(jìn)電機(jī)這件事難點(diǎn)從來(lái)不在“讓電機(jī)轉(zhuǎn)”而在于轉(zhuǎn)得穩(wěn)、轉(zhuǎn)得準(zhǔn)、能停下來(lái)、能換方向、能調(diào)速還能和按鍵、串口、定時(shí)器這些外設(shè)配合起來(lái)。這篇筆記面向的是已經(jīng)拿到源碼、想把它跑起來(lái)或者改成自己項(xiàng)目里一個(gè)執(zhí)行機(jī)構(gòu)的開(kāi)發(fā)者從工程結(jié)構(gòu)、驅(qū)動(dòng)時(shí)序、定時(shí)器中斷到二次開(kāi)發(fā)一層層拆開(kāi)講清楚。2. 項(xiàng)目定位與技術(shù)選型為什么是 STM32 加 ULN2003 這套組合2.1 這個(gè)項(xiàng)目到底在解決什么問(wèn)題28BYJ-48 是一顆四相永磁式減速步進(jìn)電機(jī)內(nèi)部帶減速齒輪輸出軸轉(zhuǎn)速低、扭矩相對(duì)可觀常見(jiàn)于小轉(zhuǎn)臺(tái)、閥門(mén)、云臺(tái)、教學(xué)演示裝置。它的線圈不能直接接單片機(jī) IO因?yàn)槊肯喙ぷ麟娏髟趲资涟布?jí)別四相同時(shí)導(dǎo)通時(shí)總電流會(huì)超過(guò) STM32 單個(gè) IO 的驅(qū)動(dòng)能力所以必須經(jīng)過(guò)驅(qū)動(dòng)級(jí)。ULN2003 是七路達(dá)林頓晶體管陣列每路帶續(xù)流二極管正好用來(lái)做四相線圈的低邊開(kāi)關(guān)這也是市面上最常見(jiàn)的“電機(jī) 驅(qū)動(dòng)板”套裝形態(tài)。項(xiàng)目要解決的核心問(wèn)題可以拆成四件事第一按正確的相序給四相線圈通電讓轉(zhuǎn)子一步步走第二控制每一步之間的時(shí)間間隔從而控制轉(zhuǎn)速第三支持正反轉(zhuǎn)和停止第四把這些動(dòng)作封裝成可復(fù)用的接口方便上層業(yè)務(wù)調(diào)用。STM32 在這里承擔(dān)的是“時(shí)序發(fā)生器 狀態(tài)機(jī)”的角色用 GPIO 輸出相序用定時(shí)器產(chǎn)生節(jié)拍用主循環(huán)或中斷處理按鍵與命令。2.2 技術(shù)棧與硬件連接從工程角度看這套項(xiàng)目涉及的技術(shù)棧并不復(fù)雜但每一層都有講究層次選型作用主控STM32F103 系列常見(jiàn)最小系統(tǒng)板產(chǎn)生相序信號(hào)、處理輸入驅(qū)動(dòng)ULN2003 驅(qū)動(dòng)板電流放大、續(xù)流保護(hù)執(zhí)行器28BYJ-48 四相步進(jìn)電機(jī)位置執(zhí)行開(kāi)發(fā)環(huán)境Keil MDK 或 STM32CubeIDE編譯、下載、調(diào)試底層庫(kù)標(biāo)準(zhǔn)外設(shè)庫(kù)或 HAL 庫(kù)GPIO、TIM 配置接線方式是固定的STM32 的四個(gè) GPIO 分別接 ULN2003 的 IN1 到 IN4驅(qū)動(dòng)板輸出端接電機(jī)五線插頭驅(qū)動(dòng)板 VCC 接 5VGND 與 STM32 共地。這里有一個(gè)容易被忽略的點(diǎn)ULN2003 驅(qū)動(dòng)板的邏輯輸入是 3.3V 兼容的但電機(jī)側(cè)供電必須是 5V如果只給 3.3V電機(jī)扭矩會(huì)明顯不足表現(xiàn)為“能轉(zhuǎn)但帶不動(dòng)負(fù)載”。共地更是血淚經(jīng)驗(yàn)不共地時(shí) IO 電平?jīng)]有參考電機(jī)行為會(huì)變得完全不可預(yù)測(cè)。2.3 相序方案的選擇28BYJ-48 常見(jiàn)有三種驅(qū)動(dòng)方式單四拍、雙四拍、八拍。單四拍每次只通一相步距角大、扭矩小雙四拍每次通兩相扭矩大但步距角同樣較大八拍是單雙交替步距角減半運(yùn)行更平滑是實(shí)際項(xiàng)目里最常用的方案。以 A、B、C、D 表示四相八拍相序?yàn)?A-AB-B-BC-C-CD-D-DA循環(huán)往復(fù)。代碼里通常把它寫(xiě)成一個(gè)二維數(shù)組或者一維編碼表按索引取相。/* 八拍相序表每行對(duì)應(yīng)一次節(jié)拍bit0~bit3 分別對(duì)應(yīng) A/B/C/D 相 */ static const uint8_t phase_table[8] { 0x01, /* A */ 0x03, /* A B */ 0x02, /* B */ 0x06, /* B C */ 0x04, /* C */ 0x0C, /* C D */ 0x08, /* D */ 0x09 /* D A */ };這張表是整個(gè)驅(qū)動(dòng)的核心數(shù)據(jù)。索引遞增即正轉(zhuǎn)遞減即反轉(zhuǎn)取模 8 保證循環(huán)。用位編碼而不是四個(gè)獨(dú)立布爾量好處是寫(xiě)端口時(shí)可以一次性輸出減少相與相之間的切換毛刺。參數(shù)上唯一需要關(guān)心的是表長(zhǎng)必須與節(jié)拍數(shù)一致八拍就是 8如果誤用四拍表卻按 8 取模會(huì)出現(xiàn)相序錯(cuò)亂導(dǎo)致電機(jī)原地抖動(dòng)。3. 工程結(jié)構(gòu)與核心驅(qū)動(dòng)代碼拆解3.1 目錄組織與模塊劃分一個(gè)能跑、能改的工程目錄不應(yīng)該把所有東西堆在 main.c 里。常見(jiàn)的組織方式是按職責(zé)分層底層驅(qū)動(dòng)放 bsp 或 drivers 目錄業(yè)務(wù)邏輯放 app 目錄公共定義放 inc 目錄。以標(biāo)準(zhǔn)外設(shè)庫(kù)工程為例典型結(jié)構(gòu)如下project/ ├── Core/ │ ├── Inc/ │ │ ├── stepper.h /* 電機(jī)接口聲明 */ │ │ └── main.h │ └── Src/ │ ├── main.c /* 初始化與主循環(huán) */ │ ├── stepper.c /* 相序輸出與節(jié)拍控制 */ │ └── stm32f1xx_it.c /* 定時(shí)器中斷入口 */ ├── Drivers/ │ └── ... /* 庫(kù)文件 */ └── MDK-ARM/ 或 STM32CubeIDE/stepper.c 只關(guān)心“怎么讓電機(jī)走一步”main.c 只關(guān)心“什么時(shí)候讓它走、走多少步”。這種劃分在后期加按鍵、加串口命令、加限位開(kāi)關(guān)時(shí)優(yōu)勢(shì)非常明顯不需要?jiǎng)域?qū)動(dòng)層。我一般會(huì)把相序表、當(dāng)前步索引、方向標(biāo)志、剩余步數(shù)都收在一個(gè)結(jié)構(gòu)體里避免全局變量散落。3.2 GPIO 初始化與相序輸出驅(qū)動(dòng)層第一件事是把四個(gè) IO 配成推挽輸出初始電平拉低保證上電瞬間電機(jī)不會(huì)亂動(dòng)。下面以標(biāo)準(zhǔn)外設(shè)庫(kù)為例HAL 庫(kù)寫(xiě)法只是函數(shù)名不同邏輯一致。/* 初始化四相控制 IO默認(rèn)全部輸出低電平 */ void Stepper_Init(void) { GPIO_InitTypeDef gpio; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); gpio.GPIO_Pin GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3; gpio.GPIO_Mode GPIO_Mode_Out_PP; /* 推挽輸出 */ gpio.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(GPIOA, gpio); GPIO_ResetBits(GPIOA, GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3); }這里四個(gè)引腳選在同一端口是為了后面能用一次寫(xiě)操作更新全部相位。如果分散在不同端口輸出時(shí)就要分多次寫(xiě)相與相之間會(huì)產(chǎn)生微秒級(jí)的不同步低速時(shí)看不出來(lái)高速時(shí)會(huì)引起丟步。參數(shù)上 GPIO_Speed 選 50MHz 對(duì)步進(jìn)電機(jī)來(lái)說(shuō)綽綽有余實(shí)際節(jié)拍頻率通常只有幾百赫茲到幾千赫茲。3.3 單步執(zhí)行與方向控制單步函數(shù)負(fù)責(zé)把當(dāng)前索引對(duì)應(yīng)的相位寫(xiě)到端口然后更新索引。方向用一個(gè)有符號(hào)變量表示正轉(zhuǎn)加一反轉(zhuǎn)減一取模時(shí)注意負(fù)數(shù)處理。static int8_t step_index 0; /* 當(dāng)前相序索引 0~7 */ static int8_t direction 1; /* 1 正轉(zhuǎn)-1 反轉(zhuǎn) */ /* 走一步根據(jù)方向和索引輸出對(duì)應(yīng)相位 */ void Stepper_Step(void) { uint8_t phase phase_table[step_index]; /* 一次性寫(xiě)四個(gè)引腳bit0~bit3 對(duì)應(yīng) PA0~PA3 */ GPIO_WriteBit(GPIOA, GPIO_Pin_0, (phase 0x01) ? Bit_SET : Bit_RESET); GPIO_WriteBit(GPIOA, GPIO_Pin_1, (phase 0x02) ? Bit_SET : Bit_RESET); GPIO_WriteBit(GPIOA, GPIO_Pin_2, (phase 0x04) ? Bit_SET : Bit_RESET); GPIO_WriteBit(GPIOA, GPIO_Pin_3, (phase 0x08) ? Bit_SET : Bit_RESET); step_index (step_index direction 8) % 8; }8再取模是為了處理 direction 為 -1 時(shí)索引變成負(fù)數(shù)的情況這是 C 語(yǔ)言取模對(duì)負(fù)數(shù)結(jié)果不直觀的經(jīng)典坑。如果用的是位帶操作或者直接操作 ODR 寄存器可以把四次 WriteBit 合并成一次寫(xiě)效率更高但在 F103 上四次函數(shù)調(diào)用的開(kāi)銷(xiāo)對(duì)步進(jìn)電機(jī)節(jié)拍來(lái)說(shuō)完全可以接受。方向切換時(shí)不要立刻反向輸出最好先停一個(gè)節(jié)拍否則電機(jī)可能因?yàn)閼T性出現(xiàn)失步。3.4 定時(shí)器節(jié)拍讓轉(zhuǎn)速可控用主循環(huán)加延時(shí)來(lái)走步轉(zhuǎn)速會(huì)受其他任務(wù)影響一旦加入按鍵掃描或串口打印電機(jī)就會(huì)一頓一頓。正確做法是用定時(shí)器中斷產(chǎn)生固定節(jié)拍在中斷里調(diào)用 Stepper_Step。以 TIM3 為例配置成 1ms 基準(zhǔn)再在中斷里做軟件分頻就能得到任意步進(jìn)間隔。/* TIM3 配置為 1ms 中斷作為步進(jìn)節(jié)拍基準(zhǔn) */ void Stepper_Timer_Init(uint16_t step_interval_ms) { TIM_TimeBaseInitTypeDef tim; NVIC_InitTypeDef nvic; RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); tim.TIM_Prescaler 7200 - 1; /* 72MHz / 7200 10kHz */ tim.TIM_Period 10 - 1; /* 10kHz / 10 1kHz即 1ms */ tim.TIM_CounterMode TIM_CounterMode_Up; tim.TIM_ClockDivision TIM_CKD_DIV1; TIM_TimeBaseInit(TIM3, tim); TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE); nvic.NVIC_IRQChannel TIM3_IRQn; nvic.NVIC_IRQChannelPreemptionPriority 1; nvic.NVIC_IRQChannelSubPriority 1; nvic.NVIC_IRQChannelCmd ENABLE; NVIC_Init(nvic); TIM_Cmd(TIM3, ENABLE); }中斷服務(wù)函數(shù)里做計(jì)數(shù)分頻計(jì)滿 step_interval_ms 才走一步這樣改轉(zhuǎn)速只需要改一個(gè)變量不用重配定時(shí)器。參數(shù)含義Prescaler 決定計(jì)數(shù)頻率Period 決定溢出周期兩者相乘再除以系統(tǒng)時(shí)鐘就是中斷間隔。72MHz 下 7200 分頻得 10kHz周期計(jì) 10 個(gè)數(shù)溢出一次正好 1ms。如果系統(tǒng)時(shí)鐘不是 72MHz這兩個(gè)值必須同步改否則節(jié)拍全錯(cuò)。volatile uint16_t step_interval 5; /* 每 5ms 走一步 */ static uint16_t tick_count 0; void TIM3_IRQHandler(void) { if (TIM_GetITStatus(TIM3, TIM_IT_Update) ! RESET) { TIM_ClearITPendingBit(TIM3, TIM_IT_Update); if (tick_count step_interval) { tick_count 0; if (steps_remaining 0) { /* 還有步數(shù)要走 */ Stepper_Step(); steps_remaining--; } } } }steps_remaining 是上層設(shè)置的目標(biāo)步數(shù)中斷里遞減到 0 自動(dòng)停這樣“走 N 步”就是一個(gè)原子操作不需要主循環(huán)去輪詢(xún)。注意這個(gè)變量要被主循環(huán)和中斷同時(shí)訪問(wèn)聲明成 volatile 是必須的否則編譯器優(yōu)化后主循環(huán)可能讀到舊值。4. 運(yùn)行與調(diào)試從編譯下載到電機(jī)真正轉(zhuǎn)起來(lái)4.1 編譯環(huán)境與下載流程工程拿到手后先確認(rèn)用的是標(biāo)準(zhǔn)外設(shè)庫(kù)還是 HAL 庫(kù)兩者不能混用。Keil 下打開(kāi)工程文件檢查 Options 里的器件型號(hào)是否與實(shí)際板子一致Debug 頁(yè)選擇 ST-Link 或 DAP-LinkFlash Download 里勾選對(duì)應(yīng)算法。編譯通過(guò)后下載復(fù)位觀察電機(jī)。如果電機(jī)完全不動(dòng)先別懷疑代碼用萬(wàn)用表量驅(qū)動(dòng)板 IN1 到 IN4 在運(yùn)行時(shí)的電平變化有變化說(shuō)明 MCU 側(cè)正常問(wèn)題在驅(qū)動(dòng)或供電沒(méi)變化再回頭查 GPIO 和定時(shí)器。STM32CubeIDE 的流程類(lèi)似只是工程由 CubeMX 生成注意生成代碼時(shí)不要把用戶代碼寫(xiě)在/* USER CODE */標(biāo)記之外否則重新生成會(huì)被覆蓋。這是很多人第一次用 CubeMX 時(shí)踩的坑改了半天發(fā)現(xiàn)一重新生成全沒(méi)了。4.2 上電自檢與最小驗(yàn)證在接入完整業(yè)務(wù)之前先寫(xiě)一個(gè)最小驗(yàn)證上電后電機(jī)正轉(zhuǎn)一圈停 1 秒反轉(zhuǎn)一圈循環(huán)。這個(gè)動(dòng)作能同時(shí)驗(yàn)證相序、方向、節(jié)拍和供電四件事。28BYJ-48 輸出軸轉(zhuǎn)一圈需要的步數(shù)不是 8 的整數(shù)倍因?yàn)閮?nèi)部有減速比常見(jiàn)參數(shù)是減速比約 1:64轉(zhuǎn)子步距角 5.625 度八拍下輸出軸一圈大約需要 4096 步。這個(gè)數(shù)字因批次略有差異實(shí)際項(xiàng)目里我一般用 4096 作為初值再根據(jù)實(shí)際轉(zhuǎn)角微調(diào)。/* 最小驗(yàn)證正轉(zhuǎn)一圈停一秒反轉(zhuǎn)一圈 */ int main(void) { Stepper_Init(); Stepper_Timer_Init(5); while (1) { Stepper_MoveSteps(4096, 1); /* 正轉(zhuǎn)一圈 */ Delay_ms(1000); Stepper_MoveSteps(4096, -1); /* 反轉(zhuǎn)一圈 */ Delay_ms(1000); } }Stepper_MoveSteps 內(nèi)部只做兩件事設(shè)置 direction 和 steps_remaining然后返回實(shí)際走步由中斷完成。這種“設(shè)置目標(biāo) 中斷執(zhí)行”的模式主循環(huán)可以繼續(xù)做別的事不會(huì)被電機(jī)阻塞。Delay_ms 在這里只是演示實(shí)際項(xiàng)目里應(yīng)該用非阻塞的定時(shí)狀態(tài)機(jī)替代。4.3 用串口或按鍵做交互驗(yàn)證電機(jī)能轉(zhuǎn)之后下一步是驗(yàn)證可控性。最省事的辦法是接一個(gè)按鍵做啟停或者用串口發(fā)命令。串口方案調(diào)試信息更豐富可以打印當(dāng)前步數(shù)、方向、間隔方便定位問(wèn)題。命令協(xié)議不用復(fù)雜單字符即可加速-減速f正轉(zhuǎn)b反轉(zhuǎn)s停止。解析放在主循環(huán)收到命令只改參數(shù)不動(dòng)中斷里的執(zhí)行邏輯這樣職責(zé)清晰也不會(huì)因?yàn)榇谥袛鄡?yōu)先級(jí)問(wèn)題打亂節(jié)拍。調(diào)試時(shí)建議把 step_interval 的當(dāng)前值一并打印出來(lái)因?yàn)椤案杏X(jué)變慢了”這種主觀判斷不可靠有數(shù)字才能對(duì)比。另外加速不要一次跨度太大從 10ms 逐步降到 2ms觀察電機(jī)是否出現(xiàn)失步或堵轉(zhuǎn)異響找到這套硬件能穩(wěn)定運(yùn)行的下限這個(gè)下限就是后續(xù)業(yè)務(wù)里轉(zhuǎn)速的上限。5. 避坑與常見(jiàn)問(wèn)題排查5.1 電機(jī)只抖不轉(zhuǎn)現(xiàn)象上電后電機(jī)發(fā)出輕微“噠噠”聲軸不轉(zhuǎn)或只來(lái)回?cái)[。原因通常是相序錯(cuò)誤或接線順序與代碼不一致。ULN2003 板上 IN1 到 IN4 對(duì)應(yīng)電機(jī) A 到 D 相如果插頭插反或者代碼里引腳映射寫(xiě)錯(cuò)相序就亂了。解決辦法是拿萬(wàn)用表逐路確認(rèn)或者寫(xiě)一個(gè)只通單相的程序手動(dòng)依次給 A、B、C、D 通電看電機(jī)是否按固定方向小步轉(zhuǎn)動(dòng)確認(rèn)物理順序后再套八拍表。5.2 轉(zhuǎn)起來(lái)但扭矩不足、帶不動(dòng)負(fù)載現(xiàn)象空載能轉(zhuǎn)一加負(fù)載就?;蛘邅G步。原因多半是供電問(wèn)題驅(qū)動(dòng)板接了 3.3V 而不是 5V或者電源電流不夠。28BYJ-48 四相同時(shí)導(dǎo)通時(shí)電流不小用 USB 口直接供電經(jīng)常不夠。解決辦法是給驅(qū)動(dòng)板單獨(dú)供 5V且與 MCU 共地電源額定電流留足余量。另一個(gè)原因是節(jié)拍太快步進(jìn)電機(jī)有啟動(dòng)頻率限制突然給很高的節(jié)拍會(huì)失步需要加減速過(guò)程。5.3 加入其他功能后電機(jī)一頓一頓現(xiàn)象單獨(dú)跑電機(jī)正常一加按鍵掃描或串口打印轉(zhuǎn)速就變得不均勻。原因是用了主循環(huán)延時(shí)走步其他任務(wù)占用了時(shí)間。解決辦法是把走步搬到定時(shí)器中斷里主循環(huán)只負(fù)責(zé)設(shè)置目標(biāo)這也是前面代碼采用的結(jié)構(gòu)。如果中斷里還有其他耗時(shí)操作要檢查中斷優(yōu)先級(jí)和執(zhí)行時(shí)間步進(jìn)節(jié)拍中斷應(yīng)保持短小。5.4 電機(jī)發(fā)熱嚴(yán)重甚至驅(qū)動(dòng)板燙手現(xiàn)象運(yùn)行一段時(shí)間后電機(jī)外殼明顯發(fā)熱ULN2003 芯片燙手。原因是線圈長(zhǎng)時(shí)間通電尤其是停止時(shí)仍保持某一相導(dǎo)通。步進(jìn)電機(jī)靜態(tài)保持需要通電但如果業(yè)務(wù)允許停止后應(yīng)關(guān)閉所有相位輸出讓電機(jī)自由狀態(tài)減少發(fā)熱。另外檢查是否誤用了雙四拍且占空比過(guò)高八拍方案在同樣轉(zhuǎn)速下發(fā)熱相對(duì)更均勻。驅(qū)動(dòng)板燙手還要確認(rèn)續(xù)流二極管方向正確驅(qū)動(dòng)板本身自帶不要額外亂接。5.5 步數(shù)不準(zhǔn)、累計(jì)誤差大現(xiàn)象讓電機(jī)走 4096 步實(shí)際轉(zhuǎn)角不是一圈多次往返后偏差越來(lái)越大。原因是每圈步數(shù)參數(shù)不準(zhǔn)或者存在丟步。先空載低速跑多次往返看是否回到原點(diǎn)排除丟步再微調(diào)每圈步數(shù)。注意 28BYJ-48 的減速比和步距角存在批次差異4096 只是常用初值精確應(yīng)用需要用外部基準(zhǔn)比如標(biāo)記線實(shí)測(cè)校準(zhǔn)把校準(zhǔn)值寫(xiě)成宏方便不同硬件復(fù)用。6. 二次開(kāi)發(fā)把電機(jī)驅(qū)動(dòng)改成項(xiàng)目里的可靠執(zhí)行模塊拿到這份代碼后真正要做的往往不是讓它轉(zhuǎn)而是把它變成一個(gè)能被業(yè)務(wù)調(diào)用的模塊。我的習(xí)慣是先定義一層清晰的接口把“走多少步、什么方向、什么速度、是否等待完成”這幾個(gè)語(yǔ)義固定下來(lái)底層怎么實(shí)現(xiàn)中斷還是 DMA、哪個(gè)定時(shí)器對(duì)上層透明。接口大致長(zhǎng)這樣/* 業(yè)務(wù)層只依賴(lài)這幾個(gè)函數(shù)不關(guān)心底層定時(shí)器 */ void Stepper_SetSpeed(uint16_t interval_ms); /* 設(shè)置節(jié)拍間隔 */ void Stepper_MoveSteps(int32_t steps); /* 帶符號(hào)步數(shù)正負(fù)即方向 */ void Stepper_Stop(void); /* 立即停止并關(guān)相 */ uint8_t Stepper_IsBusy(void); /* 是否還在走 */Stepper_MoveSteps 接收帶符號(hào)步數(shù)內(nèi)部把符號(hào)轉(zhuǎn)成 direction絕對(duì)值寫(xiě)入 steps_remaining這樣上層不用分別調(diào)正反轉(zhuǎn)。Stepper_IsBusy 讓上層可以做非阻塞等待比如主循環(huán)里判斷忙不忙忙就繼續(xù)處理其他任務(wù)不忙再發(fā)下一條指令。這套接口在加限位開(kāi)關(guān)時(shí)也很好擴(kuò)展中斷里每走一步檢查限位 IO觸發(fā)就清零 steps_remaining 并置一個(gè)到位標(biāo)志。改造時(shí)有兩個(gè)驗(yàn)證方法我每次都會(huì)走一遍。第一是往返一致性測(cè)試正轉(zhuǎn) N 步再反轉(zhuǎn) N 步用標(biāo)記線看是否回到起點(diǎn)偏差超過(guò)預(yù)期就查丟步。第二是變速邊界測(cè)試從慢到快逐步縮短間隔記錄第一次出現(xiàn)失步的間隔值把這個(gè)值乘以安全系數(shù)作為業(yè)務(wù)里的最快速度。這兩個(gè)測(cè)試不需要額外硬件一支記號(hào)筆加一張紙就能做但能擋掉大部分后期“偶發(fā)不準(zhǔn)”的問(wèn)題。還有一個(gè)容易被忽略的改造點(diǎn)多電機(jī)協(xié)同。如果項(xiàng)目里有兩個(gè)以上 28BYJ-48共用一個(gè)定時(shí)器做節(jié)拍、在中斷里輪詢(xún)每個(gè)電機(jī)的剩余步數(shù)比每個(gè)電機(jī)開(kāi)一個(gè)定時(shí)器更省資源也更容易做同步啟停。代價(jià)是中斷執(zhí)行時(shí)間變長(zhǎng)需要控制單次中斷里的工作量必要時(shí)把相序輸出做成查表加端口批量寫(xiě)。從那以后我每次把這類(lèi)電機(jī)接進(jìn)新項(xiàng)目都強(qiáng)制先跑一遍單相手動(dòng)測(cè)試和往返一致性測(cè)試再談業(yè)務(wù)邏輯。硬件這東西玄學(xué)往往只是沒(méi)量到的那根線。希望幫到你。本文還有配套的精品資源點(diǎn)擊獲取