Keil MDK : 標準IO函數重定向相關

Library Retarget File

Semihosting is not supported by MDK-ARM. Use the retargeting functionality of MDK-ARM instead. To prevent the linker from selecting libraries functions that use semihosting, MDK-ARM includes retarget files that redefine low-level I/O routines.

RETARGET.C template file, located in the folder ..\ARM\Startup, implements the functionality required to adapt character I/O functions such as printf() and scanf().

  1. Copy the RETARGET.C template to the project folder.
  2. Include the copied file to the project (described in Create Source File).
  3. Edit the file and make your changes. You may use the following code as a template and adapt it to your needs.
    /*----------------------------------------------------------------------------
     * Name:    Retarget.c
     * Purpose: 'Retarget' layer for target-dependent low level functions
     * Note(s):
     *----------------------------------------------------------------------------
     * This file is part of the µVision/ARM development tools.
     * 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.
     *
     * This software is supplied "AS IS" without warranties of any kind.
     *
     * Copyright (c) 2009 Keil - An ARM Company. All rights reserved.
     *----------------------------------------------------------------------------*/
    
    #include <stdio.h>
    #include <rt_misc.h>
    
    #pragma import(__use_no_semihosting_swi)
    
    
    extern int sendchar (int c);
    extern int getkey   (void);
    
    
    struct __FILE { int handle; /* Add whatever you need here */ };
    FILE __stdout;
    FILE __stdin;
    
    
    int fputc(int c, FILE *f) {
      return (sendchar(c));
    }
    
    
    int fgetc(FILE *f) {
      return (getkey());
    }
    
    
    int ferror(FILE *f) {
      /* Your implementation of ferror */
      return EOF;
    }
    
    
    void _ttywrch(int c) {
      sendchar(c);
    }
    
    
    void _sys_exit(int return_code) {
    label:  goto label;  /* endless loop */
    }
    

 Note

Tailoring the C library to a new execution environment

1.41 Tailoring the C library to a new execution environment

Tailoring the C library to a new execution environment involves re-implementing functions to produce an application for a new execution environment, for example, embedded in ROM or used with an RTOS.

Functions whose names start with a single or double underscore are used as part of the low-level implementation. You can re-implement some of these functions. Additional information on these library functions is available in the rt_heap.hrt_locale.hrt_misc.h, and rt_sys.h include files and thert_memory.s assembler file.

Redefining target-dependent system I/O functions in the C library

1.78 Redefining target-dependent system I/O functions in the C library

The default target-dependent I/O functions use semihosting. If any of these functions are redefined, then they must all be redefined.

The following example shows you how to redefine the required functions this, for a device that supports writing but not reading.

Retargeting the system I/O functions

FILEHANDLE _sys_open(const char *name, int openmode)
{
  return 1; /* everything goes to the same output */
}
int _sys_close(FILEHANDLE fh)
{
  return 0;
}
int _sys_write(FILEHANDLE fh, const unsigned char *buf,
               unsigned len, int mode)
{
  your_device_write(buf, len);
  return 0;
}
int _sys_read(FILEHANDLE fh, unsigned char *buf,
              unsigned len, int mode)
{
  return -1; /* not supported */
}
void _ttywrch(int ch)
{
  char c = ch;
  your_device_write(&c, 1);
}
int _sys_istty(FILEHANDLE fh)
{
  return 0; /* buffered output */
}
int _sys_seek(FILEHANDLE fh, long pos)
{
  return -1; /* not supported */
}
long _sys_flen(FILEHANDLE fh)
{
  return -1; /* not supported */
}
rt_sys.h defines the type FILEHANDLE. The value of FILEHANDLE is returned by _sys_open() and identifies an open file on the host system.
If the system I/O functions are redefined, both normal character I/O and wide character I/O work. That is, you are not required to do anything extra with these functions for wide character I/O to work.

Redefining low-level library functions to enable direct use of high-level library functions in the C library

1.74 Redefining low-level library functions to enable direct use of high-level library functions in the C library

