stm32单片机串口使用printf及u3_printf

本文介绍在STM32单片机上如何为不同串口配置类似printf的输出函数,包括修改printf函数使其适用于多个串口的方法,以及在HAL库环境下实现自定义串口输出函数的具体步骤。

无论是在51单片机还是在stm32,默认printf串口都是串口一。使用printf的时候头文件为"stdio.h",但是一些外部设备与单片机连接的时候使用的是串口(通常物联网用到的ESP8266,SIM9600等都是通过串口发送AT指令进行模式的配置的),但是printf函数却只有一个。在单片机中printf函数默认为串口一。所以试想能不能其他串口也组成类似于printf的函数。

printf()

引入头文件

#include <stdio.h>

关键部分代码

#ifdef __GNUC__
/* With GCC, small printf (option LD Linker->Libraries->Small printf
   set to 'Yes') calls __io_putchar() */
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif /* __GNUC__ */

/* USER CODE BEGIN 1 */
PUTCHAR_PROTOTYPE
{
  /* Place your implementation of fputc here */
  /* e.g. write a character to the USART2 and Loop until the end of transmission */
  HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xFFFF);
  return ch;
}

u3_printf()

引入头文件

#include <stdarg.h>

其文件内容部分为

#ifdef __clang__
  typedef __builtin_va_list va_list;
  #define va_start(ap, param) __builtin_va_start(ap, param)
  #define va_end(ap)          __builtin_va_end(ap)
  #define va_arg(ap, type)    __builtin_va_arg(ap, type)
  #if __STDC_VERSION__ >= 199900L || __cplusplus >= 201103L || !defined(__STRICT_ANSI__)
  #define va_copy(dest, src)  __builtin_va_copy(dest, src)
  #endif
#else
  #ifdef __TARGET_ARCH_AARCH64
    typedef struct __va_list {
      void *__stack;
      void *__gr_top;
      void *__vr_top;
      int __gr_offs;
      int __vr_offs;
    } va_list;
  #else
    typedef struct __va_list { void *__ap; } va_list;
  #endif

va_start,va_end,va_arg()是定义其地址,及偏移地址。
核心部分代码
1.HAL库函数版本

void u3_printf(char* fmt,...)  
{
	  u8 buffer[200];
	  u16 i;
	  va_list ap;
	  va_start(ap,fmt);          			//ap指向fmt的地址
	  i = vsprintf(buffer,fmt,ap);	                //vsprintf返回数组的长度
	  va_end(ap);

	  HAL_UART_Transmit(&huart3,(u8 *)buffer,i,0X00FF);
}

2.标准库函数版本

void u3_printf(char* fmt,...)  
{  
	u16 i,j; 
	va_list ap; 
	va_start(ap,fmt);
	vsprintf((char*)USART3_TX_BUF,fmt,ap);
	va_end(ap);
	i=strlen((const char*)USART3_TX_BUF);		//得到数据长度
	for(j=0;j<i;j++)				//循环发送数据
	{
	  while(USART_GetFlagStatus(USART3,USART_FLAG_TC)==RESET); 
		USART_SendData(USART3,USART3_TX_BUF[j]); 
	} 
}

以HAL库为例,配置串口文件(直接可以拷贝引用)

/**
  ******************************************************************************
  * File Name          : USART.c
  * Description        : This file provides code for the configuration
  *                      of the USART instances.
  ******************************************************************************
  ** This notice applies to any and all portions of this file
  * that are not between comment pairs USER CODE BEGIN and
  * USER CODE END. Other portions of this file, whether 
  * inserted by the user or by software development tools
  * are owned by their respective copyright owners.
  *
  * COPYRIGHT(c) 2018 STMicroelectronics
  *
  * Redistribution and use in source and binary forms, with or without modification,
  * are permitted provided that the following conditions are met:
  *   1. Redistributions of source code must retain the above copyright notice,
  *      this list of conditions and the following disclaimer.
  *   2. Redistributions in binary form must reproduce the above copyright notice,
  *      this list of conditions and the following disclaimer in the documentation
  *      and/or other materials provided with the distribution.
  *   3. Neither the name of STMicroelectronics nor the names of its contributors
  *      may be used to endorse or promote products derived from this software
  *      without specific prior written permission.
  *
  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
  ******************************************************************************
  */

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

