【STM32】STM32F4中USART的使用方法和Printf的重定义(基于CubeMX和Keil)

本文详细介绍了如何在STM32F407ZGT6单片机上通过STM32CubeMX生成代码,配置KeilIDE以使用MicroLIB重定义printf函数,以及解决串口通信中遇到的问题,包括串口号匹配、MicroLIB启用和调试助手选择等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、前言

主要目的:利用串口来收发一些数据
使用芯片:STM32F407ZGT6
使用函数:HAL库
使用工具:STM32CubeMX + Keil uVision5
串口工具:XCOM V2.6

二、STM32CubeMX生成代码

2.1 选择芯片

在这里插入图片描述

2.2 配置相关模式

主要配置内容:

  • 调试模式为Serial
  • 设置USART1为异步模式
    在这里插入图片描述
    在这里插入图片描述

2.3 生成代码

自己给工程命名然后选择打开的编译器

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

三、Keil重定义Printf

3.1 勾选“UseMicroLIB”

在魔术棒中打勾“UseMicroLIB”,否则即使我们include了stdio.h且编译器没没有报错的情况下仍然用不了printf。
在这里插入图片描述

3.2 添加头文件和修改fputc和fgetc

usart.c函数里面添加头文件,如下:

/* USER CODE BEGIN 0 */
#include <stdio.h>
/* USER CODE END 0 */

在这里插入图片描述
在这里插入图片描述

usart.c里面添加下面的代码。

/* USER CODE BEGIN 1 */
/**
  * 函数功能: 重定向c库函数printf到DEBUG_USARTx
  * 输入参数: 无
  * 返 回 值: 无
  * 说    明:无
  */
int fputc(int ch, FILE *f)
{
  HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xffff);
  return ch;
}
 
/**
  * 函数功能: 重定向c库函数getchar,scanf到DEBUG_USARTx
  * 输入参数: 无
  * 返 回 值: 无
  * 说    明:无
  */
int fgetc(FILE *f)
{
  uint8_t ch = 0;
  HAL_UART_Receive(&huart1, &ch, 1, 0xffff);
  return ch;
}
/* USER CODE END 1 */

在这里插入图片描述

四、测试Printf的效果

4.1 字符串测试

main.c函数的while1添加下面的代码进行测试:

    printf("终于可以使用printf函数了!!!\n");
    HAL_Delay(1000);

在这里插入图片描述
在这里插入图片描述

4.2 格式化输出测试

main.c函数的while1添加下面的代码进行测试:

  /* USER CODE BEGIN 2 */
  float f_num[] = {0.1,0.2,0.3};
  uint8_t i_num[3] = {1,2,3};
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    printf("终于可以使用printf函数了!!!\n");
    HAL_Delay(500);
    printf("试一下格式化输出 ==> 浮点数:%.3f,\t整数:%d\n", f_num[0], i_num[0]);
    HAL_Delay(500);
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */

在这里插入图片描述

五、存在问题的解决方法

5.1 检查串口号是否一致

2.2 配置相关模式中使用的是USART1,因此3.2 添加头文件和修改fputc和fgetc中的这两个函数也是用USART1,如果有问题,看看是否有对应上。
如果你使用的是USART2,那你就把这两个函数里面对应的修改一下,举例一下,就是将下面的huart1改为huart2.

int fputc(int ch, FILE *f)
{
  HAL_UART_Transmit(&huart2, (uint8_t *)&ch, 1, 0xffff);//修改这里的数字
  return ch;
}

5.2 打开MicroLIB

就是3.1 勾选“UseMicroLIB”的这一步,这个也是我解决一直没法使用printf的方法。

5.3 换一个串口调试助手

有可能是串口调试助手显示的问题,换一个试试看。

5.4 复位一下

有时候会忘记复位,程序可能还没启动。

5.5 对比博主的代码

上面的解决方法还是不行,你就对比我的上传的代码吧。
https://download.youkuaiyun.com/download/weixin_52296952/88804366

### 在HAL库中重定义printf函数以实现与接收中断相关的功能 在嵌入式开发中,使用HAL库时可以通过重定义`printf`函数来实现将调试信息输出到串口的功能。这种实现方式不仅简化了调试过程,还可以结合串口中断接收功能实现更复杂的应用场景。 #### 1. 重定义printf函数的实现 为了将`printf`函数的输出重定向到串口,需要在项目中包含标准输入输出头文件,并通过`HAL_UART_Transmit`函数将数据发送到指定的UART外设[^2]。以下是一个典型的实现代码: ```c #include <stdio.h> #define printf(...) HAL_UART_Transmit(&huart1, (uint8_t *)u_buf, sprintf((char*)u_buf, __VA_ARGS__), 0xffff) extern uint8_t u_buf[256]; // 定义一个全局缓冲区用于存储printf输出内容 ``` 上述代码中,`printf`被重定义为调用`HAL_UART_Transmit`函数,将格式化后的字符串通过UART发送出去。`u_buf`是一个全局缓冲区,用于存储格式化后的字符串[^2]。 #### 2. 在接收中断环境下的应用 在接收中断环境下,可以通过回调函数处理接收到的数据,并结合`printf`函数输出调试信息。例如,在串口中断回调函数中可以添加如下代码: ```c void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { if (huart->Instance == USART1) { // 处理接收到的数据 printf("Received byte: %c\r\n", aRxBuffer); // 继续接收下一个字节 HAL_UART_Receive_IT(&huart1, (uint8_t *)&aRxBuffer, 1); } } ``` 上述代码中,每当接收到一个字节时,都会通过`printf`函数将接收到的字节打印到串口。这种方式可以帮助开发者实时监控接收到的数据流[^3]。 #### 3. 注意事项 - 确保全局缓冲区`u_buf`的大小足够大以容纳格式化后的字符串,避免溢出。 - 在高频率接收环境中,注意`printf`函数可能带来的延迟影响,避免阻塞中断处理逻辑。 - 如果需要处理多路串口的接收发送,需为每个UART外设单独定义缓冲区处理逻辑。 #### 4. 示例:结合定时器中断串口接收中断 以下示例展示了如何结合定时器中断串口接收中断实现复杂的交互逻辑: ```c void TIM3_IRQHandler(void) { HAL_TIM_IRQHandler(&htim3); // 调用定时器中断处理函数 } void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { if (htim->Instance == TIM3) { // 定时器中断回调函数 printf("Timer interrupt occurred!\r\n"); } } void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { if (huart->Instance == USART1) { // 串口中断回调函数 printf("Received byte: %c\r\n", aRxBuffer); // 根据接收到的数据控制定时器行为 if (aRxBuffer == 'S') { __HAL_TIM_SET_COUNTER(&htim3, 0); // 重置定时器计数器 } // 继续接收下一个字节 HAL_UART_Receive_IT(&huart1, (uint8_t *)&aRxBuffer, 1); } } ``` 上述代码中,当接收到特定字符(如'S')时,会通过`printf`函数输出信息并控制定时器的行为[^1]。 ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值