1. 项目概述
最近在调试CIU32F003这颗国产MCU时,遇到了一个实际需求:需要同时控制三个通道的PWM输出。这个需求在很多电机控制、LED调光等场景都很常见。CIU32F003作为一款性价比极高的国产32位MCU,其PWM模块的功能性和灵活性如何?今天我就来实测一下它的三通道PWM功能。
CIU32F003是上海芯翼信息科技推出的一款基于ARM Cortex-M0内核的微控制器,主频最高48MHz,内置多个定时器模块,可以生成PWM信号。在实际项目中,我们经常需要同时控制多个PWM通道,比如RGB LED调光、三相电机控制等场景。通过本次测试,我将验证CIU32F003能否稳定输出三路独立的PWM信号,并分享具体的配置方法和注意事项。
2. 硬件准备与环境搭建
2.1 开发板与工具链选择
我使用的是CIU32F003的最小系统开发板,核心板引出了所有GPIO引脚。开发环境选择了Keil MDK,因为芯翼官方提供了完善的Keil支持包和示例代码。当然,如果你习惯使用IAR或者GCC工具链,也可以参考类似的配置方法。
提示:在开始前,请确保已经安装了CIU32F003的Device Family Pack(DFP),这样才能在Keil中找到对应的芯片型号和启动文件。
2.2 硬件连接方案
为了验证PWM输出,我选择了TIM1的CH1、CH2、CH3三个通道,对应开发板上的PA8、PA9、PA10三个引脚。使用逻辑分析仪连接这三个引脚,可以同时捕获三路PWM波形进行对比分析。
3. PWM模块配置详解
3.1 时钟配置
首先需要配置系统时钟和定时器时钟。CIU32F003的定时器挂载在APB1总线上,默认情况下APB1时钟与系统时钟同频。在我的测试中,将系统时钟配置为48MHz:
c复制RCC_DeInit();
RCC_HSEConfig(RCC_HSE_ON);
while(RCC_GetFlagStatus(RCC_FLAG_HSERDY) == RESET);
RCC_PLLConfig(RCC_PLLSource_HSE, RCC_PLLMul_6);
RCC_PLLCmd(ENABLE);
while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET);
RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
while(RCC_GetSYSCLKSource() != 0x08);
RCC_HCLKConfig(RCC_SYSCLK_Div1);
RCC_PCLK1Config(RCC_HCLK_Div1);
3.2 定时器基本参数配置
TIM1是CIU32F003的高级定时器,支持互补输出和死区控制等功能。我们首先配置定时器的基本参数:
c复制TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_TimeBaseStructure.TIM_Period = 999; // PWM周期 = (999+1)/48MHz ≈ 20.8us (48kHz)
TIM_TimeBaseStructure.TIM_Prescaler = 0; // 不分频
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure);
这里将PWM频率设置为约48kHz,这个频率对于大多数应用已经足够高,可以有效降低噪声。
3.3 PWM通道配置
接下来配置三个PWM输出通道。我们将三个通道的占空比分别设置为30%、50%和70%,以便观察区别:
c复制TIM_OCInitTypeDef TIM_OCInitStructure;
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = 300; // CH1占空比30% (300/1000)
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OC1Init(TIM1, &TIM_OCInitStructure);
TIM_OC1PreloadConfig(TIM1, TIM_OCPreload_Enable);
TIM_OCInitStructure.TIM_Pulse = 500; // CH2占空比50%
TIM_OC2Init(TIM1, &TIM_OCInitStructure);
TIM_OC2PreloadConfig(TIM1, TIM_OCPreload_Enable);
TIM_OCInitStructure.TIM_Pulse = 700; // CH3占空比70%
TIM_OC3Init(TIM1, &TIM_OCInitStructure);
TIM_OC3PreloadConfig(TIM1, TIM_OCPreload_Enable);
3.4 GPIO配置
配置对应的GPIO为复用功能输出:
c复制GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
3.5 启动定时器
最后使能定时器和各通道输出:
c复制TIM_CtrlPWMOutputs(TIM1, ENABLE);
TIM_ARRPreloadConfig(TIM1, ENABLE);
TIM_Cmd(TIM1, ENABLE);
4. 测试结果与分析
4.1 波形测量
使用逻辑分析仪捕获的三路PWM波形如下图所示(描述):
- 通道1(PA8):周期20.8us,高电平时间6.25us,占空比≈30%
- 通道2(PA9):周期20.8us,高电平时间10.4us,占空比≈50%
- 通道3(PA10):周期20.8us,高电平时间14.6us,占空比≈70%
三路PWM信号严格同步,相位对齐,没有观察到明显的抖动或偏移。
4.2 性能极限测试
为了测试CIU32F003的PWM性能极限,我逐步提高PWM频率:
- 100kHz(周期=10us):工作正常
- 500kHz(周期=2us):工作正常
- 1MHz(周期=1us):开始出现轻微抖动
- 2MHz(周期=0.5us):波形失真严重
注意:当PWM频率超过1MHz时,由于中断处理等系统开销,波形质量会明显下降。对于大多数应用,建议将PWM频率控制在500kHz以下。
5. 实际应用中的注意事项
5.1 死区时间配置
在电机控制等应用中,互补PWM需要配置死区时间防止上下管直通。CIU32F003的TIM1支持硬件死区插入:
c复制TIM_BDTRInitTypeDef TIM_BDTRInitStructure;
TIM_BDTRInitStructure.TIM_OSSRState = TIM_OSSRState_Enable;
TIM_BDTRInitStructure.TIM_OSSIState = TIM_OSSIState_Enable;
TIM_BDTRInitStructure.TIM_LOCKLevel = TIM_LOCKLevel_1;
TIM_BDTRInitStructure.TIM_DeadTime = 0x54; // 约1us死区时间
TIM_BDTRInitStructure.TIM_Break = TIM_Break_Disable;
TIM_BDTRInitStructure.TIM_BreakPolarity = TIM_BreakPolarity_Low;
TIM_BDTRInitStructure.TIM_AutomaticOutput = TIM_AutomaticOutput_Enable;
TIM_BDTRConfig(TIM1, &TIM_BDTRInitStructure);
5.2 动态调整占空比
在实际应用中,经常需要动态调整PWM占空比。需要注意的是,直接修改CCRx寄存器可能导致毛刺:
c复制// 不推荐的写法,可能导致毛刺
TIM1->CCR1 = new_value;
// 推荐的写法,使用影子寄存器
TIM1->CCR1 = new_value;
while(TIM_GetFlagStatus(TIM1, TIM_FLAG_CC1OF) == SET) {
TIM_ClearFlag(TIM1, TIM_FLAG_CC1OF);
}
5.3 多定时器同步
对于需要更多PWM通道的情况,可以使用多个定时器并通过主从模式同步:
c复制// 配置TIM1为主定时器
TIM_SelectMasterSlaveMode(TIM1, TIM_MasterSlaveMode_Enable);
TIM_SelectOutputTrigger(TIM1, TIM_TRGOSource_Update);
// 配置TIM2为从定时器
TIM_SelectSlaveMode(TIM2, TIM_SlaveMode_Trigger);
TIM_SelectInputTrigger(TIM2, TIM_TS_ITR0);
6. 常见问题与解决方案
6.1 PWM无输出
可能原因及解决方法:
- 时钟未使能:检查RCC_APB2PeriphClockCmd是否开启了TIM1和GPIO时钟
- GPIO配置错误:确认GPIO模式设置为GPIO_Mode_AF_PP
- 定时器未使能:检查TIM_Cmd和TIM_CtrlPWMOutputs是否调用
6.2 占空比不正确
可能原因:
- 自动重装载值(TIM_Period)设置过小
- 脉冲值(TIM_Pulse)大于周期值
- 极性配置错误
6.3 PWM波形抖动
解决方法:
- 降低PWM频率
- 检查系统时钟是否稳定
- 避免在中断服务程序中执行耗时操作
7. 扩展应用:RGB LED控制
利用三路PWM可以实现RGB LED的全彩控制。下面是一个简单的颜色渐变示例:
c复制void RGB_Update(uint8_t r, uint8_t g, uint8_t b) {
TIM1->CCR1 = r * TIM1->ARR / 255;
TIM1->CCR2 = g * TIM1->ARR / 255;
TIM1->CCR3 = b * TIM1->ARR / 255;
}
// 彩虹渐变效果
void RainbowEffect(void) {
static uint8_t hue = 0;
RGB_Color color = HSLToRGB(hue++, 255, 128);
RGB_Update(color.r, color.g, color.b);
if(hue >= 360) hue = 0;
Delay_ms(10);
}
通过���次测试验证了CIU32F003能够稳定输出三路同步PWM信号,满足大多数应用需求。在实际项目中,我还发现通过合理配置定时器的主从模式,可以扩展出更多同步PWM通道。
