MSPM0开发学习笔记:D36A驱动的42步进电机二维云台(2025电赛 附源代码及引脚配置)
2025 电赛二维云台:MSPM0G3507 + D36A 驱动双 42 步进电机,含接线表、Keil/逐飞库 C 代码(角度转脉冲、单轴/双轴转动)及细分与减速比修正说明。
前言
今年的电赛(2025),很多题都与云台相关,因此为备战电赛,博主这边也是准备了一个由两个42步进电机驱动的云台并提前进行调试,避免赛题出来之后手忙脚乱的,这边的两个42步进电机采用同一个驱动模块进行驱动(D36A),主控肯定采用MSPM0G3507。然后3D打印了一个二维云台的结构并进行组装。本章博客主要是让这个云台动起来。
如果无法很好的复现博客里的代码,可以私信作者博取源代码,电赛期间都在线
一、硬件选择
主控:MSPM0G3507
驱动:D36A双路步进电机驱动
电机:42步进电机*2
二、硬件接线
接线顺序:MSPM0G3507——驱动用户控制信号输入口——驱动电机接口——步进电机
1、MSPM0G3507
由于只是实现较为简单的测试功能,再加上与队友分开调不同的模块,所以这边并没有用学习板或者拓展版,只是简单的用核心板进行接线,关于D36A驱动,我们准备需要六个GPIO口,两个GND以及一个5V
2、D36A双路驱动模块接口
接下来我们看D36A双路驱动模块的的结构

以及用户控制信号的各个引脚的说明

在控制42步进电机的过程中我们只需要用到其中9个引脚就好,分别是5V、两个GND、ST1、DIR1以及EN1,ST2、DIR2以及EN2,后面六个分别是用来控制电机接口A与电机接口B
3、42步进电机接口

步进电机只需要接四个口既可(虽然他的接口是6P),由于电机和驱动是一起买的,所以商家是直接提供了6P转4P的线给我们,直接接到驱动上就好了
4、接线
主控—驱动
在连接主控与驱动时候,一般不建议直接用主控对驱动进行供电,需要另外的电源模块来对驱动供电。因此将5V与地线接在特定的电源模块上既可。
同时需要将ST1、DIR1以及EN1,ST2、DIR2以及EN2分别接到树莓派上的三个不同GPIO口,并记住对应的GPIO编号。
本次作者使用的对应接口如下(后续在程序中有用)
| 驱动的接口 | 树莓派接口 |
|---|---|
| ST1 | B12 |
| DIR1 | B9 |
| EN1 | B8 |
| ST1 | B11 |
| DIR1 | B10 |
| EN1 | B13 |
驱动—电机
需要将AC、BD分别接在驱动上一个象的±端,例如作者将A接在A+、C接在A-、B接B+、D接B-。如下图所示:(接线可以采用杜邦线进行连接,也可购买6P转4P的线,不过购买时需注意是否正确对应)

