本篇总结
使用 TIM3,输出 PWM 波形。
🔑 KEIL MDK、UART调试、TIM
硬件平台:本例程基于 W55MH32L-EVB 开发板运行。
采购推荐:点击前往 WIZnet 官方淘宝店铺 选购 W55MH32L-EVB
本示例演示 W55MH32 使用 TIM3 输出 PWM 波形。TIM3 在 CH2(PA7)上以 PWM2 模式输出固定频率与占空比的方波,通过 TIM_CtrlPWMOutputs 使能主输出后由硬件持续输出,CPU 初始化完成后空转。
TIM 简介
W55MH32 的高级控制定时器 TIM1/TIM8 在通用定时器基础上增加了三项专为电机/电源控制设计的能力:可编程死区互补输出(OCx + OCxN 双路带延时反相输出)、重复计数器 RCR(每 N 次溢出才产生一次更新事件)、刹车输入 BKIN(硬件立刻关断所有输出)。时基核心公式:PWM 周期 = (ARR+1) × (PSC+1) / TIMxCLK。
| 定时器 | 类型 | 互补输出 | 死区发生器 | 刹车输入 BKIN | 重复计数器 |
| TIM1、TIM8 | 高级控制 | 有(CHx/CHxN) | 有(10 位) | 有 | 有(8 位) |
| TIM2、3、4、5 | 通用 | 无 | 无 | 无 | 无 |
| TIM6、TIM7 | 基本 | 无 | 无 | 无 | 无 |
本例程
前置工具准备
开始本例程前,请确认以下工具已安装:
- WIZ UartTool V1.0:串口调试工具:点击下载
- 示波器或逻辑分析仪(可选):观察 PA7 的 PWM 波形
硬件连接
本例程使用 TIM3 的 CH2(PA7)输出 PWM 波形。USART1(PA9/PA10)通过板载 USB 转串口芯片打印状态日志。示波器探头接 PA7 即可观察 PWM 波形。仅需一根 Micro USB 线连接开发板 DEBUG USB 口到 PC。
| 引脚 | 功能 | 方向 | 模式 |
| PA7 | TIM3_CH2(PWM 输出) | MCU → 示波器 | 复用推挽输出 (AF_PP) |
| PA9 | USART1_TX | 开发板 → PC | 复用推挽输出 (AF_PP) |
| PA10 | USART1_RX | PC → 开发板 | 浮空输入 (IN_FLOATING) |
工程结构
工程位于 SDK 内 1.SDK\ModuleDemo\TIM\TIM_OutPwm\,核心文件如下:
TIM_OutPwm/
├── USER/
│ ├── main.c ← GPIO/TIM 初始化 + RCC_ClkConfiguration + 主循环 + printf 重定向
│ ├── w55mh32_conf.h ← 外设头文件集中包含
│ ├── w55mh32_it.c/h ← 中断服务函数
│ ├── system_w55mh32.c/h← 系统初始化(时钟配置)
│ └── startup_w55mh32.s ← 启动文件
└── SYSTEM/
└── delay/
└── delay.c/h ← SysTick 延时函数
步骤1:TIM3 PWM 输出初始化
文件:USER/main.c | 函数:TIM_Configuration() — 配置 PA7 为复用推挽输出,TIM3 时基 1MHz、ARR=99(PWM 周期 100μs / 10kHz),CH2 输出 PWM2 模式、占空比 50%,使能预装载与主输出。
void TIM_Configuration(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
/* 开启 TIM3 及 GPIOA/AFIO 时钟 */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE);
/* PA7: TIM3_CH2 → 复用推挽输出 */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
/* 时基:PSC 使 CK_CNT=1MHz,ARR=99 → PWM 周期 100μs(10kHz) */
TIM_TimeBaseStructure.TIM_Period = 99;
TIM_TimeBaseStructure.TIM_Prescaler = clocks.SYSCLK_Frequency / 1000000 - 1;
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
/* 输出比较:PWM2 模式,CH2,Pulse=49 → 占空比 49%,低电平有效 */
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Enable;
TIM_OCInitStructure.TIM_Pulse = 99 / 2;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low;
TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCNPolarity_Low;
TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Set;
TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCIdleState_Reset;
TIM_OC2Init(TIM3, &TIM_OCInitStructure);
/* 使能预装载寄存器:运行中改 CCR/ARR 会在下个更新事件生效 */
TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable);
TIM_ARRPreloadConfig(TIM3, ENABLE);
/* 启动定时器 + 使能主输出 */
TIM_Cmd(TIM3, ENABLE);
TIM_CtrlPWMOutputs(TIM3, ENABLE);
}
六步完成:开时钟 → GPIO 复用推挽 → 时基参数(PSC=71 使 CK_CNT=1MHz、ARR=99)→ 输出比较参数(PWM2、Pulse=49、低电平有效)→ 预装载使能 → 启动定时器与主输出。PWM 周期 = (ARR+1)/CK_CNT = 100/1MHz = 100μs(10kHz),占空比 = Pulse/(ARR+1) = 49/100 = 49%。TIM_CtrlPWMOutputs(TIM3, ENABLE) 使能主输出 MOE,是 PWM 输出必经一步。
如果你想自定义
- 改 PWM 频率 → 修改
TIM_Period(ARR)和 TIM_Prescaler(PSC)。如要 1kHz:PSC=71(1MHz)、ARR=999
- 改占空比 → 修改
TIM_Pulse(CCR)。如要 30% 占空比:TIM_Pulse = 99 * 3 / 10(即 29)
- 换用 CH1/CH3/CH4 → 同步修改 GPIO 引脚(CH1=PA6、CH3=PA8、CH4=PA9)和
TIM_OCxInit / TIM_OCxPreloadConfig 的通道号
步骤2:RCC 时钟配置与主函数
函数:RCC_ClkConfiguration() 与 main() — 先配置系统时钟(HSE 8MHz × 9 = 72MHz),再初始化延时与 UART(115200) → 打印时钟频率 → 配置 TIM3 PWM → 进入空循环,波形由硬件持续输出。
void RCC_ClkConfiguration(void)
{
RCC_DeInit();
RCC_HSEConfig(RCC_HSE_ON);
while (RCC_GetFlagStatus(RCC_FLAG_HSERDY) == RESET);
RCC_PLLCmd(DISABLE);
RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_9); /* HSE 8MHz × 9 = 72MHz */
RCC_PLLCmd(ENABLE);
while (RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET);
RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
RCC_HCLKConfig(RCC_SYSCLK_Div1);
RCC_PCLK1Config(RCC_HCLK_Div2);
RCC_PCLK2Config(RCC_HCLK_Div1);
RCC_LSICmd(ENABLE);
while (RCC_GetFlagStatus(RCC_FLAG_LSIRDY) == RESET);
RCC_HSICmd(ENABLE);
while (RCC_GetFlagStatus(RCC_FLAG_HSIRDY) == RESET);
}
RCC_ClocksTypeDef clocks;
int main(void)
{
RCC_ClkConfiguration();
delay_init();
UART_Configuration(115200);
RCC_GetClocksFreq(&clocks);
printf("SYSCLK: %3.1fMhz, HCLK: %3.1fMhz, PCLK1: %3.1fMhz, "
"PCLK2: %3.1fMhz, ADCCLK: %3.1fMhz\n",
(float)clocks.SYSCLK_Frequency / 1000000,
(float)clocks.HCLK_Frequency / 1000000,
(float)clocks.PCLK1_Frequency / 1000000,
(float)clocks.PCLK2_Frequency / 1000000,
(float)clocks.ADCCLK_Frequency / 1000000);
printf("TIM Out Test.\n");
TIM_Configuration();
while (1);
}
PWM 一旦配置完成即由硬件持续输出,while(1) 中无需任何干预。clocks 为全局变量,供 TIM_Configuration() 计算 PSC 时引用 SYSCLK 频率,保证换主频时无需改代码。
步骤3:printf 重定向
重写 fputc() 将 printf 输出重定向到 USART1,并自动在 \n 前补 \r 确保串口工具正确换行。
int SER_PutChar(int ch)
{
while (!USART_GetFlagStatus(USART_TEST, USART_FLAG_TC));
USART_SendData(USART_TEST, (uint8_t)ch);
return ch;
}
int fputc(int c, FILE *f)
{
if (c == '\n')
SER_PutChar('\r'); // 补回车,确保串口工具正确换行
return (SER_PutChar(c));
}
运行验证
打开 WIZ UartTool V1.0,选择开发板对应 COM 口,波特率 115200 / 8N1,打开串口。按 RESET 启动后,串口打印时钟信息与 "TIM Out Test.";用示波器探头接 PA7,可观察到 10kHz、49% 占空比的方波。
SYSCLK: 72.0Mhz, HCLK: 72.0Mhz, PCLK1: 36.0Mhz, PCLK2: 72.0Mhz, ADCCLK: 36.0Mhz
TIM Out Test.
看到 "TIM Out Test." 打印后,示波器上 PA7 应出现稳定的 PWM 波形。PWM 频率 = 72MHz / (71+1) / (99+1) = 10kHz,占空比 = 49/100 = 49%。
常见问题
Q: PA7 没有 PWM 波形输出
按顺序检查:① GPIO 是否配为 GPIO_Mode_AF_PP(复用推挽,不是普通推挽);② TIM_OC2Init 的 TIM_OutputState 是否为 Enable;③ TIM_Cmd(TIM3, ENABLE) 是否调用;④ TIM_CtrlPWMOutputs(TIM3, ENABLE) 是否调用——这是使能主输出 MOE 的关键步骤;⑤ 示波器探头地线是否接到开发板 GND。
Q: PWM 占空比与预期相反(该高的时候低)
PWM 模式与极性配合问题。本例用 PWM2 + OCPolarity_Low:CNT>CCR 时输出有效(低电平),所以 Pulse 越小、低电平时间越长。若想要"常规"高电平占空比,可改为 TIM_OCMode_PWM1 + TIM_OCPolarity_High,此时 Pulse/(ARR+1) 即为高电平占比。
Q: 换用 CH1/CH3/CH4 后编译报错或无输出
通道号必须三处一致:① GPIO 引脚(CH1=PA6、CH2=PA7、CH3=PA8、CH4=PA9,注意 CH4 与 USART1_TX 冲突);② TIM_OCxInit 的 TIM_Channel_x;③ 预装载 TIM_OCxPreloadConfig。三处通道号不一致会导致编译通过但运行异常。
Q: 编译报错 "cannot open source file w55mh32.h"
检查 Project → Options → C/C++ → Include Paths,确保 SDK 的 Libraries 目录已添加。
Q: 串口输出乱码
先看串口打印的时钟频率是否正常(SYSCLK 应为 72.0Mhz)。若频率异常,检查 RCC_ClkConfiguration 中 HSE_VALUE 是否与实际晶振一致。
总结
本文基于 W55MH32 的 TIM3 实现了 PWM 输出:
- GPIO:PA7(TIM3_CH2)配置为复用推挽输出(AF_PP)
- 时基 + 输出比较:PSC = SYSCLK/1M-1(CK_CNT=1MHz)、ARR=99,输出 10kHz / 49% 占空比方波;PWM2 模式、低电平有效
- 预装载:
TIM_OC2PreloadConfig 使能 CCR 预装载,运行中可安全修改占空比
- 关键调用:
TIM_CtrlPWMOutputs(TIM3, ENABLE) 使能主输出 MOE,是 PWM 输出必经一步
- 核心公式:PWM 频率 = TIMxCLK / (PSC+1) / (ARR+1),占空比 = CCR / (ARR+1)