stm32中断方式的串口通信——上位机控制串口收发


前言

前面介绍的串口通信的方式主要有3种

  • 轮询方式的串口通信
  • 中断方式的串口通信
  • DMA方式的串口通信

这里我将介绍的是中断方式的串口通信,相较于之前的轮询式的串口通信,中断式的串口通信有许多的优点,当接收或者发送一个数据时就申请中断,同时cpu可以执行其它任务,CPU的利用率更高了。


提示:以下是本篇文章正文内容,下面案例可供参考

一、什么是串口通信

关于什么是串口通信这里就不过多的赘述了,因为我前面的博客都有说明,如果还不明白的可以去看看我之前的博客。

链接:
https://blog.csdn.net/wer4567/article/details/127374342?spm=1001.2014.3001.5501

二、什么是中断——STM32

大家可能还不是很了解什么是中断,这里我就来介绍介绍有关中断的相关知识,以及stm32下的中断的一些说明。

1.中断的概念

  • 中断实质上就是在CPU正常执行程序时,遇到了内部或者外部的异常情况需要紧急处理,CPU暂时停止正在运行的程序,转而去处理发生的异常事件,当该异常事件处理完毕之后又返回之前暂停的程序处继续执行。**举个简单的例子:**当你在看电视的时候,你妈叫你去买酱油,你妈叫你买酱油就是发生的异常事件,当你买完酱油回来又继续看你的电视。
    在这里插入图片描述
  • STM32F10x芯片内有84个中断通道,包括16个内核中断和68个可屏蔽中断,这些中断通道已经按照不同优先级顺序固定分配相应的外部设备。这一部分就不过多说明,大家可以自己去参考《stm32f0x中文参考手册》,上面都有详细的介绍。

2.STM32下NVIC的介绍

  • NVIC英文全称是Nested Vectored Interrupt Controller,中文意思就是嵌套向量中断控制器,它属于M3内核的一个外设,控制着芯片的中断相关功能。由于ARM给NVIC预留了非常多的功能,但对于使用M3内核设计芯片的公司可能就不需要这么多功能,于是就需要在NVIC上裁剪。ST公司的STM32F103芯片内部中断数量就是NVIC裁剪后的结果。
  • 中断控制相关的寄存器在固件库core_cm3.h

3.中断优先级的介绍

当CPU在正常执行程序的过程种总是会收到许多的中断,这么的中断如果一股脑的处理的话显然不现实,每个中断都应该有个优先级,优先级高的先执行,执行完之后再跳回之前的程序,这样程序才能井然有序的执行。

  • STM32F103芯片支持60个可屏蔽中断通道,每个中断通道都具备自己的中断优先级控制字节(8位,但是STM32F103中只使用4位,高4位有效),用于表达优先级的高4位又被分为组成抢占式优先级和响应式优先级,每个中断源都需要被指定这两种优先级。
  • 当两个中断源的抢占式优先级相同时,这两个中断将没有嵌套关系,当一个中断到来后,如果正在处理另一个中断,这个后到来的中断就要等待前一个中断处理完之后才能被处理。如果这两个中断同时到达,则中断控制器根据他们的响应优先级高低来决定先处理哪一个;如果他们的抢占式优先级和响应优先级都相等,则根据他们在中断表中的排位顺序决定先处理哪一个。
  • 高抢占式优先级的中断事件会打断当前的主程序或中断程序运行,俗称中断嵌套。在抢占式优先级相同的情况下,高响应优先级的中断优先被响应。

4.外部中断的介绍

  • STM32F10x外部中断/事件控制器(EXTI)包含多达20个用于产生事件/中断请求的边沿检测器。EXTI的每根输入线都可单独进行配置,以选择类型(中断或事件)和相应的触发事件(上升沿触发、下降沿触发或边沿触发),还可独立地被屏蔽。
    在这里插入图片描述
  • 外部中断/事件的线映射
    在这里插入图片描述

三、串口通信keil工程文件

