NVIC中断优先级管理+串口通信基本原理+串口通信基于库函数的配置实例

没写博客的感悟:昨天没有写博客,今天就倒霉了,得写两篇,果然不能偷懒,当天没有做的事,无论如何你都得要做,为了改掉这个拖延的小毛病,给自己定了一条规则,无论多晚,哪怕没有网没有电也得写完每天更新的博客,以此勉励。

今天主要总结和复习三个知识点,NVIC中断优先级管理、串口通信、及串口通信的简单配置实例:

第一部分:

NVIC中断优先级管理:

首先是中断分组,我们知道所用的CM3内核支持256个中断,其中包含了16个内核中断和240个外部中断,并且具有256级的可编程中断设置。然而STM32并没有使用CM3内核的全部东西,而是只用了它的一部分。STM32有84个中断,包括16个内核中断和68个可屏蔽中断,具有16级可编程的中断优先级。而我所用的STM32F103系列上面,又只有60个可屏蔽中断(在107系列才有68个),而STM系列把中断分为5个组,如下图一所示

NVIC中断优先级管理+串口通信基本原理+串口通信基于库函数的配置实例_第1张图片

图一

这么多大概60个中断如何管理,这是我一开始想到的问题,当然是用寄存器进行管理,有七组寄存器对所有的中断进行管理,中断寄存器分别如下,引用MDK对寄存器组的分类:

typedef struct
{
  __IO uint32_t ISER[8];                   //中断使能寄存器组:设置为:8个32位寄存器来控制256个中断,由于我用的只有60个故只需设置ISER[0]-ISER[2]即可
       uint32_t RESERVED0[24];                                   
  __IO uint32_t ICER[8];                      //中断除能寄存器组:设置和中断使能一样
       uint32_t RSERVED1[24];                                    
  __IO uint32_t ISPR[8];                      //中断挂起寄存器组:设置和中断使能一样,通过置一把正在执行的中断挂起从而执行同级别或更高级别的中断,写0无效
       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];                   //  中断优先级控制寄存器组:这是非常重要的一个寄存器,总共有240个8位的寄存器组成,每8位代表一个中断,而每八位只用了其高八位,我所用的103系列只用了0-//67即可,详细的会在下面给出
       uint32_t RESERVED5[644];                                  
  __O  uint32_t STIR;                      // 软件触发中断寄存器组
}  NVIC_Type;      

中断优先级控制寄存器组,说这个之前得明白一个概念,优先级概念,STM系列的中断优先级分为两级,抢占优先级和响应优先级。而又得知道两者的区别,其中区别如下:

1、 高优先级的抢占优先级是可以打断正在进行的低抢占优先级中断的。
2、 抢占优先级相同的中断,高响应优先级不可以打断低响应优先级的中断
3、 抢占优先级相同的中断,当两个中断同时发生的情况下,哪个响应优先级高,哪个先执行。
4、 如果两个中断的抢占优先级和响应优先级都是一样的话,则看哪个中断先发生就先执行;
抢占优先级和响应优先级中,数字越小说明级别越大。举个例子,假定设置中断优先级组为2,然后设置中断1(RTC中断)的抢占优先级为2,响应优先级为1。  中断6(外部中断0)的抢占优先级为3,响应优先级为0。中断4(外部中断1)的抢占优先级为2,响应优先级为0。则中断优先级为中断4>中断1>中断6.
在详细弄懂了上述所有知识后,就可以设置自己我们自己的中断,具体步骤如下:
系统运行后先设置中断优先级分组。调用函数:

voidNVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup);/

 整个系统执行过程中,只设置一次中断分组。

②针对每个中断,设置对应的抢占优先级和响应优先级:

voidNVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct);

③ 如果需要挂起/解挂,查看中断当前激活状态,分别调用相关函数即可。

第二部分:串口通信基本原理
这个复习得从通信的背景开始阐述,处理器与外部的通信分为两种方式并行和串行方式:并行的优点是速度快缺点为占用IO口较多,串行则速度慢优点为占用的IO口较少。串行通信按照传输方向又有三种方式,单工,半双工,全双工如图二所示:
NVIC中断优先级管理+串口通信基本原理+串口通信基于库函数的配置实例_第2张图片
图二

看完图应该就很清晰了,再解释一下
1、 单工: 数据传输只支持数据在一个方向上传输
2、 半双工: 允许数据在两个方向上传输,但是,在某一时刻,只允许数 据在一个方向上传输,它实际上是一种切换方向的单工通信;
3、 全双工: 允许数据同时在两个方向上传输,因此,全双工通信是两个 单工通信方式的结合,它要求发送设备和接收设备都有独立 的接收和发送能力。 
按照是否带时钟分为同步和异步,其中STM32的串口通信接口, UART: 通用异步收发器 USART: 通用同步异步收发器
我所用的板子中的大概电路如图三:
NVIC中断优先级管理+串口通信基本原理+串口通信基于库函数的配置实例_第3张图片
图三

STM32串口异步通信主要的参数定义为:起始位,数据位,硬件流位,停止位,波特率设置。举个例子如图四:
NVIC中断优先级管理+串口通信基本原理+串口通信基于库函数的配置实例_第4张图片
图四


