LKS32MC08x MCPWM 寄存器级控制:实现电机 P/N 安全互锁与速度控制
本文介绍基于LKS32MC08x芯片的电机控制实现方案。通过分析直流电机驱动需求,针对官方驱动库存在的寄存器批量配置问题和项目需要的PN互锁功能,提出自定义解决方案。重点实现了SetMotorDirectionAndSpeed()函数,支持速度(±1000)和方向控制,采用直接寄存器操作方式确保PN互锁安全。详细阐述了MCPWM配置方法,包括写保护机制、通道强制输出设置和比较值更新策略。该方案优化
目录
寄存器使用实战
需求与硬件连接
有一直流电机连着一个电机驱动模块的P端和N端。
当电驱模块的P端输入PWM,N端为低电平时,电机正转且跟随PWM的占空比变换速度;当电模块的N端输入PWM,P端为低电平时,电机反转且跟随PWM的占空比变换速度。
现电驱模块的P端与 LKS32MC08x 的MCPWM的CH0P相连,电驱模块的N端与 LKS32MC08x 的MCPWM的CH0N相连。
需要写一个函数,实现对该电机的速度和方向进行控制。函数输入输出和名字设计如下:
void SetMotorDirectionAndSpeed(uint8_t channel, int16_t speed);
// speed > 0: 正转,占空比 = speed
// speed < 0: 反转,占空比 = -speed
// speed == 0: 停止
官方驱动的局限性
LKS32 的 MCPWM 模块提供MCPWM_Init()函数来配置模块。然而它采用的是整体配置,一次性设置所有的寄存器,包括工作模式、周期、所有通道的比较值、死区时间、故障保护、中断使能等。即使我们只是想改CH0P/CH0N的输出方向,调用它也是如此。另外,如果软件没有P/N互锁的设计,P和N或许会短暂同时为正,烧毁电驱的MOSFET。
因此笔者自己写模块~
子功能实现
PN互锁
为了实现 P 和 N 端的绝对互锁,我们必须确保:当其中一方输出 PWM 信号时,另一方必须被强制设定为低电平(0)。
在 MCPWM 的 IO 控制寄存器 (MCPWM_IO01 / MCPWM_IO23) 中,控制互锁的关键宏定义是:
CHx_PS / CHx_NS:PWM 信号来源选择(1:强制模式;0:PWM 模式)。
CHxP_SCTRLP / CHxN_SCTRLN:在强制模式下(CHx_PS/CHx_NS=1)的输出值(1:高电平;0:低电平)。
要实现 “强制低电平”,需设置:CHx_PS (或 CHx_NS) = 1 且 CHxP_SCTRLP (或 CHxN_SCTRLN) = 0。
这一步将融合进一个叫MCPWM_SetChannelForceOutput()的函数,用于SetMotorDirectionAndSpeed()中调用,在后续“MCPWM配置的实现方式”一节中有它的实现。
uint8_t CH0_PS; /* CH0_P信号来源,1:来自CH0_SCTRLP;0:来自MCPWM实际运行计数器*/
uint8_t CH0_NS; /* CH0_N信号来源,1:来自CH0_SCTRLN;0:来自MCPWM实际运行计数器*/
uint8_t CH0P_SCTRLP; /* 当CH0_PS = 1时,输出到CH0_P通道的值*/
uint8_t CH0N_SCTRLN; /* 当CH0_NS = 1时,输出到CH0_N通道的值*/
写保护

所以,只需 MCPWM_PRT = 0xDEAD; 就能解除写保护;
写入其他值, 如 MCPWM_PRT = 0xCAFE; 就能重新加保护。
/* ---------------------- MCPWM 保护操作宏 --------------------------------- */
#define MCPWM_UNLOCK() do { MCPWM_PRT = 0xDEAD; } while(0)
#define MCPWM_LOCK() do { MCPWM_PRT = 0xCAFE; } while(0)
MCPWM配置的实现方式
采用宏+直接修改MCPWM_IO01控制寄存器位的方式来实现MCPWM配置。
MCPWM_SetChannelForceOutput用于设置某通道P/N的强制输出状态,MCPWM_IO01控制寄存器位定义如下图。

代码扩展了0~3共4个PWM通道。

