NVIC与EXTI外部中断与TIM定时中断RCC时钟树 stm32(江科大标准库)

本文中的stm32都是由江科大的标准库教程来做的笔记。里面的某些观点可能就是江科大中的理解,或许会被我原封不动的抄下来。

且看NVIC(嵌套中断向量控制器)

在stm32中,他是统一分配中断优先级和管理中断的 。可以把它看成CPU的一个小助手。stm32的中断非常多,若是这些中断同时申请,或者中断产生了很多拥堵。那么CPU处理起来就会很拥堵,因为CPU主要是用来运算的。那么此时NVIC就可以来执行中断分配的任务。把各种中断看成病人,CPU看成医生,那么NVIC就是医院的叫号系统,它会根据病人的情况是否紧急来安排病人看病的先后顺序,且一次之后叫一个号,也就是一次只会传给CPU一个中断任务,让CPU来执行中断程序。(这部分内容几乎都是取自江科大的讲述,我只是对其内容进行复述以及排列组合了一下)

再看NVIC优先级分组

响应优先级          类似生活中的插队现象,在有很多病人排队的情况下,在上一个病人看完后,紧急的病人即使是后来的,也会最先进去看病

抢占优先级        类似于我们生活中的生命垂危的病人,那他一定是非常紧急的,那么即使这时候有一个在医生那看病,也可以直接让医生停下手头上的工作,抛下正在看病的病人。直接先去抢救生命垂危的病人,等到生命垂危的病人脱离危险了,再回去看刚刚不那么紧急的病人。这也即是中断嵌套的概念。

分组方式(自己设置)

总结一下 即响应是优先排队的概念,而抢占是优先嵌套,立刻执行的概念

好好好,那么我们现在就来接诊

首先是第一个病人:外部中断(EXTI)

上述就是EXTI的简介,但是简单来说就是引脚电平变化,申请中断

出发方式:1.上升沿:由低到高引发中断 2.下降沿:由高到低引发中断 3.双边沿:上升沿和下降沿都可以引发中断 4.软件触发:引脚啥事没有,软件里写一个程序就能引起中断

支持的GPIO口:其中的相同的Pin口不能触发中断是指,PA0,PB0不能同时使用。

触发响应方式:其中事件响应是指,引脚发生变化后不会触发中断,也就不会传给CPU中断信息,而是触发别的外设操作, 属于外设之间的联合工作。

每个GPIO外设都有十六个引脚,如果每个引脚占用一个通道,那么EXTI的16个通道显然就不够用了,所以会有一个中断引脚选择模块(AFIO),它可以在前面的GPIO外设的16个引脚里选择其中的一个连接到后面的EXTI的通道中。而其中的PA0 PB0 PC0经过选择后,只能有一个能接到EXTI的通道0上,而PA1 PB1 PC1也只能有一个接到通道1上,所以这就是为何相同的PIN不能同时触发中断的原因。然后选择十六个引脚后加上另外四个外设(此处暂时不需了解这四个外设),共同组成了EXTI20个输入信号,然后经过EXTI后,输出。但其中需要注意的是,本来20个输入应该有20个输出,但是其内部将中断的5-9,10-15分别分到了一个通道中。也就是说外部中断的5-9会触发同一个中断函数,10-15也是如此。 格外注意一点:数字越小,中断等级越高!!!

AFIO的结构图

如何配置外部中断?

如图

我们需要把GPIO到NVIC这一路中出现的外设模块都配置好,把信号电路打通,即可

具体步骤:1.配置RCC把涉及到的外部时钟都打开(不打开时钟,外设是没法工作的)                                     2.配置GPIO,选择我们的端口为输入模式。                                                                                     3.配置AFIO,选择我们用的这一路GPIO,连接到后面的EXTI                                                           4.配置EXTI ,选择边沿触发方式,比如上升沿,下降或双边沿。还有选择触发响应方式,可以选择中断响应和事件响应。(一般都是中断响应 )                                                                               5.配置NVIC,给我们的中断选择一个合适的优先级

这样之后,通过NVIC外部中断信号就能进入CPU了。如此CPU才能收到中断信号,跳转到中断函数里执行中断程序。 此处涉及的外设比较多,有RCC,GPIO,AFIO ,EXTI,NVIC

配置外部中断(每个操作都写了注释)

#include "stm32f10x.h"                  // Device header

uint16_t CountSensor_Count;

