??零知IDE 是一個真正屬于國人自己的開源軟件平臺,在開發效率上超越了Arduino平臺并且更加容易上手,大大降低了開發難度。零知開源在軟件方面提供了完整的學習教程和豐富示例代碼,讓不懂程序的工程師也能非常輕而易舉的搭建電路來創作產品,測試產品。快來動手試試吧!
?訪問零知實驗室,獲取更多實戰項目和教程資源吧!
www.lingzhilab.com
目錄
一、硬件系統設計
1.1 器件清單
1.2 接線方案
1.3 硬件連接圖
1.4 實物接線圖
二、軟件架構設計
2.1 庫文件及初始化
2.2 電壓校準算法
2.3 狀態機按鍵處理
2.4 電位器模擬值處理
2.5 完整代碼
三、操作過程及展示
3.1 系統連接
3.2 校準測試
3.3?功能驗證
四、控制系統模塊詳解
4.1?ULN2003AN驅動芯片
4.2?28BYJ-48步進電機
五、常見問題解答
Q1: 電位器控制不線性如何調整?
Q2: 顯示屏顯示異常怎么辦?
Q3: 按鍵無響應如何解決?
Q4: 電機振動大且噪音明顯?
(1)項目概述
????????本項目基于STM32F407VET6零知標準板開發了一套完整的步進電機智能控制系統,通過電位器實現精確的速度控制,按鍵控制方向切換,并配備1.3寸TFT顯示屏實時顯示系統狀態。系統采用ULN2003AN驅動芯片控制28BYJ-48步進電機,實現了平滑的速度調節和精準的位置控制。
(2)項目亮點
? ? ? ? >自動識別電位器有效電壓范圍(3.4V-4.0V),實現線性速度映射
? ? ? ? >采用狀態機設計,確保按鍵響應穩定可靠
? ? ? ? >1.3寸TFT顯示屏提供豐富的狀態信息顯示
? ? ? ? >指數曲線速度映射算法,低速控制更精準
(3)項目難點及解決方案
? ? ? ? 問題描述:電位器在3.4-4.0V范圍內電機才轉動
解決方案:
????????實現電壓范圍重新映射算法,通過校準參數自適應不同電位器特性
? ? ? ? 問題描述:28BYJ-48步進電機在低速時扭矩不足
解決方案:
????????設置最小啟動速度(MIN_SPEED),并采用指數曲線速度映射
一、硬件系統設計
1.1 器件清單
規格型號 | 數量 | 備注 |
---|---|---|
零知增強板 | 1 | STM32F407VET6主控芯片 |
28BYJ-48 | 1 | 5V減速步進電機 |
ULN2003AN驅動板 | 1 | 達林頓管陣列驅動 |
1.3寸TFT LCD (ST7789) | 1 | 240×240分辨率 |
10KΩ旋轉電位器 | 1 | 線性電位器 |
6×6mm輕觸開關 | 1 | 常開型 |
杜邦線 | 若干 | 母對母、母對公 |
1.2 接線方案
根據代碼定義進行以下接線操作、如需定義為其他引腳請自行修改代碼:
(1)電機驅動部分接線
零知增強板引腳 | ULN2003AN驅動板 | 28BYJ-48步進電機 | 功能 |
---|---|---|---|
8 | IN1 | / | 電機控制 |
9 | IN2 | / | 電機控制 |
10 | IN3 | / | 電機控制 |
11 | IN4 | / | 電機控制 |
/ | 排線連接 | 排線連接 | 電機驅動 |
5V(外部) | + | / | 電機電源 |
GND | - | / | 電機地線 |
(2)ST7789顯示屏接線
零知增強板引腳 | ST7789引腳 | 功能 |
---|---|---|
3.3V | VCC | 電源 |
GND | GND | 地 |
7 | CS | 片選 |
6 | DC | 數據/命令控制 |
5 | RES | 復位 |
12 | SDA | SPI數據線 |
13 | SCL | SPI時鐘線 |
(3)電機操作部分接線
零知增強板引腳 | 電位器 | 按鍵 | 功能 |
---|---|---|---|
5V | 左側引腳 | / | 供電 |
GND | 右側引腳 | / | 地線 |
A0 | 中間引腳 | / | 信號輸入 |
2 | / | 高電平一端 | 信號輸入,內部上拉 |
? ? ? ? ps:電機需要消耗大量電力,最好直接從外部 5V 電源供電
1.3 硬件連接圖
1.4 實物接線圖
二、軟件架構設計
2.1 庫文件及初始化
// 主要庫文件引入
#include <AccelStepper.h> // 步進電機控制庫
#include <Adafruit_GFX.h> // 圖形顯示庫
#include <Adafruit_ST7789.h> // ST7789顯示屏驅動
#include <SPI.h> // SPI通信庫#define TFT_CS 7 // 顯示屏片選
#define TFT_SCL 13 // 顯示屏時鐘
#define TFT_SDA 12 // 顯示屏數據
#define TFT_DC 6 // 顯示屏數據/命令
#define TFT_RST 5 // 顯示屏復位
#define BUTTON_PIN 2 // 按鍵引腳(內部上拉)
#define POT_PIN A0 // 電位器模擬輸入
2.2 電壓校準算法
// 電壓校準參數 - 關鍵創新點
#define POT_MIN_VOLTAGE 3.4 // 電機啟動最小電壓
#define POT_MAX_VOLTAGE 4.0 // 電機最大速度電壓// 計算對應的ADC值范圍
#define POT_MIN_ADC (int)((POT_MIN_VOLTAGE / 5.0) * 1023)
#define POT_MAX_ADC (int)((POT_MAX_VOLTAGE / 5.0) * 1023)// 速度映射函數
if (potValue <= POT_MIN_ADC) {targetSpeed = 0; // 低于最小電壓停止
} else if (potValue >= POT_MAX_ADC) {targetSpeed = MAX_SPEED; // 高于最大電壓全速
} else {// 線性映射計算targetSpeed = map(potValue, POT_MIN_ADC, POT_MAX_ADC, MIN_SPEED, MAX_SPEED);
}
2.3 狀態機按鍵處理
// 按鍵狀態枚舉
enum ButtonState { IDLE, PRESSED, DEBOUNCING };
ButtonState buttonState = IDLE;void handleButton() {int buttonReading = digitalRead(BUTTON_PIN);switch (buttonState) {case IDLE:if (buttonReading == LOW) {buttonState = DEBOUNCING;lastButtonTime = millis();}break;// ... 狀態處理邏輯}
}
2.4 電位器模擬值處理
void handlePotentiometer() {// 讀取電位器值int potValue = analogRead(POT_PIN);// 只有當電位器值顯著變化時才處理if (abs(potValue - lastPotValue) > 3) {lastPotValue = potValue;if (DEBUG_MODE && millis() % 500 < 10) {float voltage = (float)potValue / 1023.0 * ADC_REF_VOLTAGE;Serial.print("Pot ADC: ");Serial.print(potValue);Serial.print(" | Voltage: ");Serial.print(voltage);Serial.print("V");}// 重新映射電位器值到速度范圍if (potValue <= POT_MIN_ADC) {// 低于最小電壓,電機停止targetSpeed = 0;} else if (potValue >= POT_MAX_ADC) {// 高于最大電壓,電機全速運行targetSpeed = MAX_SPEED;} else {// 在有效范圍內線性映射targetSpeed = map(potValue, POT_MIN_ADC, POT_MAX_ADC, MIN_SPEED, MAX_SPEED);}if (DEBUG_MODE && millis() % 500 < 10) {Serial.print(" | Target Speed: ");Serial.println(targetSpeed);}// 平滑調整當前速度到目標速度if (abs(targetSpeed - currentSpeed) > SPEED_RAMP_RATE) {if (targetSpeed > currentSpeed) {currentSpeed += SPEED_RAMP_RATE;} else {currentSpeed -= SPEED_RAMP_RATE;}} else {currentSpeed = targetSpeed;}// 更新速度顯示updateSpeedDisplay();}
}
2.5 完整代碼
// Include the necessary libraries
#include <AccelStepper.h>
#include <Adafruit_GFX.h>
#include <Adafruit_ST7789.h>
#include <SPI.h>// Define step constant
#define MotorInterfaceType 8// Display pins
#define TFT_CS 7
#define TFT_SCL 13
#define TFT_SDA 12
#define TFT_DC 6
#define TFT_RST 5// Button pin
#define BUTTON_PIN 2// Potentiometer pin
#define POT_PIN A0// Creates motor instance
AccelStepper myStepper(MotorInterfaceType, 8, 10, 9, 11);// Creates display instance with updated pin configuration
Adafruit_ST7789 tft = Adafruit_ST7789(TFT_CS, TFT_DC, TFT_SDA, TFT_SCL, TFT_RST);// Variables for control
int motorDirection = 1; // 1 for forward, -1 for reverse
int currentSpeed = 0;
int targetSpeed = 0;
bool motorRunning = false;
int lastPotValue = 0;// Button state machine variables
enum ButtonState { IDLE, PRESSED, DEBOUNCING };
ButtonState buttonState = IDLE;
unsigned long lastButtonTime = 0;
#define DEBOUNCE_DELAY 50// UI layout constants
#define TITLE_Y 15
#define DIRECTION_Y 60
#define SPEED_Y 90
#define POSITION_Y 120
#define STATE_Y 150
#define INFO_Y 180
#define VALUE_X 130// Speed control parameters
#define MIN_SPEED 0 // 最小速度
#define MAX_SPEED 800 // 最大速度
#define SPEED_RAMP_RATE 10 // 速度變化率// 電位器校準參數 - 根據您的測量結果調整這些值
#define POT_MIN_VOLTAGE 3.4 // 電機開始轉動的最小電壓
#define POT_MAX_VOLTAGE 4.0 // 電機達到最大速度的電壓
#define ADC_REF_VOLTAGE 5.0 // ADC參考電壓// 計算對應的模擬值范圍
#define POT_MIN_ADC (int)((POT_MIN_VOLTAGE / ADC_REF_VOLTAGE) * 1023)
#define POT_MAX_ADC (int)((POT_MAX_VOLTAGE / ADC_REF_VOLTAGE) * 1023)// 調試模式
#define DEBUG_MODE truevoid setup() {// 初始化串口(用于調試)if (DEBUG_MODE) {Serial.begin(115200);Serial.println("System initialized");Serial.print("POT_MIN_ADC: ");Serial.println(POT_MIN_ADC);Serial.print("POT_MAX_ADC: ");Serial.println(POT_MAX_ADC);}// 初始化顯示屏tft.init(240, 240);tft.setRotation(1);tft.fillScreen(ST77XX_BLACK);// 初始化按鈕pinMode(BUTTON_PIN, INPUT_PULLUP);// 初始化電機myStepper.setMaxSpeed(MAX_SPEED);myStepper.setAcceleration(500.0);myStepper.setSpeed(0);// 繪制UIdrawUI();
}void loop() {// 處理按鈕狀態機handleButton();// 讀取電位器值并設置速度handlePotentiometer();// 運行電機runMotor();// 定期更新顯示static unsigned long lastUpdate = 0;if (millis() - lastUpdate > 100) {updatePositionDisplay();updateStateDisplay();lastUpdate = millis();}
}void handleButton() {int buttonReading = digitalRead(BUTTON_PIN);switch (buttonState) {case IDLE:if (buttonReading == LOW) {buttonState = DEBOUNCING;lastButtonTime = millis();}break;case DEBOUNCING:if (millis() - lastButtonTime > DEBOUNCE_DELAY) {if (digitalRead(BUTTON_PIN) == LOW) {buttonState = PRESSED;// 按鈕動作 - 改變方向motorDirection = -motorDirection;updateDirectionDisplay();if (DEBUG_MODE) {Serial.print("Direction changed to: ");Serial.println(motorDirection > 0 ? "FORWARD" : "REVERSE");}} else {buttonState = IDLE;}}break;case PRESSED:if (buttonReading == HIGH) {buttonState = IDLE;}break;}
}void handlePotentiometer() {// 讀取電位器值int potValue = analogRead(POT_PIN);// 只有當電位器值顯著變化時才處理if (abs(potValue - lastPotValue) > 3) {lastPotValue = potValue;if (DEBUG_MODE && millis() % 500 < 10) {float voltage = (float)potValue / 1023.0 * ADC_REF_VOLTAGE;Serial.print("Pot ADC: ");Serial.print(potValue);Serial.print(" | Voltage: ");Serial.print(voltage);Serial.print("V");}// 重新映射電位器值到速度范圍if (potValue <= POT_MIN_ADC) {// 低于最小電壓,電機停止targetSpeed = 0;} else if (potValue >= POT_MAX_ADC) {// 高于最大電壓,電機全速運行targetSpeed = MAX_SPEED;} else {// 在有效范圍內線性映射targetSpeed = map(potValue, POT_MIN_ADC, POT_MAX_ADC, MIN_SPEED, MAX_SPEED);}if (DEBUG_MODE && millis() % 500 < 10) {Serial.print(" | Target Speed: ");Serial.println(targetSpeed);}// 平滑調整當前速度到目標速度if (abs(targetSpeed - currentSpeed) > SPEED_RAMP_RATE) {if (targetSpeed > currentSpeed) {currentSpeed += SPEED_RAMP_RATE;} else {currentSpeed -= SPEED_RAMP_RATE;}} else {currentSpeed = targetSpeed;}// 更新速度顯示updateSpeedDisplay();}
}void runMotor() {// 設置電機速度和方向int effectiveSpeed = motorDirection * currentSpeed;myStepper.setSpeed(effectiveSpeed);// 運行電機myStepper.runSpeed();motorRunning = (currentSpeed > 0);
}void drawUI() {// 清屏tft.fillScreen(ST77XX_BLACK);// 繪制帶裝飾線的標題tft.drawFastHLine(0, 30, 240, ST77XX_WHITE);tft.setCursor(40, TITLE_Y);tft.setTextSize(3);tft.setTextColor(ST77XX_YELLOW);tft.println("MOTOR CTRL");// 繪制標簽tft.setTextSize(2);tft.setTextColor(ST77XX_WHITE);tft.setCursor(20, DIRECTION_Y);tft.println("Direction:");tft.setCursor(20, SPEED_Y);tft.println("Speed:");tft.setCursor(20, POSITION_Y);tft.println("Position:");tft.setCursor(20, STATE_Y);tft.println("Status:");// 繪制校準信息tft.setTextSize(1);tft.setCursor(10, INFO_Y);tft.print("Calib: ");tft.print(POT_MIN_VOLTAGE, 1);tft.print("V-");tft.print(POT_MAX_VOLTAGE, 1);tft.print("V (");tft.print(POT_MIN_ADC);tft.print("-");tft.print(POT_MAX_ADC);tft.print(")");// 繪制控制說明tft.setCursor(10, INFO_Y + 15);tft.println("Button: Direction | Pot: Speed");// 繪制初始值updateDirectionDisplay();updateSpeedDisplay();updatePositionDisplay();updateStateDisplay();
}void updateDirectionDisplay() {tft.fillRect(VALUE_X, DIRECTION_Y, 100, 20, ST77XX_BLACK);tft.setCursor(VALUE_X, DIRECTION_Y);tft.setTextSize(2);if (motorDirection > 0) {tft.setTextColor(ST77XX_GREEN);tft.print("FORWARD");} else {tft.setTextColor(ST77XX_RED);tft.print("REVERSE");}
}void updateSpeedDisplay() {tft.fillRect(VALUE_X, SPEED_Y, 100, 20, ST77XX_BLACK);tft.setCursor(VALUE_X, SPEED_Y);tft.setTextSize(2);tft.setTextColor(ST77XX_CYAN);tft.print(currentSpeed);tft.print(" step/s");
}void updatePositionDisplay() {tft.fillRect(VALUE_X, POSITION_Y, 100, 20, ST77XX_BLACK);tft.setCursor(VALUE_X, POSITION_Y);tft.setTextSize(2);tft.setTextColor(ST77XX_WHITE);tft.print(myStepper.currentPosition());
}void updateStateDisplay() {tft.fillRect(VALUE_X, STATE_Y, 100, 20, ST77XX_BLACK);tft.setCursor(VALUE_X, STATE_Y);tft.setTextSize(2);if (motorRunning) {tft.setTextColor(ST77XX_GREEN);tft.print("RUNNING");} else {tft.setTextColor(ST77XX_RED);tft.print("STOPPED");}
}
? ? ? ? 如圖所示,步進電機控制系統的工作流程圖
三、操作過程及展示
3.1 系統連接
????????將ULN2003AN驅動板接入5~12V外部電源,連接28BYJ-48電機到驅動板輸出端,按照接線表連接零知增強板與各模塊,確保所有電源正確連接后上電
3.2 校準測試
(1)上傳代碼到零知IDE的零知增強板
(2)旋轉電位器,觀察電機響應
(3)根據實際電壓范圍調整校準參數
(4)測試按鍵功能,確認方向切換正常
3.3 視頻演示驗證
ULN2003AN驅動28BYJ-48步進電機控制系統
????????緩慢旋轉電位器,觀察速度線性增加。按下按鍵,驗證方向切換功能。電位器逆時針旋轉到底,確認電機停止
四、控制系統模塊詳解
4.1?ULN2003AN驅動芯片
(1)功能說明
????????ULN2003AN是高壓大電流達林頓晶體管陣列,包含7個NPN達林頓對管,每個可驅動500mA和50V負載。在本項目中用于驅動28BYJ-48步進電機的四相繞組。
????????該板具有四個控制輸入接口和一個電源接口,還配備了一個與電機連接器相兼容的 Molex 連接器,可直接插入電機。同時,板上設有四個 LED,用于顯示四條控制輸入線上的活動狀態,并且帶有一個開 / 關跳線,以便在需要時禁用步進電機。
(2)工作流程
????????接收MCU發出的控制信號、通過達林頓管放大電流、驅動步進電機各相繞組、提供反向電動勢保護二極管
4.2?28BYJ-48步進電機
(1)工作原理
????????基于齒輪與電磁鐵的協同作用,通過一次推動輪子一個 “步” 來實現運動
????????向線圈發送高脈沖時,線圈會通電,進而吸引最靠近齒輪的齒,使電機以精確且固定的角度增量(即步長)旋轉。其 360 度旋轉的步數取決于齒輪上的齒數
(2)引腳排列
????????28BYJ-48 步進電機有五根線。引腳排列如下:
????????28BYJ-48 包含兩個線圈,每個線圈都設有一個中心抽頭,這兩個中心抽頭在電機內部相連,并通過紅線引出。
????????而線圈的一端與中心抽頭共同構成一相,所以 28BYJ-48 總共有四相:
????????紅線始終拉高,因此當另一根引線拉低時,該相通電。
????????僅當各相按稱為步進順序的邏輯順序通電時,步進電機才會旋轉。
(3)齒輪減速比
????????依據數據表,28BYJ-48 步進電機在全步模式下運行時,每一步對應的旋轉角度為 11.25°,由此可算出每完成 360° 旋轉需要 32 步(360°÷11.25°=32)
????????該電機配備了 1/64 的減速齒輪組(實際減速比為 1/63.68395,不過在多數情況下,1/64 的近似值已能滿足需求)。
????????這也就意味著,電機實際完成一圈旋轉需要約 2038 步(32 步 / 轉 ×63.68395≈2037.8864 步,近似為 2038 步)。
(4)能量消耗
????????28BYJ-48 步進電機的典型電流消耗約為 240mA。
????????由于其耗電量較大,因此建議直接采用外部 5V 電源供電,而非通過 零知增強板供電。
????????需要注意的是,即便處于靜止狀態,該電機也會消耗功率以維持當前位置。
(5)技術規格
工作電壓 | 5VDC |
工作電流 | 240mA(典型值) |
相數 | 4 |
齒輪減速比 | 64:1 |
步距角 | 5.625°/64 |
頻率 | 100赫茲 |
牽引扭矩 | >34.3mN.m(120Hz) |
自定位扭矩 | >34.3mN.m |
摩擦力矩 | 600-1200 克力·厘米 |
拉入扭矩 | 300 克力.厘米 |
五、常見問題解答
Q1: 電位器控制不線性如何調整?
????????A:修改代碼中的
POT_MIN_VOLTAGE
和POT_MAX_VOLTAGE
參數,匹配實際電壓范圍。
Q2: 顯示屏顯示異常怎么辦?
????????A:檢查SPI接線是否正確,確認CS、DC、RST引腳定義與代碼一致。
Q3: 按鍵無響應如何解決?
????????A:確認按鍵是否正確連接到GND和引腳2,檢查內部上拉電阻是否啟用。
Q4: 電機振動大且噪音明顯?
????????A:降低MAX_SPEED值,或增加加速度參數,使速度變化更平滑。
項目資源:
? ? ? ? 步進電機數據表:28BYJ-48 數據表
? ? ? ? 庫文件依賴:AccelStepper 庫
????????本項目成功實現了一套基于零知標準板的智能步進電機控制系統,希望能為您的嵌入式學習和開發提供有價值的參考!