这里的BITx定义在basic.h里,是LKS的一个pack。是一u32,第x位(低到高编号递增)为1,其它位为0。
// 内部辅助函数:设置某通道 P/N 的强制输出状态
static void MCPWM_SetChannelForceOutput(uint8_t channel, uint8_t p_pwm, uint8_t n_pwm)
{
if (channel > 3) return; // 通道大于3,无效值
if((p_pwm & 1u) && (n_pwm & 1u)) return; // 最低位为1为真即非0判断,不允许p_pwm和n_pwm同时输出
MCPWM_UNLOCK();
switch (channel)
{
case 0:
if (p_pwm)
{
MCPWM_IO01 |= BIT2; // CH0_NS = 1 → CH0_SCTRLN
MCPWM_IO01 &= ~BIT4; // CH0_SCTRLN,当 CH0_NS = 1 时, CH0 N通道输出值
MCPWM_IO01 &= ~BIT3; // CH0_PS = 0 → PWM
}
if (n_pwm)
{
MCPWM_IO01 |= BIT3; // CH0_PS = 1 → CH0_SCTRLP
MCPWM_IO01 &= ~BIT5; // CH0_SCTRLP,当 CH0_PS = 1 时, CH0 P通道输出值
MCPWM_IO01 &= ~BIT2; // CH0_NS = 0 → PWM
}
break;
case 1:
if (p_pwm)
{
MCPWM_IO01 |= BIT10; // CH1_NS = 1 → CH1_SCTRLN
MCPWM_IO01 &= ~BIT12; // CH1_SCTRLN,当 CH1_NS = 1 时, CH1 N通道输出值
MCPWM_IO01 &= ~BIT11; // CH1_PS = 0 → PWM
}
if (n_pwm)
{
MCPWM_IO01 |= BIT11; // CH1_PS = 1 → CH1_SCTRLP
MCPWM_IO01 &= ~BIT13; // CH1_SCTRLP,当 CH1_PS = 1 时, CH1 P通道输出值
MCPWM_IO01 &= ~BIT10; // CH1_NS = 0 → PWM
}
break;
case 2:
if (p_pwm)
{
MCPWM_IO23 |= BIT2; // CH2_NS = 1 → CHI2_SCTRLN
MCPWM_IO23 &= ~BIT4; // CH2_SCTRLN,当 CH2_NS = 1 时, CH2 N通道输出值
MCPWM_IO23 &= ~BIT3; // CH2_PS = 0 → PWM
}
if (n_pwm)
{
MCPWM_IO23 |= BIT3; // CH2_PS = 1 → CH2_SCTRLP
MCPWM_IO23 &= ~BIT5; // CH2_SCTRLP,当 CH2_PS = 1 时, CH2 P通道输出值
MCPWM_IO23 &= ~BIT2; // CH2_NS = 0 → PWM
}
break;
case 3:
if (p_pwm)
{
MCPWM_IO23 |= BIT10; // CH3_NS = 1 → CH3_SCTRLN
MCPWM_IO23 &= ~BIT12; // CH3_SCTRLN,当 CH3_NS = 1 时, CH3 N通道输出值
MCPWM_IO23 &= ~BIT11; // CH3_PS = 0 → PWM
}
if (n_pwm)
{
MCPWM_IO23 |= BIT11; // CH3_PS = 1 → CHI3_SCTRLP
MCPWM_IO23 &= ~BIT13; // CH3_SCTRLP,当 CH3_PS = 1 时, CH3 P通道输出值
MCPWM_IO23 &= ~BIT10; // CH3_NS = 0 → PWM
}
break;
}
MCPWM_LOCK();
}
波形模式和比较寄存器
使用边沿对齐模式。由于 MCPWM_THxx 是 16 位有符号数,我们使用 int16_t 作为比较值的数据类型。

// 设置 PWM 比较值(内部调用)
static void MCPWM_SetCompareValue(uint8_t channel, int16_t cmp_p, int16_t cmp_n)
{
switch (channel)
{
case 0:
MCPWM_TH00 = cmp_p;
MCPWM_TH01 = cmp_n;
break;
case 1:
MCPWM_TH10 = cmp_p;
MCPWM_TH11 = cmp_n;
break;
case 2:
MCPWM_TH20 = cmp_p;
MCPWM_TH21 = cmp_n;
break;
case 3:
MCPWM_TH30 = cmp_p;
MCPWM_TH31 = cmp_n;
break;
default:
break;
}
}
刹车函数及速度与方向函数
8路PWM,4个通道共用同一个MCPWM_TH计数器。把刹车的逻辑也提取成一个函数了。

