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

news2024/9/19 10:37:10

文章目录

  • 通信:
    • 串口通信简介:
    • 1.双工/单工:
    • 2.同步/异步:
    • 3.电平:
        • 电平标准:
  • 串口参数以及数据帧时序:
    • 数据帧:
    • 1.波特率和比特率:
      • 例:无校验,1位停止位
    • 2.校验位:
      • 例:有校验,1位停止位
      • 例:无校验,2位停止
    • 输入电路的数据采样:
      • 注意:只有在输入电路,并且是检测到潜在下降沿(起始位)的时候才开始的以16倍的波特率进行数据采样,并且第二次的连续三次采样是对应在起始位的中间部分,之后的数据位也会跟着对齐,注意只有输入电路这个情况下才以波特率的16倍进行工作,其他电路例如移位仍然是以正常的波特率进行。
      • 起始位检测:
      • 数据采样:
    • 波特率发生器:
  • CH340模块原理图:
  • USART简介:
    • STM32的USART结构:
      • 1——发送数据和接收数据:
      • 2——硬件流控制:
      • 3——多设备通信:
      • 4——时钟控制(波特率发生器,也是分频器):
  • 程序编写:
    • 一、简单发送数据:
      • 总代码:
      • 步骤:
        • 第一步:肯定要先打开时钟
        • 第二步:配置好GPIO口的模式,还有通信配置
        • 第三步:各种发送函数,之后只需要在主函数进行初始化然后调用下列函数
    • 二、接收数据并产生中断,如果数据为1则点亮led灯:
      • 总代码:
      • 步骤:
        • 第一步:和发送时一样,打开时钟,配置好GPIO口,还有通信配置
        • 第二步:打开中断使能,通过misc文件中的NVIC函数配置好USART的中断优先级和中断通道
        • 第三步:在中断程序里检测RXNE位是否 = 1,读取接收数据寄存器,判断接收的数据是否 = 1,等于1则点亮LED灯,同时将接收到的数据回传到电脑串口助手,也就是发送部分

通信:

串口通信简介:

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

在这里插入图片描述

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线发送出去

总代码:

主函数中进行函数调用:

#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文件:

#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);
}

步骤:

