这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » DIY与开源设计 » 电子DIY » 【Let'sdo2025年第4期基于单片机的直流电机控制】-过程贴

共1条 1/1 1 跳转至

【Let'sdo2025年第4期基于单片机的直流电机控制】-过程贴

助工
2026-01-11 13:28:14     打赏

项目概述

这是一个基于 NXP MCXA153 芯片的电机控制程序,实际上实现了一个完整的直流电机控制系统。该程序通过串口命令接口控制电机的转速、方向和启停,使用 CTimer 定时器生成 PWM 信号来调节电机速度。


功能实现核心功能

  1. PWM 速度控制:通过 CTimer 定时器生成可调占空比的 PWM 信号(频率 20kHz)

  2. 方向控制:通过 GPIO 引脚控制电机正反转(左转/右转)

  3. 使能控制:通过 STBY 引脚控制电机启动/停止

  4. 串口命令接口:提供命令行交互界面,实时控制电机

支持的命令命令参数说明

motor0-100设置电机速度(占空比百分比)
left-电机左转(正转)
right-电机右转(反转)
start-使能电机(解除待机)
stop-禁用电机(进入待机)
help-显示帮助信息

硬件接口GPIO 引脚配置引脚功能说明

GPIO1-1STBY电机使能控制(高电平使能)
GPIO1-0AIN1电机方向控制引脚1
GPIO1-2AIN2电机方向控制引脚2

PWM 输出

  • 定时器:CTimer

  • 匹配通道

    • 周期通道:kCTIMER_Match_3

    • 输出通道:kCTIMER_Match_2(CTIMER_MAT_OUT)

  • 频率:20 kHz

  • 占空比:0-100%(可调)

电机驱动逻辑AIN1AIN2电机状态

01左转(正转)
10右转(反转)
00停止(刹车)
11停止(刹车)

程序流程图

┌─────────────────────────────────────┐
│         程序开始 (main)             │
└──────────────┬──────────────────────┘
              │
              ▼
┌─────────────────────────────────────┐
│   初始化硬件 (BOARD_InitHardware)  │
│   - 时钟初始化                       │
│   - 引脚复用配置                     │
│   - 调试控制台初始化                 │
└──────────────┬──────────────────────┘
              │
              ▼
┌─────────────────────────────────────┐
│     初始化 GPIO 引脚                 │
│   - AIN_init(): AIN1/AIN2           │
│   - STBY_init(): STBY引脚           │
└──────────────┬──────────────────────┘
              │
              ▼
┌─────────────────────────────────────┐
│     配置 CTimer 定时器               │
│   - 获取默认配置                     │
│   - 初始化定时器                     │
│   - 计算时钟频率                     │
│   - 启动定时器                       │
└──────────────┬──────────────────────┘
              │
              ▼
┌─────────────────────────────────────┐
│  设置初始电机速度 (Motor_speed 50%)  │
└──────────────┬──────────────────────┘
              │
              ▼
      ┌───────┴───────┐
      │   主循环开始   │
      └───────┬───────┘
              │
              ▼
      ┌───────┴───────┐
      │  接收串口字符  │
      │     (ch)      │
      └───────┬───────┘
              │
      ┌───────┴───────────────┐
      │                       │
      ▼                       ▼
