STM32入门开发操作记录(八)——通用定时器计数

一、项目准备

1. 工程模板

  本篇项目所用模板包含以下模块,声明函数见头文件,模块添加和函数功能详见往期记录。
请添加图片描述

2. 器件接线

  主装置:ST-Link仿真器,STM32系统板,MB102面包板,OLED显示屏(接线详见往期记录)

  除了A15B3B4JLINK的调试端口,其他端口可随意使用。各类器件默认接线方式如下。

器件端口/电源
3.3/VCC+
GND-
LEDA1 | A2
Encoder-AB0
Encoder-BB1
OLED-SCLB8
OLED-SDAB9
Buzzer-I/OB12
Sensor-DOB13

  

二、基础时钟模块

1. 时钟电路

  STM32微控制器的电路基本单元包含:电源电路、复位电路、时钟电路。其中,时钟电路指的是像时钟一样准确运动的振荡电路,它可以产生稳定、精确的时间基准信号,任何工作都按时间顺序进行。

  时钟电路的电源称为时钟,它是一个频率精确且稳定的脉冲信号发生器。脉冲信号是按一定电压幅度连续发出的周期性循环信号。在单位时间内产生的脉冲个数称为频率,单位是赫兹Hz)。指令的执行,寄存器的写、读、复位,数据从寄存器到硬件上的移位,都是基于时钟来进行同步和处理。

  时钟电路保证了STM32的可编程性和灵活性,使其能够适应各种不同的应用场景,对于STM32的正常运行至关重要,它的主要作用如下:

  • 同步操作:产生时间信号,提供同步基准,确保各个功能模块(如CPU、存储器、输入/输出接口等)按预定的时序协同工作。
  • 数据传输:时钟信号使得数据在正确的时间被读取或写入。确保数据的可靠性和一致性。
  • 功耗管理:时钟电路可以切换不同频率的时钟,控制整体功耗。

  

2. 时钟源

  STM32微控制器的时钟系统有多个时钟源,可以根据需要进行选择和配置。其中,独立时钟源有:

  • HSIHigh-Speed Internal):内部高速时钟,频率为8MHz
  • HSEHigh-Speed External):外部高速时钟,频率为4MHz~16MHz
  • LSILow-Speed Internal):内部低速时钟。频率为40kHz
  • LSELow-Speed External):外部低速时钟。频率为32.768kHz

  内部时钟的时钟信号由芯片内部的RC振荡器产生,起振较快,但精度不高,通常在芯片刚上电时作为默认时钟源使用;而外部时钟的时钟信号由外部的晶体(石英)谐振器陶瓷谐振器产生,精度和稳定性更高,通常在芯片上电后通过软件配置使用。

  高速时钟功耗较高,一般提供给芯片主体的主时钟;而低速时钟功耗较低,只是提供给芯片中的实时时钟RTC(Real-Time Clock)及独立看门狗IWGD(Independent Watchdog)使用。

  此外,还有系统时钟SYSCLK,可以驱动它的时钟源有:

  • HSI振荡器时钟
  • HSE振荡器时钟
  • PLL锁相环时钟:以HSIHSE作为输入时钟源,可以倍频或分频输出时钟信号(频率不超过72MHz),作为系统时钟的时钟源。

  

3. 时钟树

