【STM32】【标准库开发】【固件函数库】STM32F103C8芯片学习日记(小白学习心得)【3】【定时器中断介绍和软件编写】

目录

【1】【定时器中断介绍和配置】

      (1)定时器中断配置介绍

1.打开定时器的外设时钟

2.定时器的时钟源选择内部时钟源

3.配置时基单元

【定时器计数方式】

【自动重装器】

【预分频器】

时基单元的配置函数:TIM_TimeBaseInit()

第一步结构体变量命名:

第二步结构体成员变量的赋值:

第一个结构体成员变量是滤波器的分频设置:

第二个结构体成员变量计数器的计数方式:

第三个结构体成员变量自动重装器的重装载值:

第四个结构体成员变量预分频器

这里再说明一下上面的计数器和分频器的设置如何计算时间:

第五个结构体成员变量重复计数器

第六步结构体成员变量初始化

第七步打开定时中断通道

第八步配置嵌套向量中断控制器(NVIC)

第九步打开定时器

(2)代码练习:定时器数码管时钟


【1】【定时器中断介绍和配置】

      (1)定时器中断配置介绍

1.打开定时器的外设时钟

	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);	    //打开定时器2的所挂载的APB1的时钟

这是每次使用外设时,都要做的,打开定时器外设所挂载在APB1总线的时钟。

注:定时器分为:基本定时器,通用定时器,高级定时器,当然这几个定时器的功能有些区别,这里我们先用通用定时器做介绍,后面再进行补充;STM32F10X系列的通用定时器的外设是TIM2~TIM5.

2.定时器的时钟源选择内部时钟源

这里我们先介绍定时器的信号源选择内部时钟源,定时器的信号源还可以选择外部电路信号控制定时器等,这里我们先简单的选内部的时钟源,用它来进行定时。

	TIM_InternalClockConfig(TIM2);		//定时器2选择内部时钟源

定时器内部的时钟源默认是72MHz

3.配置时基单元

前面我们选择了定时器的信号源,有了信号可以开始工作了,那么定时器一个核心的部分就是时基单元,这个单元就起到了计数,分频和自动重装载器;计数是当有一个脉冲过来时,计数器就加1 ,那么当计数器装满的时候,就会又硬件自动清0,并产生一个中断标志,并且由自动重装载器给它又重新装入初值,这个初值是什么呢,自然是你定时的时间,这里还要提到一点:

【定时器计数方式】

这个芯片的定时器的计数器还有三种计数的方式,分别是向上计数,向下计数,和中央对齐模式(向上/向下计数),向上计数自然是从0开始依次加到这个自动装载的值,溢出后由硬件置标志位并清0,向下计数那么就是从这个自动装载的值一直向下计数到0,然后下溢产生中断标志位;中央对齐模式是计数器从0开始计数到这个自动装载的值−1,产生一个计数器溢出标志位,然后向下计数到1并且产生一个计数器下溢标志位,然后再从0开始重新计数,可以在每次计数上溢和每次计数下溢时产生更新事件。

【自动重装器】

然后继续说自动重装载器,是用来装入的初值的,即自动装载的值,这个初值是做什么的呢,这里用向上计数举例,当自动重装器每次自动装入了一个值后,定时器这时是向上计数,那么它就是从0开始依次加1直到加到自动装载的值,硬件就会产生一个中断标志位来告诉你已经到时间了,那么这段时间就是你写入的自动重装值的时间;

【预分频器】