5、供电
供电这边注意最好给驱动12V的电,防止步进电机没力(D36A那边说的是7-12V)
三、软件部分
软件部分采用C语言实现,IDE采用keil,基于逐飞库进行编写
代码实现如下:
函数一:用于将角度转为步进电机的所需转动的步数
STEPS_PER_REVOLUTION 和REDUCTION_RATIO 取决于电机的型号,42步进电机的话一般采用这个。(这个函数作者发现有一点问题,最后的输出结果乘于16.5才是合适的一个结果,后面会说到原因和实现)
// Stepper motor parameters
#define STEPS_PER_REVOLUTION 200 // The number of pulses per turn of the motor
#define REDUCTION_RATIO 1 // reduction ratio (no-------1)
// Calculate the total number of pulses
static uint32 angle_to_steps(float angle) {
// Number of angular pulses: Number of pulses = (angle / 360) * number of step angle pulses * reduction ratio
return (uint32)(fabs(angle) / 360.0f * STEPS_PER_REVOLUTION * REDUCTION_RATIO);
}
函数二:D36A相关初始化
主要是对gpio的初始化以及两个EN引脚的使能(这边的GPO一定要注意,因为之前没有用过逐飞库,一开始设置成GPI了,排查了很久没有排查出问题)
// Initialize the D36A driver
void d36a_init(void) {
// Configure the control pins
gpio_init(D36A_IN1_PIN, GPO, 0, GPO_PUSH_PULL);
gpio_init(D36A_IN2_PIN, GPO, 0, GPO_PUSH_PULL);
gpio_init(D36A_IN3_PIN, GPO, 0, GPO_PUSH_PULL);
gpio_init(D36A_IN4_PIN, GPO, 0, GPO_PUSH_PULL);
gpio_init(D36A_ENA_PIN, GPO, 0, GPO_PUSH_PULL);
gpio_init(D36A_ENB_PIN, GPO, 0, GPO_PUSH_PULL);
// Enable motor
gpio_set_level(D36A_ENA_PIN,1);
gpio_set_level(D36A_ENB_PIN,1);
}
函数三:设置单个电机以某个速度转过某个角度
motor是电机编号,D36A_MOTOR_A或者D36A_MOTOR_B,需要在.h文件中进行定义,然后根据输入的角度判断DIR的方向,最后阻塞式对ST引脚输出脉冲,speed决定脉冲的延迟时间(步进电机是收到一个脉冲转过一步)。speed越大速度越慢
// Control the motor to rotate at a specified angle
void d36a_set_angle(d36a_motor_enum motor, float angle, uint16 speed) {
uint32 steps = angle_to_steps(angle);
gpio_pin_enum pulse_pin, dir_pin;
// Select the pulse and direction pins corresponding to the motor
if (motor == D36A_MOTOR_A) {
pulse_pin = D36A_IN1_PIN;
dir_pin = D36A_IN2_PIN;
} else {
pulse_pin = D36A_IN3_PIN;
dir_pin = D36A_IN4_PIN;
}
// Set the direction (positive angle forward rotation, negative angle reverse)
if (angle > 0) {
gpio_set_level(dir_pin,1);
} else {
gpio_set_level(dir_pin,0);
}
// Output pulse sequence
for (uint32 i = 0; i < steps; i++) {
gpio_set_level(pulse_pin,1);
system_delay_us(speed); // Pulse high level time
gpio_set_level(pulse_pin,0);
system_delay_us(speed); // Pulse low level time
}
}
函数四:设置AB两个电机以某个速度同时转过某个角度(角度可以不一样)
这边的angle_a和angle_b分别对应需要两个电机转过的角度,speed决定速度(AB速度一致),由于这边要一起延时来输出脉冲控制,所以简单实现的时候速度设置成一样,后续可以优化一下,例如10us 10us的延时,累积AB延时的时间,超过Speed的时候对应的累积值清零,然后翻转一下电平。
void d36a_set_angle_both(float angle_a, float angle_b, uint16 speed) {
uint32 steps_a = angle_to_steps(angle_a);
uint32 steps_b = angle_to_steps(angle_b);
uint32 max_steps = steps_a > steps_b ? steps_a : steps_b;
gpio_set_level(D36A_IN2_PIN, angle_a > 0 ? 1 : 0);
gpio_set_level(D36A_IN4_PIN, angle_b > 0 ? 1 : 0);
for (uint32 i = 0; i < max_steps; i++) {
if (i < steps_a) {
gpio_set_level(D36A_IN1_PIN, 1);
}
if (i < steps_b) {
gpio_set_level(D36A_IN3_PIN, 1);
}
system_delay_us(speed);
if (i < steps_a) {
gpio_set_level(D36A_IN1_PIN, 0);
}
if (i < steps_b) {
gpio_set_level(D36A_IN3_PIN, 0);
}
system_delay_us(speed);
}
}
函数五:将误差值调整成对应的误差角度
这是一个简单的转换函数,需要math库,具体的参数值需要根据自己的电机系统来调整
int output_to_servo(float output_x)
{
// 1. 计算atan2的第一个参数:output_x * 1.8 / 66
float numerator = output_x * 1.8f / 66.0f;
// 2. 计算反正切(atan2(对边, 邻边)),结果为弧度
float radian = atan2f(numerator, 15.0f); // 第二个参数固定为15(与Python一致)
// 3. 将弧度转换为角度(乘以180/π),并转换为整数
int servo_dx = (int)(radian * 180.0f / 3.1415926f); // 用3.1415926提高精度
return servo_dx;
}
主函数:调用刚才写到的函数
int main (void)
{
clock_init(SYSTEM_CLOCK_80M); // 时钟配置及系统初始化<务必保留>
d36a_init();
while(true)
{
d36a_set_angle_both(300,300,800);
d36a_set_angle(D36A_MOTOR_A,-300,800);
d36a_set_angle(D36A_MOTOR_B,-300,800);
}
}
如果无法很好的复现博客里的代码,可以私信作者博取源代码,电赛期间都在线
四、补充
角度转步数函数修正说明
前文提到角度转步数函数需要乘以 16.5 才能得到正确结果,这是因为实际应用中存在两个容易被忽略的参数:
驱动细分设置:D36A 默认可能采用 16 细分(通过拨码开关设置),即每圈实际需要 200×16=3200 步
机械减速比:部分云台结构自带减速齿轮(如 1:10),需要乘以对应减速比
修正后的函数应考虑这些实际参数:
// 修正后的角度转步数函数
static uint32 angle_to_steps(float angle) {
#define MICROSTEP 16 // 细分设置
#define GEAR_RATIO 10 // 减速比
return (uint32)(fabs(angle) / 360.0f * STEPS_PER_REVOLUTION * MICROSTEP * GEAR_RATIO);
}
建议通过实际测试校准:让电机转动 360 度,记录所需实际步数,反推准确参数。
MSPM0開發學習筆記:D36A驅動的42步進電機二維雲臺(2025電賽 附源代碼及引腳配置)
2025 電賽二維雲台:MSPM0G3507 + D36A 驅動雙 42 步進電機,含接線表、Keil/逐飛庫 C 程式(角度轉脈衝、單軸/雙軸轉動)及細分與減速比修正說明。
來源:https://blog.csdn.net/2403_87969572/article/details/149764685
抓取時間(ISO本地):2026-05-18 05:17:01
文章目錄
前言
今年的電賽(2025),很多題都與雲臺相關,因此為備戰電賽,博主這邊也是準備了一個由兩個42步進電機驅動的雲臺並提前進行調試,避免賽題出來之後手忙腳亂的,這邊的兩個42步進電機採用同一個驅動模塊進行驅動(D36A),主控肯定採用MSPM0G3507。然後3D打印了一個二維雲臺的結構並進行組裝。本章博客主要是讓這個雲臺動起來。
如果無法很好的復現博客裡的代碼,可以私信作者博取源代碼,電賽期間都在線
一、硬件選擇
主控:MSPM0G3507
驅動:D36A雙路步進電機驅動
電機:42步進電機*2
二、硬件接線
接線順序:MSPM0G3507——驅動用戶控制信號輸入口——驅動電機接口——步進電機
1、MSPM0G3507
由於只是實現較為簡單的測試功能,再加上與隊友分開調不同的模塊,所以這邊並沒有用學習板或者拓展版,只是簡單的用核心板進行接線,關於D36A驅動,我們準備需要六個GPIO口,兩個GND以及一個5V
2、D36A雙路驅動模塊接口
接下來我們看D36A雙路驅動模塊的的結構

