ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

MSPM0G3507 USART+DMA驱动张大头42步进电机实战

MSPM0G3507 USART+DMA驱动张大头42步进电机实战 简介利用MSPM0G3507微控制器通过USART结合DMA方式驱动张大头42步进电机的完整CCS工程适合嵌入式入门及电机控制开发者参考。资源包共17个文件压缩包约59KB包含C语言源文件、头文件、syscfg配置以及CCS工程文件等核心代码覆盖串口初始化、波特率设置、DMA传输配置与中断处理并给出电机控制相关算法实现可直接导入Code Composer Studio进行编译与调试。目前已有1456人学习下载。通过学习可掌握MSPM0G3507外设使用方法理解DMA如何在不占CPU的情况下高效搬运数据同时积累步进电机项目实战经验是结合硬件编程与工程配置的良好范例。 用MSPM0G3507驱动张大头42步进电机很多人第一反应是配个PWM定时器做脉冲输出。但如果你拿到的是带RS232/RS485串口的张大头闭环驱动器思路就该换一下——驱动器自己处理加减速和闭环校正你要做的只是通过USART发指令再让DMA当搬运工CPU全程可以腾出来干别的。这篇文章就是我在这套方案上的完整实践记录包括CCS里SysConfig的配置逻辑、USARTDMA收发链路、张大头Modbus协议指令怎么构造以及几个让我调了好几天的坑。手上有MSPM0G3507开发板、正在折腾42闭环步进的朋友应该能直接照着少走弯路。1. 为什么是USARTDMA而不是PWM脉冲——选型之前的思考1.1 张大头42步进的两类驱动方式差别很大张大头这个牌子的42步进产品线其实分成两类。一类是传统开环步进电机配一个外置驱动板靠PUL/DIR脉冲接口控制这是老玩法单片机能做的就是输出占空比可调的PWM或者说白了就是高频翻转GPIO。另一类是闭环一体化方案电机和驱动器一体或者驱动器支持串口指令你发转到位置5000步这样完整的一条指令驱动器内部自己去规划速度和位置。很多新手包括我自己一上手就搜42步进电机驱动电路ULN2003步进电机驱动电路那套东西是给五线四相28BYJ-48小电机用的和42闭环完全是两个世界。ULN2003极限电流也就500mA级别而42步进电机的相电流通常要1A以上更别说张大头这种带编码器反馈的闭环系统。所以第一步一定要认清你手里张大头驱动器的接口如果是PUL/DIR端子就走定时器PWM方案如果带串口通讯端子USARTDMA才是它设计的正经用法。1.2 DMA在这个项目里真正解决了什么不带DMA也能发串口指令用阻塞式printf一条条发单轴手动调试完全能跑。但实际运动控制中不是发一条指令就完事的。你要周期性地读当前位置、读到位状态、读报警标志每一个周期都是发查询帧-等响应-解析应答而串口字节速率只有9600或38400bps每个字节的收发间隙都是CPU在空转等待。DMA在这里面的角色就是一个不需要发工资的搬运工。发送方向它把内存里按帧构造好的Modbus报文一个字节一个字节自动塞进USART的发送寄存器接收方向它把USART接收寄存器里到的每个字节自动存进指定的内存缓冲区。CPU只需要做两件连贯的事构造语义正确的指令帧解析语义正确的响应帧。中间那些字节级搬运全部交给硬件。MSPM0G3507虽然跑在80MHz在Cortex-M0里算强的但它没有M4/M7那些除法加速、SIMD指令也不适合在中断里去处理字节级循环。用DMA把中断频率降下来让CPU只处理一帧数据来了这种完整事件整个工程代码会清爽非常多。1.3 这套方案适合什么样的应用场景我个人的判断是只要你的应用是指令型运动控制——发一条位置指令等它执行完再发下一条——USARTDMA就是正解。比如桌面小型CNC、写字机、激光雕刻机、自动送料机构节奏都是秒级到百毫秒级完全喂得饱。但如果你的需求是给电机提供微秒级的连续脉冲序列或者要做实时插补运算那还是老老实实用定时器PWM通道串口指令的异步特性不适合这种场景。另外注意一点指令型控制里驱动器收到指令到真正开始动作本身就有通讯延迟和执行延迟这是异步控制的固有代价。商用闭环驱动器一般都有内部缓冲连续发送位置指令时它会排队执行实际表现通常比想象中流畅。我的200行左右状态机就是靠这个实现的。2. MSPM0G3507的USART与DMA外设SysConfig配置和API使用逻辑2.1 打开SysConfig先把USART和DMA搭起来MSPM0系列在CCS中的开发流程和STM32CubeMX类似但工具叫SysConfig。新建一个MSPM0G3507的空工程后左侧工程树里会出现一个.syscfg文件双击打开图形化配置面板。UART部分需要做的事添加一个USART实例比如USART1波特率设为张大头驱动器默认值常见9600部分型号38400以说明书贴纸为准8位数据位1位停止位无校验无流控指定TX/RX引脚选两个方便飞线测量的引脚比如PA10/PA11具体到你的板卡要看原理图DMA部分在SysConfig里添加DMA通道时关键是把触发源和事件通道选对一个通道绑定USART1_TX一个通道绑定USART1_RX。SysConfig会根据你的命名生成一组宏比如DMA_UART1_TX_CH、DMA_UART1_RX_CH后面写代码全靠这些宏。2.2 发送通道配置single transfer还是continuous requestDMA发送通道的配置里有一个很容易踩坑的选项就是Continuous Request连续请求。这个位在不同SDK版本里的也叫法不同但含义一致——它决定DMA传输完设定长度后是否自动重新开始下一轮传输。单帧发送场景也就是我们这里发Modbus指令帧的场景一定要关闭Continuous Request。DMA搬完设定字节数后就停在空闲状态等软件重新使能下一次传输。如果打开连续请求DMA搬完设定长度后会继续响应USART的触发请求把缓冲里紧接着的数据全发出去表现在串口调试助手里就是疯狂吐数据后文第5章会展开说。传输方向设置上源地址填内存缓冲区的地址目的地址填UART1的发送数据寄存器SDK里通常写为UART1-TXDATA实际以生成的ti_msp_dl_config.h为准。总线的传输宽度选8位一个字节一次搬运。2.3 接收不定长数据RX DMA 空闲中断的思路串口接收端要处理的不是定长帧因为张大头驱动器发出的响应帧长度随着功能码不同而变化。常见的做法有两种一种是RX DMA循环缓冲配合空闲总线中断另一种是RX DMA配合定时器超时判断。我优先推荐第一种因为MSPM0系列USART自带空闲检测一条指令帧在总线上传输结束后线路会进入空闲状态USART能识别到这个空闲事件并触发中断。中断处理里做的事情很固定查询当前DMA通道剩余计数例如调用DL_DMA_getChannelCount用预定的缓冲区总长减去剩余计数得到本次收到的字节数把这段字节拷贝出来或者通过标志位交给应用层解析重新把DMA传输大小配回缓冲区总长并重新使能通道准备收下一帧需要注意的一个细节代码里要过滤frame_len等于0的情况因为空闲中断有可能在线路已经空闲时被误触发一次此时DMA没有收到任何字节剩余计数就是缓冲区全长差值算出来是0。如果你的SDK版本里找不到空闲中断相关的配置项退路是定时器超时方案每次收到字节就把定时器重置为2ms超时定时器溢出就认定当前帧结束。这个方案的可靠性不如硬件空闲中断但胜在通用我在这套代码的早期版本里就是用定时器跑通的。#define RX_BUF_SIZE 256 uint8_t rx_buf[RX_BUF_SIZE]; // 初始化时 DL_DMA_setDestAddr(DMA, DMA_UART1_RX_CH, (uint32_t)rx_buf); DL_DMA_setTransferSize(DMA, DMA_UART1_RX_CH, RX_BUF_SIZE); DL_DMA_enableChannel(DMA, DMA_UART1_RX_CH); void UART1_IRQHandler(void) { if (DL_UART_Main_getPendingInterrupt(UART1) DL_UART_MAIN_INTERRUPT_IDLE) { uint32_t remaining DL_DMA_getChannelCount(DMA, DMA_UART1_RX_CH); frame_len RX_BUF_SIZE - remaining; frame_ready (frame_len 0); DL_DMA_setTransferSize(DMA, DMA_UART1_RX_CH, RX_BUF_SIZE); DL_DMA_enableChannel(DMA, DMA_UART1_RX_CH); } }这段代码里的函数名在部分SDK版本里可能有出入建议以生成的driverlib头文件为准但整体逻辑在所有MSPM0G系列上一致。2.4 DMA发送封装等待上一帧发送完不是可选项发送方向的代码封装我喜欢用一个volatile bool标志在DMA传输完成中断里置false程序里启动发送前轮询它。但这里分两种用法阻塞等待和失败返回。简单起见可以先写阻塞版本把代码跑通后再换成非阻塞。volatile bool uart_tx_busy false; void UART_SendFrame_DMA(uint8_t *buf, uint16_t len) { while (uart_tx_busy) { ; } uart_tx_busy true; DL_DMA_setSrcAddr(DMA, DMA_UART1_TX_CH, (uint32_t)buf); DL_DMA_setDestAddr(DMA, DMA_UART1_TX_CH, (uint32_t)UART1-TXDATA); DL_DMA_setTransferSize(DMA, DMA_UART1_TX_CH, len); DL_DMA_enableChannel(DMA, DMA_UART1_TX_CH); } void DMA_IRQHandler(void) { if (DL_DMA_getChannelEnabledStatus(DMA, DMA_UART1_TX_EVT) DMA_UART1_TX_EVT) { DL_DMA_clearInterruptStatus(DMA, DMA_UART1_TX_EVT); uart_tx_busy false; } }这里有一个很多人会问的问题DMA发送需要等上一轮数据发送完吗答案是需要而且等的不是TXE发送寄存器空而是等DMA自己完成。原因很直接上一轮传输还没有结束时DMA的缓冲地址指针、剩余计数字段都还在被硬件使用如果你此时调用setSrcAddr和setTransferSize改了这些寄存器轻则这一帧数据错乱重则DMA行为完全不可预测。等DMA完成中断置回busyfalse是唯一可靠的同步点。3. 张大头驱动器通讯协议Modbus RTU指令帧的构造和解析3.1 串口参数和接线先确认三件事张大头42闭环驱动器的串口协议大量采用Modbus RTU子集。但我不建议直接照抄某个现成的寄存器表因为不同批次、不同型号之间的地址定义有差异。动手之前先去随机说明书里找到三样东西默认波特率、从机地址、Modbus寄存器表。接线这块特别提醒一下电平问题。如果驱动器接口标的是RS232说明它的串口电平是正负12V的RS232电平MSPM0G3507的引脚是3.3V TTL电平两者不能直连需要MAX3232这类电平转换芯片。如果驱动器标的是UART或TTL还要确认是3.3V还是5V电平3.3V可以直接连5V最好加电平转换或分压避免长时间工作烧坏MSPM0引脚。3.2 三个功能码的帧结构Modbus RTU帧有个特点跨厂家的基本结构完全一致变的只有寄存器地址和寄存器含义。我实际用到的功能码就三个03H读保持寄存器读位置、读状态06H写单个寄存器改控制字、改工作模式10H写多个寄存器写32位目标位置、目标速度帧格式如下功能码请求帧结构03H从机地址(1B) 0x03 起始寄存器(2B) 寄存器数量(2B) CRC16(2B)06H从机地址(1B) 0x06 寄存器地址(2B) 数据(2B) CRC16(2B)10H从机地址(1B) 0x10 起始寄存器(2B) 寄存器数量(2B) 字节数(1B) 数据(NB) CRC16(2B)CRC16-Modbus算法是0xA001多项式代码几乎是固定的uint16_t modbus_crc16(uint8_t *buf, uint16_t len) { uint16_t crc 0xFFFF; for (uint16_t i 0; i len; i) { crc ^ buf[i]; for (uint8_t j 0; j 8; j) { if (crc 0x0001) crc (crc 1) ^ 0xA001; else crc 1; } } return crc; }注意发送时CRC是低字节在前这是Modbus RTU的约定很多人第一次调不通就是栽在这个字节序上。3.3 常见寄存器映射以官方地址表为准我没法给出一个通用万能的张大头寄存器表但从我接触过的多个国产闭环驱动器来看映射逻辑高度相似寄存器含义说明0x0000控制字bit0使能bit1清除报警bit2触发回原点0x0001工作模式0位置模式1速度模式0x0002~0x0003目标位置低16位高16位0x0004~0x0005目标速度单位通常是脉冲/秒0x0006~0x0007当前位置只读反馈0x0008状态字bit0到位bit1报警bit2回原点完成请务必翻出张大头说明书里的Modbus地址表核对一遍。我遇到过同一个型号的两个驱动器一个0x0000是控制字另一个0x0000是状态字控制字挪到了0x0020。这类差异只能靠查手册解决不能靠猜。3.4 一条最基础的位置控制流程假设寄存器表和上表一致把电机从当前位置转到绝对位置5000步的流程大致是写控制字使能0x0001写工作模式位置模式0x0000写目标位置5000触发启动控制字写入启动位轮询状态字等待到位位这个流程不是每个驱动器都完全一样。有的驱动器写完目标位置后会自动启动不需要单独的启动位有的还要求先写速度再写位置。第一遍调试时我建议先在电脑上用串口调试助手手动发帧把每一步的响应抓下来确认流程之后再写进单片机程序。这一步能省掉后面大量反复烧录排查的时间。4. 应用层实现从一条指令到一个完整运动状态机4.1 发送函数封装从阻塞版到非阻塞版第2章那个阻塞版发送函数while循环等busy标志在工程里能用但不够优雅。原因很简单DMA发送期间如果主循环意外进了其他耗时逻辑比如驱动OLED、跑CRC校验busy标志迟迟没置回false主循环就会死等在发送函数里。更稳妥的做法是给出发送失败的返回路径bool UART_SendFrame_DMA(uint8_t *buf, uint16_t len) { if (uart_tx_busy) { return false; } uart_tx_busy true; DL_DMA_setSrcAddr(DMA, DMA_UART1_TX_CH, (uint32_t)buf); DL_DMA_setDestAddr(DMA, DMA_UART1_TX_CH, (uint32_t)UART1-TXDATA); DL_DMA_setTransferSize(DMA, DMA_UART1_TX_CH, len); DL_DMA_enableChannel(DMA, DMA_UART1_TX_CH); return true; }调用方如果收到false就不会傻等而是进入重试或错误计数逻辑。因为实际运动控制的轮询周期是几十毫秒级别的一帧还没发完的概率很低出现false基本意味着上一次发送异常或缓冲区冲突直接进入错误处理比死等在while里更有工程价值。4.2 接收解析帧索引、CRC校验、按功能码分发空闲中断收集到的frame_len和rx_buf只是原始字节要变成运动控制能用的数据还得走一遍解析。我的解析流程分四步校验帧长度是否在合理范围Modbus RTU最短响应帧是5字节最长也就20多字节校验CRCCRC不符直接丢弃校验从机地址是否匹配按功能码分发处理03H响应帧的数据区就是寄存器值直接提取到变量里。06H和10H的响应通常是请求帧的完整回显说明写入成功不需要额外解析数据。遇到异常帧功能码最高位置1要把异常码打印到调试串口这是排查通讯问题的重要线索。4.3 主循环里我有一个简单的运动状态机整理一下整个运动控制流程我习惯用一个枚举状态机来驱动typedef enum { ST_IDLE, ST_ENABLE, ST_SET_MODE, ST_SET_POS, ST_START, ST_WAIT_DONE, ST_READ_POS, ST_ERROR } MotionState;主循环每10ms跑一次这个状态机每个状态做一件事构造当前步骤的指令帧调用UART_SendFrame_DMA发送然后切到下一个状态。等到ST_WAIT_DONE时收到响应帧里到位位置1才切到ST_READ_POS去读当前位置。这里有一个关键节奏问题同一时刻USART总线上只能有一帧在飞。所以状态机切状态的时机不是发送完成而是收到这一帧的响应。我设计DMA发送完成中断只管busy标志真正的流程推进放在解析完响应帧之后这样天然保证了一条总线上不会有多帧重叠。5. 实测踩坑记录五个问题浪费了我一个周末5.1 Continuous Request导致的多发垃圾数据这是我在这个项目上遇到的第一个大坑。现象是明明只发了一条8字节的Modbus写指令逻辑分析仪上却看到同一块缓冲区的数据被反复发送中间没有任何停顿。根因就是SysConfig里DMA通道默认勾选了Continuous Request。DMA搬完设定的8字节后没有停下来USART的TX FIFO一旦空了又会继续产生DMA请求DMA就自作主张地重新加载源地址又把刚才那8字节发了一遍。整个系统看起来就像串口在自发垃圾消息。排查过程其实不复杂把DMA配置面板打开逐个选项看最后定位到continuous request的位置。去掉勾选只在需要连续不断输出数据流的场景比如用DMA循环播放一段音频或持续发方波才打开它。如果你是从STM32转过来的特别容易踩这个坑因为STM32的DMA循环模式和MSPM0的continuous request在概念上有相似处但行为差异很大。5.2 发送前要不要等上一轮发完要但等DMA不是等TXE网上有人问DMA串口发送需要等待上一轮数据发送完吗实测下来答案是必须否则出数据错乱是大概率事件。但等待的对象要搞清楚不是USART的TXE发送数据寄存器空标志而是DMA自身完成标志。TXE只表示当前一个字节已经移出寄存器不代表整个DMA传输结束更不代表调制到线路上的最后一帧已经发完。如果TXE一置位就启动下一轮DMA配置上一轮可能还有数据在移位寄存器里正在往外送这时候改了DMA的缓冲地址和长度轻则丢尾字节重则把下一帧数据接到上一帧后面整个通讯帧错位。可靠的等待对象只有两个二选一DMA传输完成中断的标志位或者缓存里的标志变量。我在代码里用的是busy标志位理由已经在第4章说过不阻塞主循环给错误处理留通道。5.3 方向反了和回原点方向优先改参数不是换线步进电机方向反了传统开环方案最暴力的解决办法是把同一相的两根线对调比如A和A-互换。但在张大头闭环驱动器上我不推荐这么干。闭环驱动器的编码器反馈和电机绕组相序是匹配的你换了绕组线编码器读到的相位关系就不对了驱动器可能直接报编码器错误或者低速时发生震动、堵转。正确的做法是去找驱动器参数里的方向取反或电机方向配置。张大头这类驱动器一般支持通过上位机软件或特定寄存器修改方向参数改完立即生效不用动一根线。回原点方向反了同理也是改驱动器内部回原点方向参数同时检查限位信号是常开还是常闭两者配合不到位回原点会先撞到限位再反向找原点这种问题肉眼很难察觉只能看驱动器的IO状态指示灯。5.4 CCS常用操作导入工程、取消断点、生成hex热词榜单里ccs安装ccs怎么打开已经有的工程ccs取消所有断点ccs生成hex文件说明有不少朋友刚接触CCS这里顺带说几个高频操作。打开已有工程不要双击.c文件要用File - Import - Existing CCS Projects选择包含.project文件的目录。直接Open File会让工程失去构建配置Debug完全没法用。取消所有断点用菜单Run - Remove All Breakpoints快捷操作是在Debug视图的Breakpoints窗口右键Remove All。生成hex文件的话在Project Properties - Build - Steps的After Build步骤里加一条hex生成命令或者更省事的方法是直接用官方UniFlash烧.out文件日常调试其实不太依赖hex。MSPM0G3507烧完程序不上电运行、但点Debug又能跑这类问题先检查boot配置引脚电平不少板子的boot引脚需要接高电平才能进入正常运行模式低电平进入的是BSL下载模式。5.5 张大头电机线和驱动板端子怎么区分最后说一个看起来基础但实际很多人搞混的问题42步进电机的线序。两相混合式电机一共四根动力线分别是A、A-、B、B-。用万用表电阻档去量四根线两两导通导通的两根就是同一相。比如测出红线和绿线导通它们就是A和A-蓝线和黑线导通就是B和B-。驱动器端子上有对应的A、A-、B、B-标识一一接上即可。但张大头42闭环电机引线往往不止四根还会多出一把编码器线一般是5V、GND、A、B、Z或者I2C接口。编码器线别和电机绕组线搞混接线错误轻则电机不动重则烧驱动器编码器接口。我见过有人把5V错接到A相上一上电驱动器直接冒烟。接之前先拿万用表分组再对照说明书确认每一把线的功能定义这个步骤省不得。6. 从单轴到三轴USARTDMA这套思路怎么扩展6.1 三轴雕刻机怎么做通讯拓扑如果你的目标是把这套方案扩展成三轴雕刻机需要先解决通讯拓扑问题。方案A是一路USART带一台驱动器三轴就要用三路USART占资源不说代码里每一路都是一套独立的DMA通道复杂度翻三倍。方案B是把三台张大头驱动器都支持RS485的话挂到同一对差分线上每个驱动器设为不同的从机地址1、2、3MSPM0G3507这边只需一路USART外接一个RS485收发器软件上维护一张轴表。方案B明显更适合小型三轴设备。Modbus RTU本身设计就是支持一主多从的总线上同一时刻只有主机在发询帧从机依次应答天然不会冲突。DMA资源也省了收发方向各一个通道三轴共用。6.2 多轴轮询调度的实现思路多轴轮询的实现我用的是一个带超时的循环调度器。每10ms启动一轮遍历1号轴、2号轴、3号轴每轴发送一帧状态查询指令把响应解析后更新到对应的轴数据结构然后处理下一个轴。某轴超过50ms没有响应标记通讯异常但不阻塞其他轴继续查询。这里有个容易忽视的点总线同一时刻只能有一帧在飞所以发送和接收的DMA缓冲可以是全局同一套空间但解析完必须立刻把数据拷贝到对应轴的结构体里否则下一帧响应到达后会覆盖掉。我直接给每个轴开了独立的接收缓冲避免这类隐性问题。6.3 调试这套系统我最推荐的顺序我自己调这套系统的顺序是先在电脑上用USB转TTL小板和ModbusPoll类软件把张大头驱动器的每一条指令验证通过确认寄存器地址和帧格式无误然后写一个简单的单片机串口回环测试确定MCU能正常收发数据最后才把协议栈和状态机接进去。这么做的好处是把问题分层隔离如果单片机联调时电机不动你至少能判断是通讯协议层的问题还是运动控制状态机的问题而不是两头一起抓。另一个实用技巧是逻辑分析仪抓USART总线波形对比MCU发出的帧和电脑调试时发出的帧差异基本一眼就能看出来。最后再分享一个实际体会。这套方案跑通后最大的收获不是省下那点CPU占用率而是整个工程的结构变清晰了运动控制状态机只管流程语义DMA中断只管搬运状态USART中断只管帧边界三个层次互不干扰。如果你后续要更新运动逻辑比如加一个自动回零点、加一个限位触发暂停都只需要动状态机那一层底层协议完全不用碰。这正是USARTDMA方案比阻塞式串口高级的本质原因——它在硬件层面就把数据搬运的负担接走了让软件可以用更从容的方式思考问题。本文还有配套的精品资源点击获取
返回列表