STM32——串口通信(发送/接收数据与中断函数应用)

文章目录

通信:

串口通信简介:

定义: 串口通信是指通过串口进行的数据传输方式,是通信双方根据比特遵循时间顺序的一种通信方式。在串口通信中,数据是按位顺序传输的,每一位数据占用固定的时间长度。

1.双工/单工:

全双工:两条通信线,A与B能够同时进行收、发数据,收、发互不影响,如打电话。

半双工:A与B同一时间任何一端只能进行发送或者接收,A发送,则B只能接收,如对讲机。

单工:只能进行单向通信,即A只能发送,B只能接收,如广播。

2.同步/异步:

同步:通信两端会有一根时钟线,两端时钟信号同步,根据时钟信号确认通信开始和结束时间。

异步:通信两端没有时钟同步,通过特定协议决定何时开始通信,何时结束。

3.电平:

单端:就是数据1和数据0代表的电压信号都是以GND为基准,通信两端需要共地,也就是GND引脚要接在同一处。

差分:根据两引脚的电压差来表示数据和信号,这种信号的抗干扰能力强,一般情况下,如果受到干扰两条通信线都会同时发生变化,变化产生的影响因此就会变小,因此可以长距离通信。

电平标准:

RS485电平是差分信号,大概能传输几千米,另外两种大概只有几十米。

串口参数以及数据帧时序:

下图红圈部分即为数据帧时序图:

数据帧:

这里根据时钟,在每一次时钟的上升沿就会进行一次数据采集

数据帧(Data Frame)是网络通信和数据处理中的一个基本概念,它指的是在网络传输中或数据处理系统中,由起始标志和结束标志限定的一段数据单元。数据帧是数据链路层的数据传输单元,封装了网络层传下来的数据包(Packet)。在不同的协议和上下文中,数据帧的具体结构和内容可能有所不同,但通常都包含了以下几个关键部分:

1.帧头(Frame Header): 包含了控制信息,如帧的起始标志、目的地址、源地址、数据长度等。这些控制信息对于数据的正确传输和接收至关重要。

2.数据字段(Data Field): 即实际传输的数据部分,可以是用户数据,也可以是控制信息。这部分是帧的主要负载。

3.帧尾(Frame Trailer): 包含了帧的结束标志和校验信息等,用于标识帧的结束和检测传输过程中可能发生的错误。

想象一下你正在寄送一个包裹给朋友,这个包裹就相当于网络中的数据帧。

1.包裹的封面(帧头):

包裹上会有收件人的地址(目的地址),这样快递员就知道要送到哪里。

也会有寄件人的地址(源地址),以便在需要时可以追溯包裹的来源。

还会标注包裹里面大概装的是什么东西(数据类型或长度),虽然不是详细清单,但给了一些基本信息。

2.包裹里的东西(数据字段):

这就是你要寄给朋友的实际物品,对应数据帧中的数据部分。它可以是任何东西,比如一封信、一份礼物或任何你想要分享的东西。

3.包裹的封口和标签(帧尾):

包裹会有一个封口,确保里面的东西不会掉出来。在数据帧中,这就像是一个结束标志,告诉接收方这个数据帧到此为止。

有时包裹上还会贴一个"已检查,无损坏"的标签,类似于数据帧中的校验信息。它帮助接收方检查包裹在运输过程中是否完好无损。

1.波特率和比特率:

  • 两者意义相似,数据发送方就是间隔多长时间发送一位数据,而接受方需要以相同的波特率或比特率接收,如此,如果A每间隔1秒发送一个数据,B就每间隔1秒接收一个数据,慢了会漏掉数据,快了会重复接收数据。

例:无校验,1位停止位

1.用单片机的TX引脚发送0X55Z这个数据,比特率是9600,就是1s内发送9600位的数据,

2.空闲状态TX引脚由内部上拉电阻上拉至并保持高电平,发送数据时起始位为低电平,因此电平翻转变成低电平,此时会产生下降沿表示数据开始传输,

3.然后之后发送8位数据,最后停止位为高电平,因此发送完数据位之后引脚会变成高电平表示发送数据结束,

4.如果之后又有新的数据要发送该引脚电平又会被拉低,没有则保持空闲状态高电平。

2.校验位:

