串口学习笔记(异步通讯)

串口关键参数

波特率:每秒钟传输的符号数或信号变化的速率。波特率并不等同于数据速率。数据速率是指每秒钟传输的比特数,而波特率则是指每秒钟传输的符号数或信号变化的次数。在某些情况下,一个符号可能携带多个比特的信息,因此波特率和数据速率可能不相等。
常用模式:8N1(8个数据位长度、无校验方式和1个停止位)。

STM32F103系列微控制器USART的中断事件及其使能标志位

中断事件使能标志位描述
USART发送寄存器空中断USART_IT_TXE当发送寄存器为空时触发,表示可以发送新的数据。
CTS状态改变中断USART_IT_CTS当CTS信号的状态发生变化时触发。
数据传输完成中断USART_IT_TC当最后一个数据传输完成时触发。
USART接收寄存器非空中断USART_IT_RXNE当接收寄存器非空时触发,表示有数据可读取。
USART溢出/帧错误/空闲中断USART_IT_ORE_RXNE_IDLE当接收缓冲区溢出、帧错误或空闲线路检测到时触发。
串口空闲中断USART_IT_IDLE当检测到接收线路处于空闲状态时触发。
奇偶校验错误中断USART_IT_PE当接收到的数据的奇偶校验错误时触发。
奇偶校验错误中断USART_IT_LBD当检测到奇偶校验错误时触发(仅用于LIN模式)。
奇偶校验错误中断USART_IT_NE当检测到噪声错误时触发。

使用库函数创建工程发送和接受数据

代码

Serial.c

#include "stm32f10x.h"           

void Serial_Init(unsigned int baud)
{
    // 使能USART1和GPIOA的时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    
    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  // 复用推挽输出
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;       // USART1的TX引脚
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;  
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;   //浮空输入
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;      // USART1的RX引脚
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;  
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    
    USART_InitTypeDef USART_InitStructure;
    USART_InitStructure.USART_BaudRate = baud;  // 波特率
    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_Cmd(USART1, ENABLE);  // 使能USART1
}

void Serial_Sendchar(unsigned char ch)
{
    USART_SendData(USART1, ch);  // 发送一个字符
    while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET);  // 等待发送完成
}

unsigned char USART1_RxIsNotEmpty()
{
    if(USART_GetFlagStatus(USART1, USART_FLAG_RXNE) != RESET)//接收数据寄存器非空标志位
        return 1;  // 接收缓冲区非空
    else 
        return 0;  // 接收缓冲区空
}

unsigned char USART1_RecvChar()
{
    return USART_ReceiveData(USART1);  // 从接收缓冲区读取一个字符
}

Serial.h

#ifndef __SERIAL_H
#define __SERIAL_H

void Serial_Init(unsigned int baud);
void Serial_Sendchar(unsigned char ch);

unsigned char USART1_RxIsNotEmpty(void);
unsigned char USART1_RecvChar(void);

#endif

main.c

#include "stm32f10x.h"             
#include "Serial.h"

int main(void)
{
    Serial_Init(9600);  // 初始化串口,波特率为9600
    
    while(1)
    {
        if(USART1_RxIsNotEmpty())  // 如果串口接收到数据
            Serial_Sendchar(USART1_RecvChar());  // 将接收到的数据发送出去
    }
}

实验结果

阻塞轮询式收发数据

串口通讯时,通讯双方TX(发送)和RX(接受)交叉连接。
从此处开始下边都是HAL库

什么是阻塞轮询式收发数据

阻塞轮询式收发数据是一种常见的串口通信方式,它的工作原理如下:

  1. 阻塞(Blocking): 在阻塞轮询模式下,当程序调用串口接收函数时,如果没有接收到数据,程序将会一直阻塞(即暂停执行),直到有数据到达并被接收。

  2. 轮询(Polling): 在轮询模式下,程序会周期性地检查串口接收缓冲区是否有数据到达。如果有数据到达,程序会立即进行接收处理,如果没有数据,程序继续执行其他任务或者等待。