void CountSensor_Init(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //开启GPIOB的时钟,GOIOB是APB2的外设
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);//开启AFIO的时钟,AFIO也是GPIO的外设 
	//RCC管的都是内核外的外设,所以RCC管不着NVIC
	//NVIC和EXTI都不需要开启时钟,因为本身就是打开的
	//上面这些之后时钟就配置完了
	GPIO_InitTypeDef GPIO_InitStructure;//定义结构体
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;//上拉输入,默认为高电平的输入
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14;//使用的是PB14号口
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化GPIOB的外设
	
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource14); //AFIO外部引脚选择PB14 
	//到这,AFIO外部中断引脚选择配置就完成了,当执行完这个函数,AFIO的第十四个数据选择器就弄好了
	//其中输入端被拨到了GPIOB外设上,对应的就是PB14号引脚,输出端固定连接的是EXIT的第14个中断线路
	//这样,PB14号引脚的电平信号就可以顺利通过AFIO,进入到后面的EXTI电路了
	//接下来我们就可以配置第四步,配置EXTI了
	
	EXTI_InitTypeDef EXTI_InitStructure;//配置EXTI结构体,因为EXTI只有一个,不需要像GPIO一样
	EXTI_InitStructure.EXTI_Line = EXTI_Line14;//指定我们配置的中断线
	EXTI_InitStructure.EXTI_LineCmd = ENABLE;//指定选择的中断线的新状态
	EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;//指定外部中断线的模式
	EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;//指定触发信号的有效边沿,此处为下降沿触发
	EXTI_Init(&EXTI_InitStructure);
	//上述操作为,将EXTI第十四位配置为中断模式,下降沿触发,然后开启中断	,这样,PB14的电平
	//信号就能通过EXTI通向下一级NVIC了
	
	//第五步,配置NVIC 
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//中断优先级。即配置几位响应,几位抢占
	
	NVIC_InitTypeDef NVIC_InitStructure;
	NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn;//指定中断通道
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//使选定中断通道使能
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;//配置中断优先级
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
	NVIC_Init(&NVIC_InitStructure);
}
//在stm32中,每个中断函数的命名都是固定的

uint16_t CountSensor_Get(void)
{
	return CountSensor_Count;
}

//在stm32中,每个中断函数的命名都是固定的
void EXTI15_10_IRQHandler(void)
{
	if (EXTI_GetITStatus(EXTI_Line14) == SET)//中断标志位的判断,确保是我们想要的中断源触发的这个函数
		//判断一下是不是我们想要的EXTI 14里进来的标志位是不是为一
	{
		/*如果出现数据乱跳的现象,可再次判断引脚电平,以避免抖动*/
		if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_14) == 0)
		{
			CountSensor_Count ++;
		}
		EXTI_ClearITPendingBit(EXTI_Line14);
		//清除中断标志位的函数,如果你不清除,那他就会一直申请中断
	}
}//这部分模块的用途就是,当满足外部中断的触发条件之后,一直计数触发外部中断的次数,并返回
//表现在外部就是挡一下挡光片,然后记录次数,并在OLED上显示

TIM 定时中断

定时器定时的基本功能,也就是定一个时间,然后让定时器每隔这个时间产生一个中断,来实现每隔一个固定时间执行一段程序的目的。

定时器的输出比较功能,输出比较这个模块最常见的用途就是产生PWM波形,用于驱动电机等设备,例如可以靠输出的PWM波形驱动舵机和直流电机/

定时器的输入捕获的功能,这个模块可以实现测量方波频率的例子

定时器的编码器接口,能够更加方便地读取正交编码器的波形,可以应用在编码电机测速中

其中定时器可以对输入的时钟进行计数,并在计数值达到设定值时触发中断(即定时触发中断)。其中输入的时钟是指什么意思呢?(我也不懂,于是问了一下GPT)以下是解答

输入的时钟指的是用于提供时间基准的时钟信号。定时器是一种计时设备,它通过对输入的时钟进行计数来测量经过的时间。时钟信号可以是一个外部的电子脉冲信号,也可以是来自计算机系统的内部时钟信号。

定时器接收时钟信号,并以固定的时间间隔递增计数器的值。通过监测计数器的值,可以实现各种定时和计时功能。例如,可以设置定时器在特定时间间隔后触发中断或执行某个任务。

因此,输入的时钟在这里是指供给定时器计数的时钟信号,用于测量时间和实现定时功能。

同时我们也可以看出来,定时器就是一个计数器,当他的输入是一个准确可靠的基准时钟的时候,那他对这个基准时钟进行计数的过程,实际上就是计时的过程。例如在STM32中,定时器的基准时钟一般都是主频72Mhz,比如我们对72MHz计72个数,那就是1MHz就是1us的时间,如果72000个数,那么1Khz就是1ms的时间

