猿创征文 | STM32 通用定时器与Systick外设定时器原理讲解和源码分享

在这里插入图片描述

一、Systick定时器

1、定时器概念

定时器:是芯片内部用于计数从而得到时长的一种外设。

定时器定时长短与什么有关???(定时器定时长短与频率及计数大小有关)

定时器频率换算单位:1GHZ=1000MHZ=1000 000KHZ = 1000 000 000HZ


定时器定时时间计数个数/f(频率)

>

500/1MHZ = 500/1000 000 = 0.0005s = 0.5ms = 500us
在1MHZ下,1us计1个数;在100MHZ下,1us计100个数;

2、Systick定时器

Systick定时器,是一个简单的定时器,对于CM3,CM4内核芯片,都有Systick定时器。常用来做延时,或者实时系统的心跳时钟。这样可以节省MCU资源,不用浪费一个定时器。比如uCOS/FreeRTOS中,分时复用,需要一个最小的时间戳,一般在STM32+UCOS系统中,都采用Systick做uCOS心跳时钟


Systick定时器就是系统滴答定时器,一个24 位的倒计数定时器,计到0 时,将从RELOAD 寄存器中自动重装载定时初值。只要不把它在SysTick 控制及状态寄存器中的使能位清除,就永不停息,即使在睡眠模式下也能工作。

一共4个Systick寄存器

`
CTRL             SysTick 控制和状态寄存器  
LOAD            SysTick 自动重装载除值寄存器 
VAL               SysTick 当前值寄存器  
CALIB            SysTick 校准值寄存器
`

在这里插入图片描述


在这里插入图片描述

二、STM32通用定时器

1、定时分类及时钟频率

STM32定时器分类:高级控制定时器、通用定时器、基本定时器

高级控制定时器:TIM1 TIM8

通用定时器:TIM2 TIM3 TIM4 TIM5 TIM9 TIM10 TIM11 TIM12 TIM13 TIM14
在这里插入图片描述
基本定时器:TIM6 TIM7

挂在APB1下的定时器:TIM2 TIM3 TIM4 TIM5 TIM6 TIM7 TIM12 TIM13 TIM14
挂在APB2下的定时器:TIM1 TIM8 TIM9 TIM10 TIM11


在这里插入图片描述


挂在APB1下的定时器时钟频率:42MHZ X 2 = 84MHZ
挂在APB2下的定时器时钟频率:84MHZ X 2 = 168MHZ

2、通用定时器的计数模式

通用定时器可以向上计数、向下计数、向上向下双向计数模式。
向上计数模式:计数器从0计数到自动加载值(TIMx_ARR),然后重新从0开始计数并且产生一个计数器溢出事件。
向下计数模式:计数器从自动装入的值(TIMx_ARR)开始向下计数到0,然后从自动装入的值重新开始,并产生一个计数器向下溢出事件。
中央对齐模式(向上/向下计数):计数器从0开始计数到自动装入的值-1,产生一个计数器溢出事件,然后向下计数到1并且产生一个计数器溢出事件;然后再从0开始重新计数。


在这里插入图片描述


3、定时器框架分析


在这里插入图片描述


4、通用定时器3配置流程

通用定时器需要添加的库函数文件:stm32f4xx_tim.c

1、能定时器时钟。
     RCC_APB1PeriphClockCmd();
2、初始化定时器,配置ARR,PSC。
     TIM_TimeBaseInit();
3、启定时器中断,配置NVIC。
      NVIC_Init();
4、设置 TIM3_DIER  允许更新中断
	TIM_ITConfig();
5、使能定时器。
      TIM_Cmd();
6、编写中断服务函数。
      TIMx_IRQHandler();

三、函数说明

/**
  * @brief  Configures the SysTick clock source.
  * @param  SysTick_CLKSource: specifies the SysTick clock source.
  *   This parameter can be one of the following values:
  *     @arg SysTick_CLKSource_HCLK_Div8: AHB clock divided by 8 selected as SysTick clock source.
  *     @arg SysTick_CLKSource_HCLK: AHB clock selected as SysTick clock source.
  * @retval None
  */