第三部分:
下面进行第三部分,实例演示,在这之前得明白所用的三个寄存器
1、 USART_SR 状态寄存器
2、 USART_DR 数据寄存器
3、 USART_BRR 波特率寄存器
而我们在运用库函数编写串口代码过程一般会运用到下面的几个库函数:

voidUSART_Init();//串口初始化:波特率,数据字长,奇偶校验,硬件流控以及收发使能

voidUSART_Cmd();//使能串口

voidUSART_ITConfig();//使能相关中断

voidUSART_SendData();//发送数据到串口,DR

uint16_tUSART_ReceiveData();//接受数据,从DR读取接受到的数据

FlagStatusUSART_GetFlagStatus();//获取状态标志位

voidUSART_ClearFlag();//清除状态标志位

ITStatusUSART_GetITStatus();//获取中断状态标志位

voidUSART_ClearITPendingBit();//清除中断状态标志位

其中提出一点波特率的计算方式如图5所示:
NVIC中断优先级管理+串口通信基本原理+串口通信基于库函数的配置实例_第5张图片
图五

最后就可以进行实例操作:总结步骤如下:
①串口时钟使能,GPIO时钟使能:RCC_APB2PeriphClockCmd();
②串口复位:USART_DeInit(); 这一步不是必须的
③GPIO端口模式设置:GPIO_Init(); 模式设置为GPIO_Mode_AF_PP
④串口参数初始化:USART_Init();
⑤开启中断并且初始化NVIC(如果需要开启中断才需要这个步骤)

      NVIC_Init();

      USART_ITConfig();

⑥使能串口:USART_Cmd();

⑦编写中断处理函数:USARTx_IRQHandler();

⑧串口数据收发:

void USART_SendData();//发送数据到串口,DR

uint16_t USART_ReceiveData();//接受数据,从DR读取接受到的数据

⑨串口传输状态获取:

FlagStatusUSART_GetFlagStatus(USART_TypeDef* USARTx,uint16_t USART_FLAG);

void USART_ClearITPendingBit(USART_TypeDef* USARTx, uint16_t USART_IT);

编写得主函数代码如下:

#include "stm32f10x.h"
//串口通信实验1
void my_usart_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructA;
USART_InitTypeDef USART_InitStructA;
NVIC_InitTypeDef NVIC_InitStructA;

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_USART1,ENABLE);//enable GPIOA和USART1 的时钟
//初始化GPIOA,查表可知串口1发送模式下是全双工,GPIO口设置为推挽复用模式
GPIO_InitStructA.GPIO_Mode=GPIO_Mode_AF_PP;
GPIO_InitStructA.GPIO_Pin=GPIO_Pin_9;
GPIO_InitStructA.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructA); 
//初始化GPIOA,查表可知串口1接受模式下是全双工,GPIO口设置为浮空输入模式
GPIO_InitStructA.GPIO_Mode=GPIO_Mode_IN_FLOATING;
GPIO_InitStructA.GPIO_Pin=GPIO_Pin_10;
GPIO_InitStructA.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructA); 
//初始化串口参数
USART_InitStructA.USART_BaudRate=115200;
USART_InitStructA.USART_HardwareFlowControl=USART_HardwareFlowControl_None;
USART_InitStructA.USART_Mode=USART_Mode_Tx|USART_Mode_Rx;
USART_InitStructA.USART_Parity=USART_Parity_No;
USART_InitStructA.USART_StopBits=USART_StopBits_1;
USART_InitStructA.USART_WordLength=USART_WordLength_8b;
USART_Init(USART1,&USART_InitStructA);

//开启接受中断且初始化NVIC
NVIC_InitStructA.NVIC_IRQChannel=USART1_IRQn;
NVIC_InitStructA.NVIC_IRQChannelCmd=ENABLE;
NVIC_InitStructA.NVIC_IRQChannelPreemptionPriority=1;
NVIC_InitStructA.NVIC_IRQChannelSubPriority=1;
NVIC_Init(&NVIC_InitStructA);

USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);//设置串口状态
USART_Cmd(USART1, ENABLE);                    //使能串口1 
}
void USART1_IRQHandler(void)
{
u8 res;
if(USART_GetFlagStatus(USART1,USART_FLAG_RXNE))//由于有可能开启了很多中断故需要判断是否是我们所要的中断函数
{
res=USART_ReceiveData(USART1);//接受来自串口一的数据
USART_SendData(USART1,res);//
}
}
 int main(void)
 {
   NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
my_usart_Init();
while(1);
//运行以后就一直在主函数等待,当电脑给单片机发送数据时就跳到中断
 
 }
由于我没有JLINK在线调试,故用USB直接下载到单片机进行调试,最后用串口助手可以得到所设想达到的输出:结果如图六

NVIC中断优先级管理+串口通信基本原理+串口通信基于库函数的配置实例_第6张图片

图六


其中白色的发送是先经过PC先发送给单片机,如然后单片机又发送给PC由串口助手在打印得出;



你可能感兴趣的:(嵌入式,STM32)