硬汉嵌入式论坛

 找回密码
 立即注册
查看: 3622|回复: 5
收起左侧

[Embedded Studio] Embedded Studio官方推荐printf重定向方法

[复制链接]

4

主题

84

回帖

96

积分

初级会员

积分
96
发表于 2019-1-4 11:27:07 | 显示全部楼层 |阅读模式
本帖最后由 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的区别,有人知道请告知。
     



回复

使用道具 举报

1

主题

109

回帖

112

积分

初级会员

固件開發工程師

积分
112
QQ
发表于 2019-1-4 11:35:24 | 显示全部楼层
关于Main Stack和Process Stack的区别,建议楼主看一下ARM和STM32资料中介绍MSP/PSP的部分。
书籍:
The Definitive Guide to the ARM Cortex-M0
The Definitive Guide to ARM Cortex-M3 and Cortex-M4 Processors, 3rd Edition
文档/教程:
这里有:http://www.stmcu.org.cn/,楼主自己搜一下吧
Cortex-M内核系列和STM32-讲座1
Cortex-M内核系列和STM32-讲座2
Cortex-M内核系列和STM32-讲座3
回复

使用道具 举报

36

主题

2040

回帖

2148

积分

至尊会员

积分
2148
发表于 2019-1-4 11:36:21 | 显示全部楼层
main stack 应该是MSP栈指针所指向的栈空间

process stack应该是PSP进程栈指针所指向的栈空间。

一般做OS的系统栈和任务栈区分用。

而你的ES的用法就不清楚了,没有研究过
Ever tried. Ever failed. No matter. Try Again. Fail again. Fail better.
回复

使用道具 举报

4

主题

84

回帖

96

积分

初级会员

积分
96
 楼主| 发表于 2019-1-4 11:46:24 | 显示全部楼层
----- 发表于 2019-1-4 11:35
关于Main Stack和Process Stack的区别,建议楼主看一下ARM和STM32资料中介绍MSP/PSP的部分。
书籍:
The  ...

谢谢,以前用MDK或IAR时没接触到Main Stack和Process Stack,为了尝新,用Embedded Studio才接触到
回复

使用道具 举报

4

主题

84

回帖

96

积分

初级会员

积分
96
 楼主| 发表于 2019-1-4 12:05:06 | 显示全部楼层
byccc 发表于 2019-1-4 11:36
main stack 应该是MSP栈指针所指向的栈空间

process stack应该是PSP进程栈指针所指向的栈空间。

我翻看了thumb_cr0.s,大概明白了设置main stack size和process stack size的意义,就是process stack size大于零就启用双堆栈机制否则只使用MSP
回复

使用道具 举报

36

主题

2040

回帖

2148

积分

至尊会员

积分
2148
发表于 2019-1-4 12:07:12 | 显示全部楼层
huohua1991 发表于 2019-1-4 12:05
我翻看了thumb_cr0.s,大概明白了设置main stack size和process stack size的意义,就是process stack si ...

应该是给他们家的embOS用的。
Ever tried. Ever failed. No matter. Try Again. Fail again. Fail better.
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|小黑屋|Archiver|手机版|硬汉嵌入式论坛

GMT+8, 2024-6-2 05:52 , Processed in 0.176263 second(s), 25 queries .

Powered by Discuz! X3.4 Licensed

Copyright © 2001-2023, Tencent Cloud.

快速回复 返回顶部 返回列表