那么这个时间怎么看呢,上面我们选择的时钟源是72MHz,这是产生的频率,那么计数器加1的频率就是1/72MHz=1/72000000秒=1/72微秒,这就是差不多0.013微秒了,这个时间运行速度就很快了,那么十六位计数器最大就可以计数65536次,65536×1/72微秒=910.2微秒,那么时间有点短了,这时候呢,这个芯片内部其实还设置了一个分频器,这是什么呢,分频器是用来对这个接受到的信号进行一个分频,比如说我们这里接受到的是内部的72MHz设置为2分频,那么频率就是72/2=36MHz了,那么这里分频器最大可以进行65536分频,那么就是72MHz/65536,分频后就差不多是1099Hz的频率,然后1099Hz再计数65536次这个时间就是1/1099×65536就是59.6秒左右,相当于你分频最大,计数值最大时,可以定时器59秒多,当然如果时间还不够,还支持芯片之间的级联操作,那时间就更多了;

时基单元的配置函数:TIM_TimeBaseInit()

这个函数的配置前面说过的经典的结构体变量命名,然后结构体成员变量赋值,最后在初始化结构体

第一步结构体变量命名:
TIM_TimeBaseInitTypeDef TimeBaseInitStructure;
第二步结构体成员变量的赋值:
TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1;    //采样频率选择一分频即不分频
第一个结构体成员变量是滤波器的分频设置:

那么这个是什么呢,前面提到过定时器的信号可以是从外部信号的接受,那么接受外面的信号就会出现一些杂波和不稳定的情况,那么在接入口就设置了一个滤波器,就有一个采样频率,当然这个采样频率是由内部时钟进行分频,采样频率可以由软件设置的;然后这个采样点是怎么回事呢,比如在一段很短的时间内,采集N次这个电压值,然后就会进行比较,只有连续的这N次采集的电压都是相同的电平,才代表电压已经稳定,那么这时候才会把这个值输入进去,如果这N次采集的电平并不相同,也就是不稳定,那么就会保持上次的输入电平不变;

第二个结构体成员变量计数器的计数方式:


上面已经提到了有向上计数,向下计数,和中央对齐模式,一般没什么特殊要求,我们就选择向上计数就行了

TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up;    //计数器选择向上计数
第三个结构体成员变量自动重装器的重装载值:
	TimeBaseInitStructure.TIM_Period=10000 - 1;	//装入10000的重装载值

这里为什么要在这里减1呢,因为计数器是从0开始计数的,那么就会多一次,比如我设置的是5次,就只想要计数5次,但是当刚到5时,这是已经计数了五次,但是第五次还没有计满,那么就多了一次;

第四个结构体成员变量预分频器
	TimeBaseInitStructure.TIM_Prescaler=7200 - 1;	

分频器为什么减1呢,寄存器的解释是


这里自动等于分频系数+1,那么就是我想要2分频,输入一个2,那么就要减一个1,因为它寄存器是自己加了一个1的。

这里再说明一下上面的计数器和分频器的设置如何计算时间:

上面我们选择的内部的时钟源是72MHz,那么我们先给它进行一个分频,毕竟72不好直接整除出时间来,我们这里进行一个72倍的分频,那么就是72 000 000HZ/72=1000 000HZ;这下就好看时间了,然后我们比如要定时1秒,就x/1000 000 =1,然后解出x=1000 000,然后你就会发现我们的计数器最大只能装进65536,这超过了怎么办呢,那就改分频器的值,这里分频器最大也可以填入65536,那么我们给分频器装入7200,然后给计数器装入10000,这下就好了,正好是一秒,注意这里还要让分频器和计数器都减1;

第五个结构体成员变量重复计数器

重复次数值,这个是高级定时器的功能,我们暂时用不上,直接写个0,稍微解释一下,之前我们的计数器是每次计数满了就更新一次中断事件,然而这个重复计数器,就可以让你再次继续重新计数,一直重复计数,而不更新中断事件,重复几次这个过程直到你设定的值。

	TimeBaseInitStructure.TIM_RepetitionCounter=0;	
第六步结构体成员变量初始化
	TIM_TimeBaseInit(TIM2,&TimeBaseInitStructure);
第七步打开定时中断通道

