STM32工作笔记0062---定时器中断实验

技术交流QQ群【JAVA,C++,Python,.NET,BigData,AI】:170933152

 

这里可以看到,有4部分,1是时钟来源,2是时基电路,3左边是输入检测,右边4是输出控制.

这里咱们知道,时钟来源确定以后,这里时基电路,计数器,会开始向上计数,当计数到超过设置的初始化的值的时候

会产生中断,咱们可以在中断中让LED亮灭来实现这个实验.

首先看这个内部时钟,咱们知道这个定时器1的时钟来源是内部时钟

这个从模式控制寄存器,可以配置几种模式,可以看到,如果配置为位2到0,为000的话,那么

预分频器会由内部时钟驱动.CK_INT

CEN=1

这个内部时钟的来源是什么呢?

这里AHB预分频时钟,会作为APB1的时钟来源,然后经过

分频,如果预分频APB1的预分频系数是1的话,那么是x1输出,APB1的预分频系数是2的话

那么就属于x2输出.

那么这个x1输出,还有x2输出是什么意思呢? 

可以看到这里APB1的时钟,这个时钟的来源是,AHB时钟经过一个预分频系数,得来的,然后,

因为咱们知道,AHB=72,APB1=36,那么预分频是2,所以CK_INT的时钟,会乘以2,就是

36*2=72m,也就是CK_INT=72M

然后这个时候得到CK_PSC这个时钟,然后这个时钟再去/N,会最终得到这个通用定时器的时钟

CK_CNT

这个计数器的模式,上一讲也已经说过了.

这里这个,看看向上计数器模式,这里

可以看到首先使能CNT_EN 然后,使能以后,计数器开始向上计数,当,比如初始化值是36,超过36的时候

会产生一个计数器溢出,这个时候,会产生UEV更新时间,然后,如果我们使能了中断,他就会更新这个中断标志,

并且产生中断,会执行中断函数.

首先看这个CNT他就是,计数器的当前值,其实就是框图中的

上面cnt部分.

这个预分频寄存器,实际上就是预分频器的值,,

也就是对应的最终定时器的时钟频率的值等于=FCK_PSC/(PSC[15:0]+1)

FCK_PSC其实前面有图可以看到,是AHB/2=36M APB1的值,也就是

APB1*2=72M的这个值,这个值再去/(PSC[15:0]+1)

这里要加1,前面说了

注意这里的时钟分频因子=1是加了1以后的值是1 .

这里咱们说了两个寄存器,一个是CNT寄存器存当前的值,一个是PSC寄存器,存分频系数,注意这里分频系数需要+1

这个寄存器重装载寄存器,包含了计数器需要计数的开始值.

要从这个值开始向上,或向下计数.

这个是控制寄存器,控制计数方向,还有个使能位,要用的话,需要使能.

这次咱们用到这个,UIE允许更新中断,也就是允许产生中断事件.

后面还会用到比如捕获事件等.

然后通过代码,咱们看一下如何来操作这些寄存器.

这个STM32F10X_TIM.C这个文件记录了所有的函数,所有的定时器的函数.

这个STM32F10X_TIM.H这个定义了定时器需要的所有的函数.

注意,这里的结构体,

Prescalar这个是预分频系数

CounterMode这个是向上计数,还是向下计数

然后Period这个是自动装载值

然后ClockDivision这个是在捕获比较试验中用

然后RepetitionCounter这个是在高级定时器中用的.

我们用的只有上面的三个.

基本上就是设置了图中的几个寄存器,然后

其实就是这个函数,一会咱们去写.

然后就是定时器使能函数,

这个是使能计数器的作用.操作这个寄存器.

然后

定时器中断使能函数,这个函数配置的是

是操作的这个DIER寄存器.

还有就是状态标志位的获取和清除函数

然后再看一下定时器中断实现的步骤

首先使能定时器时钟,2.然后初始化定时器,然后再去开启定时器的中断,这个时候需要配置中断优先级

然后使能定时器,然后编写中断函数.

这个溢出时间,实际上是由1,时钟频率决定的,2和装载的值决定的.

这里很好理解,首先这个TCLK是定时器的时钟频率,也就是1s钟震动的次数

那么1/TCLK就是震动一次需要多少时间,然后PSC+1这个是预分频系数,所以

PSC+1/TCLK就是,这个定时器,经过预分频后的,时钟周期,也就是震动一次,需要的时间,变多了