例:有校验,1位停止位

  • 这里采用简单的奇偶检验,就是总发送的位数会多出一位校验位,如果是偶校验位,如下图,数据1的个数已经是偶数,那么校验位就为0低电平,如果数据1的个数是奇数,那么校验位就会置高电平保证数据1个数为偶数,如果设置为偶校验,而接收端接收到的数据1个数仍然为奇数,就可以舍弃该数据,申请重新发送。

例:无校验,2位停止

下图为连续发送两个数据,中间没有空闲状态

输入电路的数据采样:

注意:只有在输入电路,并且是检测到潜在下降沿(起始位)的时候才开始的以16倍的波特率进行数据采样,并且第二次的连续三次采样是对应在起始位的中间部分,之后的数据位也会跟着对齐,注意只有输入电路这个情况下才以波特率的16倍进行工作,其他电路例如移位仍然是以正常的波特率进行。

起始位检测:

1.空闲状态下接收到的信号为高电平

2.当检测到一个下降沿时,就开始进行起始位检测,此时会以16倍的波特率进行采样,在第3,5,7和8,9,10进行采样,当前三位和后三位都被检测为0时才算检测到了起始位,这里每三位至少有两个0才算检测到0,如果有一个1两个0仍然算0但是会置一个噪声标志位NE。

3.如果此时这两个三位采样,有一个为1就不算检测到起始位,将会放弃1前的数据,中止侦测起始位的过程,重新开始侦测起始位。

如果侦测起始位成功 ,那么接收状态就从空闲状态转换为接收起始位,而8,9,10位正好位于起始位的中间,那么之后进行数据接收时,上升沿也会处于数据位的中间。

数据采样:

在一个数据位有16个采样时钟,由于在起始位侦测时已经对齐了数据位的中间,因此在第8,9,10的采样时钟对数据位进行三次连续采样,三个采样都为1则数据为1,为0同理,如果只有两个为1也将数据认为1但会置噪声标志位NE = 1。

波特率发生器:

输入电路的采样时钟 = 16倍的波特率

CH340模块原理图:

USART简介:

定义: USART是一种通用的串行通信接口,它支持同步和异步两种通信模式,并且具有很好的扩展性和可定制性。USART不仅包含了UART(通用异步收发器)的所有功能,还增加了同步通信的功能。串口是一个泛称,指的是一类通信接口;而USART是具体的一种串行通信接口标准。

当我们配置好USART电路后,直接读写数据寄存器就能进行数据的接收和发送。

STM32的USART结构:

1------发送数据和接收数据:

注意:发送和接收数据的帧头帧尾由硬件帮我们添加和提出了。

发送数据寄存器接收数据寄存器在软件上占用同一个地址,但是在硬件上它们是两个寄存器,只是软件上写入和读取地址的值时这两个地址都会被读取到,但是TDR是只写的,RDR是只读的,写数据时寻址该地址只会被写入TDR,反之同理。

发送数据时:
发送移位寄存器和接收移位寄存器 ,如果你要通过TX发送01010101数据,那么这个数据会先被保存在发送数据寄存器,然后由硬件检测发送移位数据寄存器是否正在进行数据移位操作,如果没有,则01010101这个要发送的数据就会被转移到发送移位寄存器 ,转移后会置一个标志位TXE表示发送数寄存器为空,这是我们可以通过查看TXE的值来确定是否写入下一个要发送的数据。但是此时刚转移的数据还没被发送,然后在下面的发送控制器的驱动下这个数据就会从低位开始被一位一位的移出去并通过TX发送。

接收数据时:

在接收器控制的驱动下,从RX进入的数据会被不断向右移进入接收移位寄存器,当数据完整的移位到接收移位寄存器 后就会立刻整体转移到接收数据寄存器,此时也会置一个标志位RXNE表示接收数据寄存器非空,这时我们读取该寄存器的值就能够知道接收的数据是什么。

2------硬件流控制:

  • 硬件流控制指的是利用硬件线路的电气信号进行发送和接收的控制。它通过额外的信号线(如nRTS、nCTS等,n表示低电平有效,当接收端没准备好时,RTS就会置高电平,而发送方的CTS接收到该电平信号就会暂停发送数据,只有接收数据寄存器的数据被读取,RTS被置低电平,发送方才会继续发送,反之发送数据时也同样,需要判断对方的RTS)来实时监测数据传输的状态,并根据接收端的处理能力来控制发送端的数据发送速率,从而避免数据丢失和网络拥塞。其基本原理是在发送端和接收端之间通过预先设置的阈值或固定的时间间隔来控制数据的传输速率和数量。

