文章目录
- 前言
- 一、外部中断
- 1.含义
- 2.类型
- 二、外部中断的配置
- 1.设置时钟
- 2.配置GPIO
- 3.将GPIO管脚与外部中断线连接
- 4.配置EXTI(包括触发方式等)
- 5.配置NVIC
- 6.写中断服务程序
- 三、串口的介绍
- 1.串口定义
- 2.其他必要知识
- 3.串口的配置
- 4.示例代码
前言
中断是 STM32中一个很重要的部分,但是有一些同学对他的理解不到位。如果想要我们编出来的程序效率高,就要摆脱查询式,用中断来提高他的运行效率。本文对STM32的外部中断和串口做一些简要的概述,帮助各位同学理解掌握。
提示:以下是本篇文章正文内容,下面案例可供参考
一、外部中断
1.含义
中断——当需要出现时,CPU暂时停止当前程序的执行,转而执行处理新情况的程序和执行过程。
我们所使用的STM32中断分为外部中断,定时器中断、串口中断。(以下将对外部中断进行解释)
注:外部中断框图:
EXTI控制器的主要特性:
- 每个中断都有独立的触发和屏蔽
- 每个中断都有专用的状态位
- 支持多达20个软件的中断/事件请求
- 检测脉冲宽度低于APB2时钟宽度的外部信号。
2.类型
STM32有84个中断,包括16个内核中断和68个可屏蔽中断,具有16级可编程的中断优先级。而我们常用的就是这68个可屏蔽中断。在MDK中,与NVIC相关的寄存器,MDK为其定义了如下的结构体:
typedef struct
{
__IO uint32_t ISER[8]; //中断使能寄存器组
uint32_t RESERVED0[24];
__IO uint32_t ICER[8]; //中断除能寄存器组
uint32_t RSERVED1[24];
__IO uint32_t ISPR[8]; //中断挂起控制寄存器组
uint32_t RESERVED2[24];
__IO uint32_t ICPR[8]; //中断解挂控制寄存器组
uint32_t RESERVED3[24];
__IO uint32_t IABR[8]; //中断激活标志位寄存器组
uint32_t RESERVED4[56];
__IO uint8_t IP[240]; //中断优先级控制寄存器组
uint32_t RESERVED5[644];
__O uint32_t STIR; //软件触发中断寄存器组
} NVIC_Type;
STM32的中断是在这些寄存器的控制下,有序执行的。只有了解这些中断寄存器,才能方便的使用STM32的中断。
下面简略介绍下第一个寄存器:
- ISER[8] 这是一个中断使能寄存器组。用 8 个 32 位寄存器来控制,每个位控制一个中断。
ISER[0]的 bit0~31 对应中断0~31;
ISER[1]的 bit0~32 对应中断 32~63;
ISER[2]的 bit0~3 对应中断 64~67;
这样总共 68 个中断就分别对应上了。你要使能某个中断,必须设置相应的 ISER 位为 1,使该中断使能(这里仅仅是使能,还要配合中断分组、屏蔽、IO 口映射等设置才算是一个完整的中断设置)。
具体每一位对应哪个中断,请参考 stm32f10x.h 里面的第 170 行处。
二、外部中断的配置
外部中断一般步骤为:
1)开启IO口时钟,初始化IO口为输入。调用函数:GPIO_Init();
2)开启IO口复用时钟。调用函数:RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
3)设置IO口与中断线的映射关系。调用函数:GPIO_EXTILineConfig();
4)初始化线上中断,设置触发条件等。调用函数:EXTI_Init();
5)配置中断分组(NVIC),并使能中断。调用函数:NVIC_Init();
6)编写中断服务函数。调用函数:EXTIx_IRQHandler();
7)清除中断标志位。调用函数:EXTI_ClearITPendingBit()。
以PA11管脚为例:
1.设置时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE);
2.配置GPIO
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
3.将GPIO管脚与外部中断线连接
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource11);
4.配置EXTI(包括触发方式等)
EXTI_InitStructure.EXTI_Line = EXTI_Line11;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure);
5.配置NVIC
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);
NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn; //PPP外部中断线
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
6.写中断服务程序
void EXTI15_10_IRQHandler( void )
{
if (EXTI_GetITStatus(EXTI_Line11) != RESET)
{
EXTI_ClearITPendingBit(EXTI_Line11); //清除标志
...
}
}
三、串口的介绍
1.串口定义
UART : Universal Asynchronous Receiver/Transmitter 通用异步收发器
USART : Universal Synchronous Asynchronous Receiver/Transmitter 通用同步/异步收发器
一种是常用也是最简单的串行数据传输协议。数据线只需要两根就可以实现全双工。
Tx : 发送数据线
Rx : 接收数据线
2.其他必要知识
- 波特率:在串行通讯中,数据是按位进行传送的,因此传送速率用每秒钟传送格式位的数目来表示,称为波特率。波特率决定了串口传输的速度,1波特=1bps(位/秒)。波特率为9600的话就是1s传输9600位的数据。
- 串口的传输与网络等其他的传输有着相似之处,比如常用的wifi,区别在与这些网络的单位是k,只有串口是按位来计数的。
- 单工,半双工,全双工。
- 单工:只能一个方向传输。
- 半双工:可以两个方向传输,但需要分时复用。
- 全双工:两个方向传输。
3.串口的配置
1.串口时钟使能,GPIO时钟使能:
RCC_APB2PeriphClockCmd();
2.串口复位:
USART_ Delnit();
这一步不是必须的
3.GPIO端口模式设置:
GPIO lnit();
模式设置为:
GPIO_Mode_AF_PP
4.串口参数初始化:
USART lnit();
5.开启中断并且初始化NVIC (如果需要开启中断才需要这个步骤)
NVIC_Init();
USART_ITConfig(0);
6.使能串口:
USART_ Cmd();
7.编写中断处理函数:
USARTX_IRQHandler();
8.串口数据收发:
void USART SendData();//发送数据到串口,DR
uint16 tUSART_ ReceiveData(;//接受数据,从DR读取接受到的数据
9.串口传输状态获取:
FlagStatus USART_GetFlagStatus(USART_TypeDef*USARTx,uint16_t USARTL_FLAG);
void USART_ClearlTPendingBit(USART_TypeDef*USARTx,uint16_t USART_IT;
4.示例代码
void USB_Init(void)
{
GPIO_InitTypeDef GPIO_InitStrue; //声明GPIO结构体
USART_InitTypeDef USART_InitStrue; //声明串口结构体
NVIC_InitTypeDef NVIC_InitStrue; //声明中断结构体
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); //GPIO时钟的使能
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE); //串口1的时钟使能
GPIO_InitStrue.GPIO_Mode = GPIO_Mode_AF_PP; //设置为复用推挽
GPIO_InitStrue.GPIO_Pin = GPIO_Pin_9; //9号IO口
GPIO_InitStrue.GPIO_Speed = GPIO_Speed_10MHz; //输出速率
GPIO_Init(GPIOA,&GPIO_InitStrue); //TX9的配置状态
GPIO_InitStrue.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空输入
GPIO_InitStrue.GPIO_Pin = GPIO_Pin_10; //10号IO
GPIO_InitStrue.GPIO_Speed = GPIO_Speed_10MHz; //输出速率
GPIO_Init(GPIOA,&GPIO_InitStrue); //RX10的配置
USART_InitStrue.USART_BaudRate = 115200; //波特率设置为115200
USART_InitStrue.USART_HardwareFlowControl = USART_HardwareFlowControl_None; // 无硬件数据流控制
USART_InitStrue.USART_Mode = USART_Mode_Tx | USART_Mode_Rx; //收发模式都使能
USART_InitStrue.USART_Parity = USART_Parity_No; //无奇偶校验位
USART_InitStrue.USART_StopBits = USART_StopBits_1; //一个停止位
USART_InitStrue.USART_WordLength = USART_WordLength_8b; //字长为8
USART_Init(USART1,&USART_InitStrue); //调用其Init函数
USART_Cmd(USART1,ENABLE ); //中断使能,确定端口,
USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);
//中断模式。这里是接受中断。
NVIC_InitStrue.NVIC_IRQChannel = USART1_IRQn; //指出通道,这里是串口一的通道
NVIC_InitStrue.NVIC_IRQChannelCmd = ENABLE; //开启中断通道
NVIC_InitStrue.NVIC_IRQChannelPreemptionPriority = 1; //设置抢占优先级,注意选择的模式,即抢占优先级最大位
NVIC_InitStrue.NVIC_IRQChannelSubPriority = 1; //子优先级
NVIC_Init(&NVIC_InitStrue); //调用NVIC_INIT函数,将我们定义的结构体地址作为入口参数
}
void USART1_IRQHandler(void) //只能使用这个函数名:
{
u8 data; //定义变量存储读取到的数据
if(USART_GetFlagStatus(USART1,USART_IT_RXNE)) //判断中断是不是之前设置的中断,如果是,值为1
{
data = USART_ReceiveData(USART1); //读取串口函数,并将其赋值
USART_SendData(USART1,data); //将读取到的数据发送出去
}
//主函数
int main(void)
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //在中断函数前就要调用
//2号分组,两位抢占优先级,两位响应优先级
USB_Init();
while(1);
}