void SysTick_CLKSourceConfig(uint32_t SysTick_CLKSource)
函数说明:SysTick时钟源配置
返回值:无
uint32_t SysTick_CLKSource:时钟源
SysTick_CLKSource_HCLK_Div8:  HCLK/8 = 21MHZ
SysTick_CLKSource_HCLK: HCLK = 168MHZ

void TIM_TimeBaseInit(TIM_TypeDef* TIMx, TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct);

函数功能:定时器初始化
返回值:无
TIM_TypeDef* TIMx:选择定时器
 TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct:定时器结构体

typedef struct
{
    
    
    uint16_t TIM_Prescaler;
    uint16_t TIM_CounterMode;
    uint16_t TIM_Period;
    uint16_t TIM_ClockDivision;
} TIM_TimeBaseInitTypeDef;
1.    第一个参数 TIM_Prescaler 是用来设置分频系数。
2.    第二个参数 TIM_CounterMode 是用来设置计数方式,上面讲解过,可以设置为向上计数,向下计数方式还有中央对齐计数方式,比较常用的是向上计数模式TIM_CounterMode_Up 和向
下计数模式 TIM_CounterMode_Down。
3.   第三个参数是设置自动重载计数周期值,这在前面也已经讲解过。
4.   第四个参数是用来设置时钟分频因子

void TIM_ITConfig(TIM_TypeDef* TIMx, uint16_t TIM_IT, FunctionalState NewState)
函数说明:定时器的中断配置
返回值:无
TIM_TypeDef* TIMx:选择哪个定时器
uint16_t TIM_IT:中断方式
TIM_IT_Update (更新中断)
TIM_IT_CC1    
TIM_IT_CC2    
TIM_IT_CC3    
TIM_IT_CC4    
TIM_IT_COM    
TIM_IT_Trigger
TIM_IT_Break  
FunctionalState NewState:是否使能
void TIM_Cmd(TIM_TypeDef* TIMx, FunctionalState NewState)
函数说明:定时器使能
返回值:无
TIM_TypeDef* TIMx:选择哪个定时器
 
FunctionalState NewState:是否使能

ITStatus TIM_GetITStatus(TIM_TypeDef* TIMx, uint16_t TIM_IT)
函数功能:获取定时器中断标志位
返回值:SET或RESET
TIM_TypeDef* TIMx:选择定时器
uint16_t TIM_IT:中断事件
  *            @arg TIM_IT_Update: TIM update Interrupt source(更新标志位)
  *            @arg TIM_IT_CC1: TIM Capture Compare 1 Interrupt source
  *            @arg TIM_IT_CC2: TIM Capture Compare 2 Interrupt source
  *            @arg TIM_IT_CC3: TIM Capture Compare 3 Interrupt source
  *            @arg TIM_IT_CC4: TIM Capture Compare 4 Interrupt source
  *            @arg TIM_IT_COM: TIM Commutation Interrupt source
  *            @arg TIM_IT_Trigger: TIM Trigger Interrupt source
  *            @arg TIM_IT_Break: TIM Break Interrupt source
void TIM_ClearITPendingBit(TIM_TypeDef* TIMx, uint16_t TIM_IT)
函数功能:清空定时器中断标志位
返回值:无
TIM_TypeDef* TIMx:选择定时器
uint16_t TIM_IT:中断事件
  *            @arg TIM_IT_Update: TIM update Interrupt source
  *            @arg TIM_IT_CC1: TIM Capture Compare 1 Interrupt source
  *            @arg TIM_IT_CC2: TIM Capture Compare 2 Interrupt source
  *            @arg TIM_IT_CC3: TIM Capture Compare 3 Interrupt source
  *            @arg TIM_IT_CC4: TIM Capture Compare 4 Interrupt source
  *            @arg TIM_IT_COM: TIM Commutation Interrupt source
  *            @arg TIM_IT_Trigger: TIM Trigger Interrupt source
  *            @arg TIM_IT_Break: TIM Break Interrupt source

四、定时器应用

智能公交一体机,定时器每隔固定时间向服务器发送自己的定位
在这里插入图片描述

交通红绿灯定时1S将数字进行倒数
在这里插入图片描述

五、源码分享 定时器TIM3

阅读源码,请从主函数依次观看其他函数的调用顺序,即可理清整个逻辑

ctrl+Home 快速定位到开头
ctrl+End 	快速定位到结尾