以及用戶控制信號的各個引腳的說明

在控制42步進電機的過程中我們只需要用到其中9個引腳就好,分別是5V、兩個GND、ST1、DIR1以及EN1,ST2、DIR2以及EN2,後面六個分別是用來控制電機接口A與電機接口B
3、42步進電機接口

步進電機只需要接四個口既可(雖然他的接口是6P),由於電機和驅動是一起買的,所以商家是直接提供了6P轉4P的線給我們,直接接到驅動上就好了
4、接線
主控—驅動
在連接主控與驅動時候,一般不建議直接用主控對驅動進行供電,需要另外的電源模塊來對驅動供電。因此將5V與地線接在特定的電源模塊上既可。
同時需要將ST1、DIR1以及EN1,ST2、DIR2以及EN2分別接到樹莓派上的三個不同GPIO口,並記住對應的GPIO編號。
本次作者使用的對應接口如下(後續在程序中有用)
| 驅動的接口 | 樹莓派接口 |
|---|---|
| ST1 | B12 |
| DIR1 | B9 |
| EN1 | B8 |
| ST1 | B11 |
| DIR1 | B10 |
| EN1 | B13 |
驅動—電機
需要將AC、BD分別接在驅動上一個象的±端,例如作者將A接在A+、C接在A-、B接B+、D接B-。如下圖所示:(接線可以採用杜邦線進行連接,也可購買6P轉4P的線,不過購買時需注意是否正確對應)