综合起来,阻塞轮询式收发数据的流程如下:

  • 发送数据时,程序调用串口发送函数,如果发送缓冲区有足够的空间,数据将被放入发送缓冲区并立即发送;如果发送缓冲区已满,程序将会被阻塞,直到发送缓冲区有足够的空间。

  • 接收数据时,程序调用串口接收函数,如果接收缓冲区有数据,数据将被立即接收并返回给程序;如果接收缓冲区没有数据,程序将会被阻塞,直到接收到数据。

阻塞轮询式收发数据的优点是实现简单,易于理解和调试。但它的缺点是在串口通信过程中可能会造成程序的阻塞,降低系统的实时性。

代码

uart.c

#include "stm32f1xx_hal.h"  

UART_HandleTypeDef uart1;//参数结构体被包含在总控结构体中,故使用参数结构体

// UART1 初始化函数
void U1_Init(uint32_t bandrate)
{
    // UART1 初始化结构体
    uart1.Instance = USART1; // 指定使用的串口实例为 USART1
    uart1.Init.BaudRate = bandrate; // 设置波特率为传入的参数 bandrate
    uart1.Init.WordLength = UART_WORDLENGTH_8B; // 设置数据位长度为 8 位
    uart1.Init.StopBits = UART_STOPBITS_1; // 设置停止位为 1 位
    uart1.Init.Parity = UART_PARITY_NONE; // 设置校验位为无校验
    uart1.Init.Mode = UART_MODE_TX_RX; // 设置工作模式为同时发送和接收
    uart1.Init.HwFlowCtl = UART_HWCONTROL_NONE; // 设置硬件流控为无流控 
    HAL_UART_Init(&uart1); // 使用 HAL 库初始化 UART1
}

// UART 外设初始化回调函数
void HAL_UART_MspInit(UART_HandleTypeDef *huart)
{
    GPIO_InitTypeDef GPIO_InitStruct;

    // 检查初始化的 UART 实例是否为 USART1
    if (huart->Instance == USART1)
    {
        __HAL_RCC_GPIOA_CLK_ENABLE(); // 使能 GPIOA 时钟
		__HAL_RCC_USART1_CLK_ENABLE(); // 使能 USART1 时钟

        // 配置 USART1 的引脚
        // 配置 TX 引脚(PA9)为复用推挽输出
        GPIO_InitStruct.Pin = GPIO_PIN_9;
        GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
        GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_MEDIUM;
        HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

        // 配置 RX 引脚(PA10)为浮空输入
        GPIO_InitStruct.Pin = GPIO_PIN_10;
        GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
		GPIO_InitStruct.Pull = GPIO_NOPULL; // 设置为无上拉/下拉
        HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
    }
}

uart.h

#ifndef __UART_H
#define __UART_H

void U1_Init(uint32_t bandrate);

extern UART_HandleTypeDef uart1;

#endif

main.c

#include "stm32f1xx_hal.h" 
#include "rcc.h" 
#include "uart.h" 

#define RX_SIZE  200 // 接收缓冲区大小

uint8_t buff[256]; // 接收缓冲区数组

int main(void)
{
	HAL_Init();
	RccClock_Init();
	U1_Init(115200);
	
	while(1)
	{
		// 使用 HAL_UART_Receive 函数从 UART1 接收数据
		switch(HAL_UART_Receive(&uart1, buff, RX_SIZE, 200))
		{
			case HAL_OK : // 如果接收成功
				// 使用 HAL_UART_Transmit 函数将接收到的数据发送回去
				HAL_UART_Transmit(&uart1, buff, RX_SIZE, 200);
				break;
			case HAL_TIMEOUT: // 如果超时
				// 检查是否接收到部分数据,如果是,则将部分数据发送回去
				if(uart1.RxXferCount != RX_SIZE-1)
				{
					HAL_UART_Transmit(&uart1, buff, (RX_SIZE - 1 - uart1.RxXferCount), 200);//将接受的那一部分数据发送回去
				}
				break;
		}
	}
}