上面图片说的计数器就是用来执行计数定时的一个寄存器,每来一个时钟,计数器+1

预分频器,可以对计数器的时钟进行分频,让这个计数更加灵活

自动重装寄存器,就是计数的目标值,就是我想要记多少个时钟的申请中断

这些寄存器(都是十六位的)构成了寄存器最核心的部分,我们把这一块电路称为时基单元,2的十六次方也就是65536,也就是如果预分频器设置最大,自动重装也设置最大,那么定时器的最大的定时时间也就是59.65s,接近一分钟。这个的计算方法如下:72M/65536/65536,得到的是中断频率,然后取倒数。如果还嫌不够长,那么stm32的定时器还支持级联的模式,也就是一个定时器的输出,当作另一个定时器的输入这样加在一起,最大的时间也就是59.65s再乘以2个65536,要是再级联一个定时器,定时时间还会再延长2个65536倍

三种定时器的介绍表

基本定时器框图

下面的这三个预分频器和计数器和自动重装载寄存器,构成了最基本的计数计时电路,所以这一块电路就叫做时基单元。预分频器写0,那么输出频率就是输入频率/1=72MHz,如果写1,就是2分频,输出频率=输入频率/2=36MHz,这个预分频器是十六位的,所以最大值可以写65535,也就是65536分频,预分频器就是对输入的基准频率提前进行一个分频的操作

然后是计数器,这个计数器可以对预分频后的计数时钟进行计数,计数时钟每来一个上升沿,计数器的值就加一,这个计数器也是十六位的,所以里面的值可以从0一直加到65535,再加的话,就会变成0,即计数器会不断自增运行到目标值时,产生中断,那就完成了定时的任务

现在还需要一个存储目标值的寄存器,就是自动重装寄存器了, 自动重装寄存器也是十六位的,他存入的就是我们写入的计数目标,当计数值等于自动重装值时,也就是计时时间到了,那他就会产生一个中断信号,并且清零计数器,计数器自动开始下一次的计数计时

像这种,计数值等于自动重装值产生的中断信号,我们一般把他叫做“更新中断”,这个更新中断之后就会通往NVIC,我们再配置好NVIC的定时器通道,那么定时器的更新中断就能得到CPU的响应了

主从触发DAC的功能

他能让内部的硬件在不受程序的控制下实现自动运行,如果把这个掌握好,在某些情景下将会极大地减轻CPU的负担

用途:在我们使用DAC的时候,可能会用DAC输出一段波形,那就需要每隔一段时间来触发一次DAC,让它输出到下一个电压点,如果按照正常的思路来实现,就是先设置一个定时器产生中断,每隔一段时间在中断程序中调用代码手动触发一次DAC转换,然后DAC输出,但这样会使主程序处于频繁被中断的状态。而使用主从触发的话,只需要把更新事件,通过主模式映射到TRGO,然后TRGO就会直接去触发DAC了,整个过程不需要软件的参与,实现了硬件的自动化,这就是主模式的作用

通用定时器

之前的基本定时器都是向上计数的模式,但是通用定时器和高级定时器还支持向下计数模式和中央对齐模式,向下计数模式就是从重装值开始,向下自减,减到零之后回到重装值同时申请中断,中央对齐模式就是先向上增到重装值,申请中断,再向下自减减到0,再申请中断

对于基本定时器而言,定时只能选择内部时钟,也就是系统频率72Mhz,到了通用定时器这里,时钟源不仅可以选择内部的72Mhz时钟,还可以选择外部时钟,第一个外部时钟就是TIMx_ETR引脚上的外部时钟,(ETR引脚的位置,可以参考一下引脚定义表),那我们就可以在这个TIM3的ETR引脚,也就时PA0(ETR引脚的位置)上接一个外部的方波时钟,然后配置一下内部的极性选择,边沿检测和预分频器电路,再配置一下输入滤波电路,这些电路可以对外部时钟进行一定的整形,因为是外部引脚的时钟,所以难免会有些毛刺,这些电路就可以对输入的波形进行滤波,同时也可以选择一下极性和预分频器,最后,滤波后的信号,兵分两路,上面一路ETRF进入触发控制器,紧跟着就可以作为时基单元的时钟了,如果你想在ETR外部引脚提供时钟,或者想对ETR时钟进行计数,把这个定时器当作计数器来用的话,那就可以配置以下这一电路