例如:

A给B发送数据,但是B还没处理完上一段数据A就将下一段数据发送发送了过来,导致B只能要么舍去旧数据去处理新数据或者反过来,这样会导致数据丢失,而硬件流控制就是通信两端AB之间会多一根电路,当B处理好旧数据时这个电路会被置低电平表示接收端B做好准备接收新的数据了,那么此时发送端A才会发送新的数据,就避免了数据丢失。

3------多设备通信:

串口是点对点通信,但是在一条总线上可以挂多个设备,给USART地址单元填入某个设备的地址就能够唤醒该设备,此时就能够实现USART与该设备的通信。

4------时钟控制(波特率发生器,也是分频器):

时钟输入先由APB时钟fPCLKx(x = 1或2)(接在APB1总线x就等于1),进行两次分频,就能够得到发送和接收移位的时钟,这个USARTDIV分频就是右边的篮圈部分,他分有整数部分和小数部分,绿圈中当TE = 1就是发送器使能,发送器波特率有效,RE = 1同理

程序编写:

一、简单发送数据:

简易架构图:

完整结构图:

根据结构图,我们使用的是USART是APB2总线的外设,
1.肯定要先打开时钟,
2.当我们要发送数据时,首先把要发送的数据先写入TDR,如果发送移位寄
存器此时没有在转移数据,那么TDR的值就会直接被转移到发送移位寄存器,还需要配置一帧信号的配置
3.然后通过通过波特率发生器,设置DIV的值得到波特率,然后置TE = 1,使发送器波特率有效,
4.再通过发送器控制将发生移位寄存器的数据右移至TX线发送出去

总代码:

主函数中进行函数调用:

c 复制代码
#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "COuntSensor.h"
#include "Usart.h"

int main()
{
	OLED_Init();
	Usart_Init();
	USART1_SendDate('A');
	while(1)
	{
 	}
}   

下面是Usart文件:

c 复制代码
#include "stm32f10x.h"  // Device header
#include "stdarg.h"
#include "stdio.h"

uint8_t Serial_RxData;
uint8_t Serial_RxFlag;
void Usart_Init(void)
{
	//打开GPIO和USART的时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	
	/*
	配置好TX和RX引脚,对应PA9和PA10
	TX配置为复用推挽输出
	RX配置为上拉输入
	*/
	GPIO_InitTypeDef GPIOA9_InitStructure;
	GPIOA9_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIOA9_InitStructure.GPIO_Pin = GPIO_Pin_9;
	GPIOA9_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIOA9_InitStructure);
	
	GPIO_InitTypeDef GPIOA10_InitStructure;
	GPIOA10_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
	GPIOA10_InitStructure.GPIO_Pin = GPIO_Pin_10;
	GPIOA10_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIOA10_InitStructure);
	
	
	//配置通信设置(字长,停止位,波特率)
	USART_InitTypeDef USART1_InitSturcture;
	USART1_InitSturcture.USART_BaudRate = 9600;
	USART1_InitSturcture.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
	USART1_InitSturcture.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
	USART1_InitSturcture.USART_Parity = USART_Parity_No;
	USART1_InitSturcture.USART_StopBits = USART_StopBits_1;
	USART1_InitSturcture.USART_WordLength = USART_WordLength_8b;
	USART_Init(USART1,&USART1_InitSturcture);
	
	
	USART_Cmd(USART1,ENABLE);
}
//发送8位数据
void USART1_SendDate(uint8_t Byte)
{
	USART_SendData(USART1,Byte);
	while(USART_GetFlagStatus(USART1,USART_FLAG_TXE) == RESET);
}
//发送一个数组
void USART1_SendArray(uint8_t *Array,uint8_t size)
{
	uint8_t i = 0;
	while(i < size)
	{
		USART1_SendDate(Array[i]);
		i++;
	}
}
//发送一个字符串
void USART1_SendString(char *String)
{
	uint8_t i = 0;
	while(String[i] != '\0')
	{
		USART1_SendDate(String[i]);
		i++;
	}
}
//打印函数
void USART1_Printf(char *format,...)
{
	char String[100];
	va_list arg;
	va_start(arg,format);
	vsprintf(String,format,arg);
	va_end(arg);
	USART1_SendString(String);
}