文章内容繁多,请善用目录进行检索

delay.h

#ifndef __DELAY_H
#define __DELAY_H

#include "stm32f4xx.h"
void Delay_Init(void);

void delay_us(u32 nus);
void delay_ms(u32 nms);

#endif

delay.c

#include "delay.h"

//u32 == unsigned int
u32 my_us = 21;				//计21个数用1us
u32 my_ms = 21000;			//计21000个数用1ms

void Delay_Init(void)
{
    
    
	//SysTick时钟配置 168MHZ/8 = 21MHZ
	SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8);

}

//nus取值范围:1~798 915
void delay_us(u32 nus)
{
    
    
	u32 temp = 0;

	//设置重装值寄存器
	SysTick->LOAD = my_us*nus - 1;
	//设置计数器的值为0
	SysTick->VAL = 0x00;
	//开启定时器
	SysTick->CTRL |= (0x01<<0);
	
	do
	{
    
    
		temp = SysTick->CTRL;
			//判断定时是否关闭	//判断SysTick->CTRL第16位是否为1
	}while((temp&(0x01<<0)) && (!(temp & (0x01<<16))));
		
	//关闭定时器
	SysTick->CTRL &= ~(0x01<<0);
}

//nms取值范围:1~798
void delay_ms(u32 nms)
{
    
    
	u32 temp = 0;

	//设置重装值寄存器
	SysTick->LOAD = my_ms*nms - 1;
	//设置计数器的值为0
	SysTick->VAL = 0x00;
	//开启定时器
	SysTick->CTRL |= (0x01<<0);
	
	do
	{
    
    
		temp = SysTick->CTRL;
			//判断定时是否关闭	//判断SysTick->CTRL第16位是否为1
	}while((temp&(0x01<<0)) && (!(temp & (0x01<<16))));
		
	//关闭定时器
	SysTick->CTRL &= ~(0x01<<0);
}

void delay_s(u32 ns)
{
    
    
	int i;
	for(i=0; i<ns; i++)
	{
    
    
		delay_ms(500);
		delay_ms(500);
	}
}


exti.h

#ifndef __EXTI_H
#define __EXTI_H

#include "stm32f4xx.h"
void Exti_PA0_Init(void);

#endif

exti.c

#include "exti.h"

/*********************************
引脚说明:

KEY0--PA0(选择下降沿触发)


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

void Exti_PA0_Init(void)
{
    
    
	GPIO_InitTypeDef  GPIO_InitStruct;
	EXTI_InitTypeDef  EXTI_InitStruct;
	NVIC_InitTypeDef  NVIC_InitStruct;
	
	//使能SYSCFG及GPIOA时钟: 
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);
	//使能GPIOA组时钟
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);

	
	//初始化IO口为输入。
	GPIO_InitStruct.GPIO_Pin	= GPIO_Pin_0;		//引脚0
	GPIO_InitStruct.GPIO_Mode	= GPIO_Mode_IN;		//输入模式
	GPIO_InitStruct.GPIO_PuPd	= GPIO_PuPd_UP;		//上拉
	GPIO_Init(GPIOA, &GPIO_InitStruct);	
	
	//设置IO口与中断线的映射关系。
	SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOA, EXTI_PinSource0);
	
		
	EXTI_InitStruct.EXTI_Line	= EXTI_Line0;			//中断线0
	EXTI_InitStruct.EXTI_Mode	= EXTI_Mode_Interrupt;  //中断模式
	EXTI_InitStruct.EXTI_Trigger= EXTI_Trigger_Falling;	//下降触发
	EXTI_InitStruct.EXTI_LineCmd= ENABLE;				//中断使能
	//初始化线上中断,设置触发条件等。
	EXTI_Init(&EXTI_InitStruct);
	
	
	NVIC_InitStruct.NVIC_IRQChannel						= EXTI0_IRQn; 		//中断通道,可在stm32f4xx.h文件当中查找
	NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority	= 1;				//抢占优先级
	NVIC_InitStruct.NVIC_IRQChannelSubPriority			= 1;				//响应优先级
	NVIC_InitStruct.NVIC_IRQChannelCmd					= ENABLE;			//通道使能
	//配置中断分组(NVIC),并使能中断。
	NVIC_Init(&NVIC_InitStruct);

}


void delays(int n)
{
    
    
	int i,j;
	
	for(i=0; i<n; i++)
		for(j=0; j<30000; j++);

}

/***************************************************************
1、中断服务函数是满足条件后,CPU自行执行的函数不需要主动调用
2、中断服务函数是不能传递值与返回值
3、STM32的中断服务函数名可在startup_stm32f40_41xxx.s中查找
****************************************************************/
//编写中断服务函数
void EXTI0_IRQHandler(void)
{
    
    
	//判断标志位是否1
	if(EXTI_GetITStatus(EXTI_Line0) == SET)
	{
    
    
		if(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0) == Bit_RESET)
		{
    
    
			//延时一段时间
			delays(15);
			
			if(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0) == Bit_RESET)
			{
    
    
				GPIO_ToggleBits(GPIOF, GPIO_Pin_9);
			}	
			
		}
	}
	//清空中断线0
	EXTI_ClearITPendingBit(EXTI_Line0);		
}	

