STM32学习——第六章 第二节 代码 定时器定时中断&定时外部中断1

学习来源:[6-2] 定时器定时中断&定时器外部时钟_哔哩哔哩_bilibili

此次用STM32,因为stm32的元器件到了,有空可以触类旁通玩玩N32G003。

接线图

如下STM32

为了方便,直接复制之前的oled显示屏的代码,并把文件名字改成6-1定时器中断。

给定时器建立一个模块

建立分类的好习惯,由于定时器不涉及外部硬件,所以我习惯建立在system文件中

单击右键system,添加新文件,.C文件和.h文件

 编写代码

(看完步骤,再看代码更快理解)

代码1

1·起框架

.C文件

#include "stm32f10x.h"                  // Device header

.H文件

#ifndef __TIMER_H
#define	__TIMER_H


#endif

2·初始化

想要初始化,就要打通如上通道

第一步:RCC开启时钟

(基本每个代码的第一步都需要的,定时器的基准时钟和整个外设的工作时钟,都会同时打开)

第二步:选择时基单元的时钟源

函数TIM_InternalClockConfig()选择内部时钟

函数TIM_ITRxExternalClockConfig()选择ITRx其他定时器的时钟

函数TIM_TIxExternalClockConfig()选择TIx捕获通道的时钟

函数TIM_ETRClockMode1Config()选择ETR通过外时钟模式1输入的时钟

函数TIM_ETRClockMode2Config()选择ETR通过外时钟模式2输入的时钟

(定时中断,一般选内部时钟源)、

第三步:配置时基单元        

函数TIM_TimeBaseInit()

(预分频器、自动重装器、计数模式等,只需要一个结构体就能配置好)

想要单独初始化某个参数,不用全部初始化,如预分频器,或自动重装值,可单独初始化函数

TIM_PrescalerConfig()单独写预分配值

(此函数其中有个参数TIM_PSCReloadMode,是因为预分频器有个缓冲寄存器,写入值是在更新事件之后才有效的,这个参数就是选择写入的模式,可以选择听从安排,在更新事件生效,也可以选择写入后,手动产生一个更新事件,立即生效)

TIM_CounterModeConfig()计数器计数模式的选择

TIM_ARRPreloadConfig()自动重装器预装功能配置 

TIM_GetCounter()获取当前计数器的值

TIM_GerPrescaler()获取当前预分频值

第四步:配置 输出中断控制

函数TIM_ITConfig() 
(允许输出中断到NVIC)

第五步:配置NVIC

NVIC_Init()
(在NVIC中打开定时器中断的通道,并分配一个优先级)

第六步:运行控制

函数TIM_Cmd()         

(使能一下计数器,让其开始运行)

(回顾:计数器运行达到自动重装值, 触发中断)

第七步:写中断函数 

拓展:第二步中,滤波时钟分频

外部信号输入脚,一般都有一个滤波器,可以滤掉信号的抖动干扰,

滤波原理就是,连续1多次采样,都为相同电平,代表信号稳定了,才把信号输入进去

若信号抖动,采样的电平不同,代表信号不稳定,维持上一次的输入信号进来,或直接低电平

这里的采样的频率和次数,可通过division参数  改变(随便选个参数就行,不太重要)

拓展:重复计数器

定时器设置1s产生中断,若设置重复计数器10,则10s后才产生中断。

注意:重复计数器,只有高级定时器才有,不需要用,直接给0就好。

定时的时间:由以下参数决定。

        TIM_TimeBaseInitStructure.TIM_Period = ;//自动重装载值

        TIM_TimeBaseInitStructure.TIM_Prescaler = ;//预分频值

譬如定时1s中断

选择内部时钟72MHz,公式变成:定时频率 =72M/(PCS+1)/(ARR+1)       

所以TIM_Period 填10000-1,TIM_Prescaler 7200-1,

注意:取值范围是0-65535间。

timer.c文件

#include "stm32f10x.h"                  // Device header

void Timer_Init(void)
{
	  RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);//第一步,开启时钟,因为TIM2是APB1总线的外设,所以开启APB1总线时钟
		
		//第二步,选择时基单元的时钟
		TIM_InternalClockConfig(TIM2);//选择内部时钟驱动TIM2(不写也可以,不写就默认是内部时钟驱动)
		
		TIM_TimeBaseInitTypeDef  TIM_TimeBaseInitStructure;
		TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;//滤波时钟分频
		TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;//计数模式,向上计数
		TIM_TimeBaseInitStructure.TIM_Period = 10000-1 ;//自动重装载值
		TIM_TimeBaseInitStructure.TIM_Prescaler = 7200-1 ;//预分频值,定时1s
		TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0 ;//重复计数器,高级定时器才有的,
		TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure);
		
		TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);//第四步,配置中断控制,开了更新中断到NVIC的道路
		
		NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//第五步,配置NVIC,选择分组2		
	
		NVIC_InitTypeDef NVIC_InitStructure;
		NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
		NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
		NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority		= 2;//抢占优先级
		NVIC_InitStructure.NVIC_IRQChannelSubPriority		= 1; //响应优先级
		NVIC_Init(&NVIC_InitStructure);
		
		TIM_Cmd(TIM2,ENABLE);//第六步:运行控制,使能一下计数器,开始计数,让其开始工作,达到触发中断的条件
		
	}	