5、供電
供電這邊注意最好給驅動12V的電,防止步進電機沒力(D36A那邊說的是7-12V)
三、軟件部分
軟件部分採用C語言實現,IDE採用keil,基於逐飛庫進行編寫
代碼實現如下:
函數一:用於將角度轉為步進電機的所需轉動的步數
STEPS_PER_REVOLUTION 和REDUCTION_RATIO 取決於電機的型號,42步進電機的話一般採用這個。(這個函數作者發現有一點問題,最後的輸出結果乘於16.5才是合適的一個結果,後面會說到原因和實現)
// Stepper motor parameters
#define STEPS_PER_REVOLUTION 200 // The number of pulses per turn of the motor
#define REDUCTION_RATIO 1 // reduction ratio (no-------1)
// Calculate the total number of pulses
static uint32 angle_to_steps(float angle) {
// Number of angular pulses: Number of pulses = (angle / 360) * number of step angle pulses * reduction ratio
return (uint32)(fabs(angle) / 360.0f * STEPS_PER_REVOLUTION * REDUCTION_RATIO);
}
函數二:D36A相關初始化
主要是對gpio的初始化以及兩個EN引腳的使能(這邊的GPO一定要注意,因為之前沒有用過逐飛庫,一開始設置成GPI了,排查了很久沒有排查出問題)
// Initialize the D36A driver
void d36a_init(void) {
// Configure the control pins
gpio_init(D36A_IN1_PIN, GPO, 0, GPO_PUSH_PULL);
gpio_init(D36A_IN2_PIN, GPO, 0, GPO_PUSH_PULL);
gpio_init(D36A_IN3_PIN, GPO, 0, GPO_PUSH_PULL);
gpio_init(D36A_IN4_PIN, GPO, 0, GPO_PUSH_PULL);
gpio_init(D36A_ENA_PIN, GPO, 0, GPO_PUSH_PULL);
gpio_init(D36A_ENB_PIN, GPO, 0, GPO_PUSH_PULL);
// Enable motor
gpio_set_level(D36A_ENA_PIN,1);
gpio_set_level(D36A_ENB_PIN,1);
}
函數三:設置單個電機以某個速度轉過某個角度
motor是電機編號,D36A_MOTOR_A或者D36A_MOTOR_B,需要在.h文件中進行定義,然後根據輸入的角度判斷DIR的方向,最後阻塞式對ST引腳輸出脈衝,speed決定脈衝的延遲時間(步進電機是收到一個脈衝轉過一步)。speed越大速度越慢
// Control the motor to rotate at a specified angle
void d36a_set_angle(d36a_motor_enum motor, float angle, uint16 speed) {
uint32 steps = angle_to_steps(angle);
gpio_pin_enum pulse_pin, dir_pin;
// Select the pulse and direction pins corresponding to the motor
if (motor == D36A_MOTOR_A) {
pulse_pin = D36A_IN1_PIN;
dir_pin = D36A_IN2_PIN;
} else {
pulse_pin = D36A_IN3_PIN;
dir_pin = D36A_IN4_PIN;
}
// Set the direction (positive angle forward rotation, negative angle reverse)
if (angle > 0) {
gpio_set_level(dir_pin,1);
} else {
gpio_set_level(dir_pin,0);
}
// Output pulse sequence
for (uint32 i = 0; i < steps; i++) {
gpio_set_level(pulse_pin,1);
system_delay_us(speed); // Pulse high level time
gpio_set_level(pulse_pin,0);
system_delay_us(speed); // Pulse low level time
}
}
函數四:設置AB兩個電機以某個速度同時轉過某個角度(角度可以不一樣)
這邊的angle_a和angle_b分別對應需要兩個電機轉過的角度,speed決定速度(AB速度一致),由於這邊要一起延時來輸出脈衝控制,所以簡單實現的時候速度設置成一樣,後續可以優化一下,例如10us 10us的延時,累積AB延時的時間,超過Speed的時候對應的累積值清零,然後翻轉一下電平。
void d36a_set_angle_both(float angle_a, float angle_b, uint16 speed) {
uint32 steps_a = angle_to_steps(angle_a);
uint32 steps_b = angle_to_steps(angle_b);
uint32 max_steps = steps_a > steps_b ? steps_a : steps_b;
gpio_set_level(D36A_IN2_PIN, angle_a > 0 ? 1 : 0);
gpio_set_level(D36A_IN4_PIN, angle_b > 0 ? 1 : 0);
for (uint32 i = 0; i < max_steps; i++) {
if (i < steps_a) {
gpio_set_level(D36A_IN1_PIN, 1);
}
if (i < steps_b) {
gpio_set_level(D36A_IN3_PIN, 1);
}
system_delay_us(speed);
if (i < steps_a) {
gpio_set_level(D36A_IN1_PIN, 0);
}
if (i < steps_b) {
gpio_set_level(D36A_IN3_PIN, 0);
}
system_delay_us(speed);
}
}
函數五:將誤差值調整成對應的誤差角度
這是一個簡單的轉換函數,需要math庫,具體的參數值需要根據自己的電機系統來調整
int output_to_servo(float output_x)
{
// 1. 計算atan2的第一個參數:output_x * 1.8 / 66
float numerator = output_x * 1.8f / 66.0f;
// 2. 計算反正切(atan2(對邊, 鄰邊)),結果為弧度
float radian = atan2f(numerator, 15.0f); // 第二個參數固定為15(與Python一致)
// 3. 將弧度轉換為角度(乘以180/π),並轉換為整數
int servo_dx = (int)(radian * 180.0f / 3.1415926f); // 用3.1415926提高精度
return servo_dx;
}
主函數:調用剛才寫到的函數
int main (void)
{
clock_init(SYSTEM_CLOCK_80M); // 時鐘配置及系統初始化<務必保留>
d36a_init();
while(true)
{
d36a_set_angle_both(300,300,800);
d36a_set_angle(D36A_MOTOR_A,-300,800);
d36a_set_angle(D36A_MOTOR_B,-300,800);
}
}
如果無法很好的復現博客裡的代碼,可以私信作者博取源代碼,電賽期間都在線
四、補充
角度轉步數函數修正說明
前文提到角度轉步數函數需要乘以 16.5 才能得到正確結果,這是因為實際應用中存在兩個容易被忽略的參數:
驅動細分設置:D36A 默認可能採用 16 細分(通過撥碼開關設置),即每圈實際需要 200×16=3200 步
機械減速比:部分雲臺結構自帶減速齒輪(如 1:10),需要乘以對應減速比
修正後的函數應考慮這些實際參數:
// 修正後的角度轉步數函數
static uint32 angle_to_steps(float angle) {
#define MICROSTEP 16 // 細分設置
#define GEAR_RATIO 10 // 減速比
return (uint32)(fabs(angle) / 360.0f * STEPS_PER_REVOLUTION * MICROSTEP * GEAR_RATIO);
}
建議通過實際測試校準:讓電機轉動 360 度,記錄所需實際步數,反推準確參數。
MSPM0 Notes: 2-Axis Gimbal with D36A + NEMA-42 Steppers (2025 Competition, Code & Pins)
2025 competition 2-axis gimbal: MSPM0G3507 + D36A driving two NEMA-42 motors—wiring, Keil/Seekfree C code, microstepping and gear-ratio calibration.
Captured at (local ISO): 2026-05-18 05:17:01
Preface
Many 2025 competition tasks involve gimbals. The author built a 2-DOF gimbal with two NEMA-42 steppers on one D36A driver and MSPM0G3507 MCU, 3D-printed frame, and early bring-up. This post gets the gimbal moving.
DM the author for source if reproduction is difficult—online during competition season.
I. Hardware
MCU: MSPM0G3507
Driver: D36A dual-channel stepper driver
Motors: NEMA-42 × 2
II. Wiring
Chain: MSPM0G3507 → D36A control inputs → motor ports → steppers
1. MSPM0G3507
Simple core-board wiring (no expansion board). D36A needs six GPIOs, two GND, and 5 V (from external supply, not MCU).
2. D36A interface

