在STM32中使用printf()和scanf

在STM32中使用printf()和scanf

本文出处: https://shawnhymel.com/1873/how-to-use-printf-on-stm32/

作者:Shawn Hymel

内容来源于网络,本文只做翻译和学习,已获得原文作者许可转载,欢迎访问原文章。

一般来说,大多数微控制器没有控制台的概念。而在调试时将信息输出到某种控制台非常重要。一种方式是在STM32CubeIDE 中使用半主机。但是半主机可能会很慢。
另一个选择是通过串口 (UART) 输出调试信息。我们可以调用STM32的HAL函数(例如HAL_UART_Transmit)来使用串口。
但是,如果可以使用C标准库中的printf、scanf等会更方便。因此需要重新编写底层函数。

注意: 本文的代码摘自 Carmine Noviello 的Mastering STM32 书。我只是更新了如何在 STM32Cube 中重新配置的过程。所有功劳都归功于 Carmine Noviello 的代码。如果您愿意,欢迎您编写自己的 retarget.h 和 retarget.c 文件。

新建一个默认的 STM32 工程,配置启用 UART,保存并生成代码。

第一件事是禁用 syscalls.c。这个文件定义了许多函数名与我们将要创建的相同。编译时禁用它可以避免出现“多重定义”错误。例如:

multiple definition of `_isatty'

右键单击 syscalls.c文件并选择 Properties。在 C/C++ Build > Settings 下,选中 Exclude resource from build


单击应用并关闭。工程文件资源管理器中的文件名现在应该是灰色的。

这样我们就可以在 STM32CubeIDE 中编译和链接时不包含文件(或文件夹)。该过程与大多数其他基于 Eclipse 的 IDE 相似。

扫描二维码关注公众号,回复: 13468279 查看本文章

Inc目录中创建一个名为 retarget.h的新文件 。将以下代码复制到此文件中并保存:

// All credit to Carmine Noviello for this code
// https://github.com/cnoviello/mastering-stm32/blob/master/nucleo-f030R8/system/include/retarget/retarget.h

#ifndef _RETARGET_H__
#define _RETARGET_H__

#include "stm32l4xx_hal.h"
#include <sys/stat.h>

void RetargetInit(UART_HandleTypeDef *huart);
int _isatty(int fd);
int _write(int fd, char* ptr, int len);
int _close(int fd);
int _lseek(int fd, int ptr, int dir);
int _read(int fd, char* ptr, int len);
int _fstat(int fd, struct stat* st);

#endif //#ifndef _RETARGET_H__

Src目录中创建一个名为 retarget.c的新文件 。将以下代码复制到此文件中并保存:

// All credit to Carmine Noviello for this code
// https://github.com/cnoviello/mastering-stm32/blob/master/nucleo-f030R8/system/src/retarget/retarget.c

#include <_ansi.h>
#include <_syslist.h>
#include <errno.h>
#include <sys/time.h>
#include <sys/times.h>
#include <limits.h>
#include <signal.h>
#include <../Inc/retarget.h>
#include <stdint.h>
#include <stdio.h>

#if !defined(OS_USE_SEMIHOSTING)

#define STDIN_FILENO  0
#define STDOUT_FILENO 1
#define STDERR_FILENO 2

UART_HandleTypeDef *gHuart;

void RetargetInit(UART_HandleTypeDef *huart) {
    
    
  gHuart = huart;

  /* Disable I/O buffering for STDOUT stream, so that
   * chars are sent out as soon as they are printed. */
     /* 禁用 STDOUT 流的 I/O 缓冲,以便
   * 字符在打印后立即发送出去。*/ 
  setvbuf(stdout, NULL, _IONBF, 0);
}

int _isatty(int fd) {
    
    
  if (fd >= STDIN_FILENO && fd <= STDERR_FILENO)
    return 1;

  errno = EBADF;
  return 0;
}

int _write(int fd, char* ptr, int len) {
    
    
  HAL_StatusTypeDef hstatus;

  if (fd == STDOUT_FILENO || fd == STDERR_FILENO) {
    
    
    hstatus = HAL_UART_Transmit(gHuart, (uint8_t *) ptr, len, HAL_MAX_DELAY);
    if (hstatus == HAL_OK)
      return len;
    else
      return EIO;
  }
  errno = EBADF;
  return -1;
}

int _close(int fd) {
    
    
  if (fd >= STDIN_FILENO && fd <= STDERR_FILENO)
    return 0;

  errno = EBADF;
  return -1;
}

int _lseek(int fd, int ptr, int dir) {
    
    
  (void) fd;
  (void) ptr;
  (void) dir;

  errno = EBADF;
  return -1;
}

int _read(int fd, char* ptr, int len) {
    
    
  HAL_StatusTypeDef hstatus;

  if (fd == STDIN_FILENO) {
    
    
    hstatus = HAL_UART_Receive(gHuart, (uint8_t *) ptr, 1, HAL_MAX_DELAY);
    if (hstatus == HAL_OK)
      return 1;
    else
      return EIO;
  }
  errno = EBADF;
  return -1;
}

int _fstat(int fd, struct stat* st) {
    
    
  if (fd >= STDIN_FILENO && fd <= STDERR_FILENO) {
    
    
    st->st_mode = S_IFCHR;
    return 0;
  }

  errno = EBADF;
  return 0;
}

#endif //#if !defined(OS_USE_SEMIHOSTING)

您的工程目录结构应如下所示:

注意,我们添加了 retarget.hretarget.c。并且不编译 syscalls.c 文件 ,但该文件仍然存在于我们的目录中。

main.c文件中包含 stdio.hretarget.h 头文件,就可以使用 printf 和 scanf了,如下所示:

/* Includes ------------------------------------------------------------------*/
#include "main.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include <stdio.h>
#include "retarget.h"
/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */

/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/
UART_HandleTypeDef huart2;

/* USER CODE BEGIN PV */

/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_USART2_UART_Init(void);
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */

/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
    
    
  /* USER CODE BEGIN 1 */
  char buf[100];
  /* USER CODE END 1 */
  

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_USART2_UART_Init();
  /* USER CODE BEGIN 2 */
  RetargetInit(&huart2);
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    
    
    printf("\r\nYour name: ");
    scanf("%s", buf);
    printf("\r\nHello, %s!\r\n", buf);
    /* USER CODE END WHILE */

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

// ***REST OF INITIALIZATION CODE NOT SHOWN***

编译此项目并在 STM32CubeIDE 中打开调试窗口。打开串口调试软件,连接到单片机的 COM 口(波特率为 115200,8-N-1)。进入轮询后应该会对你表示欢迎。输入一些回复并按enter
注意,你将无法看到自己输入的内容。但是,程序会把你输入的内容添加加到“Hello”字符串后并输出。

![
还有一点,尽量不要溢出分配的字符缓冲区,否则会发生一些莫名的错误。可以在代码中编写某种越界检查。无论如何,这只是一个开始,希望能帮助你调试 STM32 项目。

猜你喜欢

转载自blog.csdn.net/weixin_45636061/article/details/117969437