这一路也叫做“外部时钟模式2”,除了外部ETR可以触发时钟外,就是TRGI也可以提供时钟,这一路主要是用作触发输入来使用的,这个触发输入可以触发定时器的从模式,关于触发输入和从模式,之后再讲,这里讲的暂且是这个把TRGI当作外部时钟的输入来看,当这个TRGI当作外部时钟来使用的时候,这一路就叫做“外部时钟模式1”,那通过这一路的外部时钟都有那些呢,往左看第一个就是ETR引脚的信号,这里ETR引脚既可以通过上面这一路来当作时钟,又可以通过下面这一路当作时钟,两种情况对于时钟输入而言是等价的,只不过下面这一路输入会占用出发输入的通道而已,第二个就是ITR信号,这一部分时钟信号是来自其他定时器的,从右边可以看出,这个主模式的输出TRGO可以通向其他定时器,那通向其他定时器的时候,就接到了其他定时器的ITR引脚上来了,这个ITR0和ITR3分别来自其他四个定时器的TRGO输出           

通过这一路我们就可以实现定时器级联的功能(根本不动啥叫级联)。比如我们可以先初始化TIM3,然后使用主模式把他的更新事件映射到TRGO上,接着再初始化TIM2,这里选择ITR2,对应的就是TIM3的TRGO,然后后面再选择时钟为外部时钟模式1,这样,TIM3的更新事件就可以驱动TIM2的时基单元,也就实现了定时器的级联,那我们继续看,我们这里还可以选择TI1F_ED,这里连接的是输入捕获单元的CH1引脚,也就是从CH1引脚捕获时钟,这里后缀加一个ED,就是边沿的意思,也就是通过这一路输入的时钟,上升沿和下降沿均有效,最后,这个时钟还能通过TI1FP1和TI2FP2获得,其中TI1FP1连接到了CH1引脚的时钟,TI2FP2连接到了CH2引脚的时钟,到这里外部时钟模式1的输入就介绍完了。

总结一下就是,外部时钟输入模式1的输入可以是ETR引脚,其他定时器,CH1引脚的边沿,CH1引脚和CH2引脚,这还是比较复杂的,一般情况下,外部时钟通过ETR引脚就可以了,下面设置这么复杂的输入,不仅仅是为了扩大时钟输入的范围。更多的还是为了某些 特殊应用场景而设计的 ,比如为了定时器的级联而设计的这一部分,下面的这一部分,在输入捕获和测频率时,还会讲到。

对于时钟输入而言,最常用的还是内部的72MHz的时钟,如果要选用外部时钟,首选ETR引脚外部时钟模式2的输入,这一路最简单最直接       

               

这一部分是定时器的主模式输出,这部分电路可以把内部的一些事情映射到这个TRGO的引脚上,比如基本定时器,将更新的事件映射到TRGO,用于触发DAC ,这里也是 一样,可以把定时器内部的一些事件映射到这里来,用于触发其他定时器,DAC或ADC,可见这个触发输出的范围是比基本定时器更广一些

下面这一部分主要包含了两块电路,右边这一块是输出比较电路,总共有四个通道,分别对应CH1到CH4的引脚,可以用于输出PWM波形,驱动电机,左边这一块是输入捕获电路,也是有四个通道,对应的也是CH1到CH4的引脚,可以用于测量方波的频率等。中间这个寄存器是捕获/比较寄存器,是输入捕获和输出比较电路共用的,因为输入捕获和输出比较不能同时使用,所以这里的寄存器是共用的,引脚也是共用的

定时中断基本结构图

在这里我们可以看一下定时中断和内外时钟源的选择,运行控制就是控制寄存器的一些位,比如启动停止,向下或向上计数等等,我们操作这些寄存器就能控制时基单元的运行了,左边是为时基单元提供时钟的部分,这里可以选择RCC提供的内部时钟,也可以选用ETR引脚提供的外部时钟模式2,在这里中断信号会先在状态寄存器(位于时基单元和中断输出控制之间)里置一个中断标志位,这个标志位会通过中断输出控制, 到NVIC申请中断。

为啥需要一个中断输出控制呢,因为这个定时器模块有很多地方都要申请中断,如果需要某个中断,那就允许,如果不需要,那就禁止。简单来说,这个中断输出控制就是一个中断输出的允许位,如果需要某个中断,那就允许一下。   

实际单元运行的时序图    

第一个是预分频器时序

      

第一行是CK_PSC,预分频器的输入时钟  选内部时钟的话一般是72MHz,然后这个时钟在不断的运行,下面这个CNT_EN,计数器使能,高电平计数器正常运行,低电平计数器停止。