步骤:

第一步:肯定要先打开时钟
c 复制代码
//打开GPIO和USART的时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
第二步:配置好GPIO口的模式,还有通信配置
c 复制代码
/*
	配置好TX和RX引脚,对应PA9和PA10
	TX配置为复用推挽输出
	RX配置为上拉输入
	*/
	GPIO_InitTypeDef GPIOA9_InitStructure;
	GPIOA9_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIOA9_InitStructure.GPIO_Pin = GPIO_Pin_9;
	GPIOA9_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIOA9_InitStructure);
	
	GPIO_InitTypeDef GPIOA10_InitStructure;
	GPIOA10_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
	GPIOA10_InitStructure.GPIO_Pin = GPIO_Pin_10;
	GPIOA10_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIOA10_InitStructure);
	
	//配置通信设置(字长,停止位,波特率)
	USART_InitTypeDef USART1_InitSturcture;
	USART1_InitSturcture.USART_BaudRate = 9600;
	USART1_InitSturcture.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
	USART1_InitSturcture.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
	USART1_InitSturcture.USART_Parity = USART_Parity_No;
	USART1_InitSturcture.USART_StopBits = USART_StopBits_1;
	USART1_InitSturcture.USART_WordLength = USART_WordLength_8b;
	USART_Init(USART1,&USART1_InitSturcture);
	//启动USART1
	USART_Cmd(USART1,ENABLE);
第三步:各种发送函数,之后只需要在主函数进行初始化然后调用下列函数
c 复制代码
//以下函数都是在这个函数的基础上
void USART1_SendDate(uint8_t Byte)
{
	USART_SendData(USART1,Byte);
	while(USART_GetFlagStatus(USART1,USART_FLAG_TXE) == RESET);
}

void USART1_SendArray(uint8_t *Array,uint8_t size)
{
	uint8_t i = 0;
	while(i < size)
	{
		USART1_SendDate(Array[i]);
		i++;
	}
}
void USART1_SendString(char *String)
{
	uint8_t i = 0;
	while(String[i] != '\0')
	{
		USART1_SendDate(String[i]);
		i++;
	}
}
void USART1_Printf(char *format,...)
{
	char String[100];
	va_list arg;
	va_start(arg,format);
	vsprintf(String,format,arg);
	va_end(arg);
	USART1_SendString(String);
}

二、接收数据并产生中断,如果数据为1则点亮led灯:

在USART的控制寄存器中有一个RXNEIE位,为中断使能位

首先需要配置好USART通向NVIC的中断路径

当我们接收到数据时RXNE会被置1表示数据读取寄存器非空,这时候如果我们已经将RXNEIE = 1开启中断,那么就会产生中断申请到NVIC,此时NVIC会根据当前的中断事件选择中断优先级最合适的通道给CPU,则CPU就会去执行对应的中断程序。那么我们的配置步骤就是:

总代码:

主函数只需要调用Usart_Init函数:

c 复制代码
#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "COuntSensor.h"
#include "Usart.h"

int main()
{
	OLED_Init();
	Usart_Init();
	while(1)
	{
 	}
}   

下面是Usart.c文件中的函数:

c 复制代码
#include "stm32f10x.h"  // Device header