┌─────────────┐        ┌─────────────┐
│ 回车/换行符? │        │  其他字符   │
└──────┬──────┘        └──────┬──────┘
       │                      │
       ▼                      ▼
 ┌──────────┐          ┌──────────┐
 │是:        │          │否:        │
 │处理命令  │          │退格?回退  │
 │清空缓冲区│          │其他:存入  │
 └────┬─────┘          │缓冲区回显│
      │                └────┬─────┘
      │                     │
      └─────────┬───────────┘
                │
                ▼
      ┌─────────┴─────────┐
      │  handle_command   │
      │  解析命令字符串   │
      └─────────┬─────────┘
                │
       ┌────────┴────────┐
       │                 │
       ▼                 ▼
  ┌────────┐       ┌────────┐
  │ motor  │       │ left   │
  │ 设置PWM│       │ AIN1=0 │
  │       │       │ AIN2=1 │
  └───┬────┘       └───┬────┘
      │                │
       ──────┬─────────┘
             │
             ▼
  ┌────────┐
  │ right  │
  │ AIN1=1 │
  │ AIN2=0 │
  └───┬────┘
      │
       ──────┬─────────┐
             │         │
             ▼         ▼
  ┌────────┐  ┌────────┐
  │ start  │  │ stop   │
  │STBY=1  │  │STBY=0  │
  │ 使能电机│  │ 禁用电机│
  └───┬────┘  └───┬────┘
      │          │
      └────┬─────┘
           │
           ▼
   ┌───────┴───────┐
   │  返回主循环   │
   └───────────────┘

关键函数说明1. Motor_speed(uint8_t speedPercent)

功能:设置电机速度   参数

  • speedPercent: 速度百分比(0-100)

实现原理

// 计算PWM周期值(固定20kHz频率)
g_pwmPeriod = (timerClock / 20000) - 1U;

// 计算脉冲宽度(占空比)
g_pulsePeriod = (g_pwmPeriod + 1U) * (100 - speedPercent) / 100;

// 应用PWM设置
CTIMER_SetupPwmPeriod(CTIMER, CTIMER_MAT_PWM_PERIOD_CHANNEL,
                     CTIMER_MAT_OUT, g_pwmPeriod, g_pulsePeriod, false);

2. Motor_button(bool start)

功能:控制电机方向   参数

  • start = true: 左转(AIN1=0, AIN2=1)

  • start = false: 右转(AIN1=1, AIN2=0)

3. STBY_button(bool enable)

功能:控制电机使能状态   参数

  • enable = true: 启动电机(STBY=1)

  • enable = false: 停止电机(STBY=0)

4. handle_command(char *line)

功能:解析并执行串口命令   支持的命令

  • motor <speed>: 设置速度

  • left: 左转

  • right: 右转

  • start: 启动

  • stop: 停止

  • help: 帮助


PWM 计算原理周期计算

PWM频率 = 20 kHz
PWM周期值 = (时钟频率 / PWM频率) - 1
        = (timerClock / 20000) - 1

脉冲宽度计算

脉冲宽度值 = (周期值 + 1) × (100 - 占空比%) / 100

示例:假设时钟频率 12 MHz,速度 50%

PWM周期值 = (12000000 / 20000) - 1 = 599
脉冲宽度值 = (599 + 1) × (100 - 50) / 100 = 300
占空比 = 300 / 600 = 50%

代码详解全局变量

volatile uint32_t g_pwmPeriod   = 0U;   // PWM 周期值(计数器计数值)
volatile uint32_t g_pulsePeriod = 0U;   // PWM 脉冲宽度值(占空比控制)

uint32_t srcClock_Hz;                   // CTimer 源时钟频率
uint32_t timerClock;                    // CTimer 实际工作时钟频率(分频后)

#define MAX_BUFFER_LEN 64
char s_buffer[MAX_BUFFER_LEN];          // 串口命令缓冲区
uint32_t s_buffer_idx = 0;              // 缓冲区当前索引位置

#define AIN1_GPIO GPIO1
#define AIN1_PIN 0U                     // 电机方向控制引脚1
#define AIN2_GPIO GPIO1
#define AIN2_PIN 2U                     // 电机方向控制引脚2

说明

  • volatile 关键字确保变量在多线程或中断环境中能正确读取

  • 使用 #define 宏定义硬件引脚,便于移植和修改

  • 命令缓冲区支持最长 63 个字符的命令(保留 1 字节用于字符串结束符)


1. CTimer_GetPwmPeriodValue() 函数