Pin description:

For NEMA-42 you need 5 V, two GND, and ST1/DIR1/EN1, ST2/DIR2/EN2 (nine pins total).
3. NEMA-42 motor

Four wires to driver (6P harness often provided as 6P→4P).
4. Wiring tables
MCU → driver
Power D36A from a separate 5 V module (not MCU). Map ST/DIR/EN to GPIO:
| Driver pin | MCU pin (author) |
|---|---|
| ST1 | B12 |
| DIR1 | B9 |
| EN1 | B8 |
| ST2 | B11 |
| DIR2 | B10 |
| EN2 | B13 |
Driver → motors
Connect A+/A−, B+/B− per motor phase.

5. Power
12 V to the driver is recommended (D36A spec 7–12 V) for enough torque.
III. Software
C, Keil, 逐飞 (Seekfree) library.
Function 1: angle → steps (STEPS_PER_REVOLUTION 200, gear ratio; author notes real motion may need ×16.5 factor—see §IV):
#define STEPS_PER_REVOLUTION 200
#define REDUCTION_RATIO 1
static uint32 angle_to_steps(float angle) {
return (uint32)(fabs(angle) / 360.0f * STEPS_PER_REVOLUTION * REDUCTION_RATIO);
}
Function 2: d36a_init — configure GPO (not GPI) for IN/EN pins, enable motors.
Function 3: d36a_set_angle(motor, angle, speed) — DIR from sign, pulse ST with system_delay_us(speed) (larger speed = slower).
Function 4: d36a_set_angle_both(angle_a, angle_b, speed) — synchronized stepping for both axes.
Function 5: output_to_servo — map control error to angle via atan2f.
main: clock_init(SYSTEM_CLOCK_80M); d36a_init(); loop d36a_set_angle_both(300,300,800); then single-axis returns.
DM for full source if needed.
IV. Notes
Angle→steps may need ×16.5 because of 16× microstepping on D36A dip switches and mechanical reduction (e.g. 1:10). Corrected helper:
static uint32 angle_to_steps(float angle) {
#define MICROSTEP 16
#define GEAR_RATIO 10
return (uint32)(fabs(angle) / 360.0f * STEPS_PER_REVOLUTION * MICROSTEP * GEAR_RATIO);
}
Calibrate by commanding 360° and counting actual steps required.