变成了PSC+1/TCLK,然后

震动多少次呢,这里是ARR+1次,需要的时间就是(ARR+1)*(PSC+1)/TCLK

的时间,也就是这个溢出时间.

再看一下,咱们知道,AHB的值是72mhz,然后APB1的是36MHZ,所以这里的预分频系数是2

那么,这里的TCLK定时器的时钟就是36mhz*2=72mhz

如果我们的预分频系数PSC设置为7199的话,那么通用定时器的时钟频率就会变成

72MHZ/7200=72000000 / 7200 = 10 000赫兹

也就是10Khz  也就是1s震动 10 000次

震动1次就是1/10 000 =0.0001s

0.0001 * 1000 =0.1ms

注意1mhz=1000000 1百万hz 赫兹

也就是说,震动一次是0.1ms,那么我们要每500ms震动一次,这里就把ARR

设置为4999 加1 的话也就是 5000

5000 * 0.1ms =500 ms 

然后咱们来写代码:

可以用跑马灯的实验来做:
首先在HARDWARE文件夹中把原来的文件删除掉,然后把

timer.c timer.h新建出来.

然后把timer.c加进来,然后把文件夹路径,包含进来.

然后把STM32官方的库函数,stm32f10x_tim.c也添加进来.

然后再timer.h文件中定义用到的函数

因为这里可以看到初始化定时器需要配置ARR,PSC,所以这里就配置两个参数

然后再去写timer.c

可以看到首先要使能定时器时钟

然后下面的这个参数可以去定义中去找,第一个参数是哪个定时器,第二个是使能标志.

然后第二步去初始化,定时器这个函数.

可以看到,这个

 TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
 RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //时钟使能
 //定时器TIM3初始化
 TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的 
 值,也就是从多少开始倒计数
 TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值
 TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim
 //这个是在高级定时器中才用到的.
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式,设置计数方向
	TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根据指定的参数初始化TIMx

然后再去开启定时器中断

使用TIM_ITConfig这个函数,可以去定义看他的参数

这里第一个参数是哪个定时器,第二个是采用什么中断,这里咱们用更新中断,然后最后一个是使能.

然后使能中断优先级分组.

可以看到:
 

   TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
    NVIC_InitTypeDef NVIC_InitStructure;

    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //时钟使能
    
    //定时器TIM3初始化
    TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值    
    TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值
    TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式
    TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根据指定的参数初始化TIMx的时间基数单位
 
    TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE ); //使能指定的TIM3中断,允许更新中断

    //中断优先级NVIC设置
    NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;  //TIM3中断 中断通道
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;  //抢占优先级设置,先占优先级0级
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;  //响应优先级设置,从优先级3级
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能
    NVIC_Init(&NVIC_InitStructure);  //初始化NVIC寄存器

然后,设置中断优先级分组,再main.c中设置

然后4,使能定时器

这里是使能定时器用到的函数.

这里第一个参数是哪个定时器,第二个参数是使能.

然后就可以写中断函数了

写之前先看一下,这里的

计数到预定的值的时候,就产生一个中断,到预定的值的时候就产生一个中断,向上计数到预定的值,传入的arr这个值

就跟下图画的一样.

看看这个中断函数:

//定时器3中断服务程序
void TIM3_IRQHandler(void)   //TIM3中断
{
    //一般上来都这样写,判断到底是不是定时器3,的更新中断产生的中断,
    //如果是的话==SET,SET就是1,也可以写成!=RESET !=0的意思
	if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET)  //检查TIM3更新中断发生与否
		{
        //3.然后把定时器3的更新中断标志位清除掉.
		TIM_ClearITPendingBit(TIM3, TIM_IT_Update  );  //清除TIMx更新中断标志 
        //2.产生中断了以后,设置LED1翻转一下.
		LED1=!LED1;
		}
}

然后再去写main函数

之前咱们就算好了,如果这里,咱们要实现500ms发生一次中断,那么需要把

重装载值设置为4999,把预分频系数设置为7199,具体怎么计算的前面有说,当然也可以根据自己的需要,设置成

其他的值.

根据上面程序写的,当产生中断的时候,每隔500msLED1会闪烁一次,

而正常的情况每隔200ms,LED0会闪烁一次,所以LED0会闪烁的比LED1闪烁的要快,

下载程序试一下.

猜你喜欢

转载自blog.csdn.net/lidew521/article/details/108279790