status_t CTIMER_GetPwmPeriodValue(uint32_t pwmFreqHz, uint8_t dutyCyclePercent, uint32_t timerClock_Hz)
{
   /* 计算 PWM 周期匹配值 */
   g_pwmPeriod = (timerClock_Hz / pwmFreqHz) - 1U;

   /* 计算脉冲宽度匹配值 */
   g_pulsePeriod = (g_pwmPeriod + 1U) * (100 - dutyCyclePercent) / 100;

   return kStatus_Success;
}

代码解析

  1. PWM 周期计算

    g_pwmPeriod = (timerClock_Hz / pwmFreqHz) - 1U;
    • timerClock_Hz: 定时器时钟频率(例如 12 MHz)

    • pwmFreqHz: 目标 PWM 频率(20,000 Hz)

    • -1U: 因为定时器从 0 开始计数,所以要减 1

    • 例如:(12000000 / 20000) - 1 = 599

  2. 脉冲宽度计算

    g_pulsePeriod = (g_pwmPeriod + 1U) * (100 - dutyCyclePercent) / 100;
    • (g_pwmPeriod + 1U): 总周期计数值(加 1 抵消之前的 -1)

    • (100 - dutyCyclePercent): 计算低电平时间百分比

    • 例如:50% 占空比 → (600 * 50) / 100 = 300

PWM 工作原理

高电平时间 = g_pulsePeriod 计数值
低电平时间 = g_pwmPeriod - g_pulsePeriod 计数值
占空比 = 高电平时间 / (高电平时间 + 低电平时间)

2. Motor_speed() 函数

void Motor_speed(uint8_t speedPercent)
{
   if (speedPercent > 100U)
   {
       speedPercent = 100U;  // 参数保护,限制最大值为 100%
   }
   
   // 计算周期值(固定 20kHz 频率)
   g_pwmPeriod = (timerClock / 20000) - 1U;
   
   // 计算脉冲宽度(占空比)
   g_pulsePeriod = (g_pwmPeriod + 1U) * (100 - speedPercent) / 100;
   
   // 应用 PWM 设置
   CTIMER_SetupPwmPeriod(CTIMER, CTIMER_MAT_PWM_PERIOD_CHANNEL,
                         CTIMER_MAT_OUT, g_pwmPeriod, g_pulsePeriod, false);
}

代码解析

  1. 参数保护

    if (speedPercent > 100U)
    {
       speedPercent = 100U;
    }
    • 防止用户输入超过 100% 的值

    • 确保占空比始终在有效范围内

  2. 固定频率设计

    g_pwmPeriod = (timerClock / 20000) - 1U;
    • 20kHz 是电机控制的常用频率

    • 频率过高会增加开关损耗

    • 频率过低会导致电机噪音

  3. CTimer_SetupPwmPeriod() 参数说明

    • CTIMER: 定时器基地址

    • CTIMER_MAT_PWM_PERIOD_CHANNEL: 周期匹配通道(Match_3)

    • CTIMER_MAT_OUT: 输出匹配通道(Match_2)

    • g_pwmPeriod: 周期计数值

    • g_pulsePeriod: 脉冲计数值

    • false: 不使能中断


3. AIN_init() 和 STBY_init() 函数

void STBY_init(void)
{
   gpio_pin_config_t led_config = {kGPIO_DigitalOutput, 1};
   GPIO_PinInit(GPIO1, 1U, &led_config);  // 初始化 GPIO1-1 为输出
}

void AIN_init(void)
{
   gpio_pin_config_t led_config = {kGPIO_DigitalOutput, 1};
   GPIO_PinInit(AIN1_GPIO, AIN1_PIN, &led_config);  // 初始化 AIN1
   GPIO_PinInit(AIN2_GPIO, AIN2_PIN, &led_config);  // 初始化 AIN2
}

