本文介绍常见开发环境下printf重定向方法。
串口作为调试接口在MCU开发过程中经常用到,为了方便格式化输出(使用printf函数),我们需要printf进行重定向输出到串口,通过串口调试软件打印调试信息,方便我们查找问题。本文针对常见开发环境(gcc,Keil,IAR,CrossWorks for ARM),介绍printf重定向的方法。
1.gcc开发环境
新建“syscalls.c”,添加如下内容。
#if !defined(__CROSSWORKS_ARM) && defined(__GNUC__)
#include <sys/stat.h>
#include <stddef.h>
int _close(int file);
void _exit(int status);
int _fstat(int file, struct stat *st);
int _getpid(void);
int _isatty(int file);
int _kill(int pid, int sig);
int _lseek(int file, int ptr, int dir);
int _read(int file, char *ptr, int len);
int _write(int file, const char *ptr, int len);
/**************************************************************************//**
* Close a file.
*
* @param[in] file File you want to close.
*
* @return Returns 0 when the file is closed.
*****************************************************************************/
int _close(int file)
{
(void) file;
return 0;
}
/**************************************************************************//**
* Exit the program.
*
* @param[in] status The value to return to the parent process as the
* exit status (not used).
*****************************************************************************/
void _exit(int status)
{
(void) status;
while (1) {
} // Hang here forever...
}
/**************************************************************************//**
* Status of an open file.
*
* @param[in] file Check status for this file.
*
* @param[in] st Status information.
*
* @return Returns 0 when st_mode is set to character special.
*****************************************************************************/
int _fstat(int file, struct stat *st)
{
(void) file;
st->st_mode = S_IFCHR;
return 0;
}
/**************************************************************************//**
* Get process ID.
*
* @return Return 1 when not implemented.
*****************************************************************************/
int _getpid(void)
{
return 1;
}
/**************************************************************************//**
* Query whether output stream is a terminal.
*
* @param[in] file Descriptor for the file.
*
* @return Returns 1 when query is done.
*****************************************************************************/
int _isatty(int file)
{
(void) file;
return 1;
}
/**************************************************************************//**
* Send signal to process.
*
* @param[in] pid Process id (not used).
*
* @param[in] sig Signal to send (not used).
*****************************************************************************/
int _kill(int pid, int sig)
{
(void)pid;
(void)sig;
return -1;
}
/**************************************************************************//**
* Set position in a file.
*
* @param[in] file Descriptor for the file.
*
* @param[in] ptr Poiter to the argument offset.
*
* @param[in] dir Directory whence.
*
* @return Returns 0 when position is set.
*****************************************************************************/
int _lseek(int file, int ptr, int dir)
{
(void) file;
(void) ptr;
(void) dir;
return 0;
}
/**************************************************************************//**
* Read from a file.
*
* @param[in] file Descriptor for the file you want to read from.
*
* @param[in] ptr Pointer to the chacaters that are beeing read.
*
* @param[in] len Number of characters to be read.
*
* @return Number of characters that have been read.
*****************************************************************************/
int _read(int file, char *ptr, int len)
{
(void)file;
return readBuffer(ptr, len);
}
/**************************************************************************//**
* Write to a file.
*
* @param[in] file Descriptor for the file you want to write to.
*
* @param[in] ptr Pointer to the text you want to write
*
* @param[in] len Number of characters to be written.
*
* @return Number of characters that have been written.
*****************************************************************************/
int _write(int file, const char *ptr, int len)
{
(void)file;
return writeBuffer(ptr, len);
}
#endif /* !defined( __CROSSWORKS_ARM ) && defined( __GNUC__ ) */
在自己的程序中,只需实现:
int writeBuffer(char *ch, int length);
int readBuffer(char *ch, int length);
2.Keil开发环境
1)方法1
在Keil中勾选“use MicroLib”。
#if defined(__CC_ARM)
#include <stdio.h>
/**************************************************************************//**
* Writes character to file
*
* @param[in] f File
*
* @param[in] ch Character
*
* @return Written character
*****************************************************************************/
int fputc(int ch, FILE *f)
{
return putChar(ch);
}
/**************************************************************************//**
* Reads character from file
*
* @param[in] f File
*
* @return Character
*****************************************************************************/
int fgetc(FILE *f)
{
return getChar();
}
#endif /* defined( __CC_ARM ) */
在自己的程序中,只需实现:
int putChar(char ch);
int getChar(void);
2)方法2
非半主机模式,新建“syscalls.c”,添加如下内容。
#if defined(__CC_ARM)
/******************************************************************************/
/* RETARGET.C: 'Retarget' layer for target-dependent low-level functions */
/******************************************************************************/
/* This file is part of the uVision/ARM development tools. */
/* Copyright (c) 2005-2006 Keil Software. All rights reserved. */
/* This software may only be used under the terms of a valid, current, */
/* end user licence from KEIL for a compatible version of KEIL software */
/* development tools. Nothing else gives you the right to use this software. */
/******************************************************************************/
#include <stdio.h>
#pragma import(__use_no_semihosting_swi)
struct __FILE{
int handle;
};
//Standard output stream
FILE __stdout;
/**************************************************************************//**
* Writes character to file
*
* @param[in] f File
*
* @param[in] ch Character
*
* @return Written character
*****************************************************************************/
int fputc(int ch, FILE *f)
{
return putChar(ch);
}
/**************************************************************************//**
* Reads character from file
*
* @param[in] f File
*
* @return Character
*****************************************************************************/
int fgetc(FILE *f)
{
return getChar();
}
/**************************************************************************//**
* Tests the error indicator for the stream pointed to by file
*
* @param[in] f File
*
* @return Returns non-zero if it is set
*****************************************************************************/
int ferror(FILE *f)
{
// Your implementation of ferror
return EOF;
}
/**************************************************************************//**
* Writes a character to the console
*
* @param[in] ch Input character
*****************************************************************************/
void _ttywrch(int ch)
{
putChar(ch);
}
/**************************************************************************//**
* Library exit function. This function is called if stack overflow occurs.
*
* @param[in] return_code Return code
*****************************************************************************/
void _sys_exit(int return_code)
{
label: goto label; // endless loop
}
#endif /* defined( __CC_ARM ) */
在自己的程序中,只需实现:
int putChar(char ch);
int getChar(void);
3.IAR开发环境
新建“syscalls.c”,添加如下内容。
#if defined(__ICCARM__)
/*******************
*
* Copyright 1998-2003 IAR Systems. All rights reserved.
*
* $Revision: 38614 $
*
* This is a template implementation of the "__write" function used by
* the standard library. Replace it with a system-specific
* implementation.
*
* The "__write" function should output "size" number of bytes from
* "buffer" in some application-specific way. It should return the
* number of characters written, or _LLIO_ERROR on failure.
*
* If "buffer" is zero then __write should perform flushing of
* internal buffers, if any. In this case "handle" can be -1 to
* indicate that all handles should be flushed.
*
* The template implementation below assumes that the application
* provides the function "MyLowLevelPutchar". It should return the
* character written, or -1 on failure.
*
********************/
#include <yfuns.h>
#include <stdint.h>
_STD_BEGIN
/**************************************************************************//**
* Write to.
*
* @param[in] handle Handle
*
* @param[in] buffer Pointer to the buffer you want to write
*
* @param[in] size Number of characters to be written.
*
* @return Number of characters that have been written.
*
* @note If the __write implementation uses internal buffering, uncomment
* the following line to ensure that we are called with "buffer" as 0
* (i.e. flush) when the application terminates.
*****************************************************************************/
size_t __write(int handle, const unsigned char * buffer, size_t size)
{
// Remove the #if #endif pair to enable the implementation
if (buffer == 0) {
// This means that we should flush internal buffers. Since we
// don't we just return. (Remember, "handle" == -1 means that all
// handles should be flushed.)
return 0;
}
// This template only writes to "standard out" and "standard err",
// for all other file handles it returns failure.
if (handle != _LLIO_STDOUT && handle != _LLIO_STDERR) {
return _LLIO_ERROR;
}
return writeBuffer(buffer, size);
}
/**************************************************************************//**
* Read from.
*
* @param[in] handle Handle
*
* @param[in] buffer Pointer to the characters that have been read.
*
* @return Number of characters that have been read.
*****************************************************************************/
size_t __read(int handle, unsigned char * buffer, size_t size)
{
// This template only reads from "standard in", for all other file
// handles it returns failure.
if (handle != _LLIO_STDIN) {
return _LLIO_ERROR;
}
return readBuffer(buffer, size);
}
_STD_END
#endif /* defined( __ICCARM__ ) */
在自己的程序中,只需实现:
int writeBuffer(char *ch, int length);
int readBuffer(char *ch, int length);
4.CrossWorks for ARM开发环境
#if defined(__CROSSWORKS_ARM)
/**************************************************************************//**
* Write a character.
*
* @param[in] ch character
*
* @return 1
*****************************************************************************/
int __putchar(int ch)
{
return putChar(ch);
}
/**************************************************************************//**
* Read a character.
*
* @return Character read
*****************************************************************************/
int __getchar(void)
{
return getChar();
}
#endif /* defined( __CROSSWORKS_ARM ) */
在自己的程序中,只需实现:
int putChar(char ch);
int getChar(void);
总结,本文介绍了常见开发环境下printf重定向方法。