key.h

#ifndef __KEY_H
#define __KEY_H

#include "stm32f4xx.h"

void Key_Init(void);

#endif

key.c

#include "key.h"


/*********************************
引脚说明:

KEY0--PA0

**********************************/
void Key_Init(void)
{
    
    
	GPIO_InitTypeDef  GPIO_InitStruct;
	
	//使能GPIOA组时钟
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
	
	GPIO_InitStruct.GPIO_Pin	= GPIO_Pin_0;		//引脚0
	GPIO_InitStruct.GPIO_Mode	= GPIO_Mode_IN;		//输入模式
	GPIO_InitStruct.GPIO_PuPd	= GPIO_PuPd_UP;		//上拉
	GPIO_Init(GPIOA, &GPIO_InitStruct);	

}

led.h

#ifndef __LED_H
#define __LED_H

#include "stm32f4xx.h"

#define LED0_ON		GPIO_ResetBits(GPIOF, GPIO_Pin_9)
#define LED0_OFF	GPIO_SetBits(GPIOF, GPIO_Pin_9)


void Led_Init(void);

#endif

led.c

#include "led.h"


/*********************************
引脚说明:

LED0 -- PF9
LED1 -- PF10
LED2 -- PE13
LED3 -- PE14

**********************************/
void Led_Init(void)
{
    
    
	GPIO_InitTypeDef  GPIO_InitStruct;
	
	//使能GPIOE组时钟
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE, ENABLE);		
	//使能GPIOF组时钟
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF, ENABLE);	
	

	GPIO_InitStruct.GPIO_Pin	= GPIO_Pin_9|GPIO_Pin_10;		//引脚9 10
	GPIO_InitStruct.GPIO_Mode	= GPIO_Mode_OUT;	//输出模式
	GPIO_InitStruct.GPIO_OType	= GPIO_OType_PP;	//推挽输出
	GPIO_InitStruct.GPIO_PuPd	= GPIO_PuPd_UP;		//上拉
	GPIO_InitStruct.GPIO_Speed	= GPIO_Speed_50MHz; //速度 
	GPIO_Init(GPIOF, &GPIO_InitStruct);
	
	GPIO_InitStruct.GPIO_Pin	= GPIO_Pin_13|GPIO_Pin_14;		//引脚13 14
	GPIO_InitStruct.GPIO_Mode	= GPIO_Mode_OUT;	//输出模式
	GPIO_InitStruct.GPIO_OType	= GPIO_OType_PP;	//推挽输出
	GPIO_InitStruct.GPIO_PuPd	= GPIO_PuPd_UP;		//上拉
	GPIO_InitStruct.GPIO_Speed	= GPIO_Speed_50MHz; //速度 
	GPIO_Init(GPIOE, &GPIO_InitStruct);	
	
	GPIO_SetBits(GPIOF, GPIO_Pin_9);
	GPIO_SetBits(GPIOF, GPIO_Pin_10);
	GPIO_SetBits(GPIOE, GPIO_Pin_13);
	GPIO_SetBits(GPIOE, GPIO_Pin_14);

}

tim.h

#ifndef __TIM_H
#define __TIM_H

#include "stm32f4xx.h"
void Tim3_Init(void);

#endif

tim.c

