目录
库函数的使用
1. 库函数是分为多个文件的,每个文件都有独立的功能
Stm32f10x_rcc.c 就是和时钟相关的文件
Stm32f10x_gpio.c 就是和 GPIO 相关的文件
.c 文件存放函数的原型
.h 文件存放函数的声明或者宏定义等
2. 函数的名字是什么意思?功能是什么?
方法一:查看固件库中文翻译手册
方法二:在工程中,查看函数的英文注释

3. 函数命名是有规律的
中文固件库翻译手册,1.2 查看规则
RCC_APB2PeriphClockCmd
通过定义结构体,给结构体赋值,确定要设置的工作模式
GPIO_Init 将上面的结构体根据结构体内容,写入到寄存器中
GPIO_ResetBits 对应引脚输出低电平
GPIO_SetBits 对应引脚输出高电平
GPIO_ReadInputDataBit 读取对应引脚的电平
4. 结构体参数,填哪些值
方法一:翻看中文固件库翻译手册
方法二:查看英文注释,一般都在对应的 stm32f10x_PPP.h 文件中。找到结构体原型,看后面的@ref,能跳转就跳转,不能跳转就搜索
中断
中断的概念
中断:打断原有的执行过程,去执行临时的任务,执行完临时任务之后,再返回到原有的位置继续执行。
中断的执行过程
1、外设发出中断请求
2、处理器暂停当前执行的主程序,保护现场,将当前位置的 PC 地址压栈;
3、程序跳转到中断服务程序,执行中断服务程序;
4、恢复现场,将栈顶的值回送给 PC;
5、跳转到被中断的位置开始执行下一个指令
中断的部分专业术语
中断源:引起中断的原因,或者能够发出中断请求信号的来源统称为中断源
中断优先级:谁比较重要。Stm32 中数字越小,优先级越高。
中断响应:中断事件发生,Cortex-M3 内核准备执行该事件,即为中断响应。
中断嵌套:可嵌套的内核 -- 中断可以被其他中断打断。(Cortex-M3)不可嵌套的内核 -- 不可以打断。
中断挂起:中断事件发生了,但是 Cortex-M 内核还没准备去执行或者中断正在执行,有更高优先级的中断到来,就把原来的中断挂起。可嵌套内核中,高和低两个优先级的中断,低优先级正在执行中断服务程序,此时高优先级的中断也触发了,把低优先级的中断先挂起,等高优先级的执行完,再继续执行低优先级的。不可嵌套内核中,正在响应某个中断,有其他高优先级的中断到来,因为中断不能嵌套,所以新到来的中断,就要暂时被挂起,等原有中断执行完之后,再执行新到来的中断。
中断服务函数: 中断发生后,要执行的程序。
中断通道:可以哪些响应的中断和该中断之间就有固定的中断通道
STM32中的中断分类
嵌套向量中断控制器(NVIC):作用是管理所有中断。
异常:内核内部的中断称为中断。
中断和异常向量表主要分为两部分:
底色比较灰的部分:内核内部的中断,也称为异常
浅色部分:内核外部的中断。
下面还有很多,内核外部的中断
总结:STM32 的中断分为内核的异常和内核外部的外设中断
STM32中的中断优先级和优先级分组
中断优先级寄存器如下图
内核提供 8 位用来标记各个通道的优先级(0-255)
STM32 在对内核封装之后,STM32 只用了 4 位 (0--15) -- 高四位
(1)NVIC_IPR[X]表示有好几个这样的寄存器,每 8 位管理 1 个通道的优先级
(2)ST 公司只有了 8 位中的高 4 位,低 4 位没有意义。
优先级分组寄存器如下图
利用系统控制块(SCB)中一个名为优先级分组的配置寄存器(属于 SCB 中的应用中断和复位控制寄存器)将每个具有可编程优先级的优先级配置寄存器可被分为两部分。上半部分(左边的位)为分组(抢占)优先级﹐而下半部分(右边的位)则为子(次级)优先级。事实上 stm32 只用了 4 位优先级,分组时从优先级高位向低位方向分组。
STM32 的中,分组寄存器实际使用情况
0b:二进制
X:表示抢占优先级位
Y:表示次级(子)优先级位
[7:4]:表示优先级寄存器中 8 位优先级的 bit4 到 bit7
Group priority bits:抢占优先级的位
Subpriority bit:子优先级(次级优先级)的位
Group priorities:表示抢占优先级可填的选择有几个 16 表示 16 种选择,0--15
Sub priorities:跟上面一样
特性:抢占优先级+次级优先级(子优先级)
抢占优先级: 低优先级的中断正在执行,高优先级的可以打断低优先级运行
次级(子)优先级: 抢占优先级相同的多个中断同时请求,次级优先级越高先执行。
正是抢占优先级的存在,才会发生中断嵌套。数字越小,优先级越高
至于选择哪个分组,是自己决定使用哪个分组,分组确定之后,抢占和次级占的位数就确定了。
选择分组 5(2 位抢占,2 位次级) –SCB-AIRCR[10:8]填入 101
选择分组 6(1 位抢占,3 位次级) –SCB-AIRCR[10:8]填入 110
中断的使能和失能
外部中断或部分内部中断(异常),如果需要用中断,需要先在 NVIC 中使能中断。
中断使能寄存器如下图
中断失能寄存器如下图
中断使用的过程和相关函数接口
内核提供的操作 NVIC 的函数说明:这一部分函数全部在 core_cm3.h 中
Core_cm3.h -- 内核提供的(ARM 公司)
NVIC_SetPriorityGrouping() -- 设置中断优先级分组,整个工程只需要一次分组就可以,一般放在主函数的最开头
NVIC_SetPriority(TIM6_IRQn, 0); -- 配置中断通道的优先级
NVIC_EnableIRQ(TIM6_IRQn); -- 使能中断通道
Misc.h -- ST 公司提供的(ST 公司)
NVIC_PriorityGroupConfig() -- 设置中断优先级分组
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn; //中断通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //抢占优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; //次级优先级
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //中断使能
NVIC_Init(&NVIC_InitStructure);
外部中断--EXTI
外部中断内部框图以及外部中断线路映像
外部中断线路映像
通用 I/O 端口以下图的方式连接到 16 个外部中断/事件线上:
EXTI硬件中断选择过程
配置 20 个线路做为中断源:
1、配置 20 个中断线的屏蔽位(EXTI_IMR) ,中断屏蔽寄存器(EXTI_IMR)
2、配置所选中断线的触发选择位(EXTI_RTSR上升沿 和 EXTI_FTSR下降沿)
3、配置对应到外部中断控制器(EXTI)的 NVIC 中断通道的使能和屏蔽位,使得 20 个中断线中的请求可以被正确地响应。
外部中断(EXTI)的配置过程
EXTI 的功能作用:
(1) 检测对应 IO 口是否产生上升沿、下降沿、双边沿
(2) 现在编程的例子中,通过按键产生对应的边沿
(3) 超声波测距模块也可以用 EXTI 实现
1、配置要中断检测的 IO 引脚模式为:浮空输入模式,
上升沿和下降沿是通过单片机外部的电路产生的,需要借助 GPIO 口输入进来,那么就需要配置 GPIO模式选择哪个 :
(1)看官方例程
(2)具体查阅参考手册 8.1.11 表格,同时结合自己的硬件电路来决定
我们的按键电路有高电平和低电平,所以选择浮空输入。如果按键电路只有低电平,选择上拉输入,如果只有高电平,选择下拉输入。
2、把要中断检测的 IO 引脚映射到对应的 EXTI 中断线上,通过 AFIO 来设置(注意开 AFIO 时钟)。
参考手册 9.2.5 描述,
开时钟:配置参考手册 6.3.7
配置 EXTI 线:配置参考手册 8.4.3—8.4.6
3、配置对应的 EXTI 中断线触发的边沿
配置参考手册 9.3.3 和 9.3.4
4、配置对应的 EXTI 中断线,使能屏蔽位
配置参考手册 9.3.1
5、通过 NVIC 配置 EXTI 中断的优先级、使能 NVIC 的 EXTI 中断
6、编写中断服务函数:注意函数名、注意清中断等等。
注:当把 GPIO 用作 EXTI 外部中断或使用重定义功能的时候,必须开启 AFIO 时钟,而在使用默认复用功能的时候,就不必开启 AFIO 时钟了。
总结
主要目的:学习中断(NVIC)--管理中断
关心的问题:
中断优先级:数字越小,优先级越高
中断的分组:SCB_AIRCR:位[10:8],填入000--111
中断优先级分类:--优先级分组决定了抢占和次级可配置的范围
抢占优先级:高优先级到来的时候,可以打断原有正在执行的中断
次级(子)优先级:当抢占优先级相同的两个中断,同时到来,次级优先级高的先响应
中断优先级的设置:通过操作IPRx寄存器,来设置中断的优先级,ARM公司预留的是8位,ST公司封装只用了高4位
中断的使能:通过操作NVIC_ISER寄存器,来设置中断的打开。
代码层面实现:
中断优先级分组:NVIC_SetPriorityGrouping(5);://中断优先级分组 core_cm3.h 1430行
中断优先级的配置:NVIC_SetPriority(EXTI0_IRQn,9); //core_cm3.h 1439行
9是如何来的:根据分组情况,2位抢占,2位次级。假如抢占是2,抢占两位填10,假如次级是1,次级两位填01,拼起来4位1001,填入IPR[X]寄存器中,所以传递9,该函数会自动左移4位。
中断的使能: NVIC_EnableIRQ(EXTIO_IRQn); //core_cm3.h 1432行
NVIC管理:..内核的异常+内核外部外设的中断
EXTI去练习中断如何使用
EXTI:检测20个通道,是否产生,上升沿下降沿或者双边沿
代码
exti.c
#include "exti.h"
#include "main.h"
#include "led.h"
#include "relay.h"
/*EXTI初始化
1、GPIO EXTI的上升沿和下降沿,需要借助GPIO输入到单片机内部
KEY PA0 PC4 PC5 PC6
*/
void EXIT_Confing()
{
#if (USB_STD_LIB==0)
//1.开GPIO时钟
RCC->APB2ENR |= (0x05 << 2);
//2.设置KEY1工作模式
GPIOA->CRL &= ~(0xF << 0);//先清0
GPIOA->CRL |= (0x4 << 0);//在置1
//3.设置KEY2 KEY3 KEY4工作模式
GPIOC->CRL &= ~(0xFFF << 16);//先清0
GPIOC->CRL |= (0x444<< 16);//在置1
//4.开AFIO的时钟
RCC->APB2ENR |= (0x01 << 0);
//5.将PA0映射到EXTI0上 PC345映射到EXTI1
AFIO->EXTICR[0] &= ~(0xF << 0);
AFIO->EXTICR[0] |= (0x0 << 0);
AFIO->EXTICR[1] &= ~(0xFFFF << 0);
AFIO->EXTICR[1] |= (0x2222 << 0);
/*uAFIO->EXTICR[1] &= ~(0xF << 4);
AFIO->EXTICR[1] |= (0x2 << 4);
AFIO->EXTICR[1] &= ~(0xF << 8);
AFIO->EXTICR[1] |= (0x2 << 8);*/
//6.设置触发方式
EXTI->FTSR |= (0x01 << 0);//KEY1下降
EXTI->RTSR |= (0x07 << 4);//345上升
/*EXTI->RTSR |= (0x01 << 5);
EXTI->RTSR |= (0x01 << 6);*/
//7.允许EXTI产生中断
EXTI->IMR |= (0x01 << 0);//中断屏蔽寄存器
EXTI->IMR |= (0x01 << 4);
EXTI->IMR |= (0x01 << 5);
EXTI->IMR |= (0x01 << 6);
//8.中断层面配置:使能EXTI0、EXTI4、EXTI5、EXTI6中断
NVIC_SetPriority(EXTI0_IRQn, 9);//抢占2次级1
NVIC_SetPriority(EXTI4_IRQn, 9);//抢占2次级2
NVIC_SetPriority(EXTI9_5_IRQn, 12);//抢占3次级0
//9.允许NVIC层面的中断
NVIC_EnableIRQ(EXTI0_IRQn);
NVIC_EnableIRQ(EXTI4_IRQn);
NVIC_EnableIRQ(EXTI9_5_IRQn);
#elif (USB_STD_LIB==1)
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOC, ENABLE);//开时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure = {0};//灯的模式
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;//配置引脚
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 |GPIO_Pin_5 |GPIO_Pin_6;
GPIO_Init(GPIOC, &GPIO_InitStructure);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource0);//映射
EXTI_InitTypeDef EXTI_InitStructure = {0};
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;//下降沿
EXTI_InitStructure.EXTI_Line = EXTI_Line0;
EXTI_Init(&EXTI_InitStructure);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOC, GPIO_PinSource4);//映射
EXTI_InitStructure.EXTI_Line = EXTI_Line4;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;//上升沿
EXTI_Init(&EXTI_InitStructure);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOC, GPIO_PinSource5);//映射
EXTI_InitStructure.EXTI_Line = EXTI_Line5;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;//上升沿
EXTI_Init(&EXTI_InitStructure);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOC, GPIO_PinSource6);//映射
EXTI_InitStructure.EXTI_Line = EXTI_Line6;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;//上升沿
EXTI_Init(&EXTI_InitStructure);
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;//抢占优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;//次级优先级
NVIC_Init(&NVIC_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel = EXTI4_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;//抢占优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;//次级优先级
NVIC_Init(&NVIC_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel = EXTI9_5_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3;//抢占优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;//次级优先级
NVIC_Init(&NVIC_InitStructure);
#endif
}
void EXTI0_IRQHandler()//当中断触发条件满足时,并且NVIC允许中断,内核会自动加载该函数
{
#if (USB_STD_LIB==0)
//1.检测中断是否触发
if((EXTI->PR & (0x01 << 0)) != 0)//挂起寄存器
{
//2.执行中断服务函数内容
LED1_ON(); LED2_ON(); LED3_ON(); LED4_ON();
//3.清楚中断表示位置
EXTI->PR |= (0x01 << 0);
}
#elif (USB_STD_LIB==1)
//1.检测中断是否触发
if((EXTI_GetITStatus(EXTI_Line0) != RESET))
{
//2.执行中断服务函数内容
LED1_ON(); LED2_ON(); LED3_ON(); LED4_ON();
//3.清楚中断表示位置
EXTI_ClearITPendingBit(EXTI_Line0);
}
#endif
}
void EXTI4_IRQHandler()
{
#if (USB_STD_LIB==0)
//1.检测中断是否触发
if((EXTI->PR & (0x01 << 4)) != 0)
{
//2.执行中断服务函数内容
LED1_OFF(); LED2_OFF(); LED3_OFF(); LED4_OFF();
//3.清楚中断表示位置
EXTI->PR |= (0x01 << 4);
}
#elif (USB_STD_LIB==1)
//1.检测中断是否触发
if((EXTI_GetITStatus(EXTI_Line4) != RESET))
{
//2.执行中断服务函数内容
LED1_OFF(); LED2_OFF(); LED3_OFF(); LED4_OFF();
//3.清楚中断表示位置
EXTI_ClearITPendingBit(EXTI_Line4);
}
#endif
}
void EXTI9_5_IRQHandler()
{
#if (USB_STD_LIB==0)
//1.检测中断是否触发
if((EXTI->PR & (0x01 << 5))!= 0)
{
//2.执行中断服务函数内容
Relay_ON();
//3.清楚中断表示位置
EXTI->PR |= (0x01 << 5);
}
else if((EXTI->PR & (0x01 << 6)) != 0)
{
Relay_OFF();
//3.清楚中断表示位置
EXTI->PR |= (0x01 << 6);
}
#elif (USB_STD_LIB==1)
//1.检测中断是否触发
if((EXTI_GetITStatus(EXTI_Line5) != RESET))
{
Relay_ON();
EXTI_ClearITPendingBit(EXTI_Line5);
}
else if((EXTI_GetITStatus(EXTI_Line6) != RESET))
{
Relay_OFF();
EXTI_ClearITPendingBit(EXTI_Line6);
}
#endif
}
exti.h
#ifndef __EXTI_H__
#define __EXTI_H__
#include "stm32f10x.h"
void EXIT_Confing();
#endif