3. GD32时钟系统与低功耗模式:时钟树解析与模式配置
做电机控制,时钟就是心脏。心跳乱了,电机就抖。GD32的时钟树其实不复杂,但很多人一上来就被一堆缩写吓住了——HXTAL、LXTAL、PLL、IRC... 别急,我们一个一个捋清楚。
3.1 时钟树整体架构
GD32的时钟源主要有这么几个:
- HXTAL:外部高速晶振,通常接8MHz或16MHz
- LXTAL:外部低速晶振,32.768kHz,RTC专用
- IRC8M:内部8MHz RC振荡器,上电默认就用它
- IRC40K:内部40kHz RC振荡器,省电场景用
- PLL:锁相环,把低频倍频到高频
我个人习惯,做FOC控制时主频拉到108MHz或120MHz(看具体型号),所以PLL是必选的。但低功耗场景下,我会切回IRC8M甚至IRC40K。
核心原则:高性能跑PLL,低功耗跑IRC。切换时钟源时,一定要等目标时钟稳定后再切,否则直接死机。
3.2 时钟配置实战
上电后GD32默认跑IRC8M,也就是8MHz。做FOC肯定不够,我们要切到PLL。下面这段代码是我项目里一直在用的模板:
void system_clock_config(void)
{
/* 使能HXTAL */
RCU_CTL |= RCU_CTL_HXTALEN;
while(!(RCU_CTL & RCU_CTL_HXTALSTB)); // 等待稳定
/* 配置PLL:HXTAL 8MHz -> 108MHz */
RCU_CFG0 &= ~RCU_CFG0_PLLSEL; // 选择HXTAL作为PLL源
RCU_CFG0 |= (108 << 18); // PLL倍频因子
/* 使能PLL */
RCU_CTL |= RCU_CTL_PLLEN;
while(!(RCU_CTL & RCU_CTL_PLLSTB));
/* 切换系统时钟到PLL */
RCU_CFG0 |= RCU_CFG0_SCS_PLL;
while((RCU_CFG0 & RCU_CFG0_SCSS) != RCU_CFG0_SCSS_PLL);
}
我的经验:PLL倍频因子不要超过芯片手册上限。我曾经为了跑更高频率把倍频设到极限,结果芯片发热严重,电机控制反而更差。稳定比极限重要。
3.3 低功耗模式详解
做电池供电的电机控制,低功耗是躲不开的。GD32有三种低功耗模式:睡眠、停止、待机。区别在哪?我直接给你列个表:
| 模式 | 唤醒方式 | 内核状态 | 外设状态 | 唤醒时间 |
|---|---|---|---|---|
| 睡眠模式 | 任何中断/事件 | 暂停 | 全部保持 | ~5μs |
| 停止模式 | 外部中断/RTC | 掉电 | 可配置保持 | ~10μs |
| 待机模式 | WKUP引脚/RTC复位 | 完全掉电 | 全部关闭 | ~50μs |
你看,睡眠模式最轻量,外设全活着,适合短暂等待。待机模式最彻底,但唤醒也最慢。我一般这么用:
- 电机运行时:不进入低功耗,全速跑FOC
- 电机停止等待指令:睡眠模式,有指令就唤醒
- 长时间待机:停止模式,RTC定时唤醒检测位置
- 关机状态:待机模式,按键唤醒
3.4 睡眠模式配置
睡眠模式最简单,执行WFI或WFE指令就行。但要注意,进入前要处理好中断优先级,别被无关中断唤醒。
void enter_sleep_mode(void)
{
/* 关闭不用的外设时钟,减少唤醒后的处理 */
RCU_APB2EN &= ~RCU_APB2EN_USART0EN;
RCU_APB1EN &= ~RCU_APB1EN_TIMER1EN;
/* 进入睡眠模式 */
__WFI(); // 等待中断唤醒
/* 唤醒后恢复外设时钟 */
RCU_APB2EN |= RCU_APB2EN_USART0EN;
RCU_APB1EN |= RCU_APB1EN_TIMER1EN;
}
踩坑提醒:我曾经在睡眠前没关ADC,结果ADC的连续转换中断一直触发,芯片根本睡不沉。进入低功耗前,一定要检查所有外设的中断使能状态。
3.5 停止模式与待机模式
停止模式比睡眠深一层。内核停了,但SRAM和寄存器内容还在。配置时要注意电压调节器的设置:
void enter_stop_mode(void)
{
/* 设置电压调节器为低功耗模式 */
PMU_CTL |= PMU_CTL_LDOLP;
/* 选择停止模式 */
PMU_CTL |= PMU_CTL_STBMOD;
PMU_CTL &= ~PMU_CTL_STBMOD;
/* 执行WFI进入 */
__WFI();
}
待机模式就更彻底了。进去后SRAM内容全丢,唤醒相当于复位。所以进待机前,一定要把关键数据存到备份寄存器里:
void enter_standby_mode(void)
{
/* 保存关键数据到备份寄存器 */
PMU_BKP_DATA0 = motor_position;
PMU_BKP_DATA1 = target_speed;
/* 清除待机标志 */
PMU_CTL |= PMU_CTL_CLRSTBF;
/* 使能待机模式 */
PMU_CTL |= PMU_CTL_STBMOD;
/* 执行WFI进入 */
__WFI();
}
3.6 低功耗模式下外设工作状态
这个很多人搞混。我直接给你列清楚:
| 外设 | 睡眠模式 | 停止模式 | 待机模式 |
|---|---|---|---|
| CPU内核 | 暂停 | 掉电 | 掉电 |
| SRAM | 保持 | 保持 | 丢失 |
| GPIO | 保持状态 | 保持状态 | 高阻 |
| 定时器 | 运行 | 停止 | 停止 |
| ADC | 运行 | 停止 | 停止 |
| RTC | 运行 | 运行 | 运行 |
| DMA | 运行 | 停止 | 停止 |
关键点:停止模式下,RTC和部分GPIO还能工作。这意味着你可以用RTC定时唤醒,或者用外部按键唤醒。待机模式下只有WKUP引脚和RTC能唤醒。
3.7 低功耗与FOC的配合策略
做低功耗FOC,核心思路是:该跑的时候全力跑,该停的时候彻底停。
我常用的策略是这样的:
- 电机匀速运行时:全速108MHz,PLL输出,FOC 20kHz控制频率
- 电机减速停止后:切到IRC8M,进入睡眠模式,等待下一个指令
- 长时间无操作:切到IRC40K,进入停止模式,RTC每100ms唤醒一次检测位置变化
- 电池电量低:保存位置到备份寄存器,进入待机模式
我的习惯:每次切换模式前,我都会先关PWM输出,防止电机在模式切换瞬间产生异常力矩。等模式稳定后再重新使能PWM。
嗯,时钟系统和低功耗模式就这些。记住一句话:时钟是基础,功耗是约束,FOC是目标。三者平衡好了,你的电机控制才能既快又省电。