#include "tim.h"

/*********************************
定时器说明
TIM3 -- APB1(定时器频率:84MHZ)

TIM3是16位定时器
**********************************/

void Tim3_Init(void)
{
    
    
	TIM_TimeBaseInitTypeDef  	TIM_TimeBaseInitStruct;
	NVIC_InitTypeDef  			NVIC_InitStruct;
	//1、能定时器时钟。
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
	
	
//	TIM_TimeBaseInitStruct.TIM_Prescaler	= (84-1);				//84分频,定时器频率84MHZ/84 = 1MHZ(计一个用1us)
//	TIM_TimeBaseInitStruct.TIM_Period		= (1000-1);				//计1000个数,在1MHZ下,用时1ms
//	TIM_TimeBaseInitStruct.TIM_CounterMode	= TIM_CounterMode_Up;	//向上计数
//	TIM_TimeBaseInitStruct.TIM_ClockDivision= TIM_CKD_DIV1;			//分频因子	1脉冲计一个数
//	//2、初始化定时器,配置ARR,PSC。
//	TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStruct);


	TIM_TimeBaseInitStruct.TIM_Prescaler	= (8400-1);				//8400分频,定时器频率84MHZ/8400 = 10000HZ(1秒可计10000个数)
	TIM_TimeBaseInitStruct.TIM_Period		= (10000-1);			//计10000个数,在10000HZ下,用时1s
	TIM_TimeBaseInitStruct.TIM_CounterMode	= TIM_CounterMode_Up;	//向上计数
	TIM_TimeBaseInitStruct.TIM_ClockDivision= TIM_CKD_DIV1;			//分频因子	1脉冲计一个数
	//2、初始化定时器,配置ARR,PSC。
	TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStruct);
	
	NVIC_InitStruct.NVIC_IRQChannel						= TIM3_IRQn; 		//中断通道,可在stm32f4xx.h文件当中查找
	NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority	= 1;				//抢占优先级
	NVIC_InitStruct.NVIC_IRQChannelSubPriority			= 1;				//响应优先级
	NVIC_InitStruct.NVIC_IRQChannelCmd					= ENABLE;			//通道使能
	//3、启定时器中断,配置NVIC。
	NVIC_Init(&NVIC_InitStruct);	

	
	
	//4、设置 TIM3_DIER  允许更新中断
	TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE);
	
	//5、使能定时器。
	TIM_Cmd(TIM3, ENABLE);

 
}

//编写中断服务函数。
void  TIM3_IRQHandler(void)
{
    
    
	static int number = 0;
	//判断更新标志位是否1
	if(TIM_GetITStatus(TIM3, TIM_IT_Update) == SET)
	{
    
    

		GPIO_ToggleBits(GPIOF, GPIO_Pin_9);
		
		//清空更新标志位
		TIM_ClearITPendingBit(TIM3, TIM_IT_Update);	
	}
}

main.c

#include "stm32f4xx.h"
#include "led.h"
#include "key.h"
#include "exti.h"
#include "delay.h"
#include "tim.h"

void delay(int n)
{
    
    
        int i,j;

        for(i=0; i<n; i++)
                for(j=0; j<30000; j++);

}

int main(void)
{
    
    
        //NVIC 中断分组 一个项目一般只有一个分组:0~3
        NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);

        Delay_Init();
        Led_Init();
        Tim3_Init();




        while(1)
        {
    
    
                GPIO_ToggleBits(GPIOE, GPIO_Pin_14);
                //1S
                delay_ms(500);
                delay_ms(500);


        }
        return 0;
}

六、 延时函数delay延时

delay.h

#ifndef __DELAY_H
#define __DELAY_H

#include "stm32f4xx.h"

void Delay_Init(void);

void delay_us(u32 nus);
void delay_ms(u32 nms);

#endif

delay.c

#include "delay.h"

//u32 == unsigned int
u32 my_us = 21;				//计21个数用1us
u32 my_ms = 21000;			//计21000个数用1ms

void Delay_Init(void)
{
    
    
	//SysTick时钟配置 168MHZ/8 = 21MHZ
	SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8);

}

