当前位置: 首页 > article >正文

01-标准库开发-STM32定时器

一、GPIO介绍

前言

该笔记硬件为STM32F103RCT6,使用标准库函数进行开发。

1.1 GPIO口的介绍

在STM32等微控制器的GPIO(通用输入输出)配置中,这些模式用于定义GPIO引脚的电气特性和功能。每种模式都对应着不同的应用场景和电路要求。下面是对您列出的各种模式的解释:

  1. GPIO_Mode_AIN (0x0):
    • 模拟输入模式(Analog Input)。在此模式下,GPIO引脚被配置为模拟信号输入,用于读取外部模拟信号(如温度传感器的输出)。该模式关闭了引脚的数字输出功能,使其仅作为模拟信号路径使用。
  2. GPIO_Mode_IN_FLOATING (0x04):
    • 浮空输入模式(Floating Input)。此模式下,GPIO引脚既不上拉也不下拉,其电平状态完全由外部电路决定。这种模式适用于需要检测外部高阻态信号的情况,但也可能因为外部噪声而导致引脚电平不稳定。
  3. GPIO_Mode_IPD (0x28):
    • 下拉输入模式(Input Pull-Down)。此模式下,GPIO引脚通过一个内部电阻下拉到地,即使外部没有连接,引脚也保持低电平。这有助于在外部信号不确定时,保持引脚状态的稳定性。
  4. GPIO_Mode_IPU (0x48):
    • 上拉输入模式(Input Pull-Up)。与下拉输入相反,此模式下GPIO引脚通过一个内部电阻上拉到电源(通常是3.3V或5V),即使外部没有连接,引脚也保持高电平。这同样有助于在外部信号不确定时,保持引脚状态的稳定性。
  5. GPIO_Mode_Out_OD (0x14):
    • 开漏输出模式(Open-Drain Output)。此模式下,GPIO引脚可以作为开漏输出使用,允许外部电路通过该引脚控制一个共阳极的LED或其他设备。由于引脚只能拉低电平(通过接地),因此需要外部上拉电阻才能输出高电平。
  6. GPIO_Mode_Out_PP (0x10):
    • 推挽输出模式(Push-Pull Output)。这是最常用的输出模式,引脚可以直接输出高电平或低电平,驱动能力强,适用于直接驱动LED、继电器等负载。
  7. GPIO_Mode_AF_OD (0x1C):
    • 复用开漏输出模式(Alternate Function Open-Drain Output)。此模式下,GPIO引脚被配置为特定的复用功能(如SPI、I2C等),并以开漏方式输出。这允许GPIO引脚在执行这些特殊功能时,与外部电路灵活连接。
  8. 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 主要功能位

  1. ENABLE(位0):
    • 功能:使能SysTick计数器。当此位被设置为1时,SysTick计数器开始递减计数;当此位被设置为0时,计数器停止工作。
    • 使用方法:在需要启动SysTick定时器时,将此位设置为1;在不需要时,将其设置为0以节省资源。
  2. TICKINT(位1):
    • 功能:使能SysTick中断。当此位被设置为1时,如果SysTick计数器递减到0,将产生一个SysTick中断;如果此位被设置为0,则即使计数器递减到0也不会产生中断。
    • 使用方法:在需要SysTick定时器在计数到0时产生中断时,将此位设置为1;如果不需要中断,则将其设置为0。
  3. CLKSOURCE(位2):
    • 功能:选择SysTick定时器的时钟源。当此位被设置为0时,SysTick使用外部参考时钟;当此位被设置为1时,SysTick使用内核时钟(通常是AHB时钟的某个分频值)。
    • 使用方法:根据具体的应用需求选择合适的时钟源。在大多数情况下,使用内核时钟并配置合适的分频值以获得所需的计时精度。
  4. 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波形,通常需要执行以下步骤:

  1. 开启时钟:首先需要使能定时器和GPIO端口的时钟。
  2. 配置时基单元:包括时钟源选择、预分频器、自动重装载计数器等。
  3. 配置输出比较单元:设置CCR值、输出比较模式、极性选择、输出使能等参数。
  4. 启动定时器:启动定时器以开始生成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 基本流程

  1. 信号输入:外部信号通过STM32的某个引脚输入到定时器。
  2. 边沿检测:定时器检测到信号的电平跳变(上升沿或下降沿)。
  3. 计数值捕获:在边沿跳变发生时,将当前定时器的计数值捕获并保存到对应的捕获/比较寄存器(CCR)中。
  4. 数据处理:通过程序读取捕获的计数值,进行进一步的处理和计算,如计算频率、周期等。

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);
}


http://www.kler.cn/a/420091.html

相关文章:

  • Qt—QLineEdit 使用总结
  • 家政小程序开发,打造便捷家政生活小程序
  • 6.824/6.5840(2024)环境配置wsl2+vscode
  • 生态环境一体化智慧监管平台
  • Seq2Seq模型与Transformer模型差异
  • 使用无监督机器学习算法进行预测性维护
  • 为什么在服务器上设置 fish 为默认 shell, vscode remote ssh 默认还是 bash?
  • flink学习(13)—— 重试机制和维表join
  • 在 uniapp 项目中使用 Iconify 字体图标库
  • 《Python PDF 格式转换全攻略》
  • Linux 进程管理详解
  • 张量并行和流水线并行在Transformer中的具体部位
  • 25.4K Star 高效内存数据存储!特别好用的Redis 和 Memcached 替代品:Dragonfly!
  • redisson-spring-data与Spring-Data-Redis的版本关系问题
  • 性能监控系统Prometheus、Node-exporter与Grafana部署详解搭建
  • 黑马程序员Java项目实战《苍穹外卖》Day03
  • Xilinx PCIe高速接口入门实战(一)
  • 软件保护:从用户角度出发的安全需求与体验
  • C++之 String 类的模拟实现
  • k8s api对象,CRD
  • linux 操作系统环境配置 redhat9
  • 如何利用微型5G网关为智慧无人矿车提供精确定位
  • pytest(一)csv数据驱动
  • AI开发 - GPT之魂 用Python 演示chatGPT的自注意力机制 - 机器学习
  • JavaScript根据数据生成柱形图
  • 大数据Hadoop实战:从基础到应用