时钟树
  
  STM32微控制器的各个模块和外设通过一个复杂的时钟树与时钟源相连。复位和时钟控制模块RCC(Reset and Clock Control)负责管理这个时钟树,其功能包括:

  • 使能或禁止时钟:外围设备的寄存器需要电源(时钟)才能工作。当外设时钟没有启用时,软件不能读出外设寄存器的数值,返回值始终是0x0
  • 配置时钟分频:兼容不同速度的设备,降低芯片功耗,实现精细化的功率管理。
  • 管理复位系统:当STM32发生错误或异常时,如电源故障、系统错误等,复位系统可以将微控制器重置到初始状态,以保证系统的稳定运行。每次芯片复位后,所有外设时钟都被关闭(SRAMFlash接口除外)。
      `

4. 总线

  使用外设前,必须在RCC_AHBxENRRCC_APBxENR寄存器中使能其时钟,也就是说,RCC外设总线配置外设时钟后,才能通过外设总线操作外围设备。

  • AHB(Advanced High-performance Bus)高级高性能总线,是STM32的高速总线,用于连接主要的内部总线和外设,频率通常等于SYSCLK,但可以通过时钟分频器进行分频。
  • APB(Advanced Peripheral Bus)高级外设总线,其中,APB1低速总线,用于连接低速外设,频率通常是AHB的一半;APB2高速总线,用于连接高速外设,频率通常等于AHB,都可以通过时钟分频器进行分频。
      

三、定时中断

1. 定时器

  定时器Timer)可以对输入的时钟信号进行计数,并在计数值达到设定值时触发中断,根据复杂度和应用场景分为三种类型:

类型编号总线功能
基本定时器TIM6、TIM7APB1定时中断、主模式触发DAC
通用定时器TIM2TIM3TIM4、TIM5APB1(额外新增)内外时钟源选择、输入捕获、输出比较、编码器接口、主从触发模式
高级定时器TIM1、TIM8APB2(额外新增)重复计数器、死区生成、互补输出、刹车输入

  复杂定时器具有简单定时器的全部功能,STM32F103C8T6的定时器资源包括:TIM1TIM2TIM3TIM4.
  

2. 时基单元

  ‌时基单元是计时器中最基本的计数计时电路,通用计时器的时基单元由三个寄存器组成:预分频器Prescaler)、计数器Counter)、‌自动重装载寄存器Auto-Reload Register)。
在这里插入图片描述

  • 预分频器PSC):内部时钟SYSCLK输入预分频器的时钟频率CK_PSC一般是主频72MHz。该寄存器的值PSC分频系数1,也就是说,向计数器输出的时钟频率CK_CNT = CK_PSC/(PSC+1)
  • 计数器CNT):该寄存器输入频率为CK_CNT的时钟信号后,每检测到一个上边沿,寄存器的值CNT就加1
  • 自动重装载寄存器ARR):在上述过程中,该寄存器不断将目标值ARRCNT比较,一旦CNT = ARR+1,就产生一个更新事件U和一个更新中断信号UI,触发DAC转换,实现定时中断

  例如,PSC设为7199,则分频系数为7200,计数频率为CK_CNT = 72MHz/7200 = 10000Hz,即每秒输入10000个脉冲信号,也就是CNT每秒计10000次;若ARR设为9999,则每秒触发一次定时中断,从而实现读秒计数

  其中,数模转换器Digital-to-Analog Converter)用于将数字信号转换为模拟信号,即:将寄存器的数字输入值,模拟输出为波形信号。

  在从模式下,DAC转换的触发需要更新中断信号引发,并按时在中断程序中调用代码手动实现;在主模式下,更新事件可直接映射到触发输出Trigger Out)触发DAC转换,实现硬件自动化——这就是主从触发DAC功能。

  此外,计数器具有三种计数模式:

  • 向上计数模式CNT初始值为0,每检测到一个CK_CNT脉冲CNT1,直至与ARR-1相等,CNT恢复初始值,并产生一次上溢事件
  • 向下计数模式CNT初始值为ARR,每检测到一个CK_CNT脉冲CNT1,直至归零CNT恢复初始值,并产生一次下溢事件
  • 中心对齐模式:在向上计数模式下,产生上溢事件后,转换为向下计数模式;在向上计数模式下,产生下溢事件后,转换为向上计数模式
      

3. 滤波器

在这里插入图片描述

  滤波器用于过滤外部时钟信号中混杂的高频抖动干扰信号,它可以对输入的时钟信号按一定的采样频率f进行采样,并比较一定数目N个采样点。若采样点相同,则正常输出;若采样点不同,则保持原输出输出低电平

  其中,采样频率来自内部时钟的分频信号,通用计时器的可选分频参数ClockDivision有:一分频TIM_CKD_DIV1)、二分频TIM_CKD_DIV2)、四分频TIM_CKD_DIV4),分别对应72MHz36MHz18MHz

  

4. 流程示意

在这里插入图片描述
  因为使用内部时钟RCC给定时器供能,所以无需配置GPIO端口,只需开启外设时钟初始化时基单元NVIC,最后启动定时器即可。
  

四、定时器模块

Timer.c

#include "stm32f10x.h"

void Timer_Init(void)
{
	// 开启通用定时器TIM2外设的时钟
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
	
	// 使用内部时钟
	TIM_InternalClockConfig(TIM2);
	
	// 配置时基单元参数
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
	TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;			// 滤波器采样频率:1分频
	TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;		// 计数器模式:向上计数
	TIM_TimeBaseInitStructure.TIM_Period = 10000 - 1;					// 自动重装载寄存器参数ARR
	TIM_TimeBaseInitStructure.TIM_Prescaler = 7200 - 1; 				// 预分频器寄存器参数PSC
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;				// 重复计数器寄存器参数:仅供高级计数器使用
	// 初始化时基单元
	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);
	
	// 重置标志位:初始化参数时会产生更新中断
	TIM_ClearFlag(TIM2, TIM_IT_Update);
	// 使能更新中断
	TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
	
	// 配置中断优先级:分组2
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	
	// 配置NVIC参数
	NVIC_InitTypeDef NVIC_InitStructure;
 	NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;				// 中断请求通道:TIM2
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;				// 中断请求通道状态:使能
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;	// 抢占优先级:2
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;			// 响应优先级:1
	// 初始化嵌套中断向量控制器
 	NVIC_Init(&NVIC_InitStructure);
	
	// 启动定时器TIM2
	TIM_Cmd(TIM2, ENABLE);
}

// TIM2中断请求通道控制
/*void TIM2_IRQHandler(void)
{
	// 检查TIM2中断线请求状态寄存器的标志位
	if(TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)					// 更新中断触发
	{
		TIM_ClearITPendingBit(TIM2, TIM_IT_Update);					// 重置标志位
	}
}*/

  

Timer.h

#ifndef __TIMER_H
#define __TIMER_H

void Timer_Init(void);

#endif

  

五、读秒计数

#include "stm32f10x.h"		// 器件模块
#include "OLED.h"			// OLED模块
#include "Timer.h"			// 定时器模块

uint16_t Num;	// 数值

int main(void)
{	
	// 初始化
	OLED_Init();
	Timer_Init();
	
	// 显示计数
	OLED_ShowString(1, 1, "Num:");
	
	while(1)
	{
		OLED_ShowNum(1, 5, Num, 5);
	}
}

// TIM2中断请求通道控制
void TIM2_IRQHandler(void)
{
	// 检查TIM2中断线请求状态寄存器的标志位
	if(TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)	// 更新中断触发
	{
		Num ++;										// 计数
		TIM_ClearITPendingBit(TIM2, TIM_IT_Update);	// 重置标志位
	}
}

请添加图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值