再下面是CK_CNT,计数器时钟它既是预分频器的时钟输出,也是计数器的时钟输入 ,可以看一开始讲时序的那张图,开始时,计数器未使能,计数器时钟不运行,然后使能后,前半段,预分频器系数为1,计数器的时钟等于预分频器前的时钟,后半段,预分频器的系数变为了1,计数器的时钟也就变为了预分频器前时钟的一半了,在计数器时钟的驱动下, 下面的计数器寄存器也跟随时钟的上升沿不断自增,,在中间的这个位置FC之后,计数值变为0,上图虽然没写,但是可以推断出ARR自动重装值就是FC。当计数值记到和重装值相等,并且下一个时钟来临时,计数值才清零。同时下面产生一个更新事件。这就是一个计数周期的工作流程。上图的下半部分,还有三行时序,描述的是这个预分频器的一种缓冲机制。也就是这个预分频计数器实际上是有两个。一个是预分频控制寄存器,供我们读写用的,它并不直接决定分频系数,另外还有一个缓冲寄存器(影子寄存器),这个缓冲寄存器才是真正起作用的寄存器。比如我们在某个时刻,把预分频寄存器由0变为1,如果在这个时刻立刻改变时钟寄存器的分频系数,那就会导致在一个计数周期内,前半部分和后半部分的频率不一样。导致计数记到一般,计数频率就会改变了,这个一般不会有什么问题,但是STM32比较严谨。设置了这个缓冲寄存器,这样,当我们在计数记到一半的时候,改变了分频值,这个变化不会立即生效,而是会等到本次计数周期结束时,产生了更新事件。预分频计数器的值才会被传递到缓冲寄存器里面去,才会生效。所以哪怕我们在计数中途改变了预分频值,计数频率仍然会保持为原来的频率,直到本轮计数完成,在下一轮计数的时候,改变后的分频值才会起作用。

上图的最后一行,预分频器,内部实际上也是靠计数来分频的。当预分频器为0时,计数器就一直为0,直接输出原频率。当预分频器为1时,计数器就按照0 1 0 1 0 1 0 1这样计数,在回到0的时候,输出一个脉冲,这样输出频率就是输入频率的2分频,预分频器的值和实际的分频系数之间有一个数的偏移,就有了下面这个公式。

第二个计数器的时序

这个图是计数器时序的原理图,内部时钟分频因子是2,就是分频系数为2.第一行是内部时钟72MHz,第二行是时钟使能,高电平启动,第三行是计数器时钟,因为分频系数为2,所以频率是上面这个除2,然后计数器,在这个时钟每个上升沿自增,当增到0036的时候,发生溢出,则记到36之后再来一个上升沿,计数器清零。计数器溢出,产生一个更新事件脉冲。另外还会置一个更新中断标志位UIF,这个标志位只要置1了,就回去申请中断,然后中断响应之后,需要在中断程序中手动清零。这就是计数器的工作流程。

ARR是指自动重装寄存器

预分频器为了防止计数中途更改数值造成错误,设计了缓冲寄存器,这个计数器那肯定也少不了这样的设计了

通过设置ARPE位,就可以选择是否选择是否使用预装功能  

先看无预装的情况,在这里计数器正在自增计数,若是突然更改了自动重装寄存器,由FF改成了36,那计数值的目标值就有FF变成了36,所以这里记到36之后,就直接更新,进入下一轮计数。

而有预装的情况下,则是哪怕改变了,也是由真正起作用的影子寄存器先到未改动之前的计数目标,产生更新事件,在下一个计数周期才会变为更改后的目标值。

可以看出,引入影子寄存器的目的是为了同步,就是让值的变化和更新事情同步发生,防止在运行途中更改造成错误。 

RCC时钟树

Reset and clock control (RCC)

时钟配置,控制提供给各模块时钟信号的通断

这个时钟树,就是stm32中用来产生和配置时钟,并且把配置好的时钟发送到各个外设的系统,时钟是所有外设运行的基础,所以时钟也是最先需要配置的东西。

所有程序执行之前,还会执行一个systminit函数,这个函数就是用来配置这个时钟树的