第一步:肯定要先打开时钟
//打开GPIO和USART的时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
第二步:配置好GPIO口的模式,还有通信配置
/*
	配置好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);
第三步:各种发送函数,之后只需要在主函数进行初始化然后调用下列函数
//以下函数都是在这个函数的基础上
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函数:

#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文件中的函数:

#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口,还有通信配置
//打开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的中断优先级和中断通道
//使用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灯,同时将接收到的数据回传到电脑串口助手,也就是发送部分
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;
	}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2116213.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

B: 小球反弹

目录 一&#xff1a; 二&#xff1a; 三&#xff1a; 四&#xff1a; 一&#xff1a;问题描述 有一长方形&#xff0c;长为343720 单位长度&#xff0c;宽为233333 单位长度。在其内部左上角顶点有一小球&#xff08;无视其体积&#xff09;&#xff0c;其初速度如图所示且…

【MySQL】MySQL中表的增删改查——(基础篇)(超详解)

前言&#xff1a; &#x1f31f;&#x1f31f;本期讲解关于MySQL中CDUD的基础操作&#xff0c;希望能帮到屏幕前的你。 &#x1f308;上期博客在这里&#xff1a;http://t.csdnimg.cn/fNldO &#x1f308;感兴趣的小伙伴看一看小编主页&#xff1a;GGBondlctrl-CSDN博客 目录 …

Python Flask_APScheduler定时任务的正确(最佳)使用

描述 APScheduler基于Quartz的一个Python定时任务框架&#xff0c;实现了Quartz的所有功能。最近使用Flask框架使用Flask_APScheduler来做定时任务&#xff0c;在使用过程当中也遇到很多问题&#xff0c;例如在定时任务调用的方法中需要用到flask的app.app_context()时&#…

Transformer预测 | 基于Transformer心率时间序列预测(tensorflow)

效果一览 基本介绍 Transformer预测 | 基于Transformer心率时间序列预测(tensorflow) 程序设计 import pandas as pd from pandas.plotting import lag_plot from statsmodels.graphics

暴雨AMD,一起更YES

9月6日&#xff0c;暴雨信息联合AMD举办了“创新驱动 智能未来——2024年行业技术与应用分享会”。在这里&#xff0c;我们与行业领袖、技术专家们一起&#xff0c;深入探讨AI技术的前沿技术动态&#xff0c;洞悉高性能计算的发展趋势&#xff0c;在思维的碰撞和智慧的交融中&a…

BERT 论文逐段精读【论文精读】

BERT: 近 3 年 NLP 最火 CV: 大数据集上的训练好的 NN 模型&#xff0c;提升 CV 任务的性能 —— ImageNet 的 CNN 模型 NLP: BERT 简化了 NLP 任务的训练&#xff0c;提升了 NLP 任务的性能 BERT 如何站在巨人的肩膀上的&#xff1f;使用了哪些 NLP 已有的技术和思想&#xff…

进程等待与退出

目录 前言 0.进程如何退出 1.进程status的获取 2.进程status的组成 3.退出status的分用与验证 4.解释一下errno与waitpid输中status参数的关系 5.status设计 前言 我们在编写程序的时候&#xff0c;我们往往需要知道这个程序中的某个进程执行情况是怎么样的&#xff…

我搞了一台switch

准确的说我搞了一台switch oled。为啥要买个游戏机呢&#xff1f;前些日子去轰趴馆团建&#xff0c;玩了一会switch&#xff0c;当时玩的游戏是《刺客信条3重制版》&#xff0c;感觉挺好玩的。我其实是手残党&#xff0c;很多网络游戏玩的都不是很好&#xff0c;而且大学的时候…

【阿一网络安全】如何让你的密码更安全?(二) - 非对称加密

上次《【阿一网络安全】如何让你的密码更安全&#xff1f;(一) - 对称加密》提到加密算法的对称加密&#xff0c;我们这次来聊聊非对称加密。 和对称加密不同&#xff0c;非对称加密的加密密钥和解密密钥不同。 非对称加密 大概过程就是&#xff0c;发送方使用公钥对明文数据…

Java 后端接口入参 - 联合前端VUE 使用AES完成入参出参加密解密

加密效果&#xff1a; 解密后的数据就是正常数据&#xff1a; 后端&#xff1a;使用的是spring-cloud框架&#xff0c;在gateway模块进行操作 <dependency><groupId>com.google.guava</groupId><artifactId>guava</artifactId><version>30…

Unity | 内存优化之Reserved Unity内存

目录 一、Render Texture资源 1.抗锯齿 2.阴影分辨率 3.深度 4.HDR格式 二、动画资源 1.Resample Curves 2.动画压缩 (1)Keyframe Reduction (2)Optimal(推荐) 3.剔除Scale曲线 (1)开启Remove Constant Scale Curves (2)使用脚本剔除scale曲线 4.降低精…

WordPress上可以内容替换的插件

插件下载地址&#xff1a;WordPress内容替换插件 – 果果开发 类型 替换的类型&#xff1a;文章、自定义文章类型、分类、标签、媒体库、页面、评论、数据库表&#xff0c;不同的类型可以替换不同的字段。 替换字段 替换的字段&#xff0c;哪些字段内容需要替换。除了数据库…

《系统架构设计师教程(第2版)》第17章-通信系统架构设计理论与实践-03-移动通信网网络架构

文章目录 1. 5GS与DN互连1.1 5GS概述1.2 5GS 与DN网络的连接关系1.3 UE连接DN的两种模式1.3.1 透明模式1.3.2 非透明模式 2. 5G 网络边缘计算 1. 5GS与DN互连 1.1 5GS概述 5GS&#xff1a;5G SystemDN&#xff1a;Data NetworkIMS&#xff1a;IP Media Subsystem&#xff08;一…

Android逆向(反调,脱壳,过ssl证书脚本)

文章目录 总结 基础Android基础工具 定位关键代码页面activity定位数据包参数定位堆栈追踪 编写反调脱壳好用的脚本过ssl证书校验抓包反调的脚本打印堆栈bilibili反调的脚本 总结 暑假做了两个月的Android逆向&#xff0c;记录一下自己学到的东西。对于app渗透有了一些思路。 …

Unity下如何播放8K超高分辨率的RTMP流?

在Unity中使用RTMP播放器播放8K流&#xff0c;需要考虑到多个方面的因素和技术要求。以下是一个详细的步骤和要点概述&#xff0c;帮助实现这一目标&#xff1a; 1. 选择合适的RTMP播放器插件 首先&#xff0c;需要选择一个支持8K视频流播放的RTMP播放器插件。并非所有插件都…

MATLAB绘图基础6:MATLAB绘图基础

参考书&#xff1a;《 M A T L A B {\rm MATLAB} MATLAB与学术图表绘制》(关东升)。 6.MATLAB绘图基础 6.1 MATLAB绘图基本流程 % 1.创建图形窗口; % 1.1 创建一个空白图形窗口; figure;% 1.2 创建一个带有指定标题的图形窗口; figure(Name, 图形窗口);% 1.3 创建一个具有指定…

【干货分享】基于SSM的体育场管理系统的开题报告(附源码下载地址)

中秋送好礼 中秋佳节将至&#xff0c;祝福大家中秋快乐&#xff0c;阖家幸福。本期免费分享毕业设计作品&#xff1a;《基于SSM的体育场管理系统》。 基于SSM的体育场管理系统的开题报告 一、课题背景与意义 随着全民健身理念的深入人心&#xff0c;体育场已成为广大师生和…

熟悉Kafka组成模块、Kafka消息提交的方式及优缺点

1. Kafka概念 1.1 Kafka组成模块 Kafka其实是一款基于发布与订阅模式的消息系统&#xff0c;如果按常理来设计&#xff0c;大家是不是把消息发送者的消息直接发送给消息消费者&#xff1f;但Kafka并不是这么设计的&#xff0c;Kafka消息的生产者会对消息进行分类&#xff0c;…

【LVI-SAM】激光雷达点云处理点云帧投影LIO-SAM 之ImageProjection实现细节

【LVI-SAM】激光雷达点云处理点云帧投影LIO-SAM 之ImageProjection实现细节 1. ImageProjection激光雷达点云预处理算法1.0 总结&#xff1a;1.1 功能概述&#xff1a;1.2 算法流程&#xff1a; 2. ImageProjection激光雷达点云预处理算法数学推倒3. ImageProjection激光雷达点…

安卓玩机工具------小米工具箱扩展工具 小米机型功能拓展

小米工具箱扩展版 小米工具箱扩展版 iO_Box_Mi_Ext是由晨钟酱开发的一款适用于小米&#xff08;MIUI&#xff09;、多亲&#xff08;2、2Pro&#xff09;、多看&#xff08;多看电纸书&#xff09;的多功能工具箱。该工具所有功能均可以免root实现&#xff0c;使用前&…