|
本帖最后由 huohua1991 于 2019-1-4 11:36 编辑
为了搞明白Runtime Memory Area中的main stack和process stack的区别,翻看Embedded Studio的帮助文档,
还没查看到main stack和process stack的区别,却发现printf重定向官方推荐的方法,以下是原文:
To use the standard output functions putchar, puts, and printf,you need to customize the way that characters are written to the standard output device. These output functions rely on a function __putchar that outputs a character and returns an indication of whether it was successfully written.
The prototype for __putchar is int __putchar(int ch);
Sending all output to the SEGGER Embedded Studio virtual terminal
You can send all output to the SEGGER Embedded Studio virtual terminal by supplying the following implementation of the__putchar function in your code:
#include <debugio.h>
int __putchar(int ch)
{
return debug_putchar(ch);
}
This hands off output of the character ch to the low-level debug outputroutine, debug_putchar.
Whilst this is an adequate implementation of __putchar, it does consume stack space for an unnecessary nested call andassociated register saving. A better way of achieving the sameresult is to define the low-level symbol for __putchar to beequivalent to the low-level symbol for debug_putchar.
To do this, we need to instruct the linker to make the symbols equivalent.
- Select the project node in the Project Explorer.
- Display the Properties Window.
- Enter the text __putchar=debug_putchar into theLinker > Linker Symbol Definitions property of the Linker Optionsgroup.
Sending all output to another device
If you need to output to a physical device, such as a UART, the followingnotes will help you:
- If the character cannot be written for any reason, putchar must return EOF. Just because a character can't be written immediately is not a reason to return EOF: you can busy-wait or tasking (if applicable) to wait until the character is ready to be written.
- The higher layers of the library do not translate C's end of line character '\\n' before passing it to putchar. If you are directing output to a serial line connected to a terminal, for instance, you will most likely need to output a carriage return and line feed when given the character '\\n' (ASCII code 10).
The standard functions that perform input and output are the printf and scanf functions.These functions convert between internal binary and external printable data. In some cases, though, you need to read and write formatted data on other channels, such as other RS232 ports. This section shows how you can extend the I/O library to best implement these function.
Classic custom printf-style output
Assume that we need to output formatted data to two UARTs, numbered 0 and 1, and we have a functions uart0_putc and uart1_putc that do just that and whose prototypes are:
int uart0_putc(int ch, __printf_t *ctx);
int uart1_putc(int ch, __printf_t *ctx);
These functions return a positive value if there is no error outputting the character and EOF if there was an error. The second parameter, ctx,is the context that the high-level formatting routines use to implementthe C standard library functions.
Using a classic implementation, you would use sprintf to format the string for output and then output it:
void uart0_printf(const char *fmt, ...)
{
char buf[80], *p; va_list ap;
va_start(ap, fmt);
vsnprintf(buf, sizeof(buf), fmt, ap);
for (p = buf; *p; ++p)
uart0_putc(*p, 0); // null context
va_end(ap);
}
We would, of course, need an identical routine for outputting to the other UART.
This code is portable, but it requires an intermediate buffer of 80 characters. On small systems, this is quite an overhead, so we could reduce the buffer size to compensate. Of course, the trouble with that means that the maximum number of characters that can be output by a single call to uart0_printf is also reduced. What would be good is a way to output characters to one of the UARTs without requiring an intermediate buffer.
SEGGER Embedded Studio for ARM printf-style output
SEGGER Embedded Studio for ARM provides a solution for just this case by using some internal functions and data types in the SEGGER Embedded Studio for ARM library. These functions and types are define in the header file <__vfprintf.h>.
The first thing to introduce is the __printf_t type which captures the currentstate and parameters of the format conversion:
typedef struct __printf_tag
{
size_t charcount;
size_t maxchars;
char *string; int (*output_fn)(int, struct __printf_tag *ctx);
} __printf_t;
This type is used by the library functions to direct what the formatting routines do with each character they need to output. If string is non-zero, the characteris appended is appended to the string pointed to by string; if output_fn isnon-zero, the character is output through the function output_fn with the contextpassed as the second parameter.
The member charcount counts the number of characters currently output, and maxchars defines the maximum number of characters output by the formatting routine __vfprintf.
We can use this type and function to rewrite uart0_printf:
int uart0_printf(const char *fmt, ...)
{
int n;
va_list ap;
__printf_t iod;
va_start(ap, fmt);
iod.string = 0;
iod.maxchars = INT_MAX;
iod.output_fn = uart0_putc;
n = __vfprintf(&iod, fmt, ap);
va_end(ap);
return n;
}
This function has no intermediate buffer: when a character is ready to be output by the formatting routine, it calls the output_fn function in the descriptoriod to output it immediately. The maximum number of characters isn't limited as the maxchars member is set to INT_MAX. if you wanted to limit the number of characters output you can simply set the maxchars member to the appropriate value before calling __vfprintf.
We can adapt this function to take a UART number as a parameter:
int uart_printf(int uart, const char *fmt, ...){
int n;
va_list ap;
__printf_t iod;
va_start(ap, fmt);
iod.is_string = 0;
iod.maxchars = INT_MAX;
iod.output_fn = uart ? uart1_putc : uart0_putc;
n = __vfprintf(&iod, fmt, ap);
va_end(ap);
return n;
}
Now we can use:
uart_printf(0, "This is uart %d\n...", 0);
uart_printf(1, "..and this is uart %d\n", 1);
__vfprintf returns the actual number of characters printed, which you may wish to dispense with and make the uart_printf routine return void.
Extending input functions
The formatted input functions would be implemented in the same manner as the output functions: read a string into an intermediate buffer and parse using sscanf. However, we can use the low-level routines in the SEGGER Embedded Studio for ARM library for formatted input without requiring the intermediate buffer.
The type __stream_scanf_t is:
typedef struct{
char is_string;
int (*getc_fn)(void);
int (*ungetc_fn)(int);
} __stream_scanf_t;
The function getc_fn reads a single character from the UART, and ungetc_fnpushes back a character to the UART. You can push at most one character back onto the stream.
Here's an implementation of functions to read and write from a single UART:
static int uart0_ungot = EOF;
int uart0_getc(void){
if (uart0_ungot)
{
int c = uart0_ungot;
uart0_ungot = EOF;
return c;
}
else
return read_char_from_uart(0);
}
int uart0_unget(int c){
uart0_ungot = c;
}
You can use these two functions to perform formatted input using the UART:
int uart0_scanf(const char *fmt, ...){
__stream_scanf_t iod;
va_list a;
int n;
va_start(a, fmt);
iod.is_string = 0;
iod.getc_fn = uart0_getc;
iod.ungetc_fn = uart0_ungetc;
n = __vfscanf((__scanf_t *)&iod, (const unsigned char *)fmt, a);
va_end(a);
return n;
}
Using this template, we can add functions to do additional formatted input from other UARTs or devices, just as we did for formatted output.
点击菜单栏的Help菜单下的Contents选项,选择SEGGER Embedded Studio for ARM-->Input and output-->Customizing putchar可以查看到。
还没试验,还在纠结Runtime Memory Area中的main stack和process stack的区别,有人知道请告知。
|
|