第十一章 通用定时器(下篇)
目录
1 程序设计
1.1 TIM_InputCapture例程
此例程聚焦于 W55MH32 的定时器输入捕获功能,借助 TIM3 对外部信号的高电平持续时间展开测量,同时运用串口输出相关信息。下面是详细的程序分析:
1.初始化
- 配置系统时钟:RCC_ClkConfiguration()。
- 初始化延时函数:delay_init()。
- 配置串口:UART_Configuration(),波特率设为 115200。
- 获取系统时钟频率并输出。
- 配置定时器 3:TIM_Configuration(),用于输入捕获。
2.主循环
while (1)
{
if (TIM3_CAPTURE_STA & 0X80) //Successfully captured a rising edge
{
temp = TIM3_CAPTURE_STA & 0X3F;
temp *= 65536; //sum of overflow times
temp += TIM3_CAPTURE_VAL; //Get the total high time
printf("HIGH:%d us\r\n", temp); //Print total peak time
TIM3_CAPTURE_STA = 0; //Initiate the next capture
}
}
- 持续检查 TIM3_CAPTURE_STA 标志,若成功捕获到一个完整的高电平脉冲(TIM3_CAPTURE_STA & 0X80 为真),则计算高电平持续时间,输出该时间,接着重新初始化捕获状态,准备下一次捕获。
3.定时器中断服务程序
void TIM_Configuration(void)
{
// ...(GPIO和时基配置略)...
NVIC_InitTypeDef NVIC_InitStructure;
// 1. 配置NVIC中断优先级
NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn; // 选择TIM3中断通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; // 抢占优先级(数值越小优先级越高)
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; // 子优先级
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; // 使能中断
NVIC_Init(&NVIC_InitStructure); // 应用配置
// 2. 使能TIM3中断类型
TIM_ITConfig(TIM3, TIM_IT_Update | TIM_IT_CC2, ENABLE); // 使能更新中断(计数器溢出)和通道2捕获中断
TIM_Cmd(TIM3, ENABLE); // 启动定时器
}
- 若尚未成功捕获((TIM3_CAPTURE_STA & 0X80) == 0),则处理定时器更新中断与捕获中断。
- 捕获到上升沿时,标记已捕获上升沿,把计数器清零,同时将捕获极性设为下降沿捕获。
- 捕获到下降沿时,标记已成功捕获高电平脉冲宽度,记录捕获值,再把捕获极性设为上升沿捕获。
- 处理定时器溢出情况。
- 清除中断标志。
4.串口输出函数
- SER_PutChar()函数用于向串口发送单个字符。
- fputc()函数重定向标准输出,使 printf 能通过串口输出。
3.4 TIM_OutPwm例程
该例程借助对系统时钟、UART 和定时器的配置,实现了系统时钟的初始化、UART 通信以及定时器 PWM 输出的功能。通过printf()函数能够输出系统时钟频率信息,方便调试和监控。下面是详细的程序分析:
1.系统时钟配置
- 使用 8MHz 外部晶振(HSE)经 PLL9 倍频生成 72MHz 系统时钟
- 配置总线频率:HCLK=72MHz, PCLK1=36MHz, PCLK2=72MHz
- 使能内部低速(LSI)和高速(HSI)时钟
2.串口通信
- USART1 配置为 115200 波特率,8 位数据位,1 位停止位
- PA9(TX)设为复用推挽输出,PA10(RX)浮空输入
- 重定向printf到串口,自动添加\r\n转换
3.定时器 PWM 输出
void TIM_Configuration(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
// 使能时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE);
// ================== GPIO配置:PA7为TIM3_CH2复用输出 ==================
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7; // PA7对应TIM3_CH2
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; // 复用推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
// ================== 时基配置:1MHz计数时钟,100μs周期 ==================
TIM_TimeBaseStructure.TIM_Period = 99; // 周期值(0-99,共100个计数周期)
TIM_TimeBaseStructure.TIM_Prescaler = 35; // PCLK1=36MHz预分频36倍(35+1)
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
// ================== PWM配置:50%占空比,PWM模式2 ==================
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; // PWM模式2
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; // 使能输出
TIM_OCInitStructure.TIM_Pulse = 49; // 脉冲值(决定占空比)
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; // 低电平有效
TIM_OC2Init(TIM3, &TIM_OCInitStructure); // 初始化通道2
// 使能预装载和自动重装载
TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable);
TIM_ARRPreloadConfig(TIM3, ENABLE);
// 启动定时器
TIM_Cmd(TIM3, ENABLE);
TIM_CtrlPWMOutputs(TIM3, ENABLE); // 适用于高级定时器的输出控制
}
- TIM3 通道 2 在 PA7 输出 50% 占空比 PWM
- 周期 100μs(计数周期 99),频率 10kHz
- 时基配置:PCLK1 (36MHz) 预分频 36 得到 1MHz 计数时钟
3.5 TIM_Tim9例程
此例程主要实现了 W55MH32 的定时器(TIM9)中断测试以及 UART 串口通信功能,以下为其工作流程总结:
1. 初始化阶段
延时函数初始化:调用delay_init()函数,对延时功能进行初始化。
UART 串口配置:借助UART_Configuration(115200)函数,把 USART1 的波特率设定为 115200,并完成 GPIO 引脚和 USART 的初始化工作。
获取时钟频率:运用RCC_GetClocksFreq(&clocks)函数获取系统时钟频率,然后通过printf()函数将系统时钟、HCLK、PCLK1、PCLK2 以及 ADCCLK 的频率信息打印出来。
2. 定时器配置阶段
使能时钟:开启 TIM9 的时钟。
定时器初始化:对定时器 TIM9 的周期、预分频器、时钟分割和计数模式等参数进行配置。
使能中断:使能 TIM9 的更新中断,并对 NVIC 中断优先级进行设置。
启动定时器:启动 TIM9 定时器。
3. 主循环阶段
主函数中的while (1)是一个无限循环,程序在此处持续等待定时器中断的发生。
1.中断处理阶段
void TIM1_BRK_TIM9_IRQHandler(void)
{
// 4.1 检查更新中断标志
if (TIM_GetITStatus(TIM9, TIM_IT_Update) != RESET)
{
// 4.2 清除中断标志
TIM_ClearITPendingBit(TIM9, TIM_IT_Update);
// 4.3 串口输出中断信息(打印函数名)
printf("%s\n", __FUNCTION__);
}
}
当定时器 TIM9 产生更新中断时,TIM1_BRK_TIM9_IRQHandler()函数会被调用。在该函数里,会先检查中断标志位,若标志位被置位,则清除该标志位,并通过printf()函数打印出当前函数名。
2.串口输出阶段
// 5.1 单字符发送函数(阻塞式发送)
int SER_PutChar(int ch)
{
while (!USART_GetFlagStatus(USART_TEST, USART_FLAG_TC)); // 等待发送完成
USART_SendData(USART_TEST, (uint8_t)ch);
return ch;
}
// 5.2 重定向fputc函数(支持printf)
int fputc(int c, FILE *f)
{
if (c == '\n') // 自动添加回车符(适配串口终端)
{
SER_PutChar('\r');
}
return SER_PutChar(c);
}
- SER_PutChar()函数:用于向 USART1 发送单个字符,会等待发送完成标志位被置位后再发送字符。
- fputc()函数:对标准库的fputc()函数进行重定向,实现将字符输出到 USART1。若遇到换行符\n,会先发送回车符\r,再发送换行符\n。
该例程的核心功能是配置定时器 TIM9 使其按设定参数产生中断,在中断发生时通过串口输出信息,同时利用串口输出系统时钟频率等信息。
3.6 TIM_Touch例程
此例程的主要作用是实现触摸检测功能,并通过 LED 灯状态的改变直观地反馈触摸事件,同时借助串口输出相关调试信息,方便开发人员进行调试与监测。下面是具体功能的详细介绍:
1.初始化阶段
- 系统初始化:调用delay_init()进行延时初始化
- 串口配置:通过UART_Configuration(115200)配置 USART1 为 115200 波特率
- 时钟信息输出:获取并打印系统时钟各频率参数
- 硬件初始化:调用LED_Init()初始化 LED 控制引脚,调用TPAD_Init(6)初始化触摸检测模块
2.主循环阶段
- 触摸检测:通过TPAD_Scan(0)实时检测触摸事件检测到触摸时:打印 "Touch" 并翻转 LED1 状态
- LED0 周期性闪烁:通过计数器实现 150ms 周期(15×10ms)翻转 LED0
- 循环延时:每次循环执行 10ms 延时
3.硬件控制
// LED初始化函数(根据开发板类型选择不同引脚)
void LED_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
#ifdef PIN48_BOARD
// 48脚开发板: LED0->PA7, LED1->PB0
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; // 推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_SetBits(GPIOA, GPIO_Pin_7); // 初始状态熄灭LED
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_SetBits(GPIOB, GPIO_Pin_0);
#else
// 64脚开发板: LED0->PC2, LED1->PC3
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2 | GPIO_Pin_3;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOC, &GPIO_InitStructure);
GPIO_SetBits(GPIOC, GPIO_Pin_3 | GPIO_Pin_2); // 初始状态熄灭LED
#endif
}
// 触摸检测初始化(依赖tpad.h库实现)
void TPAD_Init(u8 sysclk)
{
// 实际代码在tpad.c中实现,此处为函数声明
// 配置触摸检测相关GPIO和定时器
}
// 触摸扫描函数(返回1表示检测到触摸)
u8 TPAD_Scan(u8 mode)
{
// 实际代码在tpad.c中实现,此处为函数声明
// mode=0: 单次触发模式
// mode=1: 连续触发模式
}
- LED 控制:根据开发板类型(PIN48/PIN64)控制对应 GPIO 引脚
- 触摸检测:通过 TPAD 模块实现触摸信号采集与处理(具体实现由 tpad 库提供)
4.功能特点
- 双 LED 状态控制:LED0 周期性闪烁,LED1 触摸触发翻转
- 串口调试支持:通过 printf 输出系统信息和触摸事件
- 硬件无关性:通过条件编译适应不同开发板(PIN48/PIN64)
2 下载验证
2.1 TIM_InputCapture例程
程序下载到开发板运行后,串口会先输出系统时钟频率信息与“TIM Input CaptureTest.”提示信息。之后,若PA7引脚有信号输入,每当成功捕获到一个完整高电平脉冲,串口就会输出该高电平的持续时间(单位为微秒),并自动准备下一次捕获。若信号频率过高致计数器溢出,或信号电平异常,捕获可能出现异常,需重新上电复位;若没有信号输入则无捕获结果输出。
2.2 TIM_OutPwm例程
该程序下载运行后,通过USART1(PA9/PA10)以115200波特率输出系统时钟信息:"SYSCLK: 72.0Mhz, HCLK: 72.0Mhz, PCLK1: 36.0Mhz, PCLK2: 72.0Mhz, ADCCLK: 72.0Mhz"和"TIM Out Test."。PA7引脚持续输出10kHz频率、50%占空比的PWM信号(低电平有效),周期100μs。程序执行完毕后进入无限循环保持运行状态,CPU空闲但PWM输出持续。验证时需用示波器观察PA7波形,通过串口工具配置相同波特率查看打印信息,同时确保硬件连接正确(PA7接示波器、PA9接串口转USB模块)。
如果想查看PWM输出的占空比,可以在循环里加这段代码:
uint16_t pwm_value = TIM_GetCapture2(TIM3);
float duty_cycle = (float)pwm_value / 99 * 100;
printf("PWM Duty Cycle: %.2f%%\n", duty_cycle);
delay_ms(1000);
这样可以打印出PWM的占空比:
2.3 TIM_Tim9例程
程序启动后,先进行延时和串口初始化,输出系统时钟频率信息与 “TIM Tim9 Base Test.” 提示,接着配置并启动定时器 TIM9,随后进入无限循环;当 TIM9 产生更新事件触发中断时,中断服务函数会清除中断标志并通过串口输出自身函数名,之后继续等待下一次中断。
2.4 TIM_Touch例程
程序运行后,LED0以150ms周期闪烁,检测到触摸时LED1状态翻转并通过串口打印"Touch",同时启动时输出系统时钟频率信息。