见博客:stm32f103c8t6新建固件库模板(可自取)
实验程序已经发布到百度网盘,本文末有链接可以自取
中断查看这篇博客STM32中断应用概括
一、利用固件库模板点灯(附模板及案例程序
EXTI-外部 中断/事件控制器,管理控制器20个中断/事件线。每一个中断/事件线都有对应的边沿触发器,用来判断信号的检测是上升沿还是下降沿。EXTI可以实现对每一个中断/事件进行单独的配置,可以配置为中断或者事件,以及触发事件的属性。
图中的斜线和20,表示控制器内部的信号线路有20个,这个与EXTI总共有20个,刚好EXTI有20个中断/事件线吻合的。
EXTI可以分为两部分,一部分是产生中断,另一部分是产生事件,
红色线路
是产生中断的线路,最终信号流入到NVIC控制器内,
编号1 是输入线,EXTI有19个中断/事件输入线,可以通过寄存器设置为任何一个GPIO,也可以是外设的事件,一般存在电平变化的信号。
编号2 是一个边缘检测电路,可以根据上升沿触发选择寄存器(EXTI_RTSR)和下降沿触发的选择寄存器(EXTI_FTSR)对应的位置来控制信号触发。边沿检测电路以输入端为信号输入端,如果检测到边沿跳变就有输出有效信号给编号3电路,否则就输出无效信号0。而EXTI_RTSR和EXTI_FTSR可以控制器需要检测哪些类型的电平跳变过程,可以是只有上升沿触发或者下降沿触发,或者上升沿和下降沿都触发。
编号3 电路是一个或门电路,一个输入来自编号2电路,另外一个输入来自软件中断事件寄存器(EXTI_SWIER),EXTI_SWIER允许我们通过程序控制就可以开启中断/事件线,或门是有1为1,所以两个输入随便一个输入是有效信号1就可以输出1给编号4和编号6电路。
编号4 电路是一个与门电路,一个输入是编号3电路,另外一个输入来自中断屏蔽寄存器(EXTI_IMR),与门要求两个输入都是1才输出1,导致的结果就是如果EXTI_IMR设置为0时,那不管任意一个是1还是0,编号4输出的信号都是0;如果EXTI_IMR设置为1时,最终编号4输出的信号由编号3的电路输出信号决定,这样我们可以通过控制EXTI_IMR来实现是否产生中断的目的。编号4电路输出的信号会被保存到挂起寄存器(EXTI_PR)内,如果确定编号4电路输出是1,就会把EXTI_PR对应位置1.
编号 5 是将 EXTI_PR 寄存器内容输出到 NVIC 内,从而实现系统中断事件控制。
接下来我们来看看绿色虚线
指示的电路流程。它是一个产生事件的线路,最终输出一个脉冲信号。产生事件线路是在编号 3 电路之后与中断线路有所不同,之前电路都是共用的。
编号 6 电路是一个与门,它一个输入来自编号 3 电路,另外一个输入来自事件屏蔽寄存器 (EXTI_EMR) 。如果EXTI_EMR 设置为 0 时,那不管编号 3 电路的输出信号是 1 还是 0,最终编号 6 电路输出的信号都为 0;如果 EXTI_EMR 设置为 1 时,最终编号 6 电路输出的信号才由编号 3 电路的输出信号决定,这样我们可以简单的控制 EXTI_EMR 来实现是否产生事件的目的。
编号 7 是一个脉冲发生器电路,当它的输入端,即编号 6 电路的输出端,是一个有效信号 1 时就
会产生一个脉冲;如果输入端是无效信号就不会输出脉冲。
编号 8 是一个脉冲信号,就是产生事件的线路最终的产物,这个脉冲信号可以给其他外设电路使用,比如定时器 TIM、模拟数字转换器 ADC 等等,这样的脉冲信号一般用来触发 TIM 或者 ADC开始转换。产生中断线路目的是把输入信号输入到 NVIC,进一步会运行中断服务函数,实现功能,这样是软件级的。而产生事件线路目的就是传输一个脉冲信号给其他外设使用,并且是电路级别的信号传输,属于硬件级的。
EXTI0 至 EXTI15 用于 GPIO,通过编程控制可以实现任意一个 GPIO 作为 EXTI 的输入源。由表EXTI 中断 _ 事件线 可知, EXTI0 可以通过 AFIO 的外部中断配置寄存器1(AFIO_EXTICR1) 的EXTI0[3:0] 位选择配置为 PA0、 PB0、 PC0、 PD0、 PE0、 PF0、 PG0、 PH0 或者 PI0,见图 EXTI0 输入源选择 。其他 EXTI 线 (EXTI 中断/事件线) 使用配置都是类似的。
标准库函数对每个外设都建立了一个初始化结构体,比如 EXTI_InitTypeDef,结构体成员用于设置外设工作参数,并由外设初始化配置函数,比如 EXTI_Init() 调用,这些设定参数将会设置外设相应的寄存器,达到配置外设工作环境的目的。
初始化结构体和初始化库函数配合使用是标准库精髓所在,理解了初始化结构体每个成员意义基本上就可以对该外设运用自如了。初始化结构体定义在 stm32f4xx_exti.h 文件中,初始化库函数定义在 stm32f4xx_exti.c 文件中,编程时我们可以结合这两个文件内注释使用。
typedef struct {
uint32_t EXTI_Line; // 中断/事件线
EXTIMode_TypeDef EXTI_Mode; // EXTI 模式
EXTITrigger_TypeDef EXTI_Trigger; // 触发类型
FunctionalState EXTI_LineCmd; // EXTI 使能
} EXTI_InitTypeDef;
EXTI_InitTypeDef:
中断在嵌入式应用中占有非常重要的地位,几乎每个控制器都有中断功能。中断对保证紧急事件得到第一时间处理是非常重要的。
我们设计使用外接的按键来作为触发源,使得控制器产生中断,并在中断服务函数中实现控制PC13亮灭的任务
轻触按键在按下时会使得引脚接通,通过电路设计可以使得按下时产生电平变化。因为我们使用的板子是stm32F103C8T6板子,上面没有按键,所以要外设一个按键。
初始化LED的GPIO
初始化连接到EXTI的GPIO
初始化EXTI用于产生的中断/事件
初始化NVIC,用于处理中断
编写中断服务函数
main函数
一、利用固件库模板点灯(附模板及案例程序)
stm32f103c8t6自带一个led灯,使用PC13引脚就行了,
切记尽量避免使用PB3、PB4,具体看stm32f103c8t6使用PB3和PB4做普通GPIO使用时发现异常
#include "led.h" //绑定led.h
void LED_GPIO_Config(void) {
GPIO_InitTypeDef GPIO_InitStruct; //初始化参数结构体指针,结构体类型为 GPIO_InitTypeDef。
//开启RCC时钟
RCC_APB2PeriphClockCmd(LED_G_GPIO_CLK, ENABLE);
//配置初始化,推挽输出方式和LED_G_GPIO_PIN管脚、赫兹
GPIO_InitStruct.GPIO_Pin = LED_G_GPIO_PIN;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
//GPIO口初始化
GPIO_Init(LED_G_GPIO_PORT, &GPIO_InitStruct);
}
#ifndef __LED_H_
#define __LED_H_
#include "stm32f10x.h"
#include "sys.h"
#define LED_G_GPIO_PIN GPIO_Pin_13
#define LED_G_GPIO_PORT GPIOC
#define LED_G_GPIO_CLK RCC_APB2Periph_GPIOC
//使用位带操作来实现操作某个IO口的 1个位,由sys.h实现
#define LED PCout(13)
void LED_GPIO_Config(void); //函数定义
#endif
#include "key.h"
static void EXTI_NVIC_Config(void)
{
NVIC_InitTypeDef NVIC_InitStruct;
/*设置NVIC优先级组 1*/
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
/*配置中断源*/
NVIC_InitStruct.NVIC_IRQChannel = KEY_INT_EXTI_IRQ;
/*主优先级*/
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1;
/*字优先级*/
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1;
/*使能NVIC*/
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStruct);
}
void EXTI_Key_Config(void)
{
EXTI_InitTypeDef EXTI_InitStruct;
GPIO_InitTypeDef GPIO_InitStruct;
//开启按键的GPIO时钟
RCC_APB2PeriphClockCmd(KEY_INT_GPIO_CLK, ENABLE);
//配置NVIC中断
EXTI_NVIC_Config();
//初始化GPIO
GPIO_InitStruct.GPIO_Pin = KEY_INT_GPIO_PIN;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(KEY_INT_GPIO_PORT, &GPIO_InitStruct);
//初始化EXTI
/*选择按键用的GPIO口*/
GPIO_EXTILineConfig(KEY_INT_EXTI_PortSource, KEY_INT_EXTI_PinSource);
/*设置中断线*/
EXTI_InitStruct.EXTI_Line = KEY_INT_EXIT_Line;
/*设置中断模式*/
EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt;
/*设置上升沿中断*/
EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Rising;
/*使能中断*/
EXTI_InitStruct.EXTI_LineCmd = ENABLE;
/*来自stm32f10x_exti*/
EXTI_Init(&EXTI_InitStruct);
}
#ifndef KEY_H_
#define KEY_H_
#include "stm32f10x.h"
#define KEY_INT_GPIO_PIN GPIO_Pin_0
#define KEY_INT_GPIO_PORT GPIOA
#define KEY_INT_GPIO_CLK RCC_APB2Periph_GPIOA|RCC_APB2Periph_AFIO
//设置中断源
#define KEY_INT_EXTI_IRQ EXTI0_IRQn
#define KEY_INT_EXIT_Line EXTI_Line0
#define KEY_INT_EXTI_PortSource GPIO_PortSourceGPIOA
#define KEY_INT_EXTI_PinSource GPIO_PinSource0
void EXTI_Key_Config(void);
#endif /*__BSP_EXTI_H_*/
如果这里我们配置两个按键并且他们的的中断软件优先级一样,如果出现了两个按键同时按下的情况,那怎么办,到底该执行哪一个中断?当两个中断的软件优先级一样的时候,中断来临时,具体先执行哪个中断服务函数由硬件的中断编号决定,编号越小,优先级越高。
有关外设的硬件编号可查询《STM32F10X-中文参考手册》的中断和事件章节中的向量表,表中的位置编号即是每个外设的硬件中断优先级。当然,我们也可以把抢占优先级设置成一样,子优先级设置成不一样,这样就可以区别两个按键同时按下的情况,而不用硬件去对比硬件编号
中断在之前STM32中断应用概括已经讲过,不清楚的可以继续查看。
首先,使用 GPIO_InitTypeDef 和 EXTI_InitTypeDef 结构体定义两个用于 GPIO 和 EXTI 初始化配置的变量,关于这两个结构体前面都已经做了详细的讲解。使用 GPIO 之前必须开启 GPIO 端口的时钟;
用到 EXTI 必须开启 AFIO 时钟。调用NVIC_Configuration 函数完成对按键 优先级配置并使能中断通道。作为中断/事件输入线时需把 GPIO 配置为输入模式,具体为浮空输入,由外部电路完全决定引脚的状态。
GPIO_EXTILineConfig 函数用来指定中断/事件线的输入源,它实际是设定外部中断配置寄存器的AFIO_EXTICRx 值,该函数接收两个参数,第一个参数指定 GPIO 端口源,第二个参数为选择对应 GPIO 引脚源编号。我们的目的是产生中断,执行中断服务函数, EXTI 选择中断模式,按键使用上升沿触发方式,并使能 EXTI 线。
这里使用上升沿触发检测的是按键按下的状态
/*编写中断函数
前4个中断名为EXTI0(1、2、3、4)_IRQHandler
EXTI9_5_IRQHandler
EXTI15_10_IRQHandler
*/
void EXTI0_IRQHandler(void)
{
if(EXTI_GetITStatus(KEY_INT_EXIT_Line) != RESET)
{
Delay_ms(5);
if(EXTI_GetITStatus(KEY_INT_EXIT_Line) != RESET){
//采用异或,1-->0 ,0-->1
GPIOC->ODR^=GPIO_Pin_13;
}
}
//清除中断标志位
EXTI_ClearITPendingBit(KEY_INT_EXIT_Line);
}
当中断发生时,对应的中断服务函数就会被执行,我们可以在中断服务函数实现一些控制。
一般为确保中断确实发生,我们会在中断服务函数中调用中断标志位状态读取函数读取外设中断标志位并判断标志位状态。EXTI_GetITStatus 函数用来获取 EXTI 的中断标志位状态,如果EXTI 线有中断发生函数返回“SET”否则返回“RESET”。实际上, EXTI_GetITStatus 函数是通过读取 EXTI_PR 寄存器值来判断 EXTI 线状态的。按键的中断服务函数我们让 LED翻转其状态,执行任务后需要调用EXTI_ClearITPendingBit 函数清除 EXTI 线的中断标志位。
#include "delay.h"
#include "sys.h"
#include "usart.h"
#include "stm32f10x.h"
#include "key.h"
#include "led.h"
int main()
{
/********************************************************************************
* Delay_init(); //本实验使用的是SysTick时钟
* CPU_TS_TmrInit(); //已经使能宏,不需要初始化
* uart1_init(115200); //串口初始化为115200,需要在usart.h中使能
* uart3_init(115200); //串口初始化为115200
********************************************************************************/
/* LED 端口初始化 */
LED_GPIO_Config();
/* 初始化 EXTI 中断,按下按键会触发中断,
* 触发中断会进入 stm32f4xx_it.c 文件中的函数
* KEY_IRQHandler,处理中断,按下点亮。
*/
EXTI_Key_Config();
/* 等待中断,由于使用中断方式, CPU 不用轮询按键 */
while(1)
{
}
}
主函数非常简单,只有两个任务函数。 LED_GPIO_Config 函数定义在 bsp_led.c 文件内,完成 RGB彩灯的 GPIO 初始化配置。 EXTI_Key_Config 函数完成两个按键的 GPIO 和 EXTI 配置 。
解决方法双击FWLib找到stm32f10x_exti.c文件并导入。
先下载试试我的代码,改编的时候注意下面这些宏定义还有stm32f10x_it文件中的中断名,本文代码
中已经提示。
编译成功
本文选择的是ST_Link烧录工具
如果没有ID号看博客:ST-Link V2烧录问题(已解决)
会有按键抖动的情况,但是基本效果会有。实验图片太模糊,就没有上传,也不难大家可以自行实现
软件方法去抖,即检测出键闭合后执行一个延时程序,5ms~10ms的延时
硬件方法去抖,添加上了一个陶瓷电容(100nf),和下拉电阻并联,利用电容的快速充放电特性来过滤掉按钮的抖动,由于电容两端电压不能突变,使得按键两端的电压平缓变化,直至电容充放电到达一定电压阈值时,单片机才读取到电平变化。
链接:https://pan.baidu.com/s/1yt-l0xJCnMZN0PT2hu5Lmw 提取码:0000