学习目标:
关于systick系统定时器和EXTI外部中断的学习学习内容:
1、 关于systick系统定时器的学习 2、 关于EXTI外部中断的学习学习时间:
1、 周六上午 8 点-上午 11 点学习产出:
1.优快云 技术博客 1 篇一、systick系统定时器
1.关于中断操作
SysTick—系统定时器有 4 个寄存器,简要介绍如下。在使用 SysTick 产生定时的时候,只需要配置前三个寄存器,最后一个校准寄存器不需要使用。
CTRL SysTick 控制及状态寄存器
LOAD SysTick 重装载数值寄存器
VAL SysTick 当前数值寄存器
CALIB SysTick 校准数值寄存器
寄存器控制相关
位段 名称 类型 复位值 描述
16 COUNTFLAG R/W 0 如果在上次读取本寄存器后, SysTick 已经计到了 0,则该位为 1。
2 CLKSOURCE R/W 0 时钟源选择位,0=AHB/8,1=处理器时钟 AHB
1 TICKINT R/W 0 1=SysTick 倒数计数到 0 时产生 SysTick 异常请求,0=数到 0 时无动作。也可以通过读取COUNTFLAG 标志位来确定计数器是否递减到0
0 ENABLE R/W 0 SysTick 定时器的使能位
SysTick 重装载数值寄存器
位段 名称 类型 复位值 描述
23:0 RELOAD R/W 0 当倒数计数至零时,将被重装载的值
SysTick 当前数值寄存器
位段 名称 类型 复位值 描述
23:0 CURRENT R/W 0 读取时返回当前倒计数的值,写它则使之清零,同时还会清除在 SysTick 控制及状态寄存器中的COUNTFLAG标志
SysTick 属于内核的外设,有关的寄存器定义和库函数都在内核相关的库文件core_cm3.h 中。
static __INLINE uint32_t SysTick_Config(uint32_t ticks)
{
if (ticks > SysTick_LOAD_RELOAD_Msk) return (1); /*不可能的重装值 寄存器24bit,最大值2^24*/
SysTick->LOAD = (ticks & SysTick_LOAD_RELOAD_Msk) - 1; /*设置重装载寄存器*/
NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1); /* 中断优先级 设置为15最低*/
SysTick->VAL = 0; /* SysTick 当前数值 */
/* 设置系统定时器的时钟源为AHBCLK=72M SysTick_CTRL_CLKSOURCE_Msk为1<<2 */
/* 使能系统定时器SysTick_CTRL_TICKINT_Msk为1<<1 */
/* 使能定时器SysTick_CTRL_ENABLE_Msk为0<<1 */
SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk |
SysTick_CTRL_TICKINT_Msk |
SysTick_CTRL_ENABLE_Msk;
return (0); /* Function successful */
}
//关于中断优先级的设置
static __INLINE void NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority)
{
/*
先判断形参IRQn的大小,如果是小于 0,则表示这个是系统异常,系统异常的优先级由内核外设SCB的寄存器 SHPRx 控制,如果大于 0 则是外部中断,外部中断的优先级由内核外设 NVIC 中的 IPx 寄存器控制。
SPRH1-SPRH3 是一个 32 位的寄存器,但是只能通过字节访问,每 8 个字段控制着一个内核外设的中断优先级的配置。在 STM32F103 中,只有位 7:3 这高四位有效,低四位没有用到,所以内核外设的中断优先级可编程为:0~15,只有 16 个可编程优先级,数值越小,优先级越高。如果软件优先级配置相同,那就根据他们在中断向量表里面的位置编号来决定优先级大小,编号越小,优先级越高。
*/
if(IRQn < 0) {
SCB->SHP[((uint32_t)(IRQn) & 0xF)-4] = ((priority << (8 - __NVIC_PRIO_BITS)) & 0xff); } /* set Priority for Cortex-M3 System Interrupts */
else {
NVIC->IP[(uint32_t)(IRQn)] = ((priority << (8 - __NVIC_PRIO_BITS)) & 0xff); } /* set Priority for device specific Interrupts */
}
main.c
#include "stm32f10x.h"
#include "bsp_SysTick.h"
#include "bsp_led.h"
/*
* t : 定时时间
* Ticks : 多少个时钟周期产生一次中断
* f : 时钟频率 72000000
* t = Ticks * 1/f = (72000000/100000) * (1/72000000) = 10us
*/
int main(void)
{
/* LED 初始化 */
LED_GPIO_Config();
/*配置systick为10us中断一次*/
SysTick_Init();
for(;;)
{
LED1( ON );
Delay_us(500000); //5s延时
//Delay_ms(500);
LED1( OFF );
LED2( ON );
Delay_us(500000); //5s延时
//Delay_ms(500);
LED2( OFF );
}
}
/*********************************************END OF FILE**********************/
bsp_SysTick.h
#ifndef __SYSTICK_H
#define __SYSTICK_H
#include "stm32f10x.h"
void SysTick_Init(void);
void Delay_us(__IO u32 nTime);
#define Delay_ms(x) Delay_us(100*x) //单位ms
#endif /* __SYSTICK_H */
bsp_SysTick.c
#include "bsp_SysTick.h"
#include "core_cm3.h"
#include "misc.h"
static __IO u32 TimingDelay;
/**
* @brief 启动系统滴答定时器 SysTick
*/
void SysTick_Init(void)
{
/* SystemFrequency / 1000 1ms
* SystemFrequency / 100000 10us
* SystemFrequency / 1000000 1us
*/
if (SysTick_Config(SystemCoreClock / 100000))
{
/* Capture error */
while (1);
}
}
/*us延时程序,10us为一个单位*/
void Delay_us(__IO u32 nTime)
{
TimingDelay = nTime;
//使能滴答定时器
SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk;
while(TimingDelay != 0);
}
/**
* @brief 获取节拍程序
* @attention 在 SysTick 中断函数 SysTick_Handler()µ÷ÓÃ
*/
void TimingDelay_Decrement(void)
{
if (TimingDelay != 0x00)
{
TimingDelay--;
}
}
/*********************************************END OF FILE**********************/
stm32f10x_it.h
void SysTick_Handler(void);
stm32f10x_it.c
extern void TimingDelay_Decrement(void);
void SysTick_Handler(void)
{
TimingDelay_Decrement();
}
2.查询操作:COUNTFLAG 如果在上次读取本寄存器后, SysTick 已经计到了 0,则该位为 1。
bsp_SysTick.h
#ifndef __SYSTICK_H
#define __SYSTICK_H
#include "stm32f10x.h"
void SysTick_Delay_Us( __IO uint32_t us);
void SysTick_Delay_Ms( __IO uint32_t ms);
#endif /* __SYSTICK_H */
bsp_SysTick.c
#include "bsp_SysTick.h"
#include "core_cm3.h"
#include "misc.h"
void SysTick_Delay_Us( __IO uint32_t us)
{
uint32_t i;
SysTick_Config(SystemCoreClock/1000000);
for(i=0;i<us;i++)
{
// 当计数器的值减小到0的时候,CRTL寄存器的位16置1,当位置1时,读取该位会清0
while( !((SysTick->CTRL)&(1<<16)) );
}
//关闭systick定时器
SysTick->CTRL &=~SysTick_CTRL_ENABLE_Msk;
}
void SysTick_Delay_Ms( __IO uint32_t ms)
{
uint32_t i;
SysTick_Config(SystemCoreClock/1000);
for(i=0;i<ms;i++)
{
// 当计数器的值减小到0的时候,CRTL寄存器的位16置1,当位置1时,读取该位会清0
while( !((SysTick->CTRL)&(1<<16)) );
}
//关闭systick定时器
SysTick->CTRL &=~ SysTick_CTRL_ENABLE_Msk;
}
/*********************************************END OF FILE**********************/
core_cm3.h中会使用到有关systick_config()的代码
static __INLINE uint32_t SysTick_Config(uint32_t ticks)
{
if (ticks > SysTick_LOAD_RELOAD_Msk) return (1); /*不可能的重装值 寄存器24bit,最大值2^24*/
SysTick->LOAD = (ticks & SysTick_LOAD_RELOAD_Msk) - 1; /*设置重装载寄存器*/
NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1); /* 中断优先级 设置为15最低*/
SysTick->VAL = 0; /* SysTick 当前数值 */
/* 设置系统定时器的时钟源为AHBCLK=72M SysTick_CTRL_CLKSOURCE_Msk为1<<2 */
/* 使能系统定时器SysTick_CTRL_TICKINT_Msk为1<<1 */
/* 使能定时器SysTick_CTRL_ENABLE_Msk为0<<1 */
SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk |
SysTick_CTRL_TICKINT_Msk |
SysTick_CTRL_ENABLE_Msk;
return (0); /* Function successful */
}
main.c
#include "stm32f10x.h"
#include "bsp_SysTick.h"
#include "bsp_led.h"
int main(void)
{
/* 配置LED的端口初始化 */
LED_GPIO_Config();
for(;;)
{
LED1( ON );
SysTick_Delay_Ms( 1000 );
LED1( OFF );
LED2( ON );
SysTick_Delay_Ms( 1000 );
LED2( OFF );
}
}
/*********************************************END OF FILE**********************/
二、关于EXTI外部中断
bsp_exti.h
#ifndef __EXTI_H
#define __EXTI_H
#include "stm32f10x.h"
//引脚定义
#define KEY1_INT_GPIO_PORT GPIOA
#define KEY1_INT_GPIO_CLK (RCC_APB2Periph_GPIOA|RCC_APB2Periph_AFIO)
#define KEY1_INT_GPIO_PIN GPIO_Pin_0
#define KEY1_INT_EXTI_PORTSOURCE GPIO_PortSourceGPIOA
#define KEY1_INT_EXTI_PINSOURCE GPIO_PinSource0
#define KEY1_INT_EXTI_LINE EXTI_Line0
#define KEY1_INT_EXTI_IRQ EXTI0_IRQn
#define KEY1_IRQHandler EXTI0_IRQHandler
#define KEY2_INT_GPIO_PORT GPIOC
#define KEY2_INT_GPIO_CLK (RCC_APB2Periph_GPIOC|RCC_APB2Periph_AFIO)
#define KEY2_INT_GPIO_PIN GPIO_Pin_13
#define KEY2_INT_EXTI_PORTSOURCE GPIO_PortSourceGPIOC
#define KEY2_INT_EXTI_PINSOURCE GPIO_PinSource13
#define KEY2_INT_EXTI_LINE EXTI_Line13
#define KEY2_INT_EXTI_IRQ EXTI15_10_IRQn
#define KEY2_IRQHandler EXTI15_10_IRQHandler
void EXTI_Key_Config(void);
#endif /* __EXTI_H */
bsp_exti.c
#include "./KEY/bsp_exti.h"
static void NVIC_Configuration(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
/*配置NVIC为中断优先级组*/
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
/* 配置中断源:key1 */
NVIC_InitStructure.NVIC_IRQChannel = KEY1_INT_EXTI_IRQ;
/* 配置抢占优先级:1 */
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
/* 配置子优先级:1 */
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
/* 使能中断通道 */
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
/* 配置中断源:key2 采取和key1相同的配置*/
NVIC_InitStructure.NVIC_IRQChannel = KEY2_INT_EXTI_IRQ;
NVIC_Init(&NVIC_InitStructure);
}
//EXTI中断配置
void EXTI_Key_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
EXTI_InitTypeDef EXTI_InitStructure;
/*开启按键的GPIO口时钟*/
RCC_APB2PeriphClockCmd(KEY1_INT_GPIO_CLK,ENABLE);
/*配置NVIC中断*/
NVIC_Configuration();
/*--------------------------KEY1-----------------------------*/
/*GPIO口*/
GPIO_InitStructure.GPIO_Pin = KEY1_INT_GPIO_PIN;
/*浮空输入*/
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(KEY1_INT_GPIO_PORT, &GPIO_InitStructure);
/*EXTI的信号源*/
GPIO_EXTILineConfig(KEY1_INT_EXTI_PORTSOURCE, KEY1_INT_EXTI_PINSOURCE);
EXTI_InitStructure.EXTI_Line = KEY1_INT_EXTI_LINE;
/*EXTI为中断模式*/
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
/*上升沿中断*/
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
/*使能中断*/
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure);
/*--------------------------KEY2-----------------------------*/
GPIO_InitStructure.GPIO_Pin = KEY2_INT_GPIO_PIN;
/*浮空输入*/
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(KEY2_INT_GPIO_PORT, &GPIO_InitStructure);
/*EXTI的信号源*/
GPIO_EXTILineConfig(KEY2_INT_EXTI_PORTSOURCE, KEY2_INT_EXTI_PINSOURCE);
EXTI_InitStructure.EXTI_Line = KEY2_INT_EXTI_LINE;
/*EXTI为中断模式*/
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
/*下降沿中断*/
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
/*使能中断*/
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure);
}
/*********************************************END OF FILE**********************/
main.c
#include "stm32f10x.h"
#include "./KEY/bsp_exti.h"
#include "bsp_led.h"
int main(void)
{
LED_GPIO_Config();
EXTI_Key_Config();
while(1)
{
}
}
/*********************************************END OF FILE**********************/
stm32f10x_it.c中key1和key2的中断服务函数
void KEY1_IRQHandler(void)
{
//是否产生EXTI Line中断
//stm32f10x_exit.c中
/*
ITStatus EXTI_GetITStatus(uint32_t EXTI_Line)
{
ITStatus bitstatus = RESET;
uint32_t enablestatus = 0;
/* Check the parameters */
assert_param(IS_GET_EXTI_LINE(EXTI_Line));
enablestatus = EXTI->IMR & EXTI_Line;
if (((EXTI->PR & EXTI_Line) != (uint32_t)RESET) && (enablestatus != (uint32_t)RESET))
{
bitstatus = SET;
}
else
{
bitstatus = RESET;
}
return bitstatus;
}
*/
if(EXTI_GetITStatus(KEY1_INT_EXTI_LINE) != RESET)
{
// LED1反转
LED1_TOGGLE;
//清除标志位
EXTI_ClearITPendingBit(KEY1_INT_EXTI_LINE);
//stm32f10x_exit.c中
/*
void EXTI_ClearITPendingBit(uint32_t EXTI_Line)
{
/* Check the parameters */
assert_param(IS_EXTI_LINE(EXTI_Line));
EXTI->PR = EXTI_Line;
}
*/
}
}
void KEY2_IRQHandler(void)
{
if(EXTI_GetITStatus(KEY2_INT_EXTI_LINE) != RESET)
{
//LED2反转
LED2_TOGGLE;
//清除标志位
EXTI_ClearITPendingBit(KEY2_INT_EXTI_LINE);
}
总结:由于设置的key1为上升沿触发中断,key2为下降沿触发中断,当按下key1时led1反转,当按下key2时led2不会反转,需要等待key2松开时才会触发led2反转。