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

我就不讲理论了,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.xdnf.cn/news/1554672.html

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

相关文章

【算法业务】数据驱动的用户增长实践、收益及思考

这篇内容是多年之前&#xff08;2020年&#xff09;的用户增长项目时自己写的总结&#xff0c;这里做一下对于实践和思考的回顾&#xff0c;便于知识的记录和经验分享&#xff0c;内容涉及用户增长理解、个性化推送系统框架、个性化推送问题建模、推送内容池构建、智能文案生成…

zotero使用koofr+google drive/onedrive同步

最早了解到这个思路是来自 知乎-【从零开始使用Zotero】(3) Zotero文献同步方式 和 How to Sync Zotero Files Using WebDAV and Google Drive with Koofr: A Step-by-Step Guide 虽然周围很多人都在用Readpaper这种web端的了&#xff0c;但是经常逛Arxiv而且zotero的web插…

MATLAB|电气互联系统有功-无功协同优化模型

目录 1 主要内容 模型示意图 目标函数 程序亮点 2 部分程序 3 程序结果 4 下载链接 1 主要内容 本程序基本复现《“碳中和”目标下电气互联系统有功-无功协同优化模型》&#xff0c;文献模型提供了一个很好的创新思路&#xff0c;把常规电气互联系统的调度和有功无功优化…

Android Framework AMS(01)AMS启动及相关初始化1-4

该系列文章总纲链接&#xff1a;专题总纲目录 Android Framework 总纲 本章关键点总结 & 说明&#xff1a; 说明&#xff1a;本章节主要涉及systemserver启动AMS及初始化AMS相关操作。同时由于该部分内容分析过多&#xff0c;因此拆成2个章节&#xff0c;本章节是第一章节&…

10.4学习

1.Transactional 注意事项&#xff1a; ①事务函数中不要处理耗时任务&#xff0c;会导致长期占有数据库连接。 ②事务函数中不要处理无关业务&#xff0c;防止产生异常导致事务回滚。 ●事务传播属性 ①REQUIRED&#xff08;默认属性&#xff09; 如果存在一个事务&#…

基于Springboot+VUE的二手奢侈品商城的设计与实现

一、摘要 当前&#xff0c;二手奢侈品市场持续蓬勃发展&#xff0c;吸引了越来越多的消费者。然而&#xff0c;现有的二手奢侈品交易平台在用户体验、安全性和功能方面仍存在一些问题&#xff0c;需要进一步改进。本研究旨在设计和实现一种基于Spring Boot 和 Vue 技术框架的二…

17 链表——21. 合并两个有序链表 ★

17 链表 21. 合并两个有序链表 将两个升序链表合并为一个新的升序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 示例 1: 输入:l1 = [1,2,4], l2 = [1,3,4] 输出:[1,1,2,3,4,4] 算法设计: 合并两个有序链表,并保持有序性,可以采用迭代法和递归法两种…

Unity代码组件,代码控制旋转+RotateAround、Translate,LookAt相关

创建脚本 在Project面板创建一个Scripts文件夹&#xff0c;用于专门存放脚本&#xff0c;创建一个脚本&#xff0c;命名为RotationControl,将该脚本拖拽到Tank上&#xff0c;则Tank物体成功挂载该脚本。 双击打开脚本&#xff0c;编写脚本如下&#xff1a; using System.Coll…

国外电商系统开发-运维系统批量添加服务器

您可以把您准备的txt文件&#xff0c;安装要求的格式&#xff0c;复制粘贴到里面就可以了。注意格式&#xff01; 如果是“#” 开头的&#xff0c;则表示注释&#xff01;

746. 使用最小花费爬楼梯

文章目录 746. 使用最小花费爬楼梯思路总结 746. 使用最小花费爬楼梯 746. 使用最小花费爬楼梯 给你一个整数数组 cost &#xff0c;其中 cost[i] 是从楼梯第 i 个台阶向上爬需要支付的费用。一旦你支付此费用&#xff0c;即可选择向上爬一个或者两个台阶。 你可以选择从下标…

Java之队列

