01-标准库开发-STM32定时器
一、GPIO介绍
前言
该笔记硬件为STM32F103RCT6,使用标准库函数进行开发。
1.1 GPIO口的介绍
在STM32等微控制器的GPIO(通用输入输出)配置中,这些模式用于定义GPIO引脚的电气特性和功能。每种模式都对应着不同的应用场景和电路要求。下面是对您列出的各种模式的解释:
- GPIO_Mode_AIN (0x0):
- 模拟输入模式(Analog Input)。在此模式下,GPIO引脚被配置为模拟信号输入,用于读取外部模拟信号(如温度传感器的输出)。该模式关闭了引脚的数字输出功能,使其仅作为模拟信号路径使用。
- GPIO_Mode_IN_FLOATING (0x04):
- 浮空输入模式(Floating Input)。此模式下,GPIO引脚既不上拉也不下拉,其电平状态完全由外部电路决定。这种模式适用于需要检测外部高阻态信号的情况,但也可能因为外部噪声而导致引脚电平不稳定。
- GPIO_Mode_IPD (0x28):
- 下拉输入模式(Input Pull-Down)。此模式下,GPIO引脚通过一个内部电阻下拉到地,即使外部没有连接,引脚也保持低电平。这有助于在外部信号不确定时,保持引脚状态的稳定性。
- GPIO_Mode_IPU (0x48):
- 上拉输入模式(Input Pull-Up)。与下拉输入相反,此模式下GPIO引脚通过一个内部电阻上拉到电源(通常是3.3V或5V),即使外部没有连接,引脚也保持高电平。这同样有助于在外部信号不确定时,保持引脚状态的稳定性。
- GPIO_Mode_Out_OD (0x14):
- 开漏输出模式(Open-Drain Output)。此模式下,GPIO引脚可以作为开漏输出使用,允许外部电路通过该引脚控制一个共阳极的LED或其他设备。由于引脚只能拉低电平(通过接地),因此需要外部上拉电阻才能输出高电平。
- GPIO_Mode_Out_PP (0x10):
- 推挽输出模式(Push-Pull Output)。这是最常用的输出模式,引脚可以直接输出高电平或低电平,驱动能力强,适用于直接驱动LED、继电器等负载。
- GPIO_Mode_AF_OD (0x1C):
- 复用开漏输出模式(Alternate Function Open-Drain Output)。此模式下,GPIO引脚被配置为特定的复用功能(如SPI、I2C等),并以开漏方式输出。这允许GPIO引脚在执行这些特殊功能时,与外部电路灵活连接。
- GPIO_Mode_AF_PP (0x18):
- 复用推挽输出模式(Alternate Function Push-Pull Output)。与复用开漏输出类似,但在此模式下,GPIO引脚以推挽方式输出其复用功能的信号,驱动能力强,适用于需要较强驱动能力的复用功能场景。
这些模式的选择取决于您的具体应用场景和电路设计需求。
1.2 点亮LED灯
硬件图如图:
1.3 实现步骤
1、使能时钟
2、配置GPIO
二、时钟树STM32时钟系统
2.1 晶振
2.2 SysTick
是一个24位的系统定时器,并且向下计数,计数到0之后会更新计数。
SysTick是一个24位的递减计数器,它可以自动重载并在计数到0时产生中断。这使得SysTick非常适合用于需要精确时间控制的应用场景,如操作系统的时间片轮转、精确延时等。SysTick定时器使用内部时钟源或外部时钟源,通过配置控制与状态寄存器(STK_CTRL)来实现不同的工作模式。
2.3 主要功能位
- ENABLE(位0):
- 功能:使能SysTick计数器。当此位被设置为1时,SysTick计数器开始递减计数;当此位被设置为0时,计数器停止工作。
- 使用方法:在需要启动SysTick定时器时,将此位设置为1;在不需要时,将其设置为0以节省资源。
- TICKINT(位1):
- 功能:使能SysTick中断。当此位被设置为1时,如果SysTick计数器递减到0,将产生一个SysTick中断;如果此位被设置为0,则即使计数器递减到0也不会产生中断。
- 使用方法:在需要SysTick定时器在计数到0时产生中断时,将此位设置为1;如果不需要中断,则将其设置为0。
- CLKSOURCE(位2):
- 功能:选择SysTick定时器的时钟源。当此位被设置为0时,SysTick使用外部参考时钟;当此位被设置为1时,SysTick使用内核时钟(通常是AHB时钟的某个分频值)。
- 使用方法:根据具体的应用需求选择合适的时钟源。在大多数情况下,使用内核时钟并配置合适的分频值以获得所需的计时精度。
- COUNTFLAG(位16):
- 功能:这是一个只读的状态位,用于指示SysTick计数器是否已经递减到0。当SysTick计数器从非零值递减到0时,此位被自动设置为1;当此位被读取后,它会被自动清零。
- 使用方法:在中断服务例程或延时函数中,可以通过检查此位来判断SysTick计数器是否已经递减到0,从而进行相应的处理。
三、TIM定时器
计算公式 =
计数器的计数频率=72*10^6 /分频系数(PSC)
频率 = 计数频率/ ARR(重装载值)
所以:定时器频率 = 时钟频率/分频系数(PSC - 1) / 自动重装载值(ARR - 1)
3.1 定时器中断
定时器中断,我们的步骤一般为,使能TIMx.
设置内部时钟
配置定时器中断(TIM_ITConfig)
启动定时器(TIM_Cmd)
由图我们可以知道:
APB1总线负责连接低速外设,如DA(数字模拟转换器)、USB、SPI、I2C、CAN、串口(USART 2、3、4、5)以及普通定时器(TIM2至TIM5等)。而APB2总线则通常负责连接高速外设,如AD(模拟数字转换器)、I/O端口、高级定时器(TIM1和TIM8等)以及串口1(USART1)
3.1.1 示例代码:
void Timer_Init(u16 arr,u16 psc)
{
// 时基结构体
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
// NVIC初始化结构体
NVIC_InitTypeDef NVIC_InitStruct;
// 使能APB1外设
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6,ENABLE);
// 设置为内部时钟 72M
TIM_InternalClockConfig(TIM6);
// 时基初始化
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStructure.TIM_Period = arr - 1;
TIM_TimeBaseInitStructure.TIM_Prescaler = psc - 1;
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM6,&TIM_TimeBaseInitStructure);
// 配置定时器6中断
TIM_ITConfig(TIM6,TIM_IT_Update,ENABLE);
// 配置NVIC
NVIC_InitStruct.NVIC_IRQChannel = TIM6_IRQn;
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 2; // 抢占优先级
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1; // 次要优先级
NVIC_Init(&NVIC_InitStruct);
// 启动定时器
TIM_Cmd(TIM6,ENABLE);
}
3.2 定时器输出比较(PWM)
在STM32中,提供了强大的定时器和输出比较(Output Compare, OC)功能,允许开发者通过比较计数器的值(CNT)与捕获/比较寄存器(CCR)的值,来控制输出电平的翻转,从而生成一定频率和占空比的PWM(脉冲宽度调制)波形。输出比较功能广泛应用于电机控制、LED呼吸灯、舵机控制等领域。
3.2.1 基本原理
输出比较功能的基本原理是通过比较CNT与CCR的值来控制输出电平的状态。每个高级定时器和通用定时器都内置了4个输出比较通道,而高级定时器的前3个通道还额外具备死区生成和互补输出的功能。当CNT的值与CCR的值相匹配时,可以根据设定的输出比较模式来切换输出电平(置1、置0或翻转)。
3.2.2 PWM波的生成
PWM波形是通过调制一系列脉冲的宽度来等效地获得所需要的模拟参量。在STM32中,PWM参数主要包括频率、占空比和分辨率。
- 频率(Freq):表示每秒产生的PWM周期数,计算公式为
Freq = CK_PSC / (PSC + 1) / (ARR + 1)
,其中CK_PSC
为预分频时钟频率,PSC
为预分频数,ARR
为自动重装载寄存器值。 - 占空比(Duty):表示每个PWM周期内高电平持续的时间占比,计算公式为
Duty = CCR / (ARR + 1)
。 - 分辨率(Rese):表示占空比可以变化的最小步距,计算公式为
Rese = 1 / (ARR + 1)
。
3.2.3 初始化步骤
要使用STM32的输出比较功能生成PWM波形,通常需要执行以下步骤:
- 开启时钟:首先需要使能定时器和GPIO端口的时钟。
- 配置时基单元:包括时钟源选择、预分频器、自动重装载计数器等。
- 配置输出比较单元:设置CCR值、输出比较模式、极性选择、输出使能等参数。
- 启动定时器:启动定时器以开始生成PWM波形。
3.2.4 关键函数和结构体
在STM32标准外设库中,有几个关键的函数和结构体用于配置和管理输出比较功能:
- TIM_OCInitTypeDef:这是一个结构体,用于配置输出比较参数,包括输出比较模式(TIM_OCMode)、输出状态(TIM_OutputState)、输出极性(TIM_OCPolarity)和脉冲值(TIM_Pulse,即CCR寄存器的值)。
- TIM_OCxInit():这些函数用于初始化指定定时器(TIMx)的输出比较通道(OCx),其中x表示通道号(1至4)。
- TIM_SetComparex():这些函数用于设置指定定时器(TIMx)的某个输出比较通道(OCx)的CCR值,从而调整PWM的占空比。
- TIM_OCxPolarityConfig():这些函数用于配置输出比较通道的极性。
- TIM_ForcedOCxConfig():这些函数用于配置强制输出模式,可以立即将输出电平设置为高或低。
3.2.5 关键示例代码
该代码由于硬件接线问题,采用高级定时器实现,PWM波形,实现呼吸灯。
void PWM_Init(u16 ARR,u16 PSC,u16 CRR)
{
// 时基结构体
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
// 使能APB2外设
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1|RCC_APB2Periph_GPIOA|RCC_APB2Periph_AFIO,ENABLE);
// 初始化GPIOA
GPIO_InitTypeDef InitStruct;
InitStruct.GPIO_Pin = GPIO_Pin_8;
InitStruct.GPIO_Mode = GPIO_Mode_AF_PP; // 复用推挽输出,输出寄存器控制转为片上外设
InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&InitStruct);
// GPIO_SetBits(GPIOA,GPIO_Pin_8);
// 设置为内部时钟 72M
TIM_InternalClockConfig(TIM1);
// 时基初始化
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStructure.TIM_Period = ARR - 1;
TIM_TimeBaseInitStructure.TIM_Prescaler = PSC - 1;
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM1,&TIM_TimeBaseInitStructure);
// 配置输出比较初始化
TIM_OCInitTypeDef TIM_OCInitStruct;
TIM_OCStructInit(&TIM_OCInitStruct);
TIM_OCInitStruct.TIM_OCMode = TIM_OCMode_PWM1; // PWM1在低于比较值时有效
TIM_OCInitStruct.TIM_OCPolarity = TIM_OCPolarity_Low; // 有效电平
TIM_OCInitStruct.TIM_OutputState = TIM_OutputState_Enable; // 输出使能
TIM_OCInitStruct.TIM_Pulse = CRR; // CRR的值
TIM_OC1Init(TIM1,&TIM_OCInitStruct);
// 启动定时器
TIM_Cmd(TIM1,ENABLE);
/* 使能TIMx在CCRx上的预装载寄存器 */
TIM_OC1PreloadConfig(TIM1, TIM_OCPreload_Enable);
/* 使能TIMx在ARRx上的预装载寄存器 */
TIM_ARRPreloadConfig(TIM1, ENABLE);
/* 高级定时器需要配置:设置MOE位以使能主输出 */
TIM_CtrlPWMOutputs(TIM1, ENABLE);
}
void PWM_SetCompare1(u16 Compare)
{
TIM_SetCompare1(TIM1,Compare);
}
3.3 定时器输出捕获(IC)
输入捕获(Input Capture, IC)是STM32定时器的一个重要功能,主要用于测量外部信号的频率、周期、占空比等时间参数。STM32中的输入捕获功能,包括其原理、配置方法以及应用实例,帮助读者深入理解并掌握这一功能。
3.3.1 基本原理
输入捕获是STM32定时器的一种工作模式,其主要作用是当外部信号在特定引脚上发生边沿变化(如上升沿或下降沿)时,记录下当前定时器的计数值。这些计数值可以用于后续的时间参数计算,如脉冲宽度、周期等。输入捕获功能在电机控制、传感器信号处理等领域有着广泛的应用。
3.3.2 频率测量方法
- 测频法:在闸门时间T内,对上升沿计次,得到N,则频率
f = N / T
- 测周法:在两个上升沿内,对标准频率fc计次,得到N,则频率
f = fc / N
- 中界频率:测频法和测周法误差相等的频率点
fm = 根号(fc/T)
3.3.3 基本流程
- 信号输入:外部信号通过STM32的某个引脚输入到定时器。
- 边沿检测:定时器检测到信号的电平跳变(上升沿或下降沿)。
- 计数值捕获:在边沿跳变发生时,将当前定时器的计数值捕获并保存到对应的捕获/比较寄存器(CCR)中。
- 数据处理:通过程序读取捕获的计数值,进行进一步的处理和计算,如计算频率、周期等。
3.3.4 示例代码
#include "ic.h"
void IC_Init(void)
{
// 使能RCC时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
// 配置GPIO初始化
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStruct);
// 配置内部时钟
TIM_InternalClockConfig(TIM3);
// 配置时基单元
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
TIM_TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStruct.TIM_Period = 65536-1;
TIM_TimeBaseInitStruct.TIM_Prescaler = 72 - 1;
TIM_TimeBaseInitStruct.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitStruct);
// 输入捕获
TIM_ICInitTypeDef TIM_ICInitStruct;
TIM_ICInitStruct.TIM_Channel = TIM_Channel_1;
TIM_ICInitStruct.TIM_ICFilter = 0xF;
TIM_ICInitStruct.TIM_ICPolarity =TIM_ICPolarity_Rising;
TIM_ICInitStruct.TIM_ICPrescaler = TIM_ICPSC_DIV1;
TIM_ICInitStruct.TIM_ICSelection = TIM_ICSelection_DirectTI;
TIM_ICInit(TIM3,&TIM_ICInitStruct);
TIM_SelectInputTrigger(TIM3,TIM_TS_TI1FP1);
TIM_SelectOutputTrigger(TIM3,TIM_TRGOSource_Reset);
TIM_Cmd(TIM3,ENABLE);
}
u32 IC_GetFreq(void)
{
return 1000000 / TIM_GetCapture1(TIM3);
}