基于Raspberry Pi Pico 2的LCD触控步进电机转速异常排查
步进电机转速过低问题排查与优化方案
问题根源分析
主循环阻塞导致电机步序延迟
代码依赖主循环轮询motor_state标志更新电机状态,但主循环包含触摸检测、屏幕绘制等耗时操作,会导致电机步序无法按时触发。即使设置了1000us定时器,主循环阻塞也会让实际步序间隔远大于预期。低效的电机驱动方式
当前采用单线圈通电的单步驱动模式,扭矩低、转速上限远低于双线圈通电的全步/半步模式,且单步切换响应速度更慢。屏幕绘制占用过多CPU资源
drawImagem函数每次先填充32x24矩形再绘制位图,LCD操作本身耗时,会大幅抢占电机控制的CPU时间,导致步序更新不及时。触摸检测的无意义轮询
主循环持续调用readPoint做触摸检测,未设置间隔或防抖,持续占用CPU资源,进一步加剧主循环阻塞。
优化方案与代码修改
核心优化方向
- 利用Pico 2双核分离任务:核心1独立运行电机控制,核心0处理UI与触摸,避免互相干扰
- 升级电机驱动为双线圈全步模式:提升扭矩与转速上限
- 优化屏幕绘制逻辑:仅在必要时更新图标,减少绘制开销
- 触摸检测添加防抖:避免重复触发,降低CPU占用
修改后的完整代码
#include <stdio.h> #include "pico/stdlib.h" #include "pico/multicore.h" #include "tft_lcd_ili9341/gfx/gfx_ili9341.h" #include "tft_lcd_ili9341/ili9341/ili9341.h" #include "tft_lcd_ili9341/touch_resistive/touch_resistive.h" #include "image_bitmap.h" #define SCREEN_ROTATION 1 const int width = 320; const int height = 240; const int BOBINAS[4] = {5, 4, 3, 2}; // 双线圈全步步序表(正/反转) const uint8_t FULL_STEP_SEQ[2][4] = { {0b1010, 0b0110, 0b0101, 0b1001}, // 顺时针 {0b1001, 0b0101, 0b0110, 0b1010} // 逆时针 }; const int rotImgPosX = (width - 32) / 2; const int rotImgPosY = (height - 24) / 2; // 跨核心共享变量,需加volatile保证可见性 volatile int motor_on = 0; volatile int direcao = 1; volatile int update_image_flag = 0; void ledButtonCallback(GFX_Button *btn) { direcao = -direcao; motor_on = 1; update_image_flag = 1; } void drawImagem() { gfx_fillRect(rotImgPosX, rotImgPosY, 32, 24, 0x0000); if (direcao == 1){ // 顺时针 static int img = 0; switch(img) { case 0: gfx_drawBitmap(rotImgPosX, rotImgPosY, horario_1, 24, 32, 0xFFFF); break; case 1: gfx_drawBitmap(rotImgPosX, rotImgPosY, horario_2, 32, 24, 0xFFFF); break; case 2: gfx_drawBitmap(rotImgPosX, rotImgPosY, horario_3, 24, 32, 0xFFFF); break; case 3: gfx_drawBitmap(rotImgPosX, rotImgPosY, horario_4, 32, 24, 0xFFFF); break; } img = (img + 1) % 4; } else { // 逆时针 static int img = 0; switch(img) { case 0: gfx_drawBitmap(rotImgPosX, rotImgPosY, anti_horario_4, 32, 24, 0xFFFF); break; case 1: gfx_drawBitmap(rotImgPosX, rotImgPosY, anti_horario_3, 24, 32, 0xFFFF); break; case 2: gfx_drawBitmap(rotImgPosX, rotImgPosY, anti_horario_2, 32, 24, 0xFFFF); break; case 3: gfx_drawBitmap(rotImgPosX, rotImgPosY, anti_horario_1, 24, 32, 0xFFFF); break; } img = (img + 1) % 4; } } // 核心1:独立处理电机控制,不受UI操作干扰 void core1_motor_task() { // 初始化电机GPIO for (int i = 0; i < 4; i++) { gpio_init(BOBINAS[i]); gpio_set_dir(BOBINAS[i], GPIO_OUT); } int step_idx = 0; const uint32_t STEP_DELAY_US = 500; // 调整此值改变转速,越小越快(需匹配电机性能) while (true) { if (motor_on) { // 加载当前步序并更新GPIO uint8_t step = FULL_STEP_SEQ[direcao == 1 ? 0 : 1][step_idx]; for (int i = 0; i < 4; i++) { gpio_put(BOBINAS[i], (step >> (3 - i)) & 1); } step_idx = (step_idx + 1) % 4; busy_wait_us_32(STEP_DELAY_US); // 精确延迟,不受主循环影响 } else { // 电机关闭时重置GPIO for (int i = 0; i < 4; i++) { gpio_put(BOBINAS[i], 0); } sleep_ms(10); // 低功耗等待 } } } int main() { stdio_init_all(); // 启动核心1的电机控制任务 multicore_launch_core1(core1_motor_task); // 初始化LCD与触摸 LCD_initDisplay(); LCD_setRotation(SCREEN_ROTATION); configure_touch(); gfx_init(); gfx_clear(); // 绘制初始UI gfx_setTextSize(2); gfx_setTextColor(0x07E0); gfx_drawText(width/6, 10, "PicoDock Motor Control"); GFX_Button ledButton = { .x = rotImgPosX, .y = rotImgPosY, .w = 32, .h = 32, .callback = ledButtonCallback }; gfx_registerButton(&ledButton); drawImagem(); uint32_t last_touch_time = 0; const uint32_t TOUCH_DEBOUNCE_MS = 200; // 触摸防抖间隔 while (true) { // 带防抖的触摸检测 uint32_t now = to_ms_since_boot(get_absolute_time()); if (now - last_touch_time > TOUCH_DEBOUNCE_MS) { int touchRawX, touchRawY; int screenTouchX, screenTouchY = 0; int touchDetected = readPoint(&touchRawX, &touchRawY); if (touchDetected) { gfx_touchTransform(SCREEN_ROTATION, touchRawX, touchRawY, &screenTouchX, &screenTouchY); gfx_updateButtons(screenTouchX, screenTouchY, touchDetected); last_touch_time = now; } } // 仅在方向切换时更新图标 if (update_image_flag) { drawImagem(); update_image_flag = 0; } sleep_ms(10); // 降低主循环CPU占用 } return 0; }
额外优化建议
- 调整
STEP_DELAY_US值:根据电机规格逐步减小该值,直到达到预期转速(注意电机扭矩随转速升高而降低,需平衡) - 使用DMA驱动GPIO:若需要更高转速,可利用Pico的DMA外设自动切换GPIO状态,完全脱离CPU控制
- 优化屏幕驱动:如果LCD库支持帧缓冲,可预先缓存图标,减少绘制时的IO操作
内容的提问来源于stack exchange,提问作者Lara Vasconcelos
相关产品推荐
相关产品推荐

