这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » DIY与开源设计 » 电子DIY » 【2025Let'sdo第4期】基于单片机的直流电机控制成果贴

共1条 1/1 1 跳转至

【2025Let'sdo第4期】基于单片机的直流电机控制成果贴

菜鸟
2026-01-29 23:33:07     打赏

我用一周的时间,通过查询步进电机的控制顺序,实现了电机控制的启动,停止,方向的改变。昨天方向不变,今天我试了把其中一相的顺序,调换一下,就能反转了,我根据这个重新定义了一下反转的数组,同单纯的逆向数组是不同的,实现了转向的改变。我用定时器1的定时中断,进行按键GPIO1.7的输入判断,因为这个没有中断。用GPIO3.29的中断,进行方向的换向。我用GPIO2.7 GPIO3.31 GPIO3.11 GPIO3.10分别作为AIN2 AIN1 BIN1 BIN2的控制管脚,STBY接到了高电平,VCC接到了5伏,VRM接到了12伏,MOTA MOTB分别接到了步进电机的一相。

设计中SW2,SW3实现了电机正反转控制和启动停止逻辑,并通过定时器中断和外部中断相结合的方式,实现了稳定可靠的触发 

 设计目标

- 实现步进电机的启动和停止控制

- 实现电机方向的可控切换

- 通过TB6612实现高效PWM驱动控制

- 通过按键实现用户交互

- 确保系统稳定性和可靠性

 

硬件系统设计 

  TB6612驱动模块特性

TB6612FNG是一款双通道H桥驱动芯片,具有以下特点:

- 最大输出电流:1.2A(连续),3.2A(峰值)

- 工作电压范围:2.5-13.5V