#include "gpio.h"
#include "time7.h"
/* USER CODE BEGIN 0 */
#include "stdarg.h"	 
#include "string.h"
#include "stdio.h"
/* USER CODE END 0 */

#ifdef __GNUC__
/* With GCC, small printf (option LD Linker->Libraries->Small printf
   set to 'Yes') calls __io_putchar() */
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif /* __GNUC__ */





UART_HandleTypeDef huart1;
UART_HandleTypeDef huart3;

/* USART1 init function */

void MX_USART1_UART_Init(void)
{

  huart1.Instance = USART1;
  huart1.Init.BaudRate = 115200;
  huart1.Init.WordLength = UART_WORDLENGTH_8B;
  huart1.Init.StopBits = UART_STOPBITS_1;
  huart1.Init.Parity = UART_PARITY_NONE;
  huart1.Init.Mode = UART_MODE_TX_RX;
  huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  huart1.Init.OverSampling = UART_OVERSAMPLING_16;
  if (HAL_UART_Init(&huart1) != HAL_OK)
  {
    _Error_Handler(__FILE__, __LINE__);
  }
}
/* USART3 init function */

void MX_USART3_UART_Init(void)
{

  huart3.Instance = USART3;
  huart3.Init.BaudRate = 115200;
  huart3.Init.WordLength = UART_WORDLENGTH_8B;
  huart3.Init.StopBits = UART_STOPBITS_1;
  huart3.Init.Parity = UART_PARITY_NONE;
  huart3.Init.Mode = UART_MODE_TX_RX;
  huart3.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  huart3.Init.OverSampling = UART_OVERSAMPLING_16;
  if (HAL_UART_Init(&huart3) != HAL_OK)
  {
    _Error_Handler(__FILE__, __LINE__);
  }
}

void HAL_UART_MspInit(UART_HandleTypeDef* uartHandle)
{

  GPIO_InitTypeDef GPIO_InitStruct;
  if(uartHandle->Instance==USART1)
  {
  /* USER CODE BEGIN USART1_MspInit 0 */

  /* USER CODE END USART1_MspInit 0 */
    /* USART1 clock enable */
    __HAL_RCC_USART1_CLK_ENABLE();
  
    /**USART1 GPIO Configuration    
    PA9     ------> USART1_TX
    PA10     ------> USART1_RX 
    */
    GPIO_InitStruct.Pin = GPIO_PIN_9;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    GPIO_InitStruct.Pin = GPIO_PIN_10;
    GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    /* USART1 interrupt Init */
    HAL_NVIC_SetPriority(USART1_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(USART1_IRQn);
  /* USER CODE BEGIN USART1_MspInit 1 */

  /* USER CODE END USART1_MspInit 1 */
  }
  else if(uartHandle->Instance==USART3)
  {
  /* USER CODE BEGIN USART3_MspInit 0 */

  /* USER CODE END USART3_MspInit 0 */
    /* USART3 clock enable */
    __HAL_RCC_USART3_CLK_ENABLE();
  
    /**USART3 GPIO Configuration    
    PB10     ------> USART3_TX
    PB11     ------> USART3_RX 
    */
    GPIO_InitStruct.Pin = GPIO_PIN_10;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    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);

    /* USART3 interrupt Init */
		__HAL_UART_ENABLE_IT(&huart3,UART_IT_RXNE);		
    HAL_NVIC_SetPriority(USART3_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(USART3_IRQn);
		TIM7_Int_Init(1000-1,9000-1);		
		USART3_RX_STA=0;		
		TIM7->CR1&=~(1<<0);     
  /* USER CODE BEGIN USART3_MspInit 1 */

  /* USER CODE END USART3_MspInit 1 */
  }
}

void HAL_UART_MspDeInit(UART_HandleTypeDef* uartHandle)
{

  if(uartHandle->Instance==USART1)
  {
  /* USER CODE BEGIN USART1_MspDeInit 0 */

  /* USER CODE END USART1_MspDeInit 0 */
    /* Peripheral clock disable */
    __HAL_RCC_USART1_CLK_DISABLE();
  
    /**USART1 GPIO Configuration    
    PA9     ------> USART1_TX
    PA10     ------> USART1_RX 
    */
    HAL_GPIO_DeInit(GPIOA, GPIO_PIN_9|GPIO_PIN_10);

    /* USART1 interrupt Deinit */
    HAL_NVIC_DisableIRQ(USART1_IRQn);
  /* USER CODE BEGIN USART1_MspDeInit 1 */

  /* USER CODE END USART1_MspDeInit 1 */
  }
  else if(uartHandle->Instance==USART3)
  {
  /* USER CODE BEGIN USART3_MspDeInit 0 */

  /* USER CODE END USART3_MspDeInit 0 */
    /* Peripheral clock disable */
    __HAL_RCC_USART3_CLK_DISABLE();
  
    /**USART3 GPIO Configuration    
    PB10     ------> USART3_TX
    PB11     ------> USART3_RX 
    */
    HAL_GPIO_DeInit(GPIOB, GPIO_PIN_10|GPIO_PIN_11);

    /* USART3 interrupt Deinit */
    HAL_NVIC_DisableIRQ(USART3_IRQn);
  /* USER CODE BEGIN USART3_MspDeInit 1 */

  /* USER CODE END USART3_MspDeInit 1 */
  }
} 

/* USER CODE BEGIN 1 */
PUTCHAR_PROTOTYPE
{
  /* Place your implementation of fputc here */
  /* e.g. write a character to the USART2 and Loop until the end of transmission */
  HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xFFFF);
  return ch;
}