实验结果

未发送数据时

点击调试,全速运行,调出参数窗口,把uart1复制到参数名称框,点开查看uart参数,发现RX_SIZE:0x00C8=200,RxXferCount:0x00c7=199。因此代码中RxXferCount需要减去1才可以判断。

发送10个数据时,接受10个数据

此时RxXferCount位BD=189,程序运行time_out分支,200-1-189=10,证明确实发了十个数据,实验结果正确。


发送200个数据时,接受200个数据

此时RxXferCount无变化,程序运行HAL_OK分支,因为发送了200个数据,实验结果无误。


串口1重映射

什么是重映射

在嵌入式系统中,特别是在一些芯片或微控制器中,重映射是指将一个或多个外设的引脚映射到不同的GPIO引脚上,以实现外设的功能扩展或灵活性增强。重映射通常用于芯片设计中为了适应不同的应用需求,允许开发者重新定义特定外设的引脚分配,使得硬件可以更灵活地应对不同的接口需求。
在STM32系列微控制器中,重映射通常用于重新定义一些特定的功能引脚,如USART、SPI、I2C等外设的引脚,以满足特定的应用需求或连接不同的外部设备。通过重映射,可以在一定程度上实现硬件资源的共享和复用,从而节约系统资源并提高系统的可扩展性。
重映射通常由芯片厂商提供的引脚复用功能支持,通过设置相关的寄存器位来实现。在配置重映射时,需要注意引脚复用的限制以及可能引起的功能冲突,确保系统的稳定性和可靠性。

代码

uart.c

#include "stm32f1xx_hal.h"  

UART_HandleTypeDef uart1;//参数结构体被包含在总控结构体中,故使用参数结构体
UART_HandleTypeDef uart2;
UART_HandleTypeDef uart3;

// UART1 初始化函数
void U1_Init(uint32_t bandrate)
{
    // UART1 初始化结构体
    uart1.Instance = USART1; // 指定使用的串口实例为 USART1
    uart1.Init.BaudRate = bandrate; // 设置波特率为传入的参数 bandrate
    uart1.Init.WordLength = UART_WORDLENGTH_8B; // 设置数据位长度为 8 位
    uart1.Init.StopBits = UART_STOPBITS_1; // 设置停止位为 1 位
    uart1.Init.Parity = UART_PARITY_NONE; // 设置校验位为无校验
    uart1.Init.Mode = UART_MODE_TX_RX; // 设置工作模式为同时发送和接收
    uart1.Init.HwFlowCtl = UART_HWCONTROL_NONE; // 设置硬件流控为无流控
    HAL_UART_Init(&uart1);// 使用 HAL 库初始化 UART1
}

void U2_Init(uint32_t bandrate)
{
    uart2.Instance = USART2; // 指定使用的串口实例为 USART2
    uart2.Init.BaudRate = bandrate; // 设置波特率为传入的参数 bandrate
    uart2.Init.WordLength = UART_WORDLENGTH_8B; // 设置数据位长度为 8 位
    uart2.Init.StopBits = UART_STOPBITS_1; // 设置停止位为 1 位
    uart2.Init.Parity = UART_PARITY_NONE; // 设置校验位为无校验
    uart2.Init.Mode = UART_MODE_TX_RX; // 设置工作模式为同时发送和接收
    uart2.Init.HwFlowCtl = UART_HWCONTROL_NONE; // 设置硬件流控为无流控 
    HAL_UART_Init(&uart2);
}

void U3_Init(uint32_t bandrate)
{
    uart3.Instance = USART3; // 指定使用的串口实例为 USART3
    uart3.Init.BaudRate = bandrate; // 设置波特率为传入的参数 bandrate
    uart3.Init.WordLength = UART_WORDLENGTH_8B; // 设置数据位长度为 8 位
    uart3.Init.StopBits = UART_STOPBITS_1; // 设置停止位为 1 位
    uart3.Init.Parity = UART_PARITY_NONE; // 设置校验位为无校验
    uart3.Init.Mode = UART_MODE_TX_RX; // 设置工作模式为同时发送和接收
    uart3.Init.HwFlowCtl = UART_HWCONTROL_NONE; // 设置硬件流控为无流控  
    HAL_UART_Init(&uart3);
}