中间的SYSCLK就是系统时钟72MHz,在时钟产生电路,有四个震荡源,分别是内部的8MHz高速RC振荡器外部的4-16MHz的高速石英晶体振荡器,也就是晶振,一般都是接8MHz,外部的32.768KHz低速晶振,这个一般是给RTC提供时钟的,最后是内部的40KHz低速RC振荡器,这个可以给看门狗提供时钟。两个高速晶振是用来提供系统时钟的。AHB,APB2,APB1的时钟都是源自于这两个高速晶振。外部的石英振荡器比内部的RC振荡器更加稳定。所以一般都用外部晶振。如果不那么精准,就可以用内部RC振荡器,就可以省下外部晶振的电路了。在systminit函数里,ST是这样来配置时钟的,首先他会启动内部时钟,选择内部8MHz为系统时钟,暂时以内部8MHz的时钟运行,然后再启动外部时钟,配置外部时钟走这一路进入PLL锁相环进行倍频,8MHz倍频9倍,得到72MHz。等到锁相环输出稳定后,选择锁相环输出作为系统时钟。 就把系统时钟由8MHz变成了72MHz。这是ST配置的流程,这样配置可以实际解决一个问题,就是如果外部晶振出现问题了,可能会导致一个现象,就是你会发现,你程序的时钟慢了大概10倍,比如用定时器定了1s的时间,结果过了10s才进中断。原因就是因为如果外部晶振出问题了,系统时钟就无法切换到72MHz,那就会以内部的8MHz运行,慢了十倍。另外上面还有一个CSS(Clock Security System),这个是时钟安全系统。它是负责切换时钟的,它可以监测外部时钟的运行状态,一旦外部时钟失效,他就会自动把外部时钟切换回内部时钟。保证系统时钟的运行,防止程序卡死造成事故。在高级定时器中,也有CSS的身影。在这个刹车输入这里一旦CSS检测到外部时钟失效,这里通过或门,就会立刻反应到输出比较这里。   让这个输出控制的电机立刻停止,防止意外。这就是stm32的一些保护措施。

接着再看一下右边的时钟分配电路 首先系统时钟72MHz进入AHB总线,AHB总线有一个预分频器,在systeminit里配置的分配系数为1,那AHB的时钟就是72MHz,然后进入APB1总线,这里配置的分配系数是2,所以APB1总线的时钟为36MHz,通用定时器和基本定时器是接在APB1上的,按理说他们的时钟应该是36MHz,而为何所有的定时器时钟都是72MHz呢,原因在于下面一条支路。

无论是什么定时器,他的内部基准时钟都是72MHz,这样就不用考虑不同定时器的时钟不一样的问题了。

在这些时钟输出这里,都有一个与门进行输出控制,控制位写的是外部时钟使能,

这就是我们在程序中写RCC_APB2/1PeriphClockCmd(外设时钟)作用的地方,打开时钟,就是在这个位置写1,让左边的时钟能够通过与门输出给外设。 

定时中断和内外时钟源配置的代码部分

首先我们要做的就是初始化定时器,这个是定时中断的整个框架,我们只需要打通这里面的每个模块就可以了。

第一步:RCC开启时钟

第二步:选择时基单元的时钟源,对于定时中断,我们就选内部时钟源

第三步:配置时基单元,包括预分频器,自动重装器,计数模式,这些参数用一个结构体就可以配置好了。

第四步:配置输出中断控制,允许更新中断输出到NVIC。

第五步:配置NVIC,在NVIC中打开定时器中断的通道,并分配一个优先级

第六步:运行控制

整个模块配置完成后,我们还需要使能计数器,要不然计数器时不会工作的,当定时器使能后,计数器就会开始计数了,当计数器更新时,触发中断,最后再写一个定时器的中断函数,这样这个中断函数就能每隔一段时间就能自动运行一次了。

关于定时器TIM的库函数介绍

第一个 TIM_DeInit,恢复初始配置

第二个TIM_TimeBaseInit,时基单元初始化,就是用来配置时基单元的,其中有两个参数,第一个TIMx选择某个定时器,第二个是结构体,里面包含了配置时基单元的一些参数

第三个,TIM_TimeBaseStructInit,这个函数可以把结构体变量赋一个默认值,

第四个,TIM_Cmd,这个是用来使能计数器的,对应的就是上图的运行控制部分,它有两个参数,第一个TIMx选择定时器,第二个NewState新的状态,也就是使能还是失能

第五个,TIM_ITConfig,这个是用来使能中断输出信号的,对应的就是上图的中断输出控制,它的参数,第一个TIMx选择定时器,第二个TIM_IT选择要配置哪个中断输出,第三个NewState,这个函数会经常遇到,就是使能外设的中断输出

下面看时基单元的时钟选择部分所对应的六个函数(头大)

可以用来选择RCC内部时钟,ETR外部时钟,ITRx其他定时器,TIx捕获通道这些

第一个TIM_InternalClockConfig,选择内部时钟,参数只有一个TIMx

第二个TIM_ITRxExternalClockConfig,选择ITRx其他定时器的时钟,参数是TIMx,选择要配置的定时器,和inputTriggerSource,选择要接入哪个其他的定时器,如下图所示