//nus取值范围:1~798 915
void delay_us(u32 nus) //500
{
    
    
	u32 temp = 0;

	//设置重装值寄存器
	SysTick->LOAD = my_us*nus - 1;  //500*21  -1 
	//设置计数器的值为0
	SysTick->VAL = 0x00;
	//开启定时器
	SysTick->CTRL |= (0x01<<0);
	
	do
	{
    
    
		temp = SysTick->CTRL;
			//判断定时是否关闭	//判断SysTick->CTRL第16位是否为1
	}while((temp&(0x01<<0)) && (!(temp & (0x01<<16))));
		
	//关闭定时器
	SysTick->CTRL &= ~(0x01<<0);
}

//nms取值范围:1~798
void delay_ms(u32 nms)
{
    
    
	u32 temp = 0;

	//设置重装值寄存器
	SysTick->LOAD = my_ms*nms - 1;
	//设置计数器的值为0
	SysTick->VAL = 0x00;
	//开启定时器
	SysTick->CTRL |= (0x01<<0);
	
	do
	{
    
    
		temp = SysTick->CTRL;
			//判断定时是否关闭	//判断SysTick->CTRL第16位是否为1
	}while((temp&(0x01<<0)) && (!(temp & (0x01<<16))));
		
	//关闭定时器
	SysTick->CTRL &= ~(0x01<<0);
}

void delay_s(u32 ns)
{
    
    
	int i;
	for(i=0; i<ns; i++)
	{
    
    
		delay_ms(500);
		delay_ms(500);
	}
}


exti.h

#ifndef __EXTI_H
#define __EXTI_H

#include "stm32f4xx.h"


void Exti_PA0_Init(void);

#endif

exti.c

#include "exti.h"

/*********************************
引脚说明:

KEY0--PA0(选择下降沿触发)


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

void Exti_PA0_Init(void)
{
    
    
	GPIO_InitTypeDef  GPIO_InitStruct;
	EXTI_InitTypeDef  EXTI_InitStruct;
	NVIC_InitTypeDef  NVIC_InitStruct;
	
	//使能SYSCFG及GPIOA时钟: 
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);
	//使能GPIOA组时钟
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);

	
	//初始化IO口为输入。
	GPIO_InitStruct.GPIO_Pin	= GPIO_Pin_0;		//引脚0
	GPIO_InitStruct.GPIO_Mode	= GPIO_Mode_IN;		//输入模式
	GPIO_InitStruct.GPIO_PuPd	= GPIO_PuPd_UP;		//上拉
	GPIO_Init(GPIOA, &GPIO_InitStruct);	
	
	//设置IO口与中断线的映射关系。
	SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOA, EXTI_PinSource0);
	
		
	EXTI_InitStruct.EXTI_Line	= EXTI_Line0;			//中断线0
	EXTI_InitStruct.EXTI_Mode	= EXTI_Mode_Interrupt;  //中断模式
	EXTI_InitStruct.EXTI_Trigger= EXTI_Trigger_Falling;	//下降触发
	EXTI_InitStruct.EXTI_LineCmd= ENABLE;				//中断使能
	//初始化线上中断,设置触发条件等。
	EXTI_Init(&EXTI_InitStruct);
	
	
	NVIC_InitStruct.NVIC_IRQChannel						= EXTI0_IRQn; 		//中断通道,可在stm32f4xx.h文件当中查找
	NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority	= 1;				//抢占优先级
	NVIC_InitStruct.NVIC_IRQChannelSubPriority			= 1;				//响应优先级
	NVIC_InitStruct.NVIC_IRQChannelCmd					= ENABLE;			//通道使能
	//配置中断分组(NVIC),并使能中断。
	NVIC_Init(&NVIC_InitStruct);

}


void delays(int n)
{
    
    
	int i,j;
	
	for(i=0; i<n; i++)
		for(j=0; j<30000; j++);

}


/***************************************************************
1、中断服务函数是满足条件后,CPU自行执行的函数不需要主动调用
2、中断服务函数是不能传递值与返回值
3、STM32的中断服务函数名可在startup_stm32f40_41xxx.s中查找
****************************************************************/
//编写中断服务函数
void EXTI0_IRQHandler(void)
{
    
    
	//判断标志位是否1
	if(EXTI_GetITStatus(EXTI_Line0) == SET)
	{
    
    
		if(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0) == Bit_RESET)
		{
    
    
			//延时一段时间
			delays(15);
			
			if(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0) == Bit_RESET)
			{
    
    
				GPIO_ToggleBits(GPIOF, GPIO_Pin_9);
			}	
			
		}
	}
	//清空中断线0
	EXTI_ClearITPendingBit(EXTI_Line0);		
}	