uint8_t Serial_RxData;
uint8_t Serial_RxFlag;
void Usart_Init(void)
{
	//打开GPIO和USART的时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	
	/*
	配置好TX和RX引脚,对应PA9和PA10
	TX配置为复用推挽输出
	RX配置为上拉输入
	*/
	GPIO_InitTypeDef GPIOA9_InitStructure;
	GPIOA9_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIOA9_InitStructure.GPIO_Pin = GPIO_Pin_9;
	GPIOA9_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIOA9_InitStructure);
	
	GPIO_InitTypeDef GPIOA10_InitStructure;
	GPIOA10_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
	GPIOA10_InitStructure.GPIO_Pin = GPIO_Pin_10;
	GPIOA10_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIOA10_InitStructure);
	
	//配置GPIOA_0口用A0口点亮LED灯
	GPIO_InitTypeDef GPIOA0_InitStructure;
	GPIOA0_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIOA0_InitStructure.GPIO_Pin = GPIO_Pin_0;
	GPIOA0_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIOA0_InitStructure);
	
	//配置通信设置(字长,停止位,波特率)
	USART_InitTypeDef USART1_InitSturcture;
	USART1_InitSturcture.USART_BaudRate = 9600;
	USART1_InitSturcture.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
	USART1_InitSturcture.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
	USART1_InitSturcture.USART_Parity = USART_Parity_No;
	USART1_InitSturcture.USART_StopBits = USART_StopBits_1;
	USART1_InitSturcture.USART_WordLength = USART_WordLength_8b;
	USART_Init(USART1,&USART1_InitSturcture);
	
	//使用IT_Comfig函数开启RXNE的中断使能位
	USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);
	//配置好中断优先级的分组,这个是所有中断一起用的只需配置一次
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	//用NVIC给USART的中断进行中断优先级和通道配置
	NVIC_InitTypeDef NVIC_USART_InitStructyre;
	//启用这个通道只会USART1的中断都会通过这个通道进入到NVIC
	NVIC_USART_InitStructyre.NVIC_IRQChannel = USART1_IRQn;
	NVIC_USART_InitStructyre.NVIC_IRQChannelCmd = ENABLE;
	NVIC_USART_InitStructyre.NVIC_IRQChannelPreemptionPriority = 1;
	NVIC_USART_InitStructyre.NVIC_IRQChannelSubPriority = 1;
	NVIC_Init(&NVIC_USART_InitStructyre);
	
	USART_Cmd(USART1,ENABLE);
}

void USART1_SendDate(uint8_t Byte)
{
	USART_SendData(USART1,Byte);
	while(USART_GetFlagStatus(USART1,USART_FLAG_TXE) == RESET);
}

void USART1_SendArray(uint8_t *Array,uint8_t size)
{
	uint8_t i = 0;
	while(i < size)
	{
		USART1_SendDate(Array[i]);
		i++;
	}
}
void USART1_SendString(char *String)
{
	uint8_t i = 0;
	while(String[i] != '\0')
	{
		USART1_SendDate(String[i]);
		i++;
	}
}
void USART1_Printf(char *format,...)
{
	char String[100];
	va_list arg;
	va_start(arg,format);
	vsprintf(String,format,arg);
	va_end(arg);
	USART1_SendString(String);
}
void LED1_ON(void)
{
	GPIO_ResetBits(GPIOA, GPIO_Pin_0);
}

void LED1_OFF(void)
{
	GPIO_SetBits(GPIOA, GPIO_Pin_0);
}
//到启动文件_md.s中查看中断向量表,确认中断程序名
void USART1_IRQHandler(void)
{
	if (USART_GetITStatus(USART1, USART_IT_RXNE) == SET)
	{
		Serial_RxData = USART_ReceiveData(USART1);
		Serial_RxFlag = 1;
		USART_ClearITPendingBit(USART1, USART_IT_RXNE);
	}
	//数据回传
	USART1_SendDate(Serial_RxData);
	//注意这里的1并不是字符1,字符1还要看ascill码表
	switch(Serial_RxData)
	{
		//事件1点灯:
		case 1:
			LED1_ON();
		break;
		//事件2灭灯:
		case 2:
		LED1_OFF();
		break;
	}
}

步骤:

第一步:和发送时一样,打开时钟,配置好GPIO口,还有通信配置
c 复制代码
//打开GPIO和USART的时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	
	/*
	配置好TX和RX引脚,对应PA9和PA10
	TX配置为复用推挽输出
	RX配置为上拉输入
	*/
	GPIO_InitTypeDef GPIOA9_InitStructure;
	GPIOA9_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIOA9_InitStructure.GPIO_Pin = GPIO_Pin_9;
	GPIOA9_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIOA9_InitStructure);
	
	GPIO_InitTypeDef GPIOA10_InitStructure;
	GPIOA10_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
	GPIOA10_InitStructure.GPIO_Pin = GPIO_Pin_10;
	GPIOA10_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIOA10_InitStructure);
	
	//配置GPIOA_0口用A0口点亮LED灯
	GPIO_InitTypeDef GPIOA0_InitStructure;
	GPIOA0_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIOA0_InitStructure.GPIO_Pin = GPIO_Pin_0;
	GPIOA0_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIOA0_InitStructure);
	
	//配置通信设置(字长,停止位,波特率)
	USART_InitTypeDef USART1_InitSturcture;
	USART1_InitSturcture.USART_BaudRate = 9600;
	USART1_InitSturcture.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
	USART1_InitSturcture.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
	USART1_InitSturcture.USART_Parity = USART_Parity_No;
	USART1_InitSturcture.USART_StopBits = USART_StopBits_1;
	USART1_InitSturcture.USART_WordLength = USART_WordLength_8b;
	USART_Init(USART1,&USART1_InitSturcture);