// UART 外设初始化回调函数
void HAL_UART_MspInit(UART_HandleTypeDef *huart)
{
    GPIO_InitTypeDef GPIO_InitStruct;

    // 检查初始化的 UART 实例是否为 USART1
    if (huart->Instance == USART1)
    {
        __HAL_RCC_AFIO_CLK_ENABLE();
		__HAL_RCC_GPIOB_CLK_ENABLE();//注意映射到GPIOB
		__HAL_RCC_USART1_CLK_ENABLE();
		__HAL_AFIO_REMAP_USART1_ENABLE();//重映射usart1

        // 配置 TX 引脚(PA9)为复用推挽输出
        GPIO_InitStruct.Pin = GPIO_PIN_6;
        GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
        GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_MEDIUM;
        HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

        // 配置 RX 引脚(PA10)为浮空输入
        GPIO_InitStruct.Pin = GPIO_PIN_7;
        GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
		GPIO_InitStruct.Pull = GPIO_NOPULL; // 设置为无上拉/下拉
        HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
    }
	else if (huart->Instance == USART2)
    {
        __HAL_RCC_GPIOA_CLK_ENABLE();
		__HAL_RCC_USART2_CLK_ENABLE();

        GPIO_InitStruct.Pin = GPIO_PIN_2;
        GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
        GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_MEDIUM;
        HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

        GPIO_InitStruct.Pin = GPIO_PIN_3;
        GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
		GPIO_InitStruct.Pull = GPIO_NOPULL; // 设置为无上拉/下拉
        HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
    }
	else if (huart->Instance == USART3)
    {
        __HAL_RCC_GPIOB_CLK_ENABLE();
		__HAL_RCC_USART3_CLK_ENABLE();

        GPIO_InitStruct.Pin = GPIO_PIN_10;
        GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
        GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_MEDIUM;
        HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

        GPIO_InitStruct.Pin = GPIO_PIN_11;
        GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
		GPIO_InitStruct.Pull = GPIO_NOPULL; // 设置为无上拉/下拉
        HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
    }
}

uart.h

#ifndef __UART_H
#define __UART_H

void U1_Init(uint32_t bandrate);
void U2_Init(uint32_t bandrate);
void U3_Init(uint32_t bandrate);

extern UART_HandleTypeDef uart1;
extern UART_HandleTypeDef uart2;
extern UART_HandleTypeDef uart3;

#endif

main.c

#include "stm32f1xx_hal.h" 
#include "rcc.h" 
#include "uart.h" 

#define RX_SIZE  200 // 接收缓冲区大小

uint8_t buff[256]; // 接收缓冲区数组

int main(void)
{
	HAL_Init();
	RccClock_Init();
	U1_Init(115200);
	U2_Init(115200);
	U3_Init(115200);
	
	while(1)//想用哪一个串口直接把里边的串口号手动改变了即可
	{
		// 使用 HAL_UART_Receive 函数从 UART1 接收数据
		switch(HAL_UART_Receive(&uart1, buff, RX_SIZE, 200))//HAL_UART_Receive可能有感叹号,可以不用管,也可以把程序补充完整,加上HAL_ERROR和HAL_BUSY
		{
			case HAL_OK : // 如果接收成功
				// 使用 HAL_UART_Transmit 函数将接收到的数据发送回去
				HAL_UART_Transmit(&uart1, buff, RX_SIZE, 200);
				break;
			case HAL_TIMEOUT: // 如果超时
				// 检查是否接收到部分数据,如果是,则将部分数据发送回去
				if(uart1.RxXferCount != RX_SIZE)
				{
					HAL_UART_Transmit(&uart1, buff, (RX_SIZE - 1 - uart1.RxXferCount), 200);
				}
				break;
            case HAL_ERROR:
                   break;
            case HAL_BUSY:
                   break;
		}
	}
}