void u3_printf(char* fmt,...)  
{
	  u8 buffer[200];
	  u16 i;
	  va_list ap;
	  va_start(ap,fmt);          				
	  i = vsprintf(buffer,fmt,ap);			
	  va_end(ap);

	  HAL_UART_Transmit(&huart3,(u8 *)buffer,i,0X00FF);

}
/* USER CODE END 1 */

/**
  * @}
  */

/**
  * @}
  */

/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

由于printf为发送函数,在串口的中断处理函数中。可根据实际情况编写接受程序。

### u3_printf 函数定义及用法 `u3_printf` 并不是一个标准的 C 库函数,而是一个可能被开发者自定义实现的函数。通常情况下,这种命名方式表明它是针对某个特定设备或模块(如 USART 或 UART)设计的一个打印函数[^1]。 #### 自定义 `u3_printf` 的典型实现 在嵌入式开发中,尤其是基于 STM32 微控制器的应用场景下,开发者经常需要重新定义 `printf` 函数的行为,使其能够通过串口或其他外设输出数据。以下是 `u3_printf` 的一种常见实现: ```c #include <stdio.h> #include <stdarg.h> // 假设 Usart3_SendString 是一个已经实现好的函数, // 它可以将字符串发送到 USART3 外设。 void Usart3_SendString(const char *str); // 使用可变参数实现 u3_printf int u3_printf(const char *format, ...) { va_list args; static char buffer[256]; // 缓冲区大小可以根据实际需求调整 va_start(args, format); vsnprintf(buffer, sizeof(buffer), format, args); // 将格式化的字符串写入缓冲区 va_end(args); Usart3_SendString(buffer); // 调用已有的串口发送函数来传输数据 return strlen(buffer); // 返回输出的字符数量 } ``` 在此代码片段中: - `vsnprintf` 是一个标准库函数,用于将格式化后的字符串存储到指定的缓冲区内[^3]。 - `va_list`, `va_start`, `va_end` 等宏提供了对可变参数的支持,允许函数接收不定数量的参数[^3]。 #### 参数说明 - **`const char *format`**: 这是格式控制字符串,类似于标准 `printf` 中使用的格式字符串。 - **`...`**: 表示后续传递的一系列参数,这些参数的数量和类型由格式字符串决定。 #### 输出机制 假设 `Usart3_SendString` 已经实现了向 USART3 发送字符串的功能,则每次调用 `u3_printf` 都会先将格式化后的字符串保存至本地缓冲区,再将其逐字节传送到串口中[^1]。 --- ### 注意事项 当使用此类自定义打印函数时需要注意以下点: 1. **缓冲区溢出风险**:应确保分配给临时存储区域的空间足够大以容纳最长预期输出内容[^3]。 2. **线程安全性**:如果程序运行在一个多任务环境中,那么访问共享资源 (比如上面提到的静态变量 `buffer`) 时需考虑同步问题。 3. **性能影响**:频繁调用涉及大量数据拷贝的操作可能会降低整体效率,在实时性强的任务里尤其如此。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值