1.新建工程模板

关于在keil下新建一个stm32f103的的工程模板这里就不过多的说明了,相信大家通过前面的学习,都已经非常清楚了。新建工程模板之后再添加相应的.c,.h文件就可以了

注意!下面的代码只是.c文件的相关代码,如果同学们要借鉴的话,需要自己添加相应的.h文件和头文件

2.串口初始化函数,以及串口发送函数定义。

void My_Uart_Init(uint32_t Bound)//这里时配置的串口1,如果配置其它串口也可自己改引脚
{
    
    
    GPIO_InitTypeDef GPIO_InitStructure;
    USART_InitTypeDef USART_InitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART1,ENABLE);

    //TX口的初始化
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA,&GPIO_InitStructure);
    //RX的初始化
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;      //浮空输入
	GPIO_Init(GPIOA, &GPIO_InitStructure);
    //初始化USART
    NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
    USART_InitStructure.USART_BaudRate = Bound;
    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
    USART_InitStructure.USART_Parity = USART_Parity_No;
    USART_InitStructure.USART_StopBits = USART_StopBits_1;
    USART_InitStructure.USART_WordLength = USART_WordLength_8b;
    USART_Init(USART1,&USART_InitStructure);//初始化
    USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);//开启串口接收中断
    USART_Cmd(USART1,ENABLE);
}
//发送一个字节
void Usart_SendByte(USART_TypeDef *USARTx,uint8_t ch)
{
    
    
    USART_SendData(USARTx,ch);
    while(USART_GetFlagStatus(USARTx,USART_FLAG_TXE) == RESET);
}
//发送8位数组
void Usart_SendArray(USART_TypeDef *USARTx,uint8_t *arry,uint16_t num)
{
    
    
    uint8_t i;
    for(i=0; i<num; i++)
    {
    
    
        Usart_SendByte(USARTx,arry[i]);
    }
    while(USART_GetFlagStatus(USARTx,USART_FLAG_TC) == RESET);
}
void Usart_SendByts(USART_TypeDef *USARTx,char *p)
{
    
    
    while(*p != '\0')
    {
    
    
        Usart_SendByte(USARTx,*p);
        p++;
    }
    while(USART_GetFlagStatus(USARTx,USART_FLAG_TC) == SET);
}

3.中断服务程序的编写

void USART1_IRQHandler()
{
    
    
	uint8_t Rx_cnt= 0;
	char ch[100];
  if(USART_GetITStatus(USART1,USART_IT_RXNE) != RESET)
  {
    
    
    GPIO_SetBits(GPIOA,GPIO_Pin_5);
    ch[Rx_cnt++] = USART1->DR;
    Usart_SendByts(USART1,ch);
    if(ch[Rx_cnt-1] == '\0') Rx_cnt = 0;
    USART_ClearITPendingBit(USART1,USART_IT_RXNE);
  }
  if(strcmp(ch,"s") == 0) 
  {
    
    
    GPIO_ResetBits(GPIOA,GPIO_Pin_5);
    Global_flag = 1;
  }
  if(strcmp(ch,"t") == 0) Global_flag = 0;

}

在这里插入图片描述

4.主程序的编写

    if(Global_flag == 0)//Global_flag为定义的全局变量,在中断服务程序中也会用到
       Usart_SendByts(USART1,str);
	Delay_ms(500);//自己编写的延时函数

将上诉代码添加到自己创建的工程文件目录下编译生成hex文件烧板测试!

四、烧板&Debug

在这里插入图片描述
在这里插入图片描述


总结

通过本次的学习,我也学习到了许多知识。在做字符串控制的实验中,因为理解错中断的相关知识,导致一直卡壳,当时一位字符串的接收是一个字符串进一次中断,不知道当时是怎么想的,导致一直没做出来,其实是收到一个字符就进一次中断,一直到字符接收完。

猜你喜欢

转载自blog.csdn.net/wer4567/article/details/127470946