一、外部中断的类型
对于互联型产品,外部中断/事件控制器由20个产生事件/中断请求的边沿检测器组成,对于其它产品,则有19个能产生事件/中断请求的边沿检测器。每个输入线可以独立地配置输入类型(脉冲或挂起)和对应的触发事件(上升沿或下降沿或者双边沿都触发)。每个输入线都可以独立地被屏蔽。挂起寄存器保持着状态线的中断请求。
STM32F103 的19 个外部中断为:
线 0~15:对应外部 IO 口的输入中断。
线 16:连接到 PVD 输出。
线 17:连接到 RTC 闹钟事件。
线 18:连接到 USB 唤醒事件。
二、外部中断配置
从上面可以看出,STM32 供 IO 口使用的中断线只有 16 个,但是 STM32 的 IO 口却远远不止 16 个,那么 STM32 是怎么把 16 个中断线和 IO 口一一对应起来的呢?GPIO 的管教GPIOx.0~GPIOx.15(x=A,B,C,D,E,F,G)分别对应中断线 0~15。这样每个中断线对应了最多 7 个 IO 口,以线 0 为例:它对应了 GPIOA.0、GPIOB.0、GPIOC.0、GPIOD.0、GPIOE.0、GPIOF.0、GPIOG.0。而中断线每次只能连接到 1 个 IO 口上,这样就需要通过配置来决定对应的中断线配置到哪个 GPIO 上了。
使用 IO 口外部中断的一般步骤:
1)初始化 IO 口为输入。
2)开启 AFIO 时钟
3)设置 IO 口与中断线的映射关系。
4)初始化线上中断,设置触发条件等。
5)配置中断分组(NVIC),并使能中断。
6)编写中断服务函数。
1.在库函数中,配置 GPIO 与中断线的映射关系的函数GPIO_EXTILineConfig()
来实现的:
例:将中断线 2 与 GPIOE 映射起来,那么很显然是 GPIOE.2 与 EXTI2 中断线连接了。
GPIO_EXTILineConfig(GPIO_PortSourceGPIOE,GPIO_PinSource2);
2.中断线上中断的初始化
中断线上中断的初始化是通过函数 EXTI_Init()
实现的。EXTI_Init()
函数的定义是:
void EXTI_Init(EXTI_InitTypeDef* EXTI_InitStruct);
例:设置中断线 4 上的中断为下降沿触发
EXTI_InitTypeDef EXTI_InitStructure;
EXTI_InitStructure.EXTI_Line=EXTI_Line4;//中断线的标号
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;//中断模式
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;//触发方式
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure); //根据 EXTI_InitStruct 中指定的
//参数初始化外设 EXTI 寄存器
3.设置 NVIC 中断优先级
例:设置中断线 2 的中断优先级
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = EXTI2_IRQn; //使能按键外部中断通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02; //抢占优先级 2,
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x02; //子优先级 2
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能外部中断通道
NVIC_Init(&NVIC_InitStructure); //中断优先级分组初始化
4.中断服务函数
void EXTI3_IRQHandler(void)
{
if(EXTI_GetITStatus(EXTI_Line3)!=RESET)//判断某个线上的中断是否发生
{
中断逻辑…
EXTI_ClearITPendingBit(EXTI_Line3); //清除 LINE 上的中断标志位
} }
三、串口介绍
关于串口需要了解的几个知识点:
1.波特率:在串行通讯中,数据是按位进行传送的,因此传送速率用每秒钟传送格式位的数目来表示,称为波特率。
波特率决定了串口传输的速度,1波特=1bps(位/秒)。波特率为9600的话就是1s传输9600位的数据。
串口的传输与网络等其他的传输有着相似之处,比如常用的wifi,区别在与这些网络的单位是k,只有串口是按位来计数的。
2.工作方式:
单工,半双工,全双工:
单工:只能一个方向传输
半双工:可以两个方向传输,但需要分时复用
全双工:两个方向传输
3.串口设置的一般步骤可以总结为如下几个步骤:
- 串口时钟使能,GPIO 时钟使能
- 串口复位
- GPIO 端口模式设置
- 串口参数初始化
- 开启中断并且初始化 NVIC(如果需要开启中断才需要这个步骤)
- 使能串口
- 编写中断处理函数
4.UART异步通信
异步通信:在异步通讯中不使用时钟信号进行数据同步,它们直接在数据信号中穿插一些同步用的信号位,或者把主体数据进行打包,以数据帧的格式传输数据,某些通讯中还需要双方约定数据的传输速率,以便更好地同步。
数据传送以字符为单位,字符与字符间的传送完全异步,位与位之间的传送基本上是同步。使用UART时不需要对外提供时钟输出,平常用到的串口通信就是UART。
5.USART同步通信
同步通信: 在同步通讯中,收发设备双方会使用一根信号线表示时钟信号,在时钟信号的驱动下双方进行协调,同步数据,通讯中通常双方会统一规定在时钟信号的上升沿或下降沿对数据线进行采样。
数据传送以数据块(一组字符)为单位,字符与字符之间、字符内部的位与位之间都同步,在使用时需要用到时钟输出。USART通常应用在"printf()"函数中,在程序调试过程中会经常使用。
四、串口配置方法
首先对串口和对应引脚进行初始化设置。
之后对NVIC进行配置。
最后进行使能。
过程中可以根据需要添加中断函数。
串口配置一般步骤
1.串口时钟使能,GPIO时钟使能:RCC_APB2PeriphClockCmd();
2.串口复位:USART_DeInit();
(这一步不是必须的)
3.GPIO端口模式设置:GPIO_Init();
模式设置为推挽复用以及浮空输入或者上拉输入(具体可参照中文参考手册)
4.串口参数初始化:USART_Init();
开启中断并且初始化NVIC(如果需要开启中断才需要这个) NVIC_Init();USART_ITConfig();
5.使能串口:USART_Cmd();
6.编写中断处理函数:USARTx_IRQHandler();
7.串口数据收发:void USART_SendData();//发送数据到串口,DR uint16_t USART_ReceiveData();//接受数据,从DR读取接受到的数据
8.串口传输状态获取:
FlagStatus USART_GetFlagStatus(USART_TypeDef* USARTx, uint16_t USART_FLAG);
void USART_ClearITPendingBit(USART_TypeDef* USARTx, uint16_t USART_IT);
总结:
//结构体指针定义
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
//引脚初始化
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);
//串口初始化
USART_InitStructure.USART_BaudRate = bound;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
USART_Init(USART2, &USART_InitStructure);
//NVIC初始化
NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
//使能
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);//引脚时钟使能
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2,ENABLE);//串口时钟使能
USART_Cmd(USART2, ENABLE); //串口使能