// 内部辅助函数:强制通道输出安全停止状态 (P/N 都强制为低电平)
static void MCPWM_ForceStop(uint8_t channel)
{
if (channel > 3) return;
// 强制输出低电平:
// 1. CHx_PS = 1, CHx_NS = 1 (选择强制输出模式)
// 2. CHx_SCTRLP = 0, CHx_SCTRLN = 0 (强制输出的值为低电平)
MCPWM_UNLOCK();
switch (channel) {
case 0:
MCPWM_IO01 |= (BIT3 | BIT2); // CH0_PS=1, CH0_NS=1 (选择强制模式)
MCPWM_IO01 &= ~(BIT5 | BIT4); // CH0_SCTRLP=0, CH0_SCTRLN=0 (强制输出低电平)
break;
case 1:
MCPWM_IO01 |= (BIT11 | BIT10); // CH1_PS=1, CH1_NS=1
MCPWM_IO01 &= ~(BIT13 | BIT12); // CH1_SCTRLP=0, CH1_SCTRLN=0
break;
case 2:
MCPWM_IO23 |= (BIT3 | BIT2); // CH2_PS=1, CH2_NS=1
MCPWM_IO23 &= ~(BIT5 | BIT4); // CH2_SCTRLP=0, CH2_SCTRLN=0
break;
case 3:
MCPWM_IO23 |= (BIT11 | BIT10); // CH3_PS=1, CH3_NS=1
MCPWM_IO23 &= ~(BIT13 | BIT12); // CH3_SCTRLP=0, CH3_SCTRLN=0
break;
}
// 同时清零比较值,确保计数器不会意外触发
MCPWM_SetCompareValue(channel, 0, 0);
MCPWM_LOCK();
}
/**
* @brief 设置指定通道的电机方向和速度 (占空比)
* @param channel MCPWM 通道 (0-3)
* @param speed 速度值,范围 -1000 到 +1000 (代表 -100% 到 +100% 占空比)
*/
void SetMotorDirectionAndSpeed(uint8_t channel, int16_t speed)
{
if (channel > 3) return;
// 假设 MCPWM_TH 已定义为全局周期值,或通过函数获取
uint32_t period = MCPWM_TH;
if (period == 0) return;
// 1. 处理停止/刹车逻辑
if (speed == 0) {
MCPWM_ForceStop(channel);
return;
}
// 2. 计算占空比比较值
// abs_speed 的最大值是 1000
uint16_t abs_speed = (speed >= 0) ? (uint16_t)speed : (uint16_t)(-speed);
if (abs_speed > 1000) {
abs_speed = 1000;
}
// 中间乘法使用 uint32_t 防止溢出
int16_t cmp = (int16_t)(((uint32_t)abs_speed * period) / 1000);
// 3. 设置方向和比较值
if (speed > 0) {
// 正转:P=PWM, N=强制低
// 设置比较值 (P侧=cmp, N侧=0)
MCPWM_SetCompareValue(channel, cmp, 0);
// P侧切回 PWM 模式 (p_pwm=1), N侧强制低 (n_pwm=0)
// 注意:这里的 1, 0 参数只用于切换 P/N 的控制源 (PWM vs Force Low)
MCPWM_SetChannelForceOutput(channel, 1, 0);
} else {
// 反转:P=强制低, N=PWM
// 设置比较值 (P侧=0, N侧=cmp)
MCPWM_SetCompareValue(channel, 0, cmp);
// P侧强制低 (p_pwm=0), N侧切回 PWM 模式 (n_pwm=1)
MCPWM_SetChannelForceOutput(channel, 0, 1);
}
}
初始化
MCPWM使用FCLK3作为外设时钟,FLK[3]的频率与系统时钟(MCLK)一致,是 96/8 MHz = 12MHz。

官方MCPWM驱动模块
void PWMOutputs(FuncState t_state);
void MCPWM_Init(MCPWM_InitTypeDef *MCPWM_InitStruct);
void MCPWM_StructInit(MCPWM_InitTypeDef *MCPWM_InitStruct);
u16 MCPWM_GetIRQFlag(uint32_t INT_flag);
void MCPWM_ClearIRQFlag(uint32_t INT_flag);
在lks32mc08x_MCPWM.c文件里,这些函数注释写得清楚,要实现某个功能,可以参考“ *@brief @b 功能描述: ”后的内容,就能知道该选择啥函数;参考 “@par 示例代码:”后的内容,就能知道该怎么使用它了。
附上配置LKS新工程教程链接:
http://bbs.sunsili.com/thread-275893-1-1.html
吐槽一下:LKS32MC08x_DevDriver_v2.22 凌鸥外设库的bug有些多了,比如以下ADC触发点,tm uint16_t,来来你告诉我负数怎么用0~65535表示。


更多推荐



所有评论(0)