之前我们的GPIO外部中断,都要设置中断通道是因为由外部的信号来触发中断的,这里我们的定时器产生的更新中断在这个定时中断函数配置后,直接把更新中断触发的信号给NVIC将中断的信号组个队排个号。

	TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);		//打开TIM2的中断
第八步配置嵌套向量中断控制器(NVIC)

我们在上一节的外部中断配置过NVIC,这里就不重复了,直接看代码举例

	NVIC_InitTypeDef NVICInitSturcture;		//NVIC的配置
	NVICInitSturcture.NVIC_IRQChannel=TIM2_IRQn;		
	NVICInitSturcture.NVIC_IRQChannelCmd=ENABLE;		
	NVICInitSturcture.NVIC_IRQChannelPreemptionPriority=1;
	NVICInitSturcture.NVIC_IRQChannelSubPriority=1;
	NVIC_Init(&NVICInitSturcture);
第九步打开定时器
TIM_Cmd(TIM2,ENABLE);

这里我们把定时器打开放在最后,免得它提前打开后就提前工作了。

第十步定时器中断函数:

上一节我们已经说过进入中断后的函数都是特定的名字的,这个中断函数怎么找呢,上一节已经介绍了,就不再重复,然后定时器中断里的基本写法也如上一节中断函数里是类同的

固定格式如下

void TIM2_IRQHandler(void)    //中断函数的名称
{
	if(TIM_GetITStatus(TIM2,TIM_IT_Update) == SET )    //当中断标志位置1后
	{
		//执行内容
		TIM_ClearITPendingBit(TIM2,TIM_IT_Update);    //将中断标志位清0
	}
}


 

(2)代码练习:定时器数码管时钟

smg.c

#include "stm32f10x.h"

uint16_t smg_code[11]={0x00c0,0x00f9,0x00a4,0x00b0,0x0099,0x0092,0x0082,0x00f8,0x0080,0x0090,0x00ff};

void SmgInit(void)
{
		RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);	

		GPIO_InitTypeDef InitStuctureA;
		InitStuctureA.GPIO_Mode=GPIO_Mode_Out_PP;
		InitStuctureA.GPIO_Pin=GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;
		InitStuctureA.GPIO_Speed=GPIO_Speed_50MHz;	
		GPIO_Init(GPIOA,&InitStuctureA);		
	
		RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);	

		GPIO_InitTypeDef InitStuctureB;
		InitStuctureB.GPIO_Mode=GPIO_Mode_Out_PP;
		InitStuctureB.GPIO_Pin=GPIO_Pin_12 | GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;
		InitStuctureB.GPIO_Speed=GPIO_Speed_50MHz;	
		GPIO_Init(GPIOB,&InitStuctureB);
		
}

void smg_show(uint8_t pos,uint8_t dat)
{
	
	GPIO_Write(GPIOA,0x00ff);
	
	GPIO_Write(GPIOB,0x1000<<pos);
	
	GPIO_Write(GPIOA,smg_code[dat]);	
}

timer.c

#include "stm32f10x.h"  
#include "smg.h"

uint8_t times;

extern uint16_t smg_buf[4];