代码解析

  1. GPIO 配置结构体

    gpio_pin_config_t led_config = {kGPIO_DigitalOutput, 1};
    • kGPIO_DigitalOutput: 设置为数字输出模式

    • 1: 初始输出高电平

  2. GPIO_PinInit() 参数

    • 第一个参数:GPIO 端口号(GPIO1)

    • 第二个参数:引脚号(0, 1, 2)

    • 第三个参数:配置结构体指针

硬件连接

MCXA153 GPIO1-1 ───> TB6612FNG STBY 引脚
MCXA153 GPIO1-0 ───> TB6612FNG AIN1 引脚
MCXA153 GPIO1-2 ───> TB6612FNG AIN2 引脚

4. Motor_button() 方向控制函数

void Motor_button(bool start)
{
   if (start)
   {
       GPIO_PinWrite(AIN1_GPIO, AIN1_PIN, 0);  // AIN1 = 0
       GPIO_PinWrite(AIN2_GPIO, AIN2_PIN, 1);  // AIN2 = 1
   }
   else
   {
       GPIO_PinWrite(AIN1_GPIO, AIN1_PIN, 1);  // AIN1 = 1
       GPIO_PinWrite(AIN2_GPIO, AIN2_PIN, 0);  // AIN2 = 0
   }
}

代码解析

  1. 布尔参数设计

    • start = true: 左转(正转)

    • start = false: 右转(反转)

  2. GPIO_PinWrite() 函数

    GPIO_PinWrite(端口, 引脚, 值);
    • 值为 0 或 1

    • 直接控制 GPIO 引脚的电平状态

电机驱动器真值表(TB6612FNG)

IN1IN2OUT1OUT2电机状态

01HL正转
10LH反转
00LL刹车
11HH刹车

5. handle_command() 命令处理函数

void handle_command(char *line)
{
   char *cmd = strtok(line, " ");  // 使用空格分隔命令
   if (cmd == NULL) return;

   if (strcmp(cmd, "help") == 0) {
       PRINTF("motor <speed> - Set motor speed (0-100)\r\n");
   }
   else if (strcmp(cmd, "left") == 0) {
       Motor_button(true);  // 调用左转函数
   }
   else if (strcmp(cmd, "right") == 0) {
       Motor_button(false);  // 调用右转函数
   }
   else if (strcmp(cmd, "motor") == 0) {
       char *speedStr = strtok(NULL, " ");  // 获取第二个参数(速度值)
       if (speedStr != NULL) {
           uint8_t speed = (uint8_t)atoi(speedStr);  // 字符串转整数
           Motor_speed(speed);
           PRINTF("Motor speed set to %d%%\r\n", speed);
       } else {
           PRINTF("Error: Speed value required\r\n");
       }
   }
   else if (strcmp(cmd, "stop") == 0)
   {
       STBY_button(false);  // 拉低 STBY,电机进入待机
   }
   else if (strcmp(cmd, "start") == 0)
   {
       STBY_button(true);   // 拉高 STBY,电机使能
   }
   else {
       PRINTF("Unknown: %s\r\n", cmd);
   }
}

代码解析

  1. 字符串分割 - strtok()

    char *cmd = strtok(line, " ");
    • 第一次调用:分割第一个空格前的字符串(命令名)

    • 第二次调用:strtok(NULL, " ") 继续分割后续参数

    • 使用空格作为分隔符

  2. 字符串比较 - strcmp()

    if (strcmp(cmd, "motor") == 0)
    • 返回 0 表示字符串相等

    • 用于匹配用户输入的命令

  3. 参数解析示例

    用户输入: "motor 75"

    第一次 strtok(NULL, " ") → "motor" (命令名)
    第二次 strtok(NULL, " ") → "75"   (参数)
    atoi("75") → 75 (整数)
    Motor_speed(75) → 设置 PWM 占空比为 75%

6. main() 函数详解