第三个,TIM_TIxExternalClockConfig,选择TIx捕获通道的时钟,参数 ,第一个TIMx,第二个TIxExternalCLKSource,选择TIx具体的某个引脚,还有ICPolarity和ICFilter,输入的极性和滤波器,对于外部引脚的选择,一般都会有极性选择和滤波器,这样更灵活一些(不是很懂)

/*

问:stm32中TIX捕获通道什么意思

答:

STM32中的TIMx捕获通道是指定时器TIMx的输入捕获功能。这是一种用于测量外部信号周期、频率等参数的重要功能。在STM32中,TIMx定时器可以配置为输入捕获模式,通过捕获通道捕获外部信号的边缘(上升或下降),并保存其对应的计数器值。通过对比连续两次捕获到的计数器值,就可以得到外部信号的周期或者频率。具体来说,STM32的TIMx定时器通常有四个捕获/比较通道,分别是CH1, CH2, CH3和CH4。每个通道都可以独立配置为输入捕获模式或输出比较模式。输入捕获模式下,每个通道都可以独立地捕获外部信号的上升沿或下降沿,并且可以设置滤波条件以防止噪声干扰。需要注意的是,不同的STM32型号可能会有不同数量的TIMx定时器和捕获/比较通道,具体的配置和使用方法需要参考相关的STM32数据手册和参考手册。

*/

第四个,TIM_ETRClockMode1Config,选择ETR通过外部时钟模式1输入的时钟, 也就是这么走的

他的参数,ExTRGPrescaler,外部触发预分频器,这里可以对ETR的外部时钟再提前做一个分频,Polarity和Filter也是一样,极性和滤波器,

还有TIM_ETRClockMode2Config,选择ETR通过外部时钟模式2输入的时钟,也就是下图这路

第五个,TIM_ETRConfig,这个不是用来选择时钟的,就是单独用来配置ETR引脚的预分频器,极性,滤波器这些参数的

接下来还有几个函数,在初始化结构体里,有比较关键参数,比如自动重装值和预分频器,这些参数可能在初始化后还需要更改,如果改某个参数还要再次更改调用一次初始化函数,会很麻烦,所以下面这几个函数,可以方便地更改这些关键参数,

TIM_PrescalerConfig,就是用来单独写预分频值的,参数,Prescaler就是要写入的预分频值,PSCReloadMode,写入的模式。预分频器有缓冲器,写入的值在更新的事件发生后才有效的,所以这里有个写入的模式,可以选择听从安排,在更新事件后生效;或者是,写入之后,手动产生一个更新事件,让这个值立刻生效

TIM_CounterModeConfig,用来改变计数器的计数模式,参数CounterMode,选择新的计数器模式,

再往下,TIM_ARRPreloadConfig,自动重装器预装功能配置,只需要一个参数,是使能还是失能就行了

/*******************************************************************

何为自动重装器预装功能配置

在STM32中,自动重装器的预装功能通常与定时器相关。这是一种特性,使得定时器在到达设定的值后,可以自动从预设的值重新开始计数,而不需要中断服务程序来手动重置。

当定时器的计数值达到其上限(或者设定的比较值)时,它会产生一个更新事件或者比较匹配事件,并且自动将计数器的值重置为预设的自动重装值(也就是ARR寄存器的值)。这个过程被称为“自动重装”。

预装功能是指,新的自动重装值可以在任何时间写入到预装载寄存器中,但只有在下一次更新事件或者比较匹配事件发生时,才会将预装载寄存器中的值更新到自动重装寄存器中。这就保证了计数器的连续运行,避免了因为更新自动重装值而产生的计数中断。

这项功能在需要定时器产生固定周期的中断,或者产生精确的PWM波形时非常有用。例如,你可以在每次中断服务程序中改变预装载寄存器的值,以实现动态调整定时器的周期或者PWM的占空比。

**************************************************************************/

TIM_SetCounter给计数器写入一个值,如果想手动给入一个计数值,就可以选择这个函数

TIM_SetAutoreload,如果你想手动给一个自动重装值,就可以用这个函数

 TIM_GetCounter,获取当前计数器的值,

 TIM_GetPrescaler,获取当前的预分频器的值,

//*****************************************************

stm32中获取标志位和清除标志位是什么意思

在STM32中,标志位(Flag)是用于表示某种状态或者事件的一种机制。每个标志位都对应于一个特定的事件,例如数据已经准备好被发送、接收到新的数据、发生了溢出错误等等。这些标志位通常位于微控制器内部的某些特殊寄存器中。