void Timerinit(void)
{
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);	//打开定时器总线的时钟
	
	TIM_InternalClockConfig(TIM3);		//定时器的时钟源选择内部时钟源72MHz
	
	TIM_TimeBaseInitTypeDef Time3BaseInitStructure;		//定时器时基单元的配置
	Time3BaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1;//采样频率选择一分频即不分频
	Time3BaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up;	//定时器向上计数
	Time3BaseInitStructure.TIM_Period=10 - 1;	//ARR
	Time3BaseInitStructure.TIM_Prescaler=7200 - 1;	//PSC
	Time3BaseInitStructure.TIM_RepetitionCounter=0;	//高级定时器使用的参数

	TIM_TimeBaseInit(TIM3,&Time3BaseInitStructure);		//这个初始化会立即同时生成一个更新中断和更新事件
	
	TIM_ClearFlag(TIM3,TIM_FLAG_Update);	//这里手动清除上面初始化产生的更新事件
	
	TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE);		//打开TIM3的中断
	
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);	//NVIC的优先级分组
	
	NVIC_InitTypeDef NVICInitSturcture_TIM3;		//NVIC的配置
	NVICInitSturcture_TIM3.NVIC_IRQChannel=TIM3_IRQn;		//选择定时器通道
	NVICInitSturcture_TIM3.NVIC_IRQChannelCmd=ENABLE;		
	NVICInitSturcture_TIM3.NVIC_IRQChannelPreemptionPriority=1;
	NVICInitSturcture_TIM3.NVIC_IRQChannelSubPriority=0;
	NVIC_Init(&NVICInitSturcture_TIM3);
	
	TIM_Cmd(TIM3,ENABLE);		//打开这个TIM2的计数器
	//--------------------------------------------------------------------------------
	
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);	//打开定时器总线的时钟
	
	TIM_InternalClockConfig(TIM2);		//定时器的时钟源选择内部时钟源72MHz
	
	TIM_TimeBaseInitTypeDef Time2BaseInitStructure;		//定时器时基单元的配置
	Time2BaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1;//采样频率选择一分频即不分频
	Time2BaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up;	//定时器向上计数
	Time2BaseInitStructure.TIM_Period=10000 - 1;	//ARR
	Time2BaseInitStructure.TIM_Prescaler=7200 - 1;	//PSC
	Time2BaseInitStructure.TIM_RepetitionCounter=0;	//高级定时器使用的参数
    //定时1ms,进行扫描数码管

	TIM_TimeBaseInit(TIM2,&Time2BaseInitStructure);		//这个初始化会立即同时生成一个更新中断和更新事件
	
	TIM_ClearFlag(TIM2,TIM_FLAG_Update);	//这里手动清除上面初始化产生的更新事件
	
	TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);		//打开TIM2的中断
	
//	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);	//NVIC的优先级分组
	
	NVIC_InitTypeDef NVICInitSturcture_TIM2;		//NVIC的配置
	NVICInitSturcture_TIM2.NVIC_IRQChannel=TIM2_IRQn;		//选择定时器通道
	NVICInitSturcture_TIM2.NVIC_IRQChannelCmd=ENABLE;		
	NVICInitSturcture_TIM2.NVIC_IRQChannelPreemptionPriority=1;
	NVICInitSturcture_TIM2.NVIC_IRQChannelSubPriority=1;
	NVIC_Init(&NVICInitSturcture_TIM2);
	
	TIM_Cmd(TIM2,ENABLE);		//打开这个TIM2的计数器
}

uint16_t numreturn(void)
{
	return times;
}

void TIM3_IRQHandler(void)
{
	static uint8_t pos;
	if(TIM_GetITStatus(TIM3,TIM_IT_Update) == SET )
	{
		if(++pos==4)	pos=0;
		smg_show(pos,smg_buf[pos]);
		TIM_ClearITPendingBit(TIM3,TIM_IT_Update);
	}
}

void TIM2_IRQHandler(void)
{
	if(TIM_GetITStatus(TIM2,TIM_IT_Update) == SET )
	{
		times++;
		TIM_ClearITPendingBit(TIM2,TIM_IT_Update);
	}
}

main.c

#include "smg.h"  
#include "timer.h"  
#include "stm32f10x.h"  

uint16_t smg_buf[4]={10,10,10,10};

uint16_t time;

int main()
{		
		Timerinit();
		SmgInit();
		while(1)
		{
			time=numreturn();	
			smg_buf[2]=time/10;
			smg_buf[3]=time%10;			
		}	
}

proteus仿真示意图

这里我们点击这个芯片,在里面配置一下晶振

这里用STM32仿真时,出现了个问题就是仿真时的时间流速非常慢几乎跟你设置的时间慢了近10倍,但用实物烧录你的代码后显示是正常的;

  • 26
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值