第二步:打开中断使能,通过misc文件中的NVIC函数配置好USART的中断优先级和中断通道
c 复制代码
//使用IT_Comfig函数开启RXNE的中断使能位
	USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);
	//配置好中断优先级的分组,这个是所有中断一起用的只需配置一次
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	//用NVIC给USART的中断进行中断优先级和通道配置
	NVIC_InitTypeDef NVIC_USART_InitStructyre;
	//启用这个通道只会USART1的中断都会通过这个通道进入到NVIC
	NVIC_USART_InitStructyre.NVIC_IRQChannel = USART1_IRQn;
	NVIC_USART_InitStructyre.NVIC_IRQChannelCmd = ENABLE;
	NVIC_USART_InitStructyre.NVIC_IRQChannelPreemptionPriority = 1;
	NVIC_USART_InitStructyre.NVIC_IRQChannelSubPriority = 1;
	NVIC_Init(&NVIC_USART_InitStructyre);
	
	USART_Cmd(USART1,ENABLE);
第三步:在中断程序里检测RXNE位是否 = 1,读取接收数据寄存器,判断接收的数据是否 = 1,等于1则点亮LED灯,同时将接收到的数据回传到电脑串口助手,也就是发送部分
c 复制代码
void LED1_ON(void)
{
	GPIO_ResetBits(GPIOA, GPIO_Pin_0);
}

void LED1_OFF(void)
{
	GPIO_SetBits(GPIOA, GPIO_Pin_0);
}
//到启动文件_md.s中查看中断向量表,确认中断程序名
void USART1_IRQHandler(void)
{
	if (USART_GetITStatus(USART1, USART_IT_RXNE) == SET)
	{
		Serial_RxData = USART_ReceiveData(USART1);
		Serial_RxFlag = 1;
		USART_ClearITPendingBit(USART1, USART_IT_RXNE);
	}
	//数据回传
	USART1_SendDate(Serial_RxData);
	//注意这里的1并不是字符1,字符1还要看ascill码表
	switch(Serial_RxData)
	{
		//事件1点灯:
		case 1:
			LED1_ON();
		break;
		//事件2灭灯:
		case 2:
		LED1_OFF();
		break;
	}
}
相关推荐
吃什么芹菜卷42 分钟前
2024.9最新:CUDA安装,pytorch库安装
人工智能·pytorch·笔记·python·深度学习
云边有个稻草人1 小时前
【刷题】Day5--数字在升序数组中出现的次数
开发语言·笔记·算法
月夕花晨3741 小时前
C++学习笔记(26)
c++·笔记·学习
CV金科1 小时前
蓝桥杯-STM32G431RBT6(解决LCD与LED引脚冲突的问题)
c语言·stm32·单片机·嵌入式硬件·蓝桥杯
每天的积累1 小时前
STM32与ESP8266的使用
stm32·单片机·嵌入式硬件
蜡笔小新星2 小时前
切换淘宝最新镜像源npm
vue.js·经验分享·学习·npm·node.js
管家婆客服中心2 小时前
管家婆云辉煌手机端怎么连接蓝牙打印机?
经验分享·管家婆软件·管家婆软件常见问题
zhangrelay2 小时前
Arduino IDE离线配置第三方库文件-ESP32开发板
笔记·学习·持续学习
我叫啥都行3 小时前
计算机基础知识复习9.13
linux·笔记·后端·系统架构
南斯拉夫的铁托3 小时前
线性代数(宋浩版)(4)
经验分享·笔记·线性代数