我用一周的时间,通过查询步进电机的控制顺序,实现了电机控制的启动,停止,方向的改变。昨天方向不变,今天我试了把其中一相的顺序,调换一下,就能反转了,我根据这个重新定义了一下反转的数组,同单纯的逆向数组是不同的,实现了转向的改变。我用定时器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 PWMA,A通道PWM控制)
│
└─── VM接12V (电机电源)
VCC接5V (逻辑电源)
AO1、AO2接步进电机A相
BO1、BO2接步进电机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微控制器
- 驱动模块: TB6612FNG双H桥电机驱动模块
- 步进电机: 两相四线步进电机(额定电压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特定技术要点
TB6612与L298N的主要区别
1. 效率更高:导通电阻仅0.5Ω(L298N约3Ω)
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. 硬件调试和故障排查技能
附录A:TB6612引脚功能表
| 引脚 | 名称 | 功能 | 连接 |
|------|------|------|------|
| 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相 |
我要赚赏金
