STM32基于HAL库串口printf使用和接收

我们这里使用HAL库直接用cubemx生成代码配置串口

1.打开cubemx,选择MCU型号

2.我这里使用的是STM32F103C8T6,根据自己的型号选择,这里不限制型号

3.选择时钟源

4.系统设置

5时钟配置

5.选择和配置串口

5.配置中断和中断优先级

6.工程设置

7.代码生成设置

cubemx代码生成注意事项

添加代码


cpp 复制代码
/* USER CODE BEGIN Prototypes */

#define RX_BUFFER_SIZE 256

typedef struct {
    uint8_t RxBuffer[RX_BUFFER_SIZE];
    uint8_t RxData;
    uint16_t RxDataCnt;
}UART_RxTypeDef;

extern UART_RxTypeDef Uart1Rx;   // 为UART1声明外部结构体变量

/* USER CODE END Prototypes */
cpp 复制代码
/* USER CODE BEGIN 0 */

UART_RxTypeDef Uart1Rx = {{0}, 0, 0};  // 为UART1初始化结构体

// 重定向c库函数printf到huart1
int fputc(int ch, FILE *f) {
    HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xFFFF);
    return ch;
}
int fgetc(FILE *f)
{
  uint8_t ch = 0;
  HAL_UART_Receive(&huart1, &ch, 1, 0xffff);
  return ch;
}
/* USER CODE END 0 */
cpp 复制代码
/* USER CODE BEGIN Includes */
	
#include <stdio.h>
#include <string.h>

#include "usart.h"
/* USER CODE END Includes */

关键的一点,如果不勾选这个选项,那么是无法运行printf代码,整个代码无法运行

然后我们如果不想每次烧录后都要按下复位键

然后我们就测试一下代码是否可以正常运行

主函数while循环

cpp 复制代码
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
	  printf("Hello World!\r\n");
	  HAL_Delay(1000);
  }
  /* USER CODE END 3 */

烧录后,打开我们的串口工具

我这里用的是正点原子的xcom串口助手

现在说明我们的串口发送是可以的了

然后我们需要串口接收

之前我们已经配置好了NVIC中断向量了

已经使能了串口中断

在HAL库中,串口如果发送中断会进入中断服务函数,然后在这个函数中他帮我们处理了很多东西,我们只需要调用一个回调函数,这个回调函数是弱定义,我们可以重新定义

在usart.c最下面添加代码:

cpp 复制代码
/* USER CODE BEGIN 1 */
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
	if (huart->Instance == USART1)
    {

		Uart1Rx.RxBuffer[Uart1Rx.RxDataCnt++] = Uart1Rx.RxData;   //接收数据转存
	
		if(Uart1Rx.RxDataCnt > RX_BUFFER_SIZE) 
		{
			memset(Uart1Rx.RxBuffer,0x00,sizeof(Uart1Rx.RxBuffer));
			Uart1Rx.RxDataCnt = 0;
		}
		
		if((Uart1Rx.RxBuffer[Uart1Rx.RxDataCnt - 2] == '\r' && Uart1Rx.RxBuffer[Uart1Rx.RxDataCnt - 1] == '\n')) //判断结束位
		{
			HAL_UART_Transmit(&huart1, (uint8_t *)&Uart1Rx.RxBuffer, Uart1Rx.RxDataCnt,0xFFFF); //将收到的信息发送出去
            while(HAL_UART_GetState(&huart1) == HAL_UART_STATE_BUSY_TX);//检测UART发送结束
			Uart1Rx.RxDataCnt = 0;
			memset(Uart1Rx.RxBuffer,0x00,sizeof(Uart1Rx.RxBuffer)); //清空数组
		}
	
	HAL_UART_Receive_IT(&huart1, (uint8_t *)&Uart1Rx.RxData, 1);   //再开启接收中断

	}
}
/* USER CODE END 1 */

这样子还不够,还需要在代码初始化时,先开启接收一次数据

cpp 复制代码
  /* USER CODE BEGIN 2 */
	HAL_UART_Receive_IT(&huart1, (uint8_t *)&Uart1Rx.RxData, 1);
  /* USER CODE END 2 */

烧录测试

补充

1.为什么使用printf需要在编译软件中勾选Use MicroLIB?

答:MicroLIB是一个针对ARM Cortex-M系列处理器优化的C库,相比标准C库,它更适用于资源有限的嵌入式系统,提供了更高效的空间和速度性能

资源优化:MicroLIB经过优化,更适合嵌入式系统的资源限制。

重定向支持 :通过MicroLIB,可以更容易地实现printf等标准输出函数的重定向,方便开发者使用这些函数输出调试信息到串口。

2.什么是重定向?

在编程中,重定向是指改变一个操作(比如输入输出)的默认行为,将其指向另一个方向或者设备。例如,将标准输出(通常是屏幕)重定向到打印机或文件。在STM32等嵌入式设备上,将printf的输出重定向到串口通讯,使得通过串口可以发送调试或其他信息到外部设备,如电脑终端。

printf通常不能直接使用来输出信息,因为标准的printf函数是用于在计算机上向终端或文件输出信息的,而微控制器一般使用串口(UART)作为与外界通信的手段。为了能够在串口上使用printf输出调试或其他信息,需要将printf重定向到串口,这就是"重定向"的含义。

相关推荐
XINVRY-FPGA1 小时前
10CL016YF484C8G Altera FPGA Cyclone
嵌入式硬件·网络协议·fpga开发·云计算·硬件工程·信息与通信·fpga
wei_shuo2 小时前
时序数据库 Apache IoTDB:从边缘到云端Apache IoTDB 全链路数据管理能力、部署流程与安全特性解读
物联网·apache·时序数据库·iotdb
Hero_11272 小时前
学习Stm32 的第一天
stm32·嵌入式硬件·学习
ye150127774555 小时前
DC6v-36V转3.2V1A恒流驱动芯片WT7017
单片机·嵌入式硬件·其他
时序数据说7 小时前
时序数据库市场前景分析
大数据·数据库·物联网·开源·时序数据库
scilwb15 小时前
RoboCon考核题——scilwb
单片机
点灯小铭16 小时前
基于STM32单片机智能RFID刷卡汽车位锁桩设计
stm32·单片机·汽车·毕业设计·课程设计
TDengine (老段)17 小时前
TDengine IDMP 高级功能(4. 元素引用)
大数据·数据库·人工智能·物联网·数据分析·时序数据库·tdengine
bai54593617 小时前
STM32 软件I2C读写MPU6050
stm32·单片机·嵌入式硬件
逼子格19 小时前
AT89C52单片机介绍
单片机·嵌入式硬件·51单片机·硬件工程师·硬件工程师真题·at89c52·器件手册