key.h

#ifndef __KEY_H
#define __KEY_H

#include "stm32f4xx.h"



void Key_Init(void);

#endif

key.c

#include "key.h"


/*********************************
引脚说明:

KEY0--PA0

**********************************/
void Key_Init(void)
{
    
    
	GPIO_InitTypeDef  GPIO_InitStruct;
	
	
	//使能GPIOA组时钟
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
	
	
	GPIO_InitStruct.GPIO_Pin	= GPIO_Pin_0;		//引脚0
	GPIO_InitStruct.GPIO_Mode	= GPIO_Mode_IN;		//输入模式
	GPIO_InitStruct.GPIO_PuPd	= GPIO_PuPd_UP;		//上拉
	GPIO_Init(GPIOA, &GPIO_InitStruct);	

}

led.h

#ifndef __LED_H
#define __LED_H

#include "stm32f4xx.h"



#define LED0_ON		GPIO_ResetBits(GPIOF, GPIO_Pin_9)
#define LED0_OFF	GPIO_SetBits(GPIOF, GPIO_Pin_9)


void Led_Init(void);

#endif

led.c

#include "led.h"


/*********************************
引脚说明:

LED0 -- PF9
LED1 -- PF10
LED2 -- PE13
LED3 -- PE14

**********************************/
void Led_Init(void)
{
    
    
	GPIO_InitTypeDef  GPIO_InitStruct;
	
	//使能GPIOE组时钟
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE, ENABLE);		
	//使能GPIOF组时钟
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF, ENABLE);	
	

	GPIO_InitStruct.GPIO_Pin	= GPIO_Pin_9|GPIO_Pin_10;		//引脚9 10
	GPIO_InitStruct.GPIO_Mode	= GPIO_Mode_OUT;	//输出模式
	GPIO_InitStruct.GPIO_OType	= GPIO_OType_PP;	//推挽输出
	GPIO_InitStruct.GPIO_PuPd	= GPIO_PuPd_UP;		//上拉
	GPIO_InitStruct.GPIO_Speed	= GPIO_Speed_50MHz; //速度 
	GPIO_Init(GPIOF, &GPIO_InitStruct);
	
	GPIO_InitStruct.GPIO_Pin	= GPIO_Pin_13|GPIO_Pin_14;		//引脚13 14
	GPIO_InitStruct.GPIO_Mode	= GPIO_Mode_OUT;	//输出模式
	GPIO_InitStruct.GPIO_OType	= GPIO_OType_PP;	//推挽输出
	GPIO_InitStruct.GPIO_PuPd	= GPIO_PuPd_UP;		//上拉
	GPIO_InitStruct.GPIO_Speed	= GPIO_Speed_50MHz; //速度 
	GPIO_Init(GPIOE, &GPIO_InitStruct);	
	
	GPIO_SetBits(GPIOF, GPIO_Pin_9);
	GPIO_SetBits(GPIOF, GPIO_Pin_10);
	GPIO_SetBits(GPIOE, GPIO_Pin_13);
	GPIO_SetBits(GPIOE, GPIO_Pin_14);

}

main.c

#include "stm32f4xx.h"
#include "led.h"
#include "key.h"
#include "exti.h"
#include "delay.h" 

void delay(int n)
{
    
    
	int i,j;
	
	for(i=0; i<n; i++)
		for(j=0; j<30000; j++);

}

int main(void)
{
    
    
	//NVIC分组一个工程只能设置一次
	//设置NVIC分组为第二分组; 抢占优先级取值范围:0~3, 响应优先级取值范围:0~3
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	
	Delay_Init();
	Led_Init();

	
	while(1)
	{
    
    
		GPIO_ToggleBits(GPIOF, GPIO_Pin_9);	//改变灯的状态
		//延时1s
		delay_ms(500);   //500+500 = 1000ms  =1s
		delay_ms(500);
	}
	return 0;
}

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/m0_45463480/article/details/126591452