ARTICLE DETAIL

资讯详情

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

串口屏控制步进电机:STM32 HAL库与USART指令完整实现

串口屏控制步进电机:STM32 HAL库与USART指令完整实现 简介STM32电机控制例程第八期是一套基于HAL库的串口屏控制步进电机完整项目面向嵌入式初学者与电机控制开发者重点演示如何通过USART接口接收HMI人机界面指令解析速度、方向、启停等控制参数再生成脉冲序列驱动步进电机精确运行。资源包共936个文件压缩后约47.26MB以C语言源码为主体包含371个C源文件与145个头文件覆盖GPIO、定时器、中断、串口外设初始化以及电机控制逻辑同时配有Keil工程文件、HMI界面工程、可直接烧写的hex固件和批量删除编译中间信息的脚本便于学习后快速移植到自己的项目。整套代码从底层驱动到上层指令解析均有体现尤其对PWM调速、细分驱动和串口协议设计提供了完整参考。已有2931人学习下载适合毕业设计、竞赛准备或工业现场电机控制场景的入门与进阶。 电机控制系列写到第八期前几期把PWM调速、按键控制、模拟量调速都聊过一遍了这期换个更有意思的玩法用一块USART串口屏通过串口指令直接控制步进电机的启动、停止、正反转和速度。标题里的压缩包就是我整理好的完整工程例程基于STM32 HAL库开发串口屏端用的是淘晶驰系列其他品牌只要明白协议原理也能快速移植。先说这个东西能解决什么问题。以前做小型设备调试要么用按键加OLED菜单要么用电脑上位机前者交互简陋、改参数麻烦后者一涉及上位机开发就劝退了很多人。串口屏是折中且很成熟的方案屏端负责显示和触摸把用户操作变成一串串USART指令扔给STM32STM32负责真正干活——解析指令、输出脉冲、驱动步进电机。整个链路清晰开发量小而且屏上的界面改起来非常快不用重新刷单片机程序。这篇文章我会按硬件接线、界面配置、STM32代码、问题排查的顺序把完整链路拆开适合正在做毕设、小型控制台或者想入手串口屏开发的工程师参考。1. 整体方案与设计思路1.1 换成人机交互方案的三个理由这次选的串口屏方案不是临时起意。以前做过一个三轴平台的控制盒用的就是按键OLED屏结果改一次速度参数就得重新烧录程序每次客户提需求都得改固件烦得不行。串口屏的好处在于界面和逻辑是分离的——屏上的按钮、滑动条、数字显示都在PC端的上位机软件里配置改完直接下载到屏里STM32代码一行不用动。另外一点是开发效率。串口屏内部自带字库、图片存储、控件库想加个状态指示灯运行时间计数在界面上拖几个控件就完事。如果换成普通LCD自己画界面光UI代码就得写几百行还不算触摸驱动和菜单逻辑。对做产品原型或者课程设计来说时间成本差别非常大。当然还有很重要的一条排查问题方便。串口屏的通信是透明的只要接上USB转TTL工具看屏发出的字节流就知道到底有没有通信指令对不对。这种看得见的特性让调试效率高了一个档次。1.2 硬件选型从MCU到驱动器的搭配清单整套例程的硬件清单如下MCUSTM32F103C8T6最经典也最便宜USART1和TIM1够用。串口屏淘晶驰TJC系列2.4寸其他如迪文、大彩也都可以原理一致。步进电机42步进电机两相四线步距角1.8°额定电流约1.5A。驱动器TB6600支持4A以内电机带光耦隔离输入适合42/57电机。电源12V/3A给驱动器供电5V给串口屏3.3V由STM32开发板自带的LDO提供。三个系统的GND必须连在一起。这里要特别提醒一下选择驱动器的逻辑。TB6600之所以常见是因为它光耦隔离做得好控制信号可以直接由STM32的3.3V GPIO驱动不需要额外电平转换。如果用的是A4988这类模块逻辑电平兼容性虽然也没问题但它的电流能力偏小驱动42电机长时间运行会发热明显。TB6600的效率更高接线也更省心。1.3 一整套控制链路是怎么走通的从整体上看控制链路是这样的操作人员在串口屏上点击按钮或者拖动滑动条串口屏内部的逻辑会把这些交互事件转换成预先配置好的USART指令帧通过串口TX引脚发出去STM32的USART1接收中断一帧一帧地把数据收进来按照协议状态机解析出命令字和参数解析成功后STM32通过定时器PWM输出对应的脉冲频率并通过两个普通IO控制步进电机驱动器的方向DIR和使能ENA引脚。脉冲不断输出电机就以对应的速度旋转。说白了串口屏是指挥官STM32是执行者步进电机是苦力。这套结构的好处是层次分明任何一环出问题都能快速定位。下面我们逐层拆解。2. 硬件接线与关键原理2.1 USART通信的三个细节USART在STM32项目里几乎是必用的外设但跟串口屏通信时有三个细节很容易踩坑。第一是TX和RX必须交叉连接。串口屏的TX要接STM32的RX引脚串口屏的RX接STM32的TX。这个看起来是常识但接反的情况在初学者里太常见了尤其是那种多线排针的扩展板一不留神就连错。我用的是USART1也就是PA9TX和PA10RX串口屏的TX接PA10串口屏的RX接PA9。第二是电平标准。淘晶驰这类串口屏的接口是TTL电平发送高电平大约5VSTM32大部分USART引脚是5V容忍的直接连接没有大问题。但如果用的是RS232串口屏或者中间夹了USB转RS232线那电平是±12V接到STM32上是会烧芯片的必须先经过MAX3232之类的电平转换芯片。所以接线前先确认屏的接口类型是TTL、RS232还是RS485。第三是共地。串口屏、STM32、驱动器三者之间GND必须连在同一个参考地上否则电平判断会乱套出现屏发数据单片机收不到或者收到一堆乱码的现象。我见过不少案例最后排查一圈发现就是地线没接白白浪费半天时间。2.2 步进电机驱动的脉冲概念步进电机的控制看似复杂本质就一句话给驱动器发多少个脉冲电机就转多少个步距角。42步进电机的步距角通常是1.8°也就是说不细分的情况下200个脉冲转一圈。TB6600驱动器支持细分细分倍数可以是1、2、4、8、16。设成8细分后一圈需要1600个脉冲。细分的作用不只是提高精度更重要的是能显著降低电机在低速运行时的振动和噪音。这里有个重要的换算公式如果STM32输出的PWM频率是F单位Hz细分数是M那么电机转速圈/秒就是F /M × 200。举例来说8细分下PWM频率1600Hz电机每秒转1圈换算成每分钟就是60转。我习惯在实际代码里把目标速度换算成PWM的频率参数这样界面层直接显示RPM底层只关心定时器参数两边各管各的事。2.3 完整接线表与电源分配下面是我这套例程的完整接线表照着接基本不会有问题。串口屏引脚接STM32引脚说明TXPA10USART1_RXRXPA9USART1_TXGNDGND必须共地VCC5V部分屏支持3.3V看具体型号TB6600信号端接STM32引脚说明PUL-PA1脉冲输入负端DIR-PA2方向输入负端ENA-PA3使能输入负端PUL/DIR/ENA接驱动器公共端5V共阳极接法TB6600的PUL、DIR、ENA在共阳极接法下统一接5VPUL-、DIR-、ENA-由STM32的GPIO控制。注意不同驱动器对共阳极还是共阴极定义有区别务必看驱动器的说明书接反了会导致信号无效。电源分配上我建议驱动器使用独立电源本案例12V/3A不要和单片机、串口屏共用一路。步进电机启动瞬间电流很大如果和逻辑电路共用开关电源容易造成电压跌落轻则串口屏闪烁重启重则STM32复位这是很多人忽略的坑。3. 串口屏工程与界面配置3.1 界面控件和事件定义串口屏端的开发我以淘晶驰的VisualTFT软件为例。首先新建一个工程选对屏幕型号然后拖几个基本控件出来一个启动按钮、一个停止按钮、两个速度调节按钮、一个方向切换按钮再放一个文本显示控件用来回显当前速度值。淘晶驰的控件配置思路是每个控件可以绑定一个事件控件被触摸时就会按预设格式发送字符串到串口。比如启动按钮我给它配置发送内容为自定义帧帧格式预先约定好速度调节按钮可以发送不同的参数值。说白了串口屏唯一要做的事情就是捕捉用户操作并翻译成约定的字节流具体怎么做在界面配置阶段全部搞定。3.2 自定义通信帧设计在项目通信中我习惯定义一套精简且固定的帧协议而不是直接用串口屏厂商的私有协议。原因很简单以后换串口屏品牌只需要改屏端配置STM32端代码完全不用动。这套例程我用的是这样的帧格式帧头(2字节) 命令字(1字节) 数据长度(1字节) 数据区(N字节) 帧尾(2字节) 帧头0xAA 0x55 帧尾0x0D 0x0A即\r\n命令字定义如下命令含义数据区0x01启动电机无0x02停止电机无0x03速度加一档无0x04速度减一档无0x05方向切换无0x06直接设置目标速度1字节目标速度档位比如启动命令完整帧就是AA 55 01 00 0D 0A。设置速度为5档就是AA 55 06 01 05 0D 0A。这个协议很朴素但完全够用而且解析逻辑非常简单不容易出错。3.3 串口屏端的具体配置方法在VisualTFT里选中按钮控件进入事件配置页。不同版本界面略有差别核心操作是一样的把触发方式选为按下触发发送类型选为十六进制发送然后在发送内容里填入对应的HEX值。要注意的是淘晶驰在发送十六进制数据时格式是类似{0xAA 0x55 0x01 0x00 0x0D 0x0A}这样的大括号表达式而不是直接填字节。配置好之后先在软件里点仿真看是否能正常收发然后把工程下载到串口屏里。这时候可以用USB转TTL工具先单独测屏按下启动按钮看串口助手能不能收到AA 55 01 00 0D 0A。这一步非常关键能确保屏侧没问题再进入STM32侧的联调把问题范围缩小一半。4. STM32端代码实现4.1 工程框架与初始化STM32端我用的HAL库Keil5工程时钟配置为72MHz外设用到USART1、TIM1、以及三个GPIO输出引脚。USART1配置成115200-8-N-1开启接收中断。TIM1配置成PWM输出模式输出通道对应脉冲引脚PA8。注意我例程里PA1是作为普通IO接TB6600的PUL-PWM脉冲如果直接接到PA8需要把PA8和驱动器PUL-连在一起。这里说明一下为了方便接线例程中我把TIM1_CH1也就是PA8配置为PWM输出PA1、PA2、PA3根据实际需要选择如果读者手里的开发板引脚不方便可以改到其他定时器通道。初始化代码大致如下void MX_GPIO_Init(void) { // 使能GPIOA时钟 __HAL_RCC_GPIOA_CLK_ENABLE(); // PA2方向引脚、PA3使能引脚初始化为输出 GPIO_InitTypeDef GPIO_InitStruct {0}; GPIO_InitStruct.Pin GPIO_PIN_2 | GPIO_PIN_3; GPIO_InitStruct.Mode GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Speed GPIO_SPEED_FREQ_LOW; HAL_GPIO_Init(GPIOA, GPIO_InitStruct); // 默认方向正转默认使能 HAL_GPIO_WritePin(GPIOA, GPIO_PIN_2, GPIO_PIN_RESET); HAL_GPIO_WritePin(GPIOA, GPIO_PIN_3, GPIO_PIN_RESET); }4.2 USART接收状态机串口接收是这套代码最核心的地方。如果完全依赖HAL库的HAL_UART_Receive_IT一次收一串串口屏发出的帧长度不固定处理起来会很别扭。所以我改成逐字节接收用状态机拼帧只在收满一帧之后才做业务处理。状态机的定义typedef enum { FRAME_IDLE 0, FRAME_HEAD1, FRAME_HEAD2, FRAME_CMD, FRAME_LEN, FRAME_DATA, FRAME_END1, FRAME_END2 } FrameState;接收回调uint8_t rx_data; FrameState rx_state FRAME_IDLE; uint8_t rx_cmd, rx_len, rx_buffer[16]; uint8_t rx_index 0; void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { if (huart-Instance USART1) { switch (rx_state) { case FRAME_IDLE: if (rx_data 0xAA) rx_state FRAME_HEAD1; break; case FRAME_HEAD1: if (rx_data 0x55) rx_state FRAME_HEAD2; else rx_state FRAME_IDLE; break; case FRAME_HEAD2: rx_cmd rx_data; rx_state FRAME_CMD; break; case FRAME_CMD: rx_len rx_data; rx_index 0; if (rx_len 0) rx_state FRAME_DATA; else rx_state FRAME_DATA; break; case FRAME_DATA: if (rx_index rx_len) { rx_buffer[rx_index] rx_data; } if (rx_index rx_len) { rx_state FRAME_END1; } break; case FRAME_END1: if (rx_data 0x0D) rx_state FRAME_END2; else rx_state FRAME_IDLE; break; case FRAME_END2: if (rx_data 0x0A) { ProcessFrame(rx_cmd, rx_buffer, rx_len); } rx_state FRAME_IDLE; break; } // 继续开启下一次接收 HAL_UART_Receive_IT(huart1, rx_data, 1); } }这个状态机的思路很简单先等帧头再收命令字和长度接着收数据最后确认帧尾这样一帧完整无误才触发业务逻辑能过滤掉大部分串口干扰。4.3 PWM输出与速度换算脉冲输出用TIM1的PWM模式。这里最关键的是脉冲频率和速度档位之间的换算。假设一档速度对应160Hz脉冲8细分下160Hz就对应每秒0.1圈。读者可以根据需求调整。我代码里直接用速度档位乘以一个系数得到PWM频率。设置PWM频率的代码void SetMotorSpeed(uint8_t level) { // 速度限制 if (level 1) level 1; if (level 10) level 10; current_speed level; // 计算定时器计数值72MHz / 频率 // 例160Hz脉冲 - 72MHz / 160 450000 // 但ARR最大65535所以需要分频或降低脉冲频率范围 // 这里采用分频 ARR 组合预分频为72计数时钟1MHz __HAL_TIM_SET_PRESCALER(htim1, 72 - 1); // 1MHz计数 __HAL_TIM_SET_AUTORELOAD(htim1, (uint32_t)(1000000 / (level * 160)) - 1); __HAL_TIM_SET_COMPARE(htim1, TIM_CHANNEL_1, (uint32_t)(1000000 / (level * 160)) / 2); }上面的公式要注意预先分频设置成71让定时器计数频率从72MHz降到1MHz这样ARR在1~20000之间完全在16位范围内方便直接算频率。PWM的占空比设成50%因为步进电机驱动器更习惯识别50%占空比的方波脉冲。4.4 方向、使能和启停控制方向切换是电机控制里最需要小心的地方。直接从正转切反转如果不做处理电机可能瞬间承受大电流冲击。我实际使用时会先停止脉冲输出等待50ms再切换DIR引脚电平然后恢复脉冲输出。代码逻辑如下void SetMotorDirection(uint8_t dir) { // 先停脉冲 __HAL_TIM_PWM_STOP(htim1, TIM_CHANNEL_1); HAL_Delay(50); // 切换方向 HAL_GPIO_WritePin(GPIOA, GPIO_PIN_2, dir ? GPIO_PIN_SET : GPIO_PIN_RESET); // 恢复脉冲 if (motor_running) { __HAL_TIM_PWM_START(htim1, TIM_CHANNEL_1); } }使能引脚ENA在TB6600默认接法下低电平有效也就是说PA3输出低电平时驱动器正常工作输出高电平时电机脱机可以自由转动。我把使能引脚单独做成一个函数方便调试时让电机解脱避免手动搬动平台时跟驱动器较劲。5. 常见问题与排查5.1 串口屏没反应/乱码遇到这个现象优先级最高的排查方向是先确认接线。TX/RX有没有接反GND有没有共地这两点占了至少一半的问题比例。接线没问题的情况下再检查波特率是否一致项目里屏配置的波特率必须和STM32的USART配置完全相同一个115200一个9600必然全是乱码。补充一个小技巧在屏端调成发送ASCII字符串比如直接发送TEST\r\n然后用STM32串口打印接收到的字节或者在串口助手里直接观察。能收到正确的TEST说明物理链路基本没问题问题就只出在协议解析或界面配置上。5.2 电机不转、抖动、丢步电机不转先看驱动器的指示灯状态。TB6600通常有一个电源指示灯和一个使能指示灯如果使能灯没亮大概率是ENA信号接错或者电平给反了。其次检查PUL信号是否真的有脉冲输出用万用表量PUL-引脚电压有脉冲时电压会轻微摆动条件允许的情况下用示波器量波形最直观。电机抖动但不转最常见的原因是脉冲频率太低特别是细分数较大时一个很慢的转速在低细分档位下几乎是看不到连续旋转的。解决办法是适当提高脉冲频率或降低细分倍数让电机运行在更合适的工作区间。另一种可能是电源电流不够电机负载稍大就丢步表现为咔咔异响这时除了换大功率电源也可以考虑把速度调低一点。5.3 指令偶尔丢失或误触发串口指令偶尔丢失多半和中断处理有关。比如在USART接收中断里直接做大量运算或者调用HAL_Delay等待都会导致后续数据来不及接收缓冲区溢出。我的做法是在接收回调里只做状态机拼帧和数据搬运把ProcessFrame的完整处理放到主循环用标志位通知这样中断执行时间极短基本不会丢字节。还有一个常见误触发原因是帧头解析太宽松。比如没有校验长度一帧数据中间本来就包含0xAA 0x55状态机就可能误判成新帧头。我的对策是严格按帧头命令长度数据帧尾的流程走帧尾校验不通过就丢弃整帧重新等待帧头宁可丢帧也不要错帧。6. 实测过程中的几点体会这套例程调试的时候我最大的体会是串口屏项目一定要分步联调不要一上来就把屏、单片机、驱动器、电机全部接好。我的顺序是先用USB转TTL接串口屏在串口助手里看屏按键发出的原始字节确认屏的配置完全正确再用电脑串口助手直接给STM32发同样的帧观察电机的启动停止和速度变化确认单片机端的解析和控制逻辑没问题最后才把屏和STM32接在一起这时候如果出问题基本只剩环节接口的地方排查范围非常小。另外关于界面设计我建议把速度参数通过文本控件实时显示在屏上。一开始我只让屏往下发指令不清楚当前速度到底在第几档调试时经常搞混。后来在STM32里每次速度变化都主动向上发一帧更新界面文本可视化之后直观很多。串口屏不是单向的它既能发也能收利用好这个特性可以让整套设备的操作体验上一个台阶。这套例程目前的控制逻辑比较简单属于位置控制的最基础版本。后续如果要做闭环思路也很明确在电机轴上装编码器STM32定时器捕获编码器脉冲把实际位置和目标位置做比较用PID输出修正频率。如果各位手头有硬件先把这套开环流程跑通再一步步加反馈遇到具体问题可以随时交流。本文还有配套的精品资源点击获取
返回列表