timer.h文件

#ifndef __TIMER_H
#define __TIMER_H

void Timer_Init(void);

#endif

main.c文件

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "timer.h"

extern void Timer_Init(void);

int  num =0;

int main(void)
{
  Timer_Init();
	OLED_Init();
	while (1)
	{
		OLED_ShowString(1,1,"seconds:");

		OLED_ShowNum(2,1,num,4);
	
	}
	
}


  void TIM2_IRQHandler(void)//第七步:写中断函数,这是在stm32的标准中断函数
{
		//按规矩,检查中断标志位
		if(TIM_GetITStatus(TIM2,TIM_IT_Update)==SET)//
		{
			
			//这里写内容
			num++;
			TIM_ClearITPendingBit(TIM2,TIM_IT_Update);//手动清除中断标志位,表示中断函数跑完了			
		}		
}

注意:extern声明的常见用法

1·没有extern void Timer_Init(void); 就会提示警报:

warning:  #223-D: function "Timer_Init" declared implicitly

虽然只是警报,不影响烧录,最好还是添加上去,减少警报。

2·如果想要跨.c文件使用变量,也可进行extern 变量,告诉编译器,此变量已经声明过,可用。

额外声明变量,操作都是同一个变量,使用时候,至于变量的变化,其实头文件里的函数声明,省略了extern。

效果:

注意:

按下复位键,发现计数是从0001开始,我们设置的是0000,为什么会这样?

分析:初始化之后,已经立即进入1次中断

原因:在stm32f10x_tim.c文件中TIM_TimeBaseInit()函数末尾有提示(绿字

翻译过来:立即生成一个更新事件,来重新装载分频器和重复计数器的值

深入分析:预分频器有缓冲寄存器,我们写的值,只有在更新事件时候,才会真正起作用 ,所以绿字下面的命令,就是手动生成了一个更新事件,这样预分频值就有效了。

但是,副作用,由于,更新中断和更新事件是同时发生的,更新中断,会把更新中断标志位置1,导致我们初始化后,更新中断就会立刻进入,这就是我们一上电,就立即进入中断的原因。

解决办法:

既然函数库,把更新中断标志位置1,我们在自写的初始化后面,手动把更新中断标志位清0即可。

TIM_ClearFlag(TIM2,TIM_FLAG_Update)

如下time.c文件:(函数在中间)

#include "stm32f10x.h"                  // Device header

void Timer_Init(void)
{
	  RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);//第一步,开启时钟,因为TIM2是APB1总线的外设,所以开启APB1总线时钟
		
		//第二步,选择时基单元的时钟
		TIM_InternalClockConfig(TIM2);//选择内部时钟驱动TIM2(不写也可以,不写就默认是内部时钟驱动)
		
		TIM_TimeBaseInitTypeDef  TIM_TimeBaseInitStructure;
		TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;//滤波时钟分频
		TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;//计数模式,向上计数
		TIM_TimeBaseInitStructure.TIM_Period = 10000-1 ;//自动重装载值
		TIM_TimeBaseInitStructure.TIM_Prescaler = 7200-1 ;//预分频值,定时1s
		TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0 ;//重复计数器,高级定时器才有的,
		TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure);
	
	    TIM_ClearFlag(TIM2,TIM_FLAG_Update);
		
		TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);//第四步,配置中断控制,开了更新中断到NVIC的道路
		
		NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//第五步,配置NVIC,选择分组2		
	
		NVIC_InitTypeDef NVIC_InitStructure;
		NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
		NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
		NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority		= 2;//抢占优先级
		NVIC_InitStructure.NVIC_IRQChannelSubPriority		= 1; //响应优先级
		NVIC_Init(&NVIC_InitStructure);
		
		TIM_Cmd(TIM2,ENABLE);//第六步:运行控制,使能一下计数器,开始计数,让其开始工作,达到触发中断的条件
		
	}	

同时可运用如下函数,显示计数器的变化
OLED_ShowNum(3,5,TIM_GetCounter(TIM2),5)

由于我们设置的自动那个重装值是10000-1,所以计数0自增到9999,共10000个数,共1秒

代码2

  • 7
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值