项目概述
这是一个基于 NXP MCXA153 芯片的电机控制程序,实际上实现了一个完整的直流电机控制系统。该程序通过串口命令接口控制电机的转速、方向和启停,使用 CTimer 定时器生成 PWM 信号来调节电机速度。
功能实现核心功能
PWM 速度控制:通过 CTimer 定时器生成可调占空比的 PWM 信号(频率 20kHz)
方向控制:通过 GPIO 引脚控制电机正反转(左转/右转)
使能控制:通过 STBY 引脚控制电机启动/停止
串口命令接口:提供命令行交互界面,实时控制电机
支持的命令命令参数说明
| motor | 0-100 | 设置电机速度(占空比百分比) |
| left | - | 电机左转(正转) |
| right | - | 电机右转(反转) |
| start | - | 使能电机(解除待机) |
| stop | - | 禁用电机(进入待机) |
| help | - | 显示帮助信息 |
硬件接口GPIO 引脚配置引脚功能说明
| GPIO1-1 | STBY | 电机使能控制(高电平使能) |
| GPIO1-0 | AIN1 | 电机方向控制引脚1 |
| GPIO1-2 | AIN2 | 电机方向控制引脚2 |
PWM 输出
定时器:CTimer
匹配通道:
周期通道:kCTIMER_Match_3
输出通道:kCTIMER_Match_2(CTIMER_MAT_OUT)
频率:20 kHz
占空比:0-100%(可调)
电机驱动逻辑AIN1AIN2电机状态
| 0 | 1 | 左转(正转) |
| 1 | 0 | 右转(反转) |
| 0 | 0 | 停止(刹车) |
| 1 | 1 | 停止(刹车) |
程序流程图
┌─────────────────────────────────────┐
│ 程序开始 (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;
}
代码解析:
PWM 周期计算
g_pwmPeriod = (timerClock_Hz / pwmFreqHz) - 1U;
timerClock_Hz: 定时器时钟频率(例如 12 MHz)
pwmFreqHz: 目标 PWM 频率(20,000 Hz)
-1U: 因为定时器从 0 开始计数,所以要减 1
例如:(12000000 / 20000) - 1 = 599
脉冲宽度计算
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);
}
代码解析:
参数保护
if (speedPercent > 100U)
{
speedPercent = 100U;
}防止用户输入超过 100% 的值
确保占空比始终在有效范围内
固定频率设计
g_pwmPeriod = (timerClock / 20000) - 1U;
20kHz 是电机控制的常用频率
频率过高会增加开关损耗
频率过低会导致电机噪音
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
}
代码解析:
GPIO 配置结构体
gpio_pin_config_t led_config = {kGPIO_DigitalOutput, 1};kGPIO_DigitalOutput: 设置为数字输出模式
1: 初始输出高电平
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
}
}
代码解析:
布尔参数设计
start = true: 左转(正转)
start = false: 右转(反转)
GPIO_PinWrite() 函数
GPIO_PinWrite(端口, 引脚, 值);
值为 0 或 1
直接控制 GPIO 引脚的电平状态
电机驱动器真值表(TB6612FNG):
IN1IN2OUT1OUT2电机状态
| 0 | 1 | H | L | 正转 |
| 1 | 0 | L | H | 反转 |
| 0 | 0 | L | L | 刹车 |
| 1 | 1 | H | H | 刹车 |
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);
}
}
代码解析:
字符串分割 - strtok()
char *cmd = strtok(line, " ");
第一次调用:分割第一个空格前的字符串(命令名)
第二次调用:strtok(NULL, " ") 继续分割后续参数
使用空格作为分隔符
字符串比较 - strcmp()
if (strcmp(cmd, "motor") == 0)
返回 0 表示字符串相等
用于匹配用户输入的命令
参数解析示例
用户输入: "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); // 回显到终端
}
}
}
代码解析:
初始化阶段
BOARD_InitHardware(): SDK 提供的硬件初始化函数
CTIMER_GetDefaultConfig(): 获取定时器默认配置(预分频值等)
CTIMER_Init(): 应用配置并初始化定时器寄存器
时钟频率计算
timerClock = srcClock_Hz / (config.prescale + 1);
例如:源时钟 12 MHz,预分频 0
实际时钟 = 12000000 / (0 + 1) = 12 MHz
命令缓冲区处理
if (ch == '\r' || ch == '\n') // 检测回车键
\r (CR) 和 \n (LF) 都作为命令结束符
兼容不同终端的换行标准
退格键处理
else if (ch == 0x08 || ch == 0x7F)
0x08: Backspace 键
0x7F: Delete 键
通过输出 \b、、\b 实现退格显示效果
字符回显
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;
串口显示

电机转动

我要赚赏金