实验结果

改变UART1连接的实物引脚,重复上节实验即可。

多指针定位+收发循环使用缓冲区设计

缓冲区为一个一维数组,循环使用,要注意缓冲区不要出现回卷覆盖,接收的数据要及时处理。

思维图

用三个指针 IN ,OUT, END 来判断此一维数组数据情况。

接受区

发送区

官方串口中断处理函数

代码

urat.c

#include "stm32f1xx_hal.h"  

UART_HandleTypeDef uart1;//参数结构体被包含在总控结构体中,故使用参数结构体
UART_HandleTypeDef uart2;
UART_HandleTypeDef uart3;

uint8_t txbuff[64], rxbuff[64];
uint8_t rxstate;

// UART1 初始化函数
void U1_Init(uint32_t bandrate)
{
    // UART1 初始化结构体
    uart1.Instance = USART1; // 指定使用的串口实例为 USART1
    uart1.Init.BaudRate = bandrate; // 设置波特率为传入的参数 bandrate
    uart1.Init.WordLength = UART_WORDLENGTH_8B; // 设置数据位长度为 8 位
    uart1.Init.StopBits = UART_STOPBITS_1; // 设置停止位为 1 位
    uart1.Init.Parity = UART_PARITY_NONE; // 设置校验位为无校验
    uart1.Init.Mode = UART_MODE_TX_RX; // 设置工作模式为同时发送和接收
    uart1.Init.HwFlowCtl = UART_HWCONTROL_NONE; // 设置硬件流控为无流控
    HAL_UART_Init(&uart1);// 使用 HAL 库初始化 UART1
	
	HAL_UART_Receive_IT(&uart1,rxbuff,20);// 使用HAL库的UART接收中断模式函数
}

void U2_Init(uint32_t bandrate)
{
    uart2.Instance = USART2; // 指定使用的串口实例为 USART2
    uart2.Init.BaudRate = bandrate; // 设置波特率为传入的参数 bandrate
    uart2.Init.WordLength = UART_WORDLENGTH_8B; // 设置数据位长度为 8 位
    uart2.Init.StopBits = UART_STOPBITS_1; // 设置停止位为 1 位
    uart2.Init.Parity = UART_PARITY_NONE; // 设置校验位为无校验
    uart2.Init.Mode = UART_MODE_TX_RX; // 设置工作模式为同时发送和接收
    uart2.Init.HwFlowCtl = UART_HWCONTROL_NONE; // 设置硬件流控为无流控
    HAL_UART_Init(&uart2);
}

void U3_Init(uint32_t bandrate)
{
    uart3.Instance = USART3; // 指定使用的串口实例为 USART3
    uart3.Init.BaudRate = bandrate; // 设置波特率为传入的参数 bandrate
    uart3.Init.WordLength = UART_WORDLENGTH_8B; // 设置数据位长度为 8 位
    uart3.Init.StopBits = UART_STOPBITS_1; // 设置停止位为 1 位
    uart3.Init.Parity = UART_PARITY_NONE; // 设置校验位为无校验
    uart3.Init.Mode = UART_MODE_TX_RX; // 设置工作模式为同时发送和接收
    uart3.Init.HwFlowCtl = UART_HWCONTROL_NONE; // 设置硬件流控为无流控
    HAL_UART_Init(&uart3);
}