If you define your own version of __FILE, your own fputc() and ferror() functions, and the __stdout object, you can use all of the printf() family, fwrite()fputs()puts() and the C++ object std::cout unchanged from the library.

These examples show you how to do this. However, consider modifying the system I/O functions instead of these low-level library functions if you require real file handling.
You are not required to re-implement every function shown in these examples. Only re-implement the functions that are used in your application.

Retargeting printf()

#include <stdio.h>
struct __FILE
{
  int handle;
  /* Whatever you require here. If the only file you are using is */
  /* standard output using printf() for debugging, no file handling */
  /* is required. */
};
/* FILE is typedef’d in stdio.h. */
FILE __stdout;
int fputc(int ch, FILE *f) 
{
  /* Your implementation of fputc(). */
  return ch;
}
int ferror(FILE *f)
{
  /* Your implementation of ferror(). */
  return 0;
}
void test(void)
{
  printf("Hello world\n");
}

Note

Be aware of endianness with fputc()fputc() takes an int parameter, but contains only a character. Whether the character is in the first or the last byte of the integer variable depends on the endianness. The following code sample avoids problems with endianness:
extern void sendchar(char *ch);
int fputc(int ch, FILE *f)
{
  /* example: write a character to an LCD */
  char tempch = ch; // temp char avoids endianness issue
  sendchar(&tempch);
  return ch;
}

Retargeting cout

File 1: Re-implement any functions that require re-implementation.
#include <stdio.h>
namespace std {
  struct __FILE
  {
    int handle;
    /* Whatever you require here. If the only file you are using is */
    /* standard output using printf() for debugging, no file handling */
    /* is required. */
  };
  FILE __stdout;
  FILE __stdin;
  FILE __stderr;
  int fgetc(FILE *f)
  {
    /* Your implementation of fgetc(). */
    return 0;
  };
  int fputc(int c, FILE *stream)
  {
    /* Your implementation of fputc(). */
  }
  int ferror(FILE *stream)
  {
    /* Your implementation of ferror(). */
  }
  long int ftell(FILE *stream)
  {
    /* Your implementation of ftell(). */
  }
  int fclose(FILE *f)
  {
    /* Your implementation of fclose(). */
    return 0;
  }
  int fseek(FILE *f, long nPos, int nMode)
  {
    /* Your implementation of fseek(). */
    return 0;
  }
  int fflush(FILE *f)
  {
    /* Your implementation of fflush(). */    
    return 0;
  }
}
File 2: Print "Hello world" using your re-implemented functions.
#include <stdio.h>
#include <iostream>
using namespace std;
int main()
{
  cout << "Hello world\n";
  return 0;
}
By default, fread() and fwrite() call fast block input/output functions that are part of the ARM stream implementation. If you define your own __FILE structure instead of using the ARM stream implementation,fread() and fwrite() call fgetc() instead of calling the block input/output functions.

What is semihosting?

Semihosting is a mechanism that enables code running on an ARM target to communicate and use the Input/Output facilities on a host computer that is running a debugger.

Examples of these facilities include keyboard input, screen output, and disk I/O. For example, you can use this mechanism to enable functions in the C library, such as printf() and scanf(), to use the screen and keyboard of the host instead of having a screen and keyboard on the target system.

This is useful because development hardware often does not have all the input and output facilities of the final system. Semihosting enables the host computer to provide these facilities.

Semihosting is implemented by a set of defined software instructions, for example, SVCs, that generate exceptions from program control. The application invokes the appropriate semihosting call and the debug agent then handles the exception. The debug agent provides the required communication with the host.

The semihosting interface is common across all debug agents provided by ARM. Semihosted operations work when you are debugging applications on your development platform, as shown in the following figure:

Figure 6. Semihosting overview


In many cases, semihosting is invoked by code within library functions. The application can also invoke the semihosting operation directly.

Note

ARM processors prior to ARMv7 use the SVC instructions, formerly known as SWI instructions, to make semihosting calls. However, if you are compiling for an ARMv6-M or ARMv7-M, for example a Cortex-M1 or Cortex-M3 processor, semihosting is implemented using the BKPT instruction.


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章