- 低导通电阻:0.5Ω(HS+LS

- 支持PWM频率高达100kHz

- 内置过热保护和欠压锁定

 

  硬件连接

NXP MCXA153 ────┐

                

GPIO2.7  ────────┼─── AIN2 (TB6612 PWMB)

GPIO3.31 ────────┼─── AIN1 (TB6612 AIN2)

GPIO3.11 ────────┼─── BIN1 (TB6612 BIN1)

GPIO3.10 ────────┼─── BIN2 (TB6612 BIN2)

GPIOx.x   ───────┼─── STBY (TB6612 STBY,高电平使能)

                

PWM输出引脚 ──────┼─── PWMA (TB6612 PWMAA通道PWM控制)

                

                 └─── VM12V (电机电源)

                      VCC5V (逻辑电源)

                      AO1AO2接步进电机A

                      BO1BO2接步进电机B

                      GND共地连接

```

 

 TB6612控制逻辑表

```

AIN1 AIN2 | A通道状态  |  STBY  |  电机A

---------|------------|--------|-----------

  0    0  |  停止/刹车 |   1    |   停止

  0    1  |    反转    |   1    |   反转

  1    0  |    正转    |   1    |   正转

  1    1  |  停止/刹车 |   1    |   停止

  X    X  |    停止    |   0    |   停止

```

 

  硬件选型说明

- 控制器: NXP MCXA153 Cortex-M33微控制器

- 驱动模块: TB6612FNGH桥电机驱动模块

- 步进电机: 两相四线步进电机(额定电压12V

- 电源: 逻辑电压5V,电机驱动电压12V

- 保护电路: VM端加入100μF电解电容和0.1μF陶瓷电容滤波 

软件设计与实现

下面是主程序的实现代码,其它的一些宏定义在别的文件,就不再粘贴了。


/*

* Copyright 2024 NXP

*

* SPDX-License-Identifier: BSD-3-Clause

*/

 

#include "fsl_debug_console.h"

#include "fsl_ctimer.h"

#include "fsl_common.h"

#include "pin_mux.h"

#include "board.h"

#include "fsl_gpio_cmsis.h"

#include "app.h"

/*******************************************************************************

* Definitions

******************************************************************************/

 

/*******************************************************************************

* Prototypes

******************************************************************************/

 

/*******************************************************************************

* Variables

******************************************************************************/

/* Whether the SW button is pressed */

volatile bool g_ButtonPress = false;

volatile bool g_SW3_ButtonPress = false;

uint32_t LEDLevel = LOGIC_LED_OFF;

uint8_t g_Run_stop = true;

const bool stepSequence[4][4] = {

{1, 0, 1, 0}, // 步序 1

{0, 1, 1, 0}, // 步序 2

{0, 1, 0, 1}, // 步序 3

{1, 0, 0, 1} // 步序 4

};

const bool cw_stepSequence[4][4] = {

{0, 1, 1, 0}, // 步序 1

{1,0, 1, 0}, // 步序 2

{1, 0,0, 1}, // 步序 3

{0, 1, 0, 1} // 步序 4

};

int currentStep = 0; // 当前步序索引

int direction = 1; // 方向:1为正转,-1为反转

unsigned long stepDelay = 10; // 每步延时(毫秒),控制转速

/* Match Configuration for Channel 0 */

static ctimer_match_config_t matchConfig0;

/* Match Configuration for Channel 1 */

static ctimer_match_config_t matchConfig1;

void Delay(unsigned int j)

{

for(;j!=1;j--){}

}

void DelayMS(unsigned int i)

{

for(;i!=1;i--)

Delay(10000);

}

void ctimer_match1_callback(uint32_t flags)

{

static uint32_t count_1 = 0;

static uint32_t matchUpdateCount_1 = 8;

static uint8_t last_state;

static uint8_t current_state;

current_state = GPIO_PinRead(GPIO1, 7);

 

if ((last_state == 0) && (current_state == 1)) {

if(count_1>5)

{

g_SW3_ButtonPress=true;

count_1=0;

}

}

last_state = current_state;

if(last_state==0)

{

if(count_1<6)

count_1++;

}

else

count_1=0;

/* if (++count > matchUpdateCount)

{

count = 0;

matchConfig1.matchValue >>= 1;

matchUpdateCount <<= 1;

if (matchUpdateCount == (1 << 8))

{

matchUpdateCount = 8;

matchConfig1.matchValue = CTIMER_CLK_FREQ / 2;

}

CTIMER_SetupMatch(CTIMER, CTIMER_MAT1_OUT, &matchConfig1);

}*/

#if defined(BOARD_HAS_NO_CTIMER_OUTPUT_PIN_CONNECTED_TO_LED)

/* No timer match output pin connected to a LED

* toggle LED manually according to match status

*/

if (CTIMER_GetOutputMatchStatus(CTIMER, CTIMER_EMT1_OUT))

{

LED_RED2_ON();

}

else

{

LED_RED2_OFF();

}

#endif

}

 

void ctimer_match0_callback(uint32_t flags)

{

static uint32_t count = 0;

static uint32_t matchUpdateCount = 8;

 

/* if (++count > matchUpdateCount)

{

count = 0;

matchConfig0.matchValue >>= 1;

matchUpdateCount <<= 1;

if (matchUpdateCount == (1 << 8))

{

matchUpdateCount = 8;

matchConfig0.matchValue = CTIMER_CLK_FREQ / 2;

}

CTIMER_SetupMatch(CTIMER, CTIMER_MAT0_OUT, &matchConfig0);

}*/

#if defined(BOARD_HAS_NO_CTIMER_OUTPUT_PIN_CONNECTED_TO_LED)

/* No timer match output pin connected to a LED

* toggle LED manually according to match status

*/

if (CTIMER_GetOutputMatchStatus(CTIMER, CTIMER_EMT0_OUT))

{

LED_RED1_ON();

}

else

{

LED_RED1_OFF();

}

#endif

}

/*******************************************************************************

* Code

******************************************************************************/

static void BUTTON_EventCallback(uint32_t pin, uint32_t event)

{

if (pin == EXAMPLE_BUTTON_PIN && event == ARM_GPIO_TRIGGER_FALLING_EDGE)

{

g_ButtonPress = true;

PRINTF("\r\nBUTTON SW2 Pressed! \r\n");

}

}

 

/*!

* @brief Main function

*/

int main(void)

{

ctimer_config_t config;

BOARD_InitHardware();

 

PRINTF("\r\nCMSIS GPIO Example! \r\n");

PRINTF("\r\nUse Button to toggle LED! \r\n");

 

/* BUTTON pin set up */

EXAMPLE_BUTTON_GPIO_INTERFACE.Setup(EXAMPLE_BUTTON_PIN, BUTTON_EventCallback);

EXAMPLE_BUTTON_GPIO_INTERFACE.SetEventTrigger(EXAMPLE_BUTTON_PIN, ARM_GPIO_TRIGGER_FALLING_EDGE);

/* LED pin set up */

EXAMPLE_LED_GPIO_INTERFACE.Setup(EXAMPLE_LED_PIN, NULL);

EXAMPLE_LED_GPIO_INTERFACE.SetDirection(EXAMPLE_LED_PIN, ARM_GPIO_OUTPUT);

EXAMPLE_LED_GPIO_INTERFACE.SetOutput(EXAMPLE_LED_PIN, LOGIC_LED_OFF);

 

CTIMER_GetDefaultConfig(&config);

 

CTIMER_Init(CTIMER, &config);

 

/* Configuration 0 */

matchConfig0.enableCounterReset = true;

matchConfig0.enableCounterStop = false;

matchConfig0.matchValue = CTIMER_CLK_FREQ / 20;

matchConfig0.outControl = kCTIMER_Output_Toggle;

matchConfig0.outPinInitState = false;

matchConfig0.enableInterrupt = true;

 

/* Configuration 1 */

matchConfig1.enableCounterReset = true;

matchConfig1.enableCounterStop = false;

matchConfig1.matchValue = CTIMER_CLK_FREQ / 20;

matchConfig1.outControl = kCTIMER_Output_Toggle;

matchConfig1.outPinInitState = true;

matchConfig1.enableInterrupt = true;

 

/* Create different ctimer_callback_table array for different CTimer instance. */

CTIMER_RegisterCallBack(CTIMER, &ctimer_callback_table[0], kCTIMER_MultipleCallback);

 

/*

* Macros CTIMER_MAT0_OUT and CTIMER_MAT1_OUT are nominal match output, instead of

* hardware MR0 and MR1 register match output.

* So CTIMER_MAT0_OUT can be defined as kCTIMER_Match_1, CTIMER_MAT1_OUT can be defined

* as kCTIMER_Match_3, which means they are MR1 and MR3 register match output.

*/

CTIMER_SetupMatch(CTIMER, CTIMER_MAT0_OUT, &matchConfig0);

CTIMER_SetupMatch(CTIMER, CTIMER_MAT1_OUT, &matchConfig1);

CTIMER_StartTimer(CTIMER);

while (1)

{

if (g_ButtonPress)

{

if(LEDLevel == LOGIC_LED_OFF)

{

EXAMPLE_LED_GPIO_INTERFACE.SetOutput(EXAMPLE_LED_PIN, LOGIC_LED_ON);

LEDLevel = LOGIC_LED_ON;

PWM_AIN2_OFF();PWM_AIN1_OFF();PWM_BIN2_OFF();PWM_BIN1_OFF();

DelayMS(1);

PWM_AIN2_ON();PWM_AIN1_ON();PWM_BIN2_ON();PWM_BIN1_ON();

direction=1;currentStep=0;

}

else

{

EXAMPLE_LED_GPIO_INTERFACE.SetOutput(EXAMPLE_LED_PIN, LOGIC_LED_OFF);

LEDLevel = LOGIC_LED_OFF;

PWM_AIN2_OFF();PWM_AIN1_OFF();PWM_BIN2_OFF();PWM_BIN1_OFF();

DelayMS(1);

PWM_AIN2_ON();PWM_AIN1_ON();PWM_BIN2_ON();PWM_BIN1_ON();

direction=0;currentStep=0;

}

g_ButtonPress = false;

}

if (g_SW3_ButtonPress)

{

g_SW3_ButtonPress=false;

g_Run_stop=!g_Run_stop;

}

if(g_Run_stop)

{

if(direction)

{

if(stepSequence[currentStep][0]) PWM_AIN1_ON();

else PWM_AIN1_OFF();

if(stepSequence[currentStep][1]) PWM_AIN2_ON();

else PWM_AIN2_OFF();

if(stepSequence[currentStep][2]) PWM_BIN1_ON();

else PWM_BIN1_OFF();

if(stepSequence[currentStep][3]) PWM_BIN2_ON();

else PWM_BIN2_OFF();

}

else

{

if(cw_stepSequence[currentStep][0]) PWM_AIN1_ON();

else PWM_AIN1_OFF();

if(cw_stepSequence[currentStep][1]) PWM_AIN2_ON();

else PWM_AIN2_OFF();

if(cw_stepSequence[currentStep][2]) PWM_BIN1_ON();

else PWM_BIN1_OFF();

if(cw_stepSequence[currentStep][3]) PWM_BIN2_ON();

else PWM_BIN2_OFF();

}

// 延时,控制转速

DelayMS(stepDelay);

 

// 计算下一步,实现循环

currentStep = (currentStep + 1) & 0x03;

}

else

{

PWM_AIN2_ON();PWM_AIN1_ON();PWM_BIN2_ON();PWM_BIN1_ON();

currentStep=0;

}

}

}

 

 

TB6612特定技术要点 

  TB6612L298N的主要区别

1. 效率更高:导通电阻仅0.5Ω(L298N3Ω)

2. 热性能更好:无需大散热片

3. 控制逻辑简化:支持PWM使能控制

4. 保护功能完善:内置过热和欠压保护

 

 TB6612驱动步进电机的优势

1. 支持高频PWM:可实现微步控制

2. 电流控制精确:可通过PWM调节相电流

3. 热保护:防止电机堵转损坏

4. 尺寸小巧:适合嵌入式应用

 

 实际调试中的发现

1. VM电压稳定性:需要足够的滤波电容

2. 地线布置:数字地和功率地要分开布局

3. STBY控制:在初始化时必须置高

4. PWM频率选择:20kHz左右效果最佳 

通过本项目,我掌握了:

1. TB6612电机驱动芯片的深入应用

2. 步进电机控制的核心原理

3. 嵌入式系统中断设计

4. 电机控制的安全保护机制

5. 硬件调试和故障排查技能 

 附录ATB6612引脚功能表

 

| 引脚 | 名称 | 功能 | 连接 |

|------|------|------|------|

| 1,2 | AO1,AO2 | A通道输出 | 电机A|

| 3,4 | GND | | 系统地 |

| 5,6 | VM | 电机电源 | 12V |

| 7,8 | VCC | 逻辑电源 | 5V |

| 9 | PWMA | A通道PWM |高电平 |

| 10 | AIN2 | A通道输入2 | GPIO3.31 |

| 11 | AIN1 | A通道输入1 | GPIO2.7 |

| 12 | STBY | 待机控制 | 高电平 |

| 13 | BIN1 | B通道输入1 | GPIO3.11 |

| 14 | BIN2 | B通道输入2 | GPIO3.10 |

| 15 | PWMB | B通道PWM |高电平 |

| 16,17 | BO2,BO1 | B通道输出 | 电机B|

 




共1条 1/1 1 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]