You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于Raspberry Pi Pico 2的LCD触控步进电机转速异常排查

步进电机转速过低问题排查与优化方案

问题根源分析

  1. 主循环阻塞导致电机步序延迟
    代码依赖主循环轮询motor_state标志更新电机状态,但主循环包含触摸检测、屏幕绘制等耗时操作,会导致电机步序无法按时触发。即使设置了1000us定时器,主循环阻塞也会让实际步序间隔远大于预期。

  2. 低效的电机驱动方式
    当前采用单线圈通电的单步驱动模式,扭矩低、转速上限远低于双线圈通电的全步/半步模式,且单步切换响应速度更慢。

  3. 屏幕绘制占用过多CPU资源
    drawImagem函数每次先填充32x24矩形再绘制位图,LCD操作本身耗时,会大幅抢占电机控制的CPU时间,导致步序更新不及时。

  4. 触摸检测的无意义轮询
    主循环持续调用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.11 10:24:54