printf重定向

Posted 旭日初扬

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了printf重定向相关的知识,希望对你有一定的参考价值。

 

目录

 

一、printf重定向简介

1.1、fputc函数解析

1.1.1、0x01FF 解析

串口,最大支持数据位长度为9bit。(DATA) <= 0x1FF 是断言检测数据位是否不大于9bit     0001  1111 1111

1.1.2、宏定义的参数检查函数(  assert_param())

1.1.3、USART数据寄存器(DR)

1.1.4、USART结构框图

1.2、USART_GetFlagStatus()函数

 1.3、USART_ClearFlag()

二、printf函数格式

2.1、常用格式化规定字符:

三、编写printf重定向


一、printf重定向简介

c语言中printf函数默认输出设备是显示器,如果实现在串口或者LCD上显示,必须重定义标准库函数里面调用的输出设备定义的相关函数。

如:使用printf输出到串口,需要将fputc里面的输出指向串口,这一过程称为重定向。

 

int fputc(int ch,FILE *p)  //  函数默认的,在使用printf函数时自动调用

{

USART_SendDate(USART1,(u8)ch);

while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET);

return ch;

}
//  此函数定义在stdio.h中,调用此函数需先导入stdio.h头文件

1.1、fputc函数解析

使用此函数需要导入stdio.h头文件

 

/**
  * @brief  Transmits single data through the USARTx peripheral.   通过USARTx外设传输单个数据
  * @param  USARTx: Select the USART or the UART peripheral.  选择USART或UART外设
  *   This parameter can be one of the following values:
  *   USART1, USART2, USART3, UART4 or UART5.
  * @param  Data: the data to transmit.                                            传输的数据
  * @retval None
  */
void USART_SendData(USART_TypeDef* USARTx, uint16_t Data)

/*

 

 

*/
{
  /* Check the parameters */
  assert_param(IS_USART_ALL_PERIPH(USARTx));

/*#define IS_USART_ALL_PERIPH(PERIPH) (((PERIPH) == USART1) || \\
                                     ((PERIPH) == USART2) || \\
                                     ((PERIPH) == USART3) || \\
                                     ((PERIPH) == UART4) || \\
                                     ((PERIPH) == UART5))

    USART1, USART2, USART3, UART4 or UART5

*/
  assert_param(IS_USART_DATA(Data)); 
   /*

#define IS_USART_DATA(DATA) ((DATA) <= 0x1FF)   // 串口,最大支持数据位长度为9bit。(DATA) <= 0x1FF 是断言检测数据位是否不大于9bit     0001  1111 1111

*/
  /* Transmit Data */
  USARTx->DR = (Data & (uint16_t)0x01FF);        Data  & 0000 0001 1111 1111
}

1.1.1、0x01FF 解析

十进制:  1*16*16 + 16*15 +15 =  511

                0*16*16*16(16的3次方) +   1*16*16(16平方)+15*16(F的十进制位15,16的一次方)+15*16的0次方

 二进制      0000 0001  1111  1111

                    左边的0000  可省略不改变其真值

八进制:

串口,最大支持数据位长度为9bit。(DATA) <= 0x1FF 是断言检测数据位是否不大于9bit     0001  1111 1111

 

1.1.2、宏定义的参数检查函数(  assert_param())

#ifdef  USE_FULL_ASSERT

/**
  * @brief  The assert_param macro is used for function's parameters check.
            assert参数宏用于函数的参数检查。
  * @param  expr: If expr is false, it calls assert_failed function which reports 
  *         the name of the source file and the source line number of the call 
  *         that failed. If expr is true, it returns no value.
            expr:如果expr为false,它将调用assert失败的函数,该函数返回失败调用的源文件的名称和源行号

            如果expr为真,则不返回任何值
  * @retval None
  */
  #define assert_param(expr) ((expr) ? (void)0 : assert_failed((uint8_t *)__FILE__, __LINE__))  // 调用失败
/* Exported functions ------------------------------------------------------- */
  void assert_failed(uint8_t* file, uint32_t line);                                                                 //  调用此函数  显示调用失败的文件  与代码行
#else
  #define assert_param(expr) ((void)0)                     // 调用成功
