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编号。
本次作者使用的对应接口如下(后续在程序中有用)

驱动的接口树莓派接口
ST1B12
DIR1B9
EN1B8
ST1B11
DIR1B10
EN1B13

驱动—电机

需要将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編號。
本次作者使用的對應接口如下(後續在程序中有用)

驅動的接口樹莓派接口
ST1B12
DIR1B9
EN1B8
ST1B11
DIR1B10
EN1B13

驅動—電機

需要將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 pinMCU pin (author)
ST1B12
DIR1B9
EN1B8
ST2B11
DIR2B10
EN2B13

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.