int main(void)
{
   char ch;
   ctimer_config_t config;

   /* 1. 初始化硬件 */
   BOARD_InitHardware();  // 时钟、引脚复用、控制台初始化
   
   /* 2. 初始化 GPIO 引脚 */
   AIN_init();            // 初始化电机方向引脚
   STBY_init();           // 初始化电机使能引脚
   
   PRINTF("MCUX SDK version: %s\r\n", MCUXSDK_VERSION_FULL_STR);

   /* 3. 配置 CTimer */
   srcClock_Hz = CTIMER_CLK_FREQ;  // 获取 CTimer 源时钟频率
   PRINTF("CTimer example to generate a PWM signal\r\n");
   
   CTIMER_GetDefaultConfig(&config);  // 获取默认配置
   CTIMER_Init(CTIMER, &config);      // 初始化定时器
   
   // 计算定时器实际时钟频率(考虑预分频)
   timerClock = srcClock_Hz / (config.prescale + 1);
   
   CTIMER_StartTimer(CTIMER);  // 启动定时器

   /* 4. 设置初始速度 */
   Motor_speed(50);  // 默认 50% 速度

   /* 5. 主循环 - 串口命令处理 */
   while (1)
   {
       ch = GETCHAR();  // 从串口读取一个字符(阻塞等待)
       
       if (ch == '\r' || ch == '\n')  // 回车或换行
       {
           if (s_buffer_idx > 0)
           {
               s_buffer[s_buffer_idx] = '\0';  // 添加字符串结束符
               PRINTF("\r\n");
               handle_command(s_buffer);      // 处理命令
               s_buffer_idx = 0;              // 重置缓冲区索引
               memset(s_buffer, 0, MAX_BUFFER_LEN);  // 清空缓冲区
           }
       }
       else if (ch == 0x08 || ch == 0x7F)  // 退格键 (BS 或 DEL)
       {
           if (s_buffer_idx > 0) {
               s_buffer_idx--;           // 索引减 1
               PUTCHAR('\b');            // 输出退格字符
               PUTCHAR(' ');             // 输出空格覆盖
               PUTCHAR('\b');            // 再次退格
           }
       }
       else if (s_buffer_idx < (MAX_BUFFER_LEN - 1))  // 缓冲区未满
       {
           s_buffer[s_buffer_idx++] = ch;  // 存入字符
           PUTCHAR(ch);                    // 回显到终端
       }
   }
}

代码解析

  1. 初始化阶段

    • BOARD_InitHardware(): SDK 提供的硬件初始化函数

    • CTIMER_GetDefaultConfig(): 获取定时器默认配置(预分频值等)

    • CTIMER_Init(): 应用配置并初始化定时器寄存器

  2. 时钟频率计算

    timerClock = srcClock_Hz / (config.prescale + 1);
    • 例如:源时钟 12 MHz,预分频 0

    • 实际时钟 = 12000000 / (0 + 1) = 12 MHz

  3. 命令缓冲区处理

    if (ch == '\r' || ch == '\n')  // 检测回车键
    • \r (CR) 和 \n (LF) 都作为命令结束符

    • 兼容不同终端的换行标准

  4. 退格键处理

    else if (ch == 0x08 || ch == 0x7F)
    • 0x08: Backspace 键

    • 0x7F: Delete 键

    • 通过输出 \b\b 实现退格显示效果

  5. 字符回显

    PUTCHAR(ch);
    • 将用户输入的字符立即输出到终端

    • 让用户看到自己输入的内容


数据结构说明ctimer_config_t 结构体(简化版)

typedef struct _ctimer_config
{
   uint32_t prescale;        // 预分频值(0-255)
   uint8_t counterMode;      // 计数模式
   uint8_t inputSource;      // 时钟源选择
   // ... 其他配置项
} ctimer_config_t;

gpio_pin_config_t 结构体

typedef struct _gpio_pin_config
{
   gpio_pin_direction_t direction;  // 引脚方向(输入/输出)
   uint8_t outputLogic;            // 初始输出电平(0/1)
   // ... 其他配置项
} gpio_pin_config_t;

串口显示

image.png

电机转动

image.png


共1条 1/1 1 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]