基于TI TMS320F28P550的N20编码器减速电机驱动与位置读取实战最近在做一个机器人小车的项目需要精确控制轮子的转动位置和速度于是就用上了带编码器的N20减速电机。这种电机体积小、扭矩大配合编码器能实现非常精准的位置控制非常适合机器人、智能小车这类应用。很多刚开始接触电机控制的朋友可能会觉得有点复杂既要驱动电机转动又要读取编码器信号。别担心今天我就以TI的TMS320F28P550开发板为例手把手带你走一遍完整的驱动和位置读取流程。咱们的目标很明确用开发板的PWM模块驱动N20电机正反转和调速同时用EQEP模块增强型正交编码器脉冲模块读取电机编码器的信号从而知道电机转了多少圈、转得多快。我会从硬件接线开始讲到SysConfig图形化配置最后给出可以直接用的代码并解释关键部分。跟着做一遍你就能掌握这套组合拳了。1. 认识我们的“演员”N20编码器电机与开发板在动手接线和写代码之前咱们先搞清楚要用的两个核心部件。N20带编码器减速电机你可以把它理解成一个“三合一”的模块。直流电机核心动力部分通电就转。减速箱电机轴后面连着的一堆小齿轮。电机本身转速很快但力气小经过减速箱后输出轴的转速会变慢但扭矩可以理解为“劲儿”会变大。这就是为什么它叫“减速电机”。编码器装在电机尾部的一个传感器。电机每转动一个很小的角度它就会输出两路有相位差的脉冲信号A相和B相。通过数这些脉冲我们就能精确知道电机转了多少角度、朝哪个方向转的。TI TMS320F28P550开发板这是咱们的大脑。它有两个关键的外设模块我们需要用到PWM模块用来产生脉冲宽度可调的方波信号控制电机的速度和方向。简单说PWM波的高电平时间比例占空比越大电机转速越快。EQEP模块这是TI C2000系列芯片为电机控制量身定做的“编码器专用接口”。它能自动识别A、B两相脉冲的先后顺序来判断方向并自动计数我们直接读它的计数寄存器就能得到位置信息非常方便。2. 硬件连接把电机“插”到开发板上接线是第一步接错了后面全白搭。根据原始资料我们需要连接电机的电源线和编码器的信号线。接线原理驱动这种小型直流电机通常使用H桥电路来控制方向和速度。为了简化我们可以把开发板上的两个PWM输出引脚一个接电机正极M一个接电机负极P想象成一个简单的H桥。通过改变这两个引脚输出的PWM占空比组合来控制电机的正转、反转和停止。具体引脚连接表电机/编码器端开发板引脚 (GPIO)引脚功能说明电机电源正极 (M)GPIO6 (EPWM4-A)使用PWM4的A通道驱动电机正极编码器地线 (GND)GND共地非常重要编码器A相信号GPIO50 (EQEP1-A)连接到EQEP1模块的A相输入编码器B相信号GPIO51 (EQEP1-B)连接到EQEP1模块的B相输入编码器电源 (VCC)3V3为编码器提供3.3V工作电压电机电源负极 (P)GPIO14 (EPWM8-A)使用PWM8的A通道驱动电机负极注意上表中的“EPWM4-A”和“EPWM8-A”指的是这些GPIO引脚背后对应的PWM模块通道。在后续的软件配置中我们就是通过配置这些PWM模块来产生驱动信号的。接线实操提示电源确保编码器的VCC接到开发板的3.3V不要接错到5V可能会损坏编码器。共地电机驱动的地和编码器的地GND必须与开发板的GND连接在一起这是信号正常读取的基础。电机驱动如果你手头的电机工作电压是5V或更高GPIO输出的3.3V PWM信号可能无法直接驱动。这时你需要一个电机驱动模块如DRV8833、TB6612等用开发板的PWM信号去控制驱动模块再由驱动模块输出足够的功率给电机。本例假设电机可在3.3V下工作。3. 软件配置使用SysConfig图形化工具TI为C2000系列提供了非常方便的图形化配置工具SysConfig它藏在CCSCode Composer Studio开发环境里。用它可以可视化地配置引脚功能和模块参数自动生成初始化代码省去了我们手动查手册、写寄存器的麻烦。配置步骤打开配置文件在你的CCS工程里找到并双击c2000.syscfg文件SysConfig界面就会打开。添加EQEP配置在界面中找到“ADD”按钮搜索并添加“EQEP”配置模块。在添加的EQEP配置中选择对应的实例例如EQEP1。将“Input A”和“Input B”分别映射到我们之前接线的GPIO50和GPIO51。其他参数如计数模式、解码模式对于正交编码器通常保持默认即可。添加PWM配置同样点击“ADD”搜索并添加两个“PWM”配置模块。一个PWM实例选择“EPWM4”并将其输出A通道EPWM4-A映射到GPIO6。这个用来控制电机正极M。另一个PWM实例选择“EPWM8”并将其输出A通道EPWM8-A映射到GPIO14。这个用来控制电机负极P。配置PWM频率在PWM模块的配置中设置你想要的PWM频率。对于小型直流电机几千赫兹如5kHz是比较常用的范围。占空比周期如设置周期值为10000决定了PWM调节的精细度。保存与生成代码按下Ctrl S保存配置。按下Ctrl B编译工程。这里可能会弹出一些警告可以暂时不用管它。保存编译后SysConfig会根据你的图形化设置自动在工程中生成board.h和board.c等文件里面已经定义好了所有引脚和模块的初始化代码。我们自己的代码只需要包含board.h在示例中它被包含在tjx_init.h里就可以使用这些配置好的模块了。4. 代码实战编写电机驱动与编码器读取函数配置好硬件抽象层接下来就是写我们自己的应用层代码了。我们在工程里新建一个模块文件夹module_driver并在里面创建两个文件bsp_motor_hallencoder.c和bsp_motor_hallencoder.h。记得把头文件路径添加到编译器中。4.1 头文件定义 (bsp_motor_hallencoder.h)头文件主要进行函数声明和宏定义。#ifndef __BSP_MOTOR_HALLENCODER_H__ #define __BSP_MOTOR_HALLENCODER_H__ #include tjx_init.h // 这个头文件包含了board.h等所有必要的初始化信息 // 一个简单的绝对值宏后面计算位置差时会用到 #define ABS(a) (a0 ? a:(-a)) // 函数声明 void Encoder_Init(void); // 编码器模块初始化 int32_t Get_Encoder_Dir(void); // 获取电机转动方向 uint32_t Get_Encoder_Value(void); // 获取编码器计数值位置差 void Motor_Set_PWM(uint8_t Mode, uint16_t Speed); // 设置电机PWM控制转向和速度 #endif4.2 核心驱动文件 (bsp_motor_hallencoder.c)这里是所有功能的实现。#include bsp_motor_hallencoder.h // 全局变量定义 volatile uint32_t Encoder_Count 0; // 编码器计数值用户通过Get_Encoder_Value()读取这个值 volatile uint32_t Encoder_Last_Count 0; // 存储上一次中断时的编码器位置值 volatile int32_t Motor_dir 0; // 电机旋转方向正转/反转 bool Encoder_InitDone false; // 编码器初始化完成标志 /** * brief 编码器初始化函数 * note 主要是在第一次进入中断前记录一个初始位置值。 */ void Encoder_Init(void) { // 读取EQEP模块的初始位置锁存值 Encoder_Last_Count EQEP_getPositionLatch(Module_EQEP_BASE); Encoder_InitDone false; // 标记初始化未完成等待第一次中断 } /** * brief 电机PWM控制函数 * param Mode: 1-正转2-反转0-停止 * param Speed: PWM占空比范围0-9999对应0%-100% * note 通过设置两个PWM通道的占空比来控制H桥的状态从而实现转向。 * 正转M0, PSpeed * 反转MSpeed, P0 * 停止M0, P0 */ void Motor_Set_PWM(uint8_t Mode, uint16_t Speed) { if(Mode 1){ // 正转 // 电机正极(M)拉低负极(P)给PWM波 EPWM_setCounterCompareValue(Module_PWM_M_BASE, EPWM_COUNTER_COMPARE_A, 0); EPWM_setCounterCompareValue(Module_PWM_P_BASE, EPWM_COUNTER_COMPARE_A, Speed); }else if(Mode 2){ // 反转 // 电机正极(M)给PWM波负极(P)拉低 EPWM_setCounterCompareValue(Module_PWM_M_BASE, EPWM_COUNTER_COMPARE_A, Speed); EPWM_setCounterCompareValue(Module_PWM_P_BASE, EPWM_COUNTER_COMPARE_A, 0); }else { // 停止或其他无效模式 // 两个引脚都拉低电机两端没有电压差停止 EPWM_setCounterCompareValue(Module_PWM_M_BASE, EPWM_COUNTER_COMPARE_A, 0); EPWM_setCounterCompareValue(Module_PWM_P_BASE, EPWM_COUNTER_COMPARE_A, 0); } } /** * brief 获取电机方向 * retval 方向值由EQEP模块提供 */ int32_t Get_Encoder_Dir(void) { return Motor_dir; } /** * brief 获取编码器位置差值 * retval 从上一次读取到这一次读取之间编码器计数的变化量绝对值 * note 这个值反映了电机在这段时间内转动的“相对位置”。 */ uint32_t Get_Encoder_Value(void) { return Encoder_Count; } // // EQEP中断服务函数 // 这个函数会周期性地被调用用于更新编码器位置和方向 // __interrupt void INT_Module_EQEP_ISR(void) { // 1. 读取当前时刻编码器的位置锁存值和方向 uint32_t newCount EQEP_getPositionLatch(Module_EQEP_BASE); int32_t newDir EQEP_getDirection(Module_EQEP_BASE); // 2. 如果是第一次进入中断只记录初始值不计算差值 if (!Encoder_InitDone) { Encoder_Last_Count newCount; Encoder_InitDone true; // 清除中断标志必不可少 EQEP_clearInterruptStatus(Module_EQEP_BASE,EQEP_INT_UNIT_TIME_OUT|EQEP_INT_GLOBAL); Interrupt_clearACKGroup(INT_Module_EQEP_INTERRUPT_ACK_GROUP); return; } // 3. 计算本次中断与上次中断之间的位置差值 int32_t diff (int32_t)(newCount - Encoder_Last_Count); uint32_t delta (uint32_t)ABS(diff); // 取绝对值得到变化的脉冲数 // 4. 更新全局变量 Encoder_Count delta; // 存储变化量供主循环读取 Encoder_Last_Count newCount; // 更新“上一次”的位置 Motor_dir newDir; // 更新方向 // 5. 再次清除中断标志为下一次中断做准备 EQEP_clearInterruptStatus(Module_EQEP_BASE,EQEP_INT_UNIT_TIME_OUT|EQEP_INT_GLOBAL); Interrupt_clearACKGroup(INT_Module_EQEP_INTERRUPT_ACK_GROUP); }代码关键点解析中断服务函数这是核心。EQEP模块可以配置为定期例如每1ms产生中断。在中断里我们读取位置值并与上一次的值做差就得到了这段时间电机转动的脉冲数。这个脉冲数乘以每个脉冲对应的角度就是转动的角度。方向判断EQEP_getDirection函数直接返回当前旋转方向无需我们通过A、B相相位差手动判断。PWM控制Motor_Set_PWM函数通过控制两个PWM输出的占空比组合模拟了H桥的逻辑从而控制电机转向和速度。Speed参数的范围取决于你在SysConfig中为PWM计数器设置的周期值。4.3 主函数测试 (main.c)最后我们在主函数里把上面的模块用起来进行一个简单的测试。#include driverlib.h #include device.h #include board.h #include c2000ware_libraries.h #include tjx_init.h #include bsp_motor_hallencoder.h void main(void) { // CCS自动生成的初始化代码 [开始] Device_init(); Device_initGPIO(); Interrupt_initModule(); Interrupt_initVectorTable(); Board_init(); C2000Ware_libraries_init(); EINT; // 开启全局中断 ERTM; // CCS自动生成的初始化代码 [结束] // 初始化串口用于打印信息假设lc_printf已实现 lc_printf(\r\n 开始N20编码器电机测试 \r\n); // 初始化编码器模块 Encoder_Init(); // 让电机以中等速度正转2秒 lc_printf(电机正转...\r\n); Motor_Set_PWM(1, 5000); // 模式1正转速度50%5000/10000 delay_ms(2000); // 停止电机 lc_printf(电机停止...\r\n); Motor_Set_PWM(0, 0); delay_ms(1000); while(1) { // 为了安全地读取全局变量先关闭中断 DINT; uint32_t current_pulses Get_Encoder_Value(); // 读取自上次读取后的脉冲变化量 int32_t current_dir Get_Encoder_Dir(); // 读取当前方向 EINT; // 读取完毕重新开启中断 // 打印编码器信息 lc_printf(脉冲变化量: %lu, 方向: %s\r\n, current_pulses, (current_dir 0) ? 正转 : ((current_dir 0) ? 反转 : 静止)); // 这里可以加入你的控制逻辑例如 // if (current_pulses 目标脉冲数) { Motor_Set_PWM(0,0); } // 位置到达停止 // 用一个简单的LED闪烁指示程序在运行 GPIO_writePin(RGB_B, 0); // 蓝灯亮 GPIO_writePin(RGB_G, 1); // 绿灯灭 delay_ms(200); GPIO_writePin(RGB_B, 1); // 蓝灯灭 GPIO_writePin(RGB_G, 0); // 绿灯亮 delay_ms(200); } }5. 调试心得与常见问题电机不转检查电源首先确认电机供电是否足够。开发板GPIO的驱动能力有限如果电机需要较大电流必须外接电机驱动模块。检查PWM输出用示波器或逻辑分析仪测量GPIO6和GPIO14看是否有PWM波形输出。如果没有回头检查SysConfig中PWM模块的配置和使能。检查接线再三核对电机M、P线是否接反或接触不良。编码器读数始终为0检查编码器供电确保VCC接3.3VGND共地。检查信号线用示波器看GPIO50和GPIO51在电机转动时是否有方波脉冲。如果没有可能是编码器损坏或接线错误。检查EQEP配置确认SysConfig中EQEP模块的A、B相输入引脚映射正确。检查中断确认EQEP的中断已经正确启用并且中断服务函数被成功链接。可以在中断函数入口加一个翻转LED的代码来测试中断是否触发。方向判断错误如果电机实际正转但读到的方向是反转可以尝试在代码中交换Get_Encoder_Dir()函数的返回值正负或者直接在硬件上交换编码器A、B两相的接线。位置计数不准确电机转速很快时EQEP的中断频率要足够高否则会丢失脉冲。可以调整EQEP的单位超时中断周期让它更频繁地进入中断读取位置。当然EQEP本身有32位的位置计数器即使中断间隔稍长只要不溢出最终的位置值还是准确的但实时性会差一些。把这个流程走通你就掌握了基于C2000芯片进行直流有刷电机位置控制的基本方法。这套框架稍加修改就可以应用到你的小车、机械臂或者其他需要精确运动控制的项目中去。动手试试吧遇到问题多在开发板社区里交流往往能事半功倍。
基于TI TMS320F28P550的N20编码器减速电机驱动与位置读取实战
基于TI TMS320F28P550的N20编码器减速电机驱动与位置读取实战最近在做一个机器人小车的项目需要精确控制轮子的转动位置和速度于是就用上了带编码器的N20减速电机。这种电机体积小、扭矩大配合编码器能实现非常精准的位置控制非常适合机器人、智能小车这类应用。很多刚开始接触电机控制的朋友可能会觉得有点复杂既要驱动电机转动又要读取编码器信号。别担心今天我就以TI的TMS320F28P550开发板为例手把手带你走一遍完整的驱动和位置读取流程。咱们的目标很明确用开发板的PWM模块驱动N20电机正反转和调速同时用EQEP模块增强型正交编码器脉冲模块读取电机编码器的信号从而知道电机转了多少圈、转得多快。我会从硬件接线开始讲到SysConfig图形化配置最后给出可以直接用的代码并解释关键部分。跟着做一遍你就能掌握这套组合拳了。1. 认识我们的“演员”N20编码器电机与开发板在动手接线和写代码之前咱们先搞清楚要用的两个核心部件。N20带编码器减速电机你可以把它理解成一个“三合一”的模块。直流电机核心动力部分通电就转。减速箱电机轴后面连着的一堆小齿轮。电机本身转速很快但力气小经过减速箱后输出轴的转速会变慢但扭矩可以理解为“劲儿”会变大。这就是为什么它叫“减速电机”。编码器装在电机尾部的一个传感器。电机每转动一个很小的角度它就会输出两路有相位差的脉冲信号A相和B相。通过数这些脉冲我们就能精确知道电机转了多少角度、朝哪个方向转的。TI TMS320F28P550开发板这是咱们的大脑。它有两个关键的外设模块我们需要用到PWM模块用来产生脉冲宽度可调的方波信号控制电机的速度和方向。简单说PWM波的高电平时间比例占空比越大电机转速越快。EQEP模块这是TI C2000系列芯片为电机控制量身定做的“编码器专用接口”。它能自动识别A、B两相脉冲的先后顺序来判断方向并自动计数我们直接读它的计数寄存器就能得到位置信息非常方便。2. 硬件连接把电机“插”到开发板上接线是第一步接错了后面全白搭。根据原始资料我们需要连接电机的电源线和编码器的信号线。接线原理驱动这种小型直流电机通常使用H桥电路来控制方向和速度。为了简化我们可以把开发板上的两个PWM输出引脚一个接电机正极M一个接电机负极P想象成一个简单的H桥。通过改变这两个引脚输出的PWM占空比组合来控制电机的正转、反转和停止。具体引脚连接表电机/编码器端开发板引脚 (GPIO)引脚功能说明电机电源正极 (M)GPIO6 (EPWM4-A)使用PWM4的A通道驱动电机正极编码器地线 (GND)GND共地非常重要编码器A相信号GPIO50 (EQEP1-A)连接到EQEP1模块的A相输入编码器B相信号GPIO51 (EQEP1-B)连接到EQEP1模块的B相输入编码器电源 (VCC)3V3为编码器提供3.3V工作电压电机电源负极 (P)GPIO14 (EPWM8-A)使用PWM8的A通道驱动电机负极注意上表中的“EPWM4-A”和“EPWM8-A”指的是这些GPIO引脚背后对应的PWM模块通道。在后续的软件配置中我们就是通过配置这些PWM模块来产生驱动信号的。接线实操提示电源确保编码器的VCC接到开发板的3.3V不要接错到5V可能会损坏编码器。共地电机驱动的地和编码器的地GND必须与开发板的GND连接在一起这是信号正常读取的基础。电机驱动如果你手头的电机工作电压是5V或更高GPIO输出的3.3V PWM信号可能无法直接驱动。这时你需要一个电机驱动模块如DRV8833、TB6612等用开发板的PWM信号去控制驱动模块再由驱动模块输出足够的功率给电机。本例假设电机可在3.3V下工作。3. 软件配置使用SysConfig图形化工具TI为C2000系列提供了非常方便的图形化配置工具SysConfig它藏在CCSCode Composer Studio开发环境里。用它可以可视化地配置引脚功能和模块参数自动生成初始化代码省去了我们手动查手册、写寄存器的麻烦。配置步骤打开配置文件在你的CCS工程里找到并双击c2000.syscfg文件SysConfig界面就会打开。添加EQEP配置在界面中找到“ADD”按钮搜索并添加“EQEP”配置模块。在添加的EQEP配置中选择对应的实例例如EQEP1。将“Input A”和“Input B”分别映射到我们之前接线的GPIO50和GPIO51。其他参数如计数模式、解码模式对于正交编码器通常保持默认即可。添加PWM配置同样点击“ADD”搜索并添加两个“PWM”配置模块。一个PWM实例选择“EPWM4”并将其输出A通道EPWM4-A映射到GPIO6。这个用来控制电机正极M。另一个PWM实例选择“EPWM8”并将其输出A通道EPWM8-A映射到GPIO14。这个用来控制电机负极P。配置PWM频率在PWM模块的配置中设置你想要的PWM频率。对于小型直流电机几千赫兹如5kHz是比较常用的范围。占空比周期如设置周期值为10000决定了PWM调节的精细度。保存与生成代码按下Ctrl S保存配置。按下Ctrl B编译工程。这里可能会弹出一些警告可以暂时不用管它。保存编译后SysConfig会根据你的图形化设置自动在工程中生成board.h和board.c等文件里面已经定义好了所有引脚和模块的初始化代码。我们自己的代码只需要包含board.h在示例中它被包含在tjx_init.h里就可以使用这些配置好的模块了。4. 代码实战编写电机驱动与编码器读取函数配置好硬件抽象层接下来就是写我们自己的应用层代码了。我们在工程里新建一个模块文件夹module_driver并在里面创建两个文件bsp_motor_hallencoder.c和bsp_motor_hallencoder.h。记得把头文件路径添加到编译器中。4.1 头文件定义 (bsp_motor_hallencoder.h)头文件主要进行函数声明和宏定义。#ifndef __BSP_MOTOR_HALLENCODER_H__ #define __BSP_MOTOR_HALLENCODER_H__ #include tjx_init.h // 这个头文件包含了board.h等所有必要的初始化信息 // 一个简单的绝对值宏后面计算位置差时会用到 #define ABS(a) (a0 ? a:(-a)) // 函数声明 void Encoder_Init(void); // 编码器模块初始化 int32_t Get_Encoder_Dir(void); // 获取电机转动方向 uint32_t Get_Encoder_Value(void); // 获取编码器计数值位置差 void Motor_Set_PWM(uint8_t Mode, uint16_t Speed); // 设置电机PWM控制转向和速度 #endif4.2 核心驱动文件 (bsp_motor_hallencoder.c)这里是所有功能的实现。#include bsp_motor_hallencoder.h // 全局变量定义 volatile uint32_t Encoder_Count 0; // 编码器计数值用户通过Get_Encoder_Value()读取这个值 volatile uint32_t Encoder_Last_Count 0; // 存储上一次中断时的编码器位置值 volatile int32_t Motor_dir 0; // 电机旋转方向正转/反转 bool Encoder_InitDone false; // 编码器初始化完成标志 /** * brief 编码器初始化函数 * note 主要是在第一次进入中断前记录一个初始位置值。 */ void Encoder_Init(void) { // 读取EQEP模块的初始位置锁存值 Encoder_Last_Count EQEP_getPositionLatch(Module_EQEP_BASE); Encoder_InitDone false; // 标记初始化未完成等待第一次中断 } /** * brief 电机PWM控制函数 * param Mode: 1-正转2-反转0-停止 * param Speed: PWM占空比范围0-9999对应0%-100% * note 通过设置两个PWM通道的占空比来控制H桥的状态从而实现转向。 * 正转M0, PSpeed * 反转MSpeed, P0 * 停止M0, P0 */ void Motor_Set_PWM(uint8_t Mode, uint16_t Speed) { if(Mode 1){ // 正转 // 电机正极(M)拉低负极(P)给PWM波 EPWM_setCounterCompareValue(Module_PWM_M_BASE, EPWM_COUNTER_COMPARE_A, 0); EPWM_setCounterCompareValue(Module_PWM_P_BASE, EPWM_COUNTER_COMPARE_A, Speed); }else if(Mode 2){ // 反转 // 电机正极(M)给PWM波负极(P)拉低 EPWM_setCounterCompareValue(Module_PWM_M_BASE, EPWM_COUNTER_COMPARE_A, Speed); EPWM_setCounterCompareValue(Module_PWM_P_BASE, EPWM_COUNTER_COMPARE_A, 0); }else { // 停止或其他无效模式 // 两个引脚都拉低电机两端没有电压差停止 EPWM_setCounterCompareValue(Module_PWM_M_BASE, EPWM_COUNTER_COMPARE_A, 0); EPWM_setCounterCompareValue(Module_PWM_P_BASE, EPWM_COUNTER_COMPARE_A, 0); } } /** * brief 获取电机方向 * retval 方向值由EQEP模块提供 */ int32_t Get_Encoder_Dir(void) { return Motor_dir; } /** * brief 获取编码器位置差值 * retval 从上一次读取到这一次读取之间编码器计数的变化量绝对值 * note 这个值反映了电机在这段时间内转动的“相对位置”。 */ uint32_t Get_Encoder_Value(void) { return Encoder_Count; } // // EQEP中断服务函数 // 这个函数会周期性地被调用用于更新编码器位置和方向 // __interrupt void INT_Module_EQEP_ISR(void) { // 1. 读取当前时刻编码器的位置锁存值和方向 uint32_t newCount EQEP_getPositionLatch(Module_EQEP_BASE); int32_t newDir EQEP_getDirection(Module_EQEP_BASE); // 2. 如果是第一次进入中断只记录初始值不计算差值 if (!Encoder_InitDone) { Encoder_Last_Count newCount; Encoder_InitDone true; // 清除中断标志必不可少 EQEP_clearInterruptStatus(Module_EQEP_BASE,EQEP_INT_UNIT_TIME_OUT|EQEP_INT_GLOBAL); Interrupt_clearACKGroup(INT_Module_EQEP_INTERRUPT_ACK_GROUP); return; } // 3. 计算本次中断与上次中断之间的位置差值 int32_t diff (int32_t)(newCount - Encoder_Last_Count); uint32_t delta (uint32_t)ABS(diff); // 取绝对值得到变化的脉冲数 // 4. 更新全局变量 Encoder_Count delta; // 存储变化量供主循环读取 Encoder_Last_Count newCount; // 更新“上一次”的位置 Motor_dir newDir; // 更新方向 // 5. 再次清除中断标志为下一次中断做准备 EQEP_clearInterruptStatus(Module_EQEP_BASE,EQEP_INT_UNIT_TIME_OUT|EQEP_INT_GLOBAL); Interrupt_clearACKGroup(INT_Module_EQEP_INTERRUPT_ACK_GROUP); }代码关键点解析中断服务函数这是核心。EQEP模块可以配置为定期例如每1ms产生中断。在中断里我们读取位置值并与上一次的值做差就得到了这段时间电机转动的脉冲数。这个脉冲数乘以每个脉冲对应的角度就是转动的角度。方向判断EQEP_getDirection函数直接返回当前旋转方向无需我们通过A、B相相位差手动判断。PWM控制Motor_Set_PWM函数通过控制两个PWM输出的占空比组合模拟了H桥的逻辑从而控制电机转向和速度。Speed参数的范围取决于你在SysConfig中为PWM计数器设置的周期值。4.3 主函数测试 (main.c)最后我们在主函数里把上面的模块用起来进行一个简单的测试。#include driverlib.h #include device.h #include board.h #include c2000ware_libraries.h #include tjx_init.h #include bsp_motor_hallencoder.h void main(void) { // CCS自动生成的初始化代码 [开始] Device_init(); Device_initGPIO(); Interrupt_initModule(); Interrupt_initVectorTable(); Board_init(); C2000Ware_libraries_init(); EINT; // 开启全局中断 ERTM; // CCS自动生成的初始化代码 [结束] // 初始化串口用于打印信息假设lc_printf已实现 lc_printf(\r\n 开始N20编码器电机测试 \r\n); // 初始化编码器模块 Encoder_Init(); // 让电机以中等速度正转2秒 lc_printf(电机正转...\r\n); Motor_Set_PWM(1, 5000); // 模式1正转速度50%5000/10000 delay_ms(2000); // 停止电机 lc_printf(电机停止...\r\n); Motor_Set_PWM(0, 0); delay_ms(1000); while(1) { // 为了安全地读取全局变量先关闭中断 DINT; uint32_t current_pulses Get_Encoder_Value(); // 读取自上次读取后的脉冲变化量 int32_t current_dir Get_Encoder_Dir(); // 读取当前方向 EINT; // 读取完毕重新开启中断 // 打印编码器信息 lc_printf(脉冲变化量: %lu, 方向: %s\r\n, current_pulses, (current_dir 0) ? 正转 : ((current_dir 0) ? 反转 : 静止)); // 这里可以加入你的控制逻辑例如 // if (current_pulses 目标脉冲数) { Motor_Set_PWM(0,0); } // 位置到达停止 // 用一个简单的LED闪烁指示程序在运行 GPIO_writePin(RGB_B, 0); // 蓝灯亮 GPIO_writePin(RGB_G, 1); // 绿灯灭 delay_ms(200); GPIO_writePin(RGB_B, 1); // 蓝灯灭 GPIO_writePin(RGB_G, 0); // 绿灯亮 delay_ms(200); } }5. 调试心得与常见问题电机不转检查电源首先确认电机供电是否足够。开发板GPIO的驱动能力有限如果电机需要较大电流必须外接电机驱动模块。检查PWM输出用示波器或逻辑分析仪测量GPIO6和GPIO14看是否有PWM波形输出。如果没有回头检查SysConfig中PWM模块的配置和使能。检查接线再三核对电机M、P线是否接反或接触不良。编码器读数始终为0检查编码器供电确保VCC接3.3VGND共地。检查信号线用示波器看GPIO50和GPIO51在电机转动时是否有方波脉冲。如果没有可能是编码器损坏或接线错误。检查EQEP配置确认SysConfig中EQEP模块的A、B相输入引脚映射正确。检查中断确认EQEP的中断已经正确启用并且中断服务函数被成功链接。可以在中断函数入口加一个翻转LED的代码来测试中断是否触发。方向判断错误如果电机实际正转但读到的方向是反转可以尝试在代码中交换Get_Encoder_Dir()函数的返回值正负或者直接在硬件上交换编码器A、B两相的接线。位置计数不准确电机转速很快时EQEP的中断频率要足够高否则会丢失脉冲。可以调整EQEP的单位超时中断周期让它更频繁地进入中断读取位置。当然EQEP本身有32位的位置计数器即使中断间隔稍长只要不溢出最终的位置值还是准确的但实时性会差一些。把这个流程走通你就掌握了基于C2000芯片进行直流有刷电机位置控制的基本方法。这套框架稍加修改就可以应用到你的小车、机械臂或者其他需要精确运动控制的项目中去。动手试试吧遇到问题多在开发板社区里交流往往能事半功倍。