電機模塊電路圖如下:
電機驅動芯片TB6612FNG驅動邏輯如下:
電機驅動代碼邏輯:通過改變驅動芯片輸入腳IN1、IN2的電平來控制電機的正反轉;通過改變芯片驅動PWM輸入腳的占空比改變電機的轉速。
/******************************************************************************
* FILENAME : Bsp_Motor.c
* PURPOSE : 電機驅動函數接口
* Author: 電筆小新 Created on: 2025年9月12日
******************************************************************************/
#include
#include "User_Include.h"
//======================================================
// * Variables & Function_Defines
//======================================================
// Motor_AIN1 引腳宏定義
#define Motor_AIN1_H (GpioDataRegs.GPBSET.bit.GPIO42=1)
#define Motor_AIN1_L (GpioDataRegs.GPBCLEAR.bit.GPIO42=1)
// Motor_AIN2 引腳宏定義
#define Motor_AIN2_H (GpioDataRegs.GPBSET.bit.GPIO33=1)
#define Motor_AIN2_L (GpioDataRegs.GPBCLEAR.bit.GPIO33=1)
// Motor_BIN1 引腳宏定義
#define Motor_BIN1_H (GpioDataRegs.GPASET.bit.GPIO3=1)
#define Motor_BIN1_L (GpioDataRegs.GPACLEAR.bit.GPIO3=1)
// Motor_BIN2 引腳宏定義
#define Motor_BIN2_H (GpioDataRegs.GPASET.bit.GPIO2=1)
#define Motor_BIN2_L (GpioDataRegs.GPACLEAR.bit.GPIO2=1)
//======================================================
// * Function : InitMotor
// * Purpose : Initialize the resource ports required for motor drive.
// * Parameters : Null
// * Return : Null
//======================================================
void InitMotor(void)
{
/* ===== 步驟1:GPIO配置 ===== */
EALLOW; // 解除寄存器寫保護
// Motor_AIN1 初始化
GpioCtrlRegs.GPBMUX1.bit.GPIO42=0; //IO口
GpioCtrlRegs.GPBDIR.bit.GPIO42=1; //端口設置輸出
GpioCtrlRegs.GPBPUD.bit.GPIO42=1; //上拉
// Motor_AIN2 初始化
GpioCtrlRegs.GPBMUX1.bit.GPIO33=0; //IO口
GpioCtrlRegs.GPBDIR.bit.GPIO33=1; //端口設置輸出
GpioCtrlRegs.GPBPUD.bit.GPIO33=1; //上拉
// Motor_BIN1 初始化
GpioCtrlRegs.GPAMUX1.bit.GPIO3=0; //IO口
GpioCtrlRegs.GPADIR.bit.GPIO3=1; //端口設置輸出
GpioCtrlRegs.GPAPUD.bit.GPIO3=1; //上拉
// Motor_BIN2 初始化
GpioCtrlRegs.GPAMUX1.bit.GPIO2=0; //IO口
GpioCtrlRegs.GPADIR.bit.GPIO2=1; //端口設置輸出
GpioCtrlRegs.GPAPUD.bit.GPIO2=1; //上拉
// 啟用 ePWM1 時鐘
SysCtrlRegs.PCLKCR0.bit.TBCLKSYNC = 0; // 暫停所有 ePWM 時基
SysCtrlRegs.PCLKCR1.bit.EPWM1ENCLK = 1; // 開啟 ePWM1 時鐘
// 配置 GPIO 引腳為 PWM 功能
GpioCtrlRegs.GPAMUX1.bit.GPIO0 = 1; // 配置 GPIO0 為 PWM1A
GpioCtrlRegs.GPAMUX1.bit.GPIO1 = 1; // 配置 GPIO1 為 PWM1B
EDIS; // 恢復寄存器保護
/* ===== 步驟2:配置 ePWM1 時基模塊 ===== */
// PWM 頻率 = SYSCLKOUT / (TBPRD + 1) / (HSPCLKDIV * CLKDIV)
EPwm1Regs.TBPRD = 60000; // 周期值 (60MHz / 60000 = 1KHz PWM)
EPwm1Regs.TBPHS.half.TBPHS = 0; // 相位偏移
EPwm1Regs.TBCTR = 0; // 計數器清零
// 時基時鐘配置
EPwm1Regs.TBCTL.bit.CTRMODE = 0; // 增減計數模式
EPwm1Regs.TBCTL.bit.PHSEN = 0; // 禁用相位加載
EPwm1Regs.TBCTL.bit.HSPCLKDIV = 0; // 高速時鐘分頻 (0=/1, 1=/2)
EPwm1Regs.TBCTL.bit.CLKDIV = 0; // 基準時鐘分頻 (0=/1, 1=/2...)
/* ===== 步驟3:配置比較模塊 ===== */
// PWM1A 占空比 = CMPA / TBPRD
EPwm1Regs.CMPA.half.CMPA = 30000; // 50% 占空比 (30000/60000)
// PWM1B 占空比 = CMPB / TBPRD
EPwm1Regs.CMPB = 15000; // 25% 占空比 (15000/60000)
/* ===== 步驟4:配置動作限定模塊 (關鍵步驟) ===== */
// PWM1A 配置:
EPwm1Regs.AQCTLA.bit.ZRO = 2; // CTR=0 時設置高 (2=SET)
EPwm1Regs.AQCTLA.bit.CAU = 1; // CTR=CMPA 時清除 (1=CLEAR)
// PWM1B 配置:
EPwm1Regs.AQCTLB.bit.ZRO = 2; // CTR=0 時設置高
EPwm1Regs.AQCTLB.bit.CBU = 1; // CTR=CMPB 時清除
/* ===== 步驟5:禁用不需要的模塊 ===== */
// 死區模塊 (DB) - 適用于獨立輸出
EPwm1Regs.DBCTL.bit.OUT_MODE = 0; // 禁用死區生成
EPwm1Regs.DBCTL.bit.IN_MODE = 0; // 禁用輸入控制
// 事件觸發 (ET) - 可選禁用
EPwm1Regs.ETSEL.bit.INTSEL = 0; // 禁用中斷觸發
EPwm1Regs.ETCLR.bit.INT = 1; // 清除中斷標志
// 斬波模塊 (PC) - 禁用
EPwm1Regs.PCCTL.bit.CHPEN = 0; // 禁用斬波生成
/* ===== 步驟6:啟動 PWM ===== */
EALLOW;
SysCtrlRegs.PCLKCR0.bit.TBCLKSYNC = 1; // 啟動所有 ePWM 時基
EDIS;
}
//======================================================
// * Function : Set_Pwm()
// * Purpose : Assign to PWM register.
// * Parameters : motor_left->Left wheel PWM;motor_right->Right wheel PWM
// * Return : Null
//======================================================
void Set_Pwm(int motor_left,int motor_right)
{
if(motor_left>0)
{
Motor_AIN1_H;
Motor_AIN2_L; //前進
}
else
{
Motor_AIN1_L;
Motor_AIN2_H; //后退
}
EPwm1Regs.CMPA.half.CMPA = Cal_abs(motor_left);
if(motor_right>0)
{
Motor_BIN1_H;
Motor_BIN2_L; //前進
}
else
{
Motor_BIN1_L;
Motor_BIN2_H; //后退
}
EPwm1Regs.CMPB=Cal_abs(motor_right);
}
//======================================================
// End of file.
//======================================================
編碼器的為AB兩相輸出的信號,如下圖
F28035集成了1個eQEP(Enhanced Quadrature Encoder Pulse)模塊(eQEP1),專門用于處理正交編碼器(AB相)的信號。
電機測速代碼邏輯:檢測左右兩個電機的轉速需要用到兩個eQEP,F28035內部的eQEP模塊不夠;但是F28035提供了6路可屏蔽外部中斷(XINT1至XINT6),每個中斷線可配置為響應特定GPIO引腳的邊沿(上升沿、下降沿或雙邊沿)或電平變化觸發。我們可以考慮用一路外部中斷和一個普通IO口組合起來檢測電機轉速;具體操作為設置外部中斷為上升沿觸發中斷,然后在中斷響應里面檢測IO的高低電平,高電平為正轉,計數值加一;低電平為反轉,計數值減一;通過固定時間讀取計數值計算電機轉速。
/******************************************************************************
* FILENAME : Bsp_Encoder.c
* PURPOSE : 編碼器應用函數接口
* Author: 電筆小新 Created on: 2025年9月12日
******************************************************************************/
#include "User_Include.h"
#include "Bsp_Encoder.h"
//======================================================
// * Variables & Function_Defines
//======================================================
// XINT1B 引腳宏定義
#define XINT1B() (GpioDataRegs.GPADAT.bit.GPIO24) /* 讀XINT1B狀態 */
// XINT2B 引腳宏定義
#define XINT2B() (GpioDataRegs.GPBDAT.bit.GPIO32) /* 讀XINT2B狀態 */
int16 Encoder_left,Encoder_Right;
//======================================================
// * Function : InitEncoder
// * Purpose : Initialize the resource ports required for the encoder.
// * Parameters : Null
// * Return : Null
//======================================================
void InitEncoder(void)
{
//--- 1. GPIO配置 ---
EALLOW;
// Encoder_XINT1A 初始化
GpioCtrlRegs.GPADIR.bit.GPIO15=0; // 設置為輸入模式
GpioCtrlRegs.GPAPUD.bit.GPIO15=0; // 啟用上拉
GpioCtrlRegs.GPAMUX1.bit.GPIO15=0;
GpioCtrlRegs.GPAQSEL1.bit.GPIO15 = 0;
// 外部中斷1(XINT1)與系統時鐘SYSCLKOUT同步
// Encoder_XINT1B 初始化
GpioCtrlRegs.GPADIR.bit.GPIO24=0; // 設置為輸入模式
GpioCtrlRegs.GPAPUD.bit.GPIO24=0; // 啟用上拉
GpioCtrlRegs.GPAMUX2.bit.GPIO24 = 0; // 普通IO模式
// Encoder_XINT2A 初始化
GpioCtrlRegs.GPADIR.bit.GPIO13=0; // 設置為輸入模式
GpioCtrlRegs.GPAPUD.bit.GPIO13=0; // 啟用上拉
GpioCtrlRegs.GPAMUX1.bit.GPIO13=0;
GpioCtrlRegs.GPAQSEL1.bit.GPIO13 = 0;
// 外部中斷2(XINT2)與系統時鐘SYSCLKOUT同步
// Encoder_XINT2B 初始化
GpioCtrlRegs.GPBDIR.bit.GPIO32=0; // 設置為輸入模式
GpioCtrlRegs.GPBPUD.bit.GPIO32=0; // 啟用上拉
GpioCtrlRegs.GPBMUX1.bit.GPIO32 = 0; // 普通IO模式
EDIS;
//--- 2. 觸發源配置 ---
EALLOW;
GpioIntRegs.GPIOXINT1SEL.bit.GPIOSEL = 15; // XINT1是GPIO15
GpioIntRegs.GPIOXINT2SEL.bit.GPIOSEL = 13; // XINT2是GPIO13
EDIS;
//--- 3. 觸發模式配置 ---
XIntruptRegs.XINT1CR.bit.POLARITY = 1; // 上升沿觸發中斷
XIntruptRegs.XINT1CR.bit.ENABLE= 1; // 使能XINT1
XIntruptRegs.XINT2CR.bit.POLARITY = 1; // 上升沿觸發中斷
XIntruptRegs.XINT2CR.bit.ENABLE= 1; // 使能XINT
//--- 4. 中斷配置 ---
EALLOW;
PieVectTable.XINT1 = &XINT1_ISR; // 注冊ISR
PieCtrlRegs.PIEIER1.bit.INTx4 = 1; // 使能PIE組1的INT4
PieVectTable.XINT2 = &XINT2_ISR; // 注冊ISR
PieCtrlRegs.PIEIER1.bit.INTx5 = 1; // 使能PIE組1的INT5
EDIS;
//--- 5. 全局中斷使能 ---
IER |= M_INT1; // 使能CPU中斷1
EINT; // 開全局中斷
}
//============================================================================
// * Function : Read_Encoder()
// * Purpose : Read encoder count per unit time.
// * Parameters : TIMX->External interrupt number
// * Return : Encoder_Cnt
//======================================================
int Read_Encoder(u8 TIMX)
{
int16 Encoder_Cnt;
switch(TIMX)
{
case 1: Encoder_Cnt= Encoder_left; Encoder_left= 0;break;
case 2: Encoder_Cnt= Encoder_Right; Encoder_Right=0;break;
default: Encoder_Cnt=0;
}
return Encoder_Cnt;
}
//---------------------------------------------------------------------------
// INT_ISR for XINT1:
//---------------------------------------------------------------------------
// INT1.4
interrupt void XINT1_ISR(void)
{
// Insert ISR Code here
if (XINT1B()== 1)
{
Encoder_left++;
}
else
{
Encoder_left--;
}
PieCtrlRegs.PIEACK.bit.ACK1=1;
PieCtrlRegs.PIEACK.all = PIEACK_GROUP1; // 確認PIE組1中斷
}
//---------------------------------------------------------------------------
// INT_ISR for XINT2:
//---------------------------------------------------------------------------
// INT1.5
__interrupt void XINT2_ISR(void)
{
// Insert ISR Code here
if (XINT2B() == 1)
{
Encoder_Right++;
}
else
{
Encoder_Right--;
}
PieCtrlRegs.PIEACK.bit.ACK1=1;
PieCtrlRegs.PIEACK.all = PIEACK_GROUP1; // 確認PIE組1中斷
}
//======================================================
// End of file.
//======================================================