// UART 外设初始化回调函数
void HAL_UART_MspInit(UART_HandleTypeDef *huart)
{
    GPIO_InitTypeDef GPIO_InitStruct;

    // 检查初始化的 UART 实例是否为 USART1
    if (huart->Instance == USART1)
    {
		__HAL_RCC_GPIOA_CLK_ENABLE();
		__HAL_RCC_USART1_CLK_ENABLE();

        // 配置 TX 引脚(PA9)为复用推挽输出
        GPIO_InitStruct.Pin = GPIO_PIN_9;
        GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
        GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_MEDIUM;
        HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

        // 配置 RX 引脚(PA10)为浮空输入
        GPIO_InitStruct.Pin = GPIO_PIN_10;
        GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
		GPIO_InitStruct.Pull = GPIO_NOPULL; // 设置为无上拉/下拉
        HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
		
		HAL_NVIC_SetPriority(USART1_IRQn, 3, 0);// 设置USART1中断的优先级为3,子优先级为0
		HAL_NVIC_EnableIRQ(USART3_IRQn);// 使能USART3中断
    }
	else if (huart->Instance == USART2)
    {
        __HAL_RCC_GPIOA_CLK_ENABLE();
		__HAL_RCC_USART2_CLK_ENABLE();

        GPIO_InitStruct.Pin = GPIO_PIN_2;
        GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
        GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_MEDIUM;
        HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

        GPIO_InitStruct.Pin = GPIO_PIN_3;
        GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
		GPIO_InitStruct.Pull = GPIO_NOPULL; // 设置为无上拉/下拉
        HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
    }
	else if (huart->Instance == USART3)
    {
        __HAL_RCC_GPIOB_CLK_ENABLE();
		__HAL_RCC_USART3_CLK_ENABLE();

        GPIO_InitStruct.Pin = GPIO_PIN_10;
        GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
        GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_MEDIUM;
        HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

        GPIO_InitStruct.Pin = GPIO_PIN_11;
        GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
		GPIO_InitStruct.Pull = GPIO_NOPULL; // 设置为无上拉/下拉
        HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
    }
}

// UART 接收完成回调函数
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
    if (huart->Instance == USART1)
    {
        // 将接收缓冲区的内容复制到发送缓冲区
        memcpy(txbuff, rxbuff, 20);
        rxstate = 1; // 设置接收状态为已完成
        HAL_UART_Receive_IT(&uart1, rxbuff, 20); // 启动下一次接收中断
    }
}

// UART 错误回调函数
void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart)
{
    if (huart->Instance == USART1)
    {
        // 在此处处理 UART1 的错误情况,可以根据具体需求进行相应的处理
        // 可以在此添加相关的错误处理代码
    }
}

void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{
	if (huart->Instance == USART1)
    {       
    }
}

uart.h

#ifndef __UART_H
#define __UART_H

#include "string.h"
#include "stdint.h" 
#include "stm32f1xx_hal_uart.h" 

void U1_Init(uint32_t bandrate);
void U2_Init(uint32_t bandrate);
void U3_Init(uint32_t bandrate);

extern UART_HandleTypeDef uart1;
extern UART_HandleTypeDef uart2;
extern UART_HandleTypeDef uart3;
extern uint8_t txbuff[64], rxbuff[64];
extern uint8_t rxstate;

#endif 

main.c

#include "stm32f1xx_hal.h" 
#include "rcc.h" 
#include "uart.h" 

#define RX_SIZE  200 // 接收缓冲区大小

uint8_t buff[256]; // 接收缓冲区数组

int main(void)
{
	HAL_Init();
	RccClock_Init();
	U1_Init(9600);
	
	while(1)
	{
    // 持续检查接收状态
    if(rxstate == 1)
		{
			rxstate = 0; // 重置接收状态为0,表示接收处理已完成        			
			HAL_UART_Transmit_IT(&uart1, txbuff, 20);// 使用 UART1 发送接收到的数据
		}
	}
}

stm32f1xx_it.c中添加中断函数

#include "uart.h" 

/ UART1_IRQHandler: USART1中断的中断处理函数
// 当USART1接收到数据或发送数据时,调用 HAL_UART_IRQHandler() 处理函数处理USART1的中断
void UART1_IRQHandler(void)
{
    HAL_UART_IRQHandler(&uart1);
}

实验结果

发送20个字节数据接受20个,当少于20个时不会触发。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值