获取标志位(Reading a Flag)就是读取这些特殊寄存器,查看相应的事件是否已经发生。例如,如果我们想知道是否已经接收到新的数据,就可以读取相应的接收完成标志位。

清除标志位(Clearing a Flag)则是将这些标志位复位,表示相应的事件已经被处理完毕。通常来说,一旦我们处理完相应的事件(例如读取了接收到的新数据),就应该立即清除相应的标志位,以防止误判。在STM32中,清除标志位通常是通过写入1到相应的位来实现的。

需要注意的是,不同的硬件设备可能有不同的方式来处理标志位。在使用之前,应该详细阅读相关的硬件手册或者参考手册,了解其具体的工作原理和使用方法。

***********************************************************///

输出比较功能

       这一块就是输出比较的结构图

CNT在这里,就是时基单元的计数器。

CCR就是捕获/比较寄存器,CC是捕获的意思,R是register,寄存器的意思。这个CCR是输入捕获和输出比较共用的,当使用输入捕获时,他就是捕获寄存器,当使用输出比较时,他就是比较寄存器,那在输出比较这里,这块电路会比较CNT和CCR的值,CNT计数自增,CCR是我们给定的一个值,当CNT大于,小于或等于CCR的时候,这里输出就会对应的置1,置0,置1,置0,这样就可以输出一个不断跳变的PWM波形了。这就是输出比较的基本功能。

输出比较通道(通用)

左边就是CNT计数器和CCR1第一路的捕获/比较寄存器,当CNT>CCR1或者CNT=CCR1时,就会给这个输出模式控制器传一个信号,然后输出模式控制器就会改变它输出OC1REF的高低电平,OC1REF信号实际上就是指这里信号的高低电平,REF时reference的缩写,意思是参考信号。上面还要一个ETRF输入,这是定时器的一个小功能,一般不用。接着这个OC1REF信号可以前往主模式控制器,你可以把这个REF映射到主模式的TRGO输出上去,不过REF的主要去向还是下面这一路,通过下面这一路到达极性选择,给0信号就会往上走,给就是信号电平不翻转,进来啥样出去还是啥样,写1的话就会往下走,通过一个非门取反,输出就是高低电平反转的信号。

这就是极性选择,就是选择是不是要把高低电平反转一下。

接着就是输出使能电路,选择要不要输出,最后就是OC1引脚,这个引脚就是CH1通道的引脚,再引脚定义表里就知道是具体的哪个GPIO口了。

我们再来看一下输出模式控制器是怎么工作的,什么时候给REF高电平,什么时候给REF低电平。看一下下面的这个表,这八种模式就是输出模式控制器里的执行逻辑,这个模式控制器输入是CNT和CCR的大小关系,输出是REF的高低电平,里面可以选用多种模式来更加灵活地控制REF输出,这个模式的选择可以通过下面的寄存器来配置。

冻结模式:可以理解为CNT和CCR无效,REF保持原状态。                                                             用处:比如正在输出PWM波,你想要暂停一会输出,就可以设置成这个模式。一旦切换为冻结,输出就暂停了。

有效电平与无效电平一般是高级定时器里的说法,是和关断,刹车这些功能相互配合表述的,为了便于理解,可以认为此处置有效电平就是高电平,无效就是低电平。下面这三个模式都是CNT和CCR的值相等时进行操作                                                                                                                    用处:这三个模式就可以用作波形输出了。比如相等时电平翻转这个模式,可以很方便的输出一个频率可调,占空比始终为50%的PWM的波形。比如你设置CCR为0,那么CNT每次更新清0时,就会产生一次CNT=CCR的事件,这就会导致输出电平翻转一次,没更新两次,输出为一个周期,并且高电平与低电平的时间始终是相等的,也就是占空比始终为50%。当你改变定时器更新的频率时,输出的波形频率也会随之改变,他俩的关系是,输出波形的频率=更新频率/2,因为更新两次,输出才为一个周期

下面的两个强制的跟冻结的差不多,就不管你输进去的是啥,都会强制有效或者无效,如果你想暂停波形输出,并且在暂停期间保持低电平或者高电平,

最后两个模式:比较重要,他们可以用于输出频率和占空比都可调的PWM波形,可以观察得到PWM模式2就是PWM模式1的取反,改变的只是REF电平的极性罢了。使用的话,我们可以只使用PWM模式1,并且是向上计数,这一种模式就好了,

那么PWM模式1是怎么输出频率和占空比都可调的PWM波形的呢?

左上角是时基单元和运行控制部分,再左边是时钟源选择,这里忽略了,

  • 6
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值