1. 概念 队列&#xff1a;只允许在一端进行插入数据操作&#xff0c;在另一端进行删除数据操作的特殊线性 特点&#xff1a; 队列具有先进先出FIFO(First In First Out) 入队列&#xff1a;进行插入操作的一端称为队尾&#xff08;Tail/Rear&#xff09; 出队列&#xff1a;进…

【论文笔记】DKTNet: Dual-Key Transformer Network for small object detection

【引用格式】&#xff1a;Xu S, Gu J, Hua Y, et al. Dktnet: dual-key transformer network for small object detection[J]. Neurocomputing, 2023, 525: 29-41. 【网址】&#xff1a;https://cczuyiliu.github.io/pdf/DKTNet%20Dual-Key%20Transformer%20Network%20for%20s…

等额本金和等额本息是什么意思?

等额本金和等额本息是两种常见的贷款还款方式&#xff0c;它们各自有着不同的特点和适用场景。下面我将用通俗易懂的语言来解释这两种还款方式&#xff1a; 等额本金 定义&#xff1a;等额本金指的是在贷款期限内&#xff0c;每月偿还相同数额的本金&#xff0c;而利息则随着剩…

buuctf[安洵杯 2019]easy misc1

解压的一个文件夹和图片一个&#xff0c;zip压缩包有密码 FLAG IN ((√2524921X8552)15-1794)NNULLULL, ((√2524921X8552)15-1794)7 我用passware kit 2022 所以试试7位数字NNULLULL,掩码&#xff08;mask&#xff09;攻击试试 mask &#xff1a;?d?d?d?d?d?d?dNNUL…

C++【类和对象】(友元、内部类与匿名对象)

文章目录 1.友元2.内部类3.匿名对象结语 1.友元 友元提供了⼀种突破类访问限定符封装的方式&#xff0c;友元分为&#xff1a;友元函数和友元类&#xff0c;在函数声明或者类声明的前面加friend&#xff0c;并且把友元声明放到⼀个类的里面。外部友元函数可访问类的私有和保护…

图文深入理解Oracle DB企业级集中管理神器-GC

值此国庆佳节&#xff0c;深宅家中&#xff0c;闲来无事&#xff0c;就多写几篇博文。今天继续宅继续写。 本文将图文深入介绍Oracle DB企业级集中管理神器-GC&#xff1a;即Oracle Grid Control&#xff08;Oracle 企业管理器网格控制&#xff09;。 Oracle DB的牛逼之处实在太…

GS-SLAM论文阅读笔记-CaRtGS

前言 这篇文章看起来有点像Photo-slam的续作&#xff0c;行文格式和图片类型很接近&#xff0c;而且貌似是出自同一所学校的&#xff0c;所以推测可能是Photo-slam的优化与改进方法&#xff0c;接下来具体看看改进了哪些地方。 文章目录 前言1.背景介绍GS-SLAM方法总结 2.关键…

基于Keras的U-Net模型在图像分割与计数中的应用

关于深度实战社区 我们是一个深度学习领域的独立工作室。团队成员有&#xff1a;中科大硕士、纽约大学硕士、浙江大学硕士、华东理工博士等&#xff0c;曾在腾讯、百度、德勤等担任算法工程师/产品经理。全网20多万粉丝&#xff0c;拥有2篇国家级人工智能发明专利。 社区特色&a…

【C++算法】10.滑动窗口_长度最小的子数组

文章目录 题目链接&#xff1a;题目描述&#xff1a;解法C 算法代码&#xff1a;图解 题目链接&#xff1a; 209. 长度最小的子数组 题目描述&#xff1a; 解法 解法一&#xff1a;暴力求解&#xff08;会超时&#xff09; 暴力枚举出所有子数组的和。 查找子数组n2&#xff0…

九、怪物行为逻辑

一、怪物正常行为 1、导入资源 2、创建怪物预制体 新建一个空物体&#xff0c;作为父级、怪物作为子级 3、导入怪物动画 在怪物身上添加角色控制器和动画组件 此时怪物会有碰撞不会穿模 创建一个动画控制器&#xff0c;放入攻击状态和正常状态 并设置一个Bool值&#xff0…