UART通信—基于江科大源码基础进行的改进和解析

news/2024/12/21 21:58:46/

我就不讲理论了,CSDN上大佬属实多,我就只讲代码了,串口的基本理论,大家去看其他大佬写的吧

一、源文件的组成

1、包含的头文件

stm32f10x.h 是STM32F10x系列微控制器的标准外设库(Standard Peripheral Library)的主头文件。这个文件通常包含了对整个STM32F10x系列微控制器的所有硬件外设支持的定义和声明。

下面这个在stm32f10x.h中的文件就是包含了外设的头文件

stm32f10x_conf.h 文件是STM32F10x系列微控制器的标准配置头文件。这个文件通常包含了一些宏定义,用于启用或禁用特定的外设库功能。

2、UART初始化

①、开启时钟

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE); //开启串口1对应的时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);	//开启串口对应GPIO口的时钟

在配置时钟的时候,需要用到哪些外设,除了到相关手册中查询外,也可以直接到配置文件中查询。

②、GPIO初始化

/*GPIO初始化*/GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;   //复用推免输出模式GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;     //上拉输入GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);	

PA9对应着串口1的发送端,因此这里选择模式为复用推免输出

复用推免输出模式详解

  • 复用功能模式 (AF)

在复用功能模式下,GPIO 引脚被配置为支持外设的特定功能,例如 USART、SPI、I2C 等。这些引脚可以连接到多个外设,具体取决于你选择的复用功能。

  • 推挽输出 (PP)

推挽输出是一种常见的输出模式,具有以下特点:

高电平:当输出为高电平时,引脚直接连接到 VDD(电源电压),驱动能力较强。

低电平:当输出为低电平时,引脚直接连接到 GND(地),驱动能力较强。

无上拉/下拉电阻:不需要外部上拉或下拉电阻,因为内部电路已经提供了足够的驱动能力。

  • GPIO_Mode_AF_PP 配置详解

GPIO_Mode_AF_PP 将 GPIO 引脚配置为复用推挽输出模式。这种配置通常用于需要高速和强驱动能力的应用,例如 UART、SPI 和 I2C 的数据传输引脚。

PA10对应着串口1的输入端,因此这里选择 上拉输入 模式

上拉输入模式 (GPIO_Mode_IPU) 详解

  1. 定义
    • 上拉输入模式:在这种模式下,GPIO引脚被配置为输入模式,并且内部有一个上拉电阻将其默认拉到高电平(VDD)。
    • 当外部信号未连接或处于高阻态时,引脚的默认状态是高电平。
    • 当外部信号为低电平时,引脚会被拉低。
  1. 优点:减少噪声干扰
    • 防止浮空:避免了引脚在没有外部信号时处于不确定的状态(浮空)。
    • 减少噪声:上拉电阻有助于减少噪声和干扰,提高信号的稳定性。
    • 简化电路设计:不需要外部上拉电阻,减少了外部元件的数量。
  1. 应用场景
    • 按钮输入:通常用于检测按钮按下事件。按钮未按下时,引脚通过上拉电阻保持高电平;按钮按下时,引脚被拉低。
    • 开关状态检测:用于检测开关的状态,开关断开时引脚为高电平,开关闭合时引脚为低电平。
    • 传感器输入:某些传感器输出可能需要一个上拉电阻来确保信号的稳定性。

③、串口初始化

/*USART初始化*/USART_InitTypeDef USART_InitStructure;					//定义结构体变量USART_InitStructure.USART_BaudRate = 9600;				//波特率USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;	//硬件流控制,不需要USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;	//模式,发送模式和接收模式均选择USART_InitStructure.USART_Parity = USART_Parity_No;		//奇偶校验,不需要USART_InitStructure.USART_StopBits = USART_StopBits_1;	//停止位,选择1位USART_InitStructure.USART_WordLength = USART_WordLength_8b;		//字长,选择8位USART_Init(USART1, &USART_InitStructure);	
波特率:(传输消息要保证,输入和输出两端的波特率保持一致,要不然可能会出现乱码的现象)

(Baud Rate)是串行通信中的一个重要参数,用于衡量数据传输的速度。它表示每秒钟传输的符号(码元)数量。在数字通信中,这些符号通常代表比特(bit)。

硬件流控制(Hardware Flow Control):

硬件流控制(Hardware Flow Control)在串行通信中用于管理数据流,以防止发送方的数据速率超过接收方的处理能力。使用硬件流控制可以有效避免数据丢失和缓冲区溢出问题。下面详细解释何时需要使用硬件流控制以及不使用时可能产生的影响。

何时需要使用硬件流控制
  1. 高速数据传输
    • 当数据传输速率非常高时,接收方可能无法及时处理所有接收到的数据,导致缓冲区溢出。硬件流控制可以通过CTS/RTS信号线动态调整数据流,确保接收方能够处理所有数据。
  1. 长距离通信
    • 在长距离通信中,信号传输延迟较大,可能会导致接收方来不及处理数据。硬件流控制可以更好地管理数据流,确保数据的可靠传输。
  1. 嵌入式系统
    • 在嵌入式系统中,处理器资源有限,处理能力可能不足。硬件流控制可以减轻处理器负担,提高系统的稳定性和可靠性。
  1. 实时应用
    • 对于需要实时处理数据的应用,如工业自动化、医疗设备等,硬件流控制可以确保数据的及时处理,避免因数据丢失而导致的系统故障。
  1. 高可靠性要求
    • 对于对数据完整性有高要求的应用,如金融交易、航空航天等,硬件流控制可以提供更高的数据传输可靠性。
不使用硬件流控制的影响
  1. 数据丢失
    • 如果接收方的缓冲区已满而发送方继续发送数据,可能会导致数据丢失。特别是在高速数据传输或处理器处理能力不足的情况下,数据丢失的风险更高。
  1. 缓冲区溢出
    • 接收方的缓冲区可能会溢出,导致数据被覆盖或系统崩溃。这不仅会导致数据丢失,还可能影响系统的稳定性。
  1. 性能下降
    • 为了防止数据丢失,发送方可能需要频繁地检查接收方的状态,这会增加软件开销,降低整体性能。
  1. 复杂性增加
    • 如果不使用硬件流控制,需要通过软件实现流量控制机制,如XON/XOFF协议。这会增加软件的复杂性,并且不如硬件流控制可靠。
  1. 实时性降低
    • 在实时应用中,数据的及时处理非常重要。如果数据丢失或处理延迟,可能会导致系统响应时间延长,影响实时性能。
软件流控制 vs 硬件流控制
  • 软件流控制(如XON/XOFF):
    • 通过特定的字符(通常是ASCII码中的XON (0x11) 和 XOFF (0x13))来控制数据流。
    • 优点:不需要额外的硬件信号线。
    • 缺点:增加了软件开销,不如硬件流控制可靠,容易受到数据干扰。
  • 硬件流控制(如CTS/RTS):
    • 通过专用的硬件信号线(CTS和RTS)来控制数据流。
    • 优点:可靠性高,无需软件干预,适用于高速数据传输。
    • 缺点:需要额外的硬件信号线,配置相对复杂。

总结

  • 使用硬件流控制:适用于高速数据传输、长距离通信、嵌入式系统、实时应用和高可靠性要求的场景。
  • 不使用硬件流控制:可能导致数据丢失、缓冲区溢出、性能下降、软件复杂性增加和实时性降低。

    /*中断输出配置*/USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);					//开启串口接收数据的中断;中断模式、接收数据寄存器非空中断。/*NVIC中断分组*/NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);					//配置NVIC为分组2/*NVIC配置*/NVIC_InitTypeDef NVIC_InitStructure;							//定义结构体变量NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;				//选择配置NVIC的USART1线NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;					//指定NVIC线路使能NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;		//指定NVIC线路的抢占优先级为1NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;				//指定NVIC线路的响应优先级为1NVIC_Init(&NVIC_InitStructure);									//将结构体变量交给NVIC_Init,配置NVIC外设/*USART使能*/USART_Cmd(USART1, ENABLE);	

串口中断配置

  • USART_IT_RXNE:接收数据寄存器非空 (Receive Data Register Not Empty) 中断。
  • 数据寄存器中不是空的就启动中断

NVIC中断配置

选择分组二,抢占优先级可以有4个值,响应优先级也可以有4个,是一个比较中和的分组

中断通道,选择串口1的中断通道

抢占优先级和响应优先级

超市购物结账

抢占:霸道,我只要比你的抢占优先级高,我来了,不管你是正在排队准备付款还是正在被结账员扫描物品,你都得靠边站,我付完款了才轮的到你。

响应:基于抢占优先级相同的情况下

响应,有响才有应,一个正在结账的人结账完成,就是对后面所有的人的一个响,那么后面接下来谁先来应呢,就得看谁的响应优先级高了,响应优先级高的时候是不管先来后到的,可以插队,但是不可以打断正在执行过程中的中断。

假设我们有以下四个中断,配置如下:

中断

抢占优先级

子优先级

IRQ1

1

0

IRQ2

1

1

IRQ3

2

0

IRQ4

2

1

  • IRQ1 和 IRQ2
    • 抢占优先级相同(都是1),但子优先级不同。
    • IRQ2 的子优先级更高,所以在同一抢占优先级组内,IRQ2 会先于 IRQ1 被处理。
  • IRQ3 和 IRQ4
    • 抢占优先级相同(都是2),但子优先级不同。
    • IRQ4 的子优先级更高,所以在同一抢占优先级组内,IRQ4 会先于 IRQ3 被处理。
  • IRQ1/IRQ2 和 IRQ3/IRQ4
    • IRQ3 和 IRQ4 的抢占优先级(2)高于 IRQ1 和 IRQ2 的抢占优先级(1)。
    • 因此,如果 IRQ3 或 IRQ4 发生时,IRQ1 或 IRQ2 正在执行,IRQ3 或 IRQ4 会立即抢占 IRQ1 或 IRQ2。

④使能串口1

/*USART使能*/USART_Cmd(USART1, ENABLE);	

3、串口通信的相关功能函数

①、串口发送一个字节

/*** 函    数:串口发送一个字节* 参    数:Byte 要发送的一个字节* 返 回 值:无*/
void Serial_SendByte(uint8_t Byte)
{USART_SendData(USART1, Byte);		//将字节数据写入数据寄存器,写入后USART自动生成时序波形while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);	//等待发送完成/*下次写入数据寄存器会自动清除发送完成标志位,故此循环后,无需清除标志位*/
}

void USART_SendData(USART_TypeDef* USARTx, uint16_t Data)
{/* Check the parameters */assert_param(IS_USART_ALL_PERIPH(USARTx));assert_param(IS_USART_DATA(Data)); /* Transmit Data */USARTx->DR = (Data & (uint16_t)0x01FF);
}

assert_param:

是一个宏,通常用于在嵌入式系统编程中进行参数检查。它的主要目的是确保传入函数的参数是有效的,如果参数无效,则触发断言失败,从而帮助开发者发现和修复错误。

因此以下这两行分别是验证 串口号 传输数据 是否有效。

这里是写寄存器,主要是将9位或者8位数据保留(最终保留几位数据根据配置所定)

保留的原理:符号&的作用是按位与,全真则真,一假则假

举例

0x01FF转化为二进制为 0000 0001 1111 1111

如果传输的数据的二进制是 0000 0000 0000 1111

那么两个数据按位与后,还是传输数据的 0000 0000 0000 1111

因为计算机种就是以二进制传递信息的,所以最终是以二进制的数据形式被存储在了DR寄存器中

USART 数据寄存器 DR 通常只能处理 8 位或 9 位的数据,以上面这种方法也是为了将高于9位的数据清零,前面都是0了,不管传过来的数据是1还是0,最终经过按位与后都为0了。

USART(通用同步异步收发传输器)的SR寄存器(状态寄存器)是一个非常重要的寄存器,它用于指示USART外设的各种状态。通过读取SR寄存器,可以获取当前USART的状态信息,如数据是否准备好发送、是否接收到数据、是否有错误发生等。

这是一个检测当前寄存器的状态
USART_FLAG_TXE 检测的是发送寄存器中的数据是否为空

②、发送各种形式的数据

以下是几种数据格式的发送函数,都是围绕着发送字节函数来的,借助可以检测特殊符号的循环完成整个的发送,但是其中内在和发送字节是相同的

/*** 函    数:串口发送一个字符串* 参    数:String 要发送字符串的首地址* 返 回 值:无*/
void Serial_SendString(char *String)
{uint8_t i;for (i = 0; String[i] != '\0'; i ++)//遍历字符数组(字符串),遇到字符串结束标志位后停止{Serial_SendByte(String[i]);		//依次调用Serial_SendByte发送每个字节数据}
}/*** 函    数:次方函数(内部使用)* 返 回 值:返回值等于X的Y次方*/
uint32_t Serial_Pow(uint32_t X, uint32_t Y)
{uint32_t Result = 1;	//设置结果初值为1while (Y --)			//执行Y次{Result *= X;		//将X累乘到结果}return Result;
}/*** 函    数:串口发送数字* 参    数:Number 要发送的数字,范围:0~4294967295* 参    数:Length 要发送数字的长度,范围:0~10* 返 回 值:无*/
void Serial_SendNumber(uint32_t Number, uint8_t Length)
{uint8_t i;for (i = 0; i < Length; i ++)		//根据数字长度遍历数字的每一位{Serial_SendByte(Number / Serial_Pow(10, Length - i - 1) % 10 + '0');	//依次调用Serial_SendByte发送每位数字}
}

③、重定向函数

  1. Serial_SendByte 函数
    • 这是一个假设的串口发送函数,用于通过串口发送一个字节的数据。
    • 具体实现取决于你的硬件平台和串口驱动程序。
  1. 重写 fputc 函数
    • fputc 是标准I/O库中的一个函数,用于将一个字符写入指定的文件流。
    • 通过重写 fputc,你可以改变其默认行为,使其将字符发送到串口而不是标准输出。
    • 代码中,Serial_SendByte(ch) 被调用来发送字符 ch 到串口。
    • return ch; 确保 fputc 返回正确的字符值,以便 printf 可以继续正常工作。
  1. 使用 printf 输出字符串
    • main 函数中,printf("Hello, World!\n"); 会被调用。
    • 由于 fputc 已被重写,printf 会通过 fputc 将每个字符发送到 Serial_SendByte 函数,从而通过串口输出字符串。
/*** 函    数:使用printf需要重定向的底层函数* 参    数:保持原始格式即可,无需变动* 返 回 值:保持原始格式即可,无需变动*/
int fputc(int ch, FILE *f)
{Serial_SendByte(ch);			//将printf的底层重定向到自己的发送字节函数return ch;
}

Printf函数的封装,本质上是将数据转化为字符串后,以字符串的形式发出

/*** 函    数:自己封装的prinf函数* 参    数:format 格式化字符串* 参    数:... 可变的参数列表* 返 回 值:无*/
void Serial_Printf(char *format, ...)
{char String[100];				//定义字符数组va_list arg;					//定义可变参数列表数据类型的变量argva_start(arg, format);			//从format开始,接收参数列表到arg变量vsprintf(String, format, arg);	//使用vsprintf打印格式化字符串和参数列表到字符数组中va_end(arg);					//结束变量argSerial_SendString(String);		//串口发送字符数组(字符串)
}

接收中断函数

这里我使用的环形缓冲区,可以用来存储更多的字节

/*** 函    数:USART1中断函数* 参    数:无* 返 回 值:无* 注意事项:此函数为中断函数,无需调用,中断触发后自动执行*           函数名为预留的指定名称,可以从启动文件复制*           请确保函数名正确,不能有任何差异,否则中断函数将不能进入*/
void USART1_IRQHandler(void)
{// 检查是否是接收中断if (USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) {// 从USART数据寄存器读取一个字节的数据uint8_t data = USART_ReceiveData(USART1);// 将接收到的数据放入环形缓冲区Rx_Data[WriteIndex] = data;// 更新写索引WriteIndex = (WriteIndex + 1) % Data_size;// 设置接收标志位Rx_Flag = 1;USART_ClearITPendingBit(USART1, USART_IT_RXNE);	}}

二、源码

USART.c

#include <stm32f10x.h>		//包含头文件
#include <stdarg.h>
#include "uart.h"
#include "stdio.h"#define Data_size 100			//给数据接收缓存足够的空间uint8_t Rx_Data[Data_size];     //
uint16_t WriteIndex = 0;
uint16_t ReadIndex = 0;
uint8_t Rx_Flag;void UART_Init(void)
{RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE); //开启串口1对应的时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);	//开启串口对应GPIO口的时钟/*GPIO初始化*/GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);	/*USART初始化*/USART_InitTypeDef USART_InitStructure;					//定义结构体变量USART_InitStructure.USART_BaudRate = 9600;				//波特率USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;	//硬件流控制,不需要USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;	//模式,发送模式和接收模式均选择USART_InitStructure.USART_Parity = USART_Parity_No;		//奇偶校验,不需要USART_InitStructure.USART_StopBits = USART_StopBits_1;	//停止位,选择1位USART_InitStructure.USART_WordLength = USART_WordLength_8b;		//字长,选择8位USART_Init(USART1, &USART_InitStructure);	/*中断输出配置*/USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);			//开启串口接收数据的中断/*NVIC中断分组*/NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);			//配置NVIC为分组2/*NVIC配置*/NVIC_InitTypeDef NVIC_InitStructure;					//定义结构体变量NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;		//选择配置NVIC的USART1线NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;			//指定NVIC线路使能NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;		//指定NVIC线路的抢占优先级为1NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;		//指定NVIC线路的响应优先级为1NVIC_Init(&NVIC_InitStructure);							//将结构体变量交给NVIC_Init,配置NVIC外设/*USART使能*/USART_Cmd(USART1, ENABLE);	}/*** 函    数:串口发送一个字节* 参    数:Byte 要发送的一个字节* 返 回 值:无*/
void Serial_SendByte(uint8_t Byte)
{USART_SendData(USART1, Byte);		//将字节数据写入数据寄存器,写入后USART自动生成时序波形while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);	//等待发送完成/*下次写入数据寄存器会自动清除发送完成标志位,故此循环后,无需清除标志位*/
}/*** 函    数:串口发送一个数组* 参    数:Array 要发送数组的首地址* 参    数:Length 要发送数组的长度* 返 回 值:无*/
void Serial_SendArray(uint8_t *Array, uint16_t Length)
{uint16_t i;for (i = 0; i < Length; i ++)		//遍历数组{Serial_SendByte(Array[i]);		//依次调用Serial_SendByte发送每个字节数据}
}/*** 函    数:串口发送一个字符串* 参    数:String 要发送字符串的首地址* 返 回 值:无*/
void Serial_SendString(char *String)
{uint8_t i;for (i = 0; String[i] != '\0'; i ++)//遍历字符数组(字符串),遇到字符串结束标志位后停止{Serial_SendByte(String[i]);		//依次调用Serial_SendByte发送每个字节数据}
}/*** 函    数:次方函数(内部使用)* 返 回 值:返回值等于X的Y次方*/
uint32_t Serial_Pow(uint32_t X, uint32_t Y)
{uint32_t Result = 1;	//设置结果初值为1while (Y --)			//执行Y次{Result *= X;		//将X累乘到结果}return Result;
}/*** 函    数:串口发送数字* 参    数:Number 要发送的数字,范围:0~4294967295* 参    数:Length 要发送数字的长度,范围:0~10* 返 回 值:无*/
void Serial_SendNumber(uint32_t Number, uint8_t Length)
{uint8_t i;for (i = 0; i < Length; i ++)		//根据数字长度遍历数字的每一位{Serial_SendByte(Number / Serial_Pow(10, Length - i - 1) % 10 + '0');	//依次调用Serial_SendByte发送每位数字}
}/*** 函    数:使用printf需要重定向的底层函数* 参    数:保持原始格式即可,无需变动* 返 回 值:保持原始格式即可,无需变动*/
int fputc(int ch, FILE *f)
{Serial_SendByte(ch);			//将printf的底层重定向到自己的发送字节函数return ch;
}/*** 函    数:自己封装的prinf函数* 参    数:format 格式化字符串* 参    数:... 可变的参数列表* 返 回 值:无*/
void Serial_Printf(char *format, ...)
{char String[100];				//定义字符数组va_list arg;					//定义可变参数列表数据类型的变量argva_start(arg, format);			//从format开始,接收参数列表到arg变量vsprintf(String, format, arg);	//使用vsprintf打印格式化字符串和参数列表到字符数组中va_end(arg);					//结束变量argSerial_SendString(String);		//串口发送字符数组(字符串)
}/*** 函    数:获取串口接收的数据* 参    数:无* 返 回 值:接收的数据,范围:0~255*/
uint8_t Serial_GetRxData(void)
{return *Rx_Data;			//返回接收的数据变量
}
/*** 函    数:获取串口接收标志位* 参    数:无* 返 回 值:串口接收标志位,范围:0~1,接收到数据后,标志位置1,读取后标志位自动清零*/
uint8_t Serial_GetRxFlag(void)
{if (Rx_Flag == 1)			//如果标志位为1{Rx_Flag = 0;return 1;					//则返回1,并自动清零标志位}return 0;						//如果标志位为0,则返回0
}/*** 函    数:USART1中断函数* 参    数:无* 返 回 值:无* 注意事项:此函数为中断函数,无需调用,中断触发后自动执行*           函数名为预留的指定名称,可以从启动文件复制*           请确保函数名正确,不能有任何差异,否则中断函数将不能进入*/
void USART1_IRQHandler(void)
{// 检查是否是接收中断if (USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) {// 从USART数据寄存器读取一个字节的数据uint8_t data = USART_ReceiveData(USART1);// 将接收到的数据放入环形缓冲区Rx_Data[WriteIndex] = data;// 更新写索引WriteIndex = (WriteIndex + 1) % Data_size;// 设置接收标志位Rx_Flag = 1;USART_ClearITPendingBit(USART1, USART_IT_RXNE);	}}

UART.h

#ifndef __UART_H
#define __UART_Hvoid UART_Init(void);
void Serial_SendByte(uint8_t Byte);
void Serial_SendArray(uint8_t *Array, uint16_t Length);
void Serial_SendString(char *String);
void Serial_SendNumber(uint32_t Number, uint8_t Length);
void Serial_Printf(char *format, ...);
void Serial_ProcessRxData(void);uint8_t Serial_GetRxFlag(void);
uint8_t Serial_GetRxData(void);#endif

main.c

#include "stm32f10x.h"                  // Device header
#include "uart.h"uint8_t RxData;int main(void)
{		UART_Init();while (1){if (Serial_GetRxFlag() == 1)			//检查串口接收数据的标志位{RxData = Serial_GetRxData();		//获取串口接收的数据
//			Serial_Printf("%x\n",RxData);Serial_SendByte(RxData);			//串口将收到的数据回传回去,用于测试}}
}

http://www.ppmy.cn/news/1534958.html

相关文章

rabbitMq-----路由匹配模块

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言交换机类型binding_key 和 routing_key匹配算法 前言 交换机有三种类型&#xff0c;直接交换&#xff0c;广播交换&#xff0c;主题交换。 其中交换机类型不同…

60 序列到序列学习(seq2seq)_by《李沐:动手学深度学习v2》pytorch版

系列文章目录 文章目录 系列文章目录一、理论知识比喻机器翻译Seq2seq编码器-解码器细节训练衡量生成序列的好坏的BLEU(值越大越好)总结 二、代码编码器解码器损失函数训练预测预测序列的评估小结练习 一、理论知识 比喻 seq2seq就像RNN的转录工作一样&#xff0c;非常形象的比…

05_图片剪切

import cv2 img cv2.imread(libarary.JPG, 1)dst img[350:650,550:800] #这里选取矩形区域X&#xff1a;550-800 Y&#xff1a;350-650#cv2.imshow(image,dst) #cv2.waitKey(0)以下会在jupyterLab控件中显示两种压缩后的图像对比显示 #bgr8转jpeg格式 import enum import …

c++_ 多态

目录 一.多态 1.1多态(polymorphism)的概念 1.2实现多态还有两个必须重要条件&#xff1a; 1.3 重载 和 虚函数的重写/覆盖 和 隐藏 的比对 1.4 协变(了解) 1.5 析构函数的重写 1.6 override 和final关键字 二.纯虚函数和抽象类 三. 多态的原理 3.1虚函数表指针 3.…

SolidWorks机器转ROS2 URDF

文章目录 开发环境SolidWords插件使用生成urdf文件之后的处理CMakeLists文件修改package.xml变更Launch更改运行 开发环境 Linux系统&#xff1a;Ubuntu 22.04 Ros2版本&#xff1a;humble Solidwords版本&#xff1a;2023 &#xff08;2019以上版本应该都是可以的&#xff09…

【CKA】二、节点管理-设置节点不可用

2、节点管理-设置节点不可用 1. 考题内容&#xff1a; 2. 答题思路&#xff1a; 先设置节点不可用&#xff0c;然后驱逐节点上的pod 这道题就两条命令&#xff0c;直接背熟就行。 也可以查看帮助 kubectl cordon -h kubectl drain -h 参数详情&#xff1a; –delete-empty…

gm/ID设计方法学习笔记(一)

前言&#xff1a;为什么需要gm/id &#xff08;一&#xff09;主流设计方法往往侧重于强反型区&#xff08;过驱>0.2V&#xff09;&#xff0c;低功耗设计则侧重于弱反型区&#xff08;<0&#xff09;&#xff0c;但现在缺乏对中反型区的简单和准确的手算模型。 1.对于…

DualGS:高效人体体积视频渲染技术,实现复杂4D数字人表演的实时播放引言

随着虚拟现实(VR)和增强现实(AR)技术的发展,对高质量、低延迟的人体体积视频的需求日益增长。传统的视频压缩和渲染方法在处理复杂的4D人体动作时往往面临性能瓶颈。为了解决这一问题,研究人员开发了一种名为DualGS的新型高效人体体积视频渲染技术。本文将详细介绍DualGS…