#endif /* USE_FULL_ASSERT */

1.1.3、USART数据寄存器(DR)

1.1.4、USART结构框图

 

1.2、USART_GetFlagStatus()函数

/**
  * @brief  Checks whether the specified USART flag is set or not.
             检测设置的串口标志是否修改
  * @param  USARTx: Select the USART or the UART peripheral. 
                    选择串口或串口外设
  *   This parameter can be one of the following values:
              这个参数有以下值
  *   USART1, USART2, USART3, UART4 or UART5.
  * @param  USART_FLAG: specifies the flag to check.
                         检查设置标志
  *   This parameter can be one of the following values:
  *     @arg USART_FLAG_CTS:  CTS Change flag (not available for UART4 and UART5)
                              CTS 改变标志     (不适合于串口2和串口4)
  *     @arg USART_FLAG_LBD:  LIN Break detection flag
                               Lin 中断检查标志
  *     @arg USART_FLAG_TXE:  Transmit data register empty flag
                               发送寄存器 空的标志
  *     @arg USART_FLAG_TC:   Transmission Complete flag
                               发送完成标志
  *     @arg USART_FLAG_RXNE: Receive data register not empty flag
                                接收数据寄存器不为空的标志
  *     @arg USART_FLAG_IDLE: Idle Line detection flag
                              空闲线路探测标志
  *     @arg USART_FLAG_ORE:  OverRun Error flag
                              覆盖错误标志
  *     @arg USART_FLAG_NE:   Noise Error flag
                              噪声错误标志
  *     @arg USART_FLAG_FE:   Framing Error flag
                                框架错误标志
  *     @arg USART_FLAG_PE:   Parity Error flag
                              奇偶校验错误标志
  * @retval The new state of USART_FLAG (SET or RESET).
  */
FlagStatus USART_GetFlagStatus(USART_TypeDef* USARTx, uint16_t USART_FLAG)
{
  FlagStatus bitstatus = RESET;
  /* Check the parameters */
  assert_param(IS_USART_ALL_PERIPH(USARTx));
  assert_param(IS_USART_FLAG(USART_FLAG));
  /* The CTS flag is not available for UART4 and UART5 */
  if (USART_FLAG == USART_FLAG_CTS)
  {
    assert_param(IS_USART_123_PERIPH(USARTx));
  }  
  
  if ((USARTx->SR & USART_FLAG) != (uint16_t)RESET) //  SR状态寄存器
  {
    bitstatus = SET;
  }
  else
  {
    bitstatus = RESET;
  }
  return bitstatus;
}

 

USART1->SR&0X40  !=set or reset

  •  串口1操作状态寄存器的第7位  置0或置1
  • stm32中很多标记位,只能硬件置1,软件写1是没有效果的,但是可以软件写0。

while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET);  // USART_FLAG_TXE 发送数据寄存器空标志位

 1.3、USART_ClearFlag()

功能:清除 USARTx 的待处理标志位。

注意:

  •  PE (Parity error 奇偶错误 ), FE (Framing error帧错误), NE (Noise error噪声错误), ORE (OverRun    error 溢出错误) and IDLE (Idle line detected  检测到空闲线路错误) flags are cleared by software sequence: a read operation to USART_SR register (USART_GetFlagStatus())    followed by a read operation to USART_DR register (USART_ReceiveData()).  - RXNE flag can be also cleared by a read to the USART_DR register  (USART_ReceiveData()).
  • PE (Parity error), FE (Framing error), NE (Noise error), ORE (OverRun error)和IDLE (IDLE line detected)标志被软件序列清除:一个读操作到USART SR寄存器(USART GetFlagStatus()),然后一个读操作到USART DR。
  • TC标志也可以通过软件序列清除:一个到USART SR寄存器的读操作(USART GetFlagStatus()),接着一个到USART DR寄存器的写操作(USART SendData())。
  • TXE标志仅通过写入USART DR寄存器来清除 (USART SendData())。
/**
  * @brief  Clears the USARTx's pending flags.
  * @param  USARTx: Select the USART or the UART peripheral. 
  *   This parameter can be one of the following values:
  *   USART1, USART2, USART3, UART4 or UART5.
  * @param  USART_FLAG: specifies the flag to clear.
  *   This parameter can be any combination of the following values:
  *     @arg USART_FLAG_CTS:  CTS Change flag (not available for UART4 and UART5).
  *     @arg USART_FLAG_LBD:  LIN Break detection flag.
  *     @arg USART_FLAG_TC:   Transmission Complete flag.
  *     @arg USART_FLAG_RXNE: Receive data register not empty flag.
  *   
  * @note
  *   - PE (Parity error), FE (Framing error), NE (Noise error), ORE (OverRun 
  *     error) and IDLE (Idle line detected) flags are cleared by software 
  *     sequence: a read operation to USART_SR register (USART_GetFlagStatus()) 
  *     followed by a read operation to USART_DR register (USART_ReceiveData()).
  *   - RXNE flag can be also cleared by a read to the USART_DR register 
  *     (USART_ReceiveData()).
  *   - TC flag can be also cleared by software sequence: a read operation to 
  *     USART_SR register (USART_GetFlagStatus()) followed by a write operation
  *     to USART_DR register (USART_SendData()).
  *   - TXE flag is cleared only by a write to the USART_DR register 
  *     (USART_SendData()).
  * @retval None
  */
void USART_ClearFlag(USART_TypeDef* USARTx, uint16_t USART_FLAG)
{
  /* Check the parameters */
  assert_param(IS_USART_ALL_PERIPH(USARTx));
  assert_param(IS_USART_CLEAR_FLAG(USART_FLAG));
  /* The CTS flag is not available for UART4 and UART5 */
  if ((USART_FLAG & USART_FLAG_CTS) == USART_FLAG_CTS)
  {
    assert_param(IS_USART_123_PERIPH(USARTx));
  } 

二、printf函数格式

printf("<格式化字符串>",<参量表>)

 

2.1、常用格式化规定字符:

  • %d   按照十进制整形数打印
  • %6d  按照十进制整形数打印,至少6个字符宽
  • %f     按照浮点数打印
  • %6f  按照浮点数打印,至少6个字符宽
  • %.2f  按照浮点数打印,小数点后2位小数
  • %6.2f 按照浮点数打印,至少6个字符宽,小数点后有2位小数
  • %x  按照十六进制打印
  • %c  打印字符
  • %s  打印字符串

 

三、编写printf重定向

初始化 USART1 后,就需要将 fputc 里面的输出指向 STM32 的串口
int fputc(int ch,FILE *p)  //函数默认的,在使用printf函数时自动调用
{
	USART_SendData(USART1,(u8)ch);	
	while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET);  // USART_FLAG_TXE 发送数据寄存器空标志位
	return ch;
}
当使用 printf 函数时,自动会调用 fputc 函数,而 fputc 函数内又将输出 设备重定义为 STM32 的 USART1,所以要输出的数据就会在串口 1 上输出
 
使用printf函数的keil配置 
 
 
 
主函数
#include "system.h"
#include "SysTick.h"
#include "led.h"
#include "usart.h"


/*******************************************************************************
* 函 数 名         : main
* 函数功能		   : 主函数
* 输    入         : 无
* 输    出         : 无
*******************************************************************************/
int main()
{
	u8 i=0; 
	u16 data=1234;
	float fdata=12.34;
	char str[]="Hello World!";	
	SysTick_Init(72);
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);  //中断优先级分组 分2组
	LED_Init();
	USART1_Init(115200);
	
	while(1)
	{
		i++;
		if(i%50==0)
		{
			LED1=!LED1;
			
			printf("输出整型数data=%d\\r\\n",data);
			printf("输出浮点型数fdata=%0.2f\\r\\n",fdata);
			printf("输出十六进制数data=%X\\r\\n",data);
			printf("输出八进制数data=%o\\r\\n",data);
			printf("输出字符串str=%s\\r\\n",str);
			
		}
		delay_ms(10); // i每次自加的延时时间   500ms LED状态翻转一次  50*10ms = 500ms
	}
}

usart .c

#include "usart.h"		 

int fputc(int ch,FILE *p)  //函数默认的,在使用printf函数时自动调用
{
	USART_SendData(USART1,(u8)ch);	
	while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET);  // USART_FLAG_TXE 发送数据寄存器空标志位
	return ch;
}

//串口1中断服务程序
//注意,读取USARTx->SR能避免莫名其妙的错误   	
u8 USART1_RX_BUF[USART1_REC_LEN];     //接收缓冲,最大USART_REC_LEN个字节.
//接收状态
//bit15,	接收完成标志
//bit14,	接收到0x0d
//bit13~0,	接收到的有效字节数目
u16 USART1_RX_STA=0;       //接收状态标记


/*******************************************************************************
* 函 数 名         : USART1_Init
* 函数功能		   : USART1初始化函数
* 输    入         : bound:波特率
* 输    出         : 无
*******************************************************************************/ 
void USART1_Init(u32 bound)
{
   //GPIO端口设置
	GPIO_InitTypeDef GPIO_InitStructure;
	USART_InitTypeDef USART_InitStructure;
	NVIC_InitTypeDef NVIC_InitStructure;
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
 
	
	/*  配置GPIO的模式和IO口 */
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_9;//TX			   //串口输出PA9
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;	    //复用推挽输出
	GPIO_Init(GPIOA,&GPIO_InitStructure);  /* 初始化串口输入IO */
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_10;//RX			 //串口输入PA10
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING;		  //模拟输入
	GPIO_Init(GPIOA,&GPIO_InitStructure); /* 初始化GPIO */
	

	//USART1 初始化设置
	USART_InitStructure.USART_BaudRate = bound;//波特率设置
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式
	USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位
	USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制
	USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;	//收发模式
	USART_Init(USART1, &USART_InitStructure); //初始化串口1
	
	USART_Cmd(USART1, ENABLE);  //使能串口1 
	
	USART_ClearFlag(USART1, USART_FLAG_TC);
		
	USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启相关中断

	//Usart1 NVIC 配置
	NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;//串口1中断通道
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3;//抢占优先级3
	NVIC_InitStructure.NVIC_IRQChannelSubPriority =3;		//子优先级3
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;			//IRQ通道使能
	NVIC_Init(&NVIC_InitStructure);	//根据指定的参数初始化VIC寄存器、	
}

/*******************************************************************************
* 函 数 名         : USART1_IRQHandler
* 函数功能		   : USART1中断函数
* 输    入         : 无
* 输    出         : 无
*******************************************************************************/ 
void USART1_IRQHandler(void)                	//串口1中断服务程序
{
	u8 r;
	if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)  //接收中断
	{
		r =USART_ReceiveData(USART1);//(USART1->DR);	//读取接收到的数据
		if((USART1_RX_STA&0x8000)==0)//接收未完成
		{
			if(USART1_RX_STA&0x4000)//接收到了0x0d
			{
				if(r!=0x0a)USART1_RX_STA=0;//接收错误,重新开始
				else USART1_RX_STA|=0x8000;	//接收完成了 
			}
			else //还没收到0X0D
			{	
				if(r==0x0d)USART1_RX_STA|=0x4000;
				else
				{
					USART1_RX_BUF[USART1_RX_STA&0X3FFF]=r;
					USART1_RX_STA++;
					if(USART1_RX_STA>(USART1_REC_LEN-1))USART1_RX_STA=0;//接收数据错误,重新开始接收	  
				}		 
			}
		}   		 
	} 
}

usart.h

#ifndef _usart_H
#define _usart_H

#include "system.h" 
#include "stdio.h" 

#define USART1_REC_LEN		200  	//定义最大接收字节数 200

extern u8  USART1_RX_BUF[USART1_REC_LEN]; //接收缓冲,最大USART_REC_LEN个字节.末字节为换行符 
extern u16 USART1_RX_STA;         		//接收状态标记


void USART1_Init(u32 bound);


#endif


 

 

以上是关于printf重定向的主要内容,如果未能解决你的问题,请参考以下文章

STM32串口printf()重定向问题

STM32 Cubemx Printf重定向

301重定向没有片段[重复]

Jsp获取Java的重定向赋值(String)

STM32为啥要用重定向printf来打印串口数据?直接用串口发送函数不行吗

重定向printf,不使用微库,采用ARM Compiler 6 报错如何解决?