打印
[牛人杂谈]

Keil重定向printf到串口UART输出

[复制链接]
3280|11
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
玛尼玛尼哄|  楼主 | 2018-6-20 17:05 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
下面是我搜索到的可以借鉴的讨论+我的评述

评述:在一个芯片系统里,uart的驱动是厂商自己写好的,那他们是怎么关联printf到uart的呢?有人说,printf最终是调用了putchar,我搜索了源码,没有这个函数,估计是开发工具,像KeilC u3,里面已经集成了putchar。于是我奇怪,这个工具怎么知道厂商哪个函数时uart的,有没有命名规则要求?好像没有,看了下面的讨论,估计是通过UART的收发寄存器来关联的。我猜想应该是在某个地方,keilc关联了系统的寄存器列表,找到了串口的寄存器的地址,然后putchar操作该寄存器,就相当于操作了厂商或者开发者自定义的uart_write_byte.uart_read_byte.

所以在keilc里,别人写好的系统,可以直接调用printf输出。我觉得要弄明白,得研究下keil是怎么处理库函数和寄存器的关系的。
其实也不用首先写一个数据到SBUF寄存器,只需在串口初始化后,加上一句TI=1;即可。原因是printf函数事实上是调用putchar输出字符的。之所以能输出到串口上,就是因为putchar函数把字符通过串口输出。
这是keil中putchar最简单的版本,其他版本也一样,看函数就明白为什么要先让TI=1;了,楼主写的那个SBUF=0,原理是相同的,写入了数据,那么TI就等于1了,然后就可以使用putchar函数和printf函数了。
putchar函数的源码在{keil安装目录下}\C51\LIB文件夹里的PUTCHAR.C文件里,另外在keil的帮助文档里有说明
char putchar (char c)  {
  while (!TI);
  TI = 0;
  return (SBUF = c);
}


沙发
玛尼玛尼哄|  楼主 | 2018-6-20 17:05 | 只看该作者
从下面这篇**中,我好像找到答案了
评述:我在某个CortextM3的源码里的debug.c找到了fputc。应该说,某些系统是通过fputc建立联系,而不是putchar的。如下:

void fputc_hook(char ch)
{
    if (DebugType == 0)
    {
        UARTWriteByte(ch, 1000);
    }
    else
    {
        VirtualUartWrite(ch);
    }
}

int fputc(int ch, FILE *f)
{
    uint8 dgbBuffer[DEBUG_TIME_LEN];
    uint32 tmpcnt, i;

    if (ch == '\n')
    {        
        tmpcnt = SysTickCounter;
        for (i = 0; i < DEBUG_TIME_LEN; i++)
        {
            dgbBuffer[i] = tmpcnt % 10;
            tmpcnt = tmpcnt / 10;
        }
        
        fputc_hook('\r');
        fputc_hook('\n');
        fputc_hook('[');
        for (i = 0; i < DEBUG_TIME_LEN; i++)
        {
            fputc_hook(dgbBuffer[DEBUG_TIME_LEN - 1 -i]+0x30);
            if (DEBUG_TIME_LEN - 1 -i == 2)
            {
                fputc_hook('.');
            }
        }
        fputc_hook(']');
        
        return OK;
    }
   
    fputc_hook(ch);

    return OK;
}

使用特权

评论回复
板凳
玛尼玛尼哄|  楼主 | 2018-6-20 17:06 | 只看该作者
在实际工作中,遇到了这么一个问题,需要向不同的串口传输ASCII码,无疑使用printf函数是最方便的。然而printf打印出的信息无法选择出口。在网上搜到的程序,printf要调用fputc函数发送字符。该函数如下:
int fputc(int ch, FILE *f)

{

  /* e.g. write a character to the USART */

  USART_SendData(USART1, (uint8_t) ch);


  /* Loop until the end of transmission */

  while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET)

  {}


  return ch;

}

入口参数有字符和字符要输出到的文件指针。根据搜索,printf函数输出到stdout,而fprintf可以指定字符到达的文件。可FILE结构体中,没有一个是与串口相关的。

使用特权

评论回复
地板
玛尼玛尼哄|  楼主 | 2018-6-20 17:06 | 只看该作者
typedef struct {

  char *fpos; /* Current position of file pointer (absolute address) */

  void *base; /* Pointer to the base of the file */

  unsigned short handle; /* File handle */

  short flags; /* Flags (see FileFlags) */

  short unget; /* 1-byte buffer for ungetc (b15=1 if non-empty) */

  unsigned long alloc; /* Number of currently allocated bytes for the file */

  unsigned short buffincrement; /* Number of bytes allocated at once */

} FILE;


    该如何重定向呢?不知道。但有一个弥补的方法。

    自己定义n个FILE*指针,并任意赋值。在fputc中利用if..else来做判断,代码如下:


FILE* FileUart1 = (FILE*)0x19;

FILE* FileUart2 = (FILE*)0x28;

int fputc(int ch, FILE *f) {

  if ( f == FileUart1 ) {

    USART_SendData(COM_USART[0], (uint8_t) ch);

    while (USART_GetFlagStatus(COM_USART[0], USART_FLAG_TC) == RESET){}

  }

  else if ( f == FileUart2 ) {

    USART_SendData(COM_USART[1], (uint8_t) ch);

    while (USART_GetFlagStatus(COM_USART[1], USART_FLAG_TC) == RESET){}

  }

}


    这样,fprintf(FileUart1,...)和fprintf(FileUart2,...)便能向不同的串口发送数据。哈哈,虽然没有真正做到重定向,只是用了个歪招,但最初的目的还是达到了。

    如果哪位牛人能告知如何真正重定向,感激感激~~~~


    还要请问,如何使用__DEBUG__宏定义才能使fprintf("val is %d", val)或fprintf("val1 is %d, val2 is %d", val1, val2)在程序中任意被编译上或编译掉。当然,不包括#ifdef..#endif方法。不这么做就是为了代码清晰一些,便于阅读。谢谢啊~~

使用特权

评论回复
5
xinpian101| | 2018-6-20 22:50 | 只看该作者
char putchar (char c)  {
。。。。。。。。。。
}
看来实现一个这个函数就行了啊

使用特权

评论回复
6
zhuomuniao110| | 2018-6-20 23:52 | 只看该作者
/***********************************************************************/
/*  This file is part of the C51 Compiler package                      */
/*  Copyright KEIL ELEKTRONIK GmbH 1990 - 2002                         */
/***********************************************************************/
/*                                                                     */
/*  PUTCHAR.C:  This routine is the general character output of C51.   */
/*  You may add this file to a uVision2 project.                       */
/*                                                                     */
/*  To translate this file use C51 with the following invocation:      */
/*     C51 PUTCHAR.C <memory model>                                    */
/*                                                                     */
/*  To link the modified PUTCHAR.OBJ file to your application use the  */
/*  following Lx51 invocation:                                         */
/*     Lx51 <your object file list>, PUTCHAR.OBJ <controls>            */
/*                                                                     */
/***********************************************************************/

#include <reg51.h>

#define XON  0x11
#define XOFF 0x13


/*
* putchar (full version):  expands '\n' into CR LF and handles
*                          XON/XOFF (Ctrl+S/Ctrl+Q) protocol
*/
char putchar (char c)  {

  if (c == '\n')  {
    if (RI)  {
      if (SBUF == XOFF)  {
        do  {
          RI = 0;
          while (!RI);
        }
        while (SBUF != XON);
        RI = 0;
      }
    }
    while (!TI);
    TI = 0;
    SBUF = 0x0d;                         /* output CR  */
  }
  if (RI)  {
    if (SBUF == XOFF)  {
      do  {
        RI = 0;
        while (!RI);
      }
      while (SBUF != XON);
      RI = 0;
    }
  }
  while (!TI);
  TI = 0;
  return (SBUF = c);
}



#if 0         // comment out versions below

/*
* putchar (basic version): expands '\n' into CR LF
*/
char putchar (char c)  {
  if (c == '\n')  {
    while (!TI);
    TI = 0;
    SBUF = 0x0d;                         /* output CR  */
  }
  while (!TI);
  TI = 0;
  return (SBUF = c);
}


/*
* putchar (mini version): outputs charcter only
*/
char putchar (char c)  {
  while (!TI);
  TI = 0;
  return (SBUF = c);
}

#endif

使用特权

评论回复
7
zhuomuniao110| | 2018-6-20 23:54 | 只看该作者
看了楼主的总结,又去看了库函数,懂了。

使用特权

评论回复
8
幸福小强| | 2018-6-21 17:49 | 只看该作者
总结到位,这个函数用的太多了。

使用特权

评论回复
9
yiyigirl2014| | 2018-6-21 20:22 | 只看该作者
这个问题好深奥,我要试试

使用特权

评论回复
10
dongliushui| | 2018-6-22 21:32 | 只看该作者
学会了,以前这个我都是默认,没有修改过

使用特权

评论回复
11
zhuomuniao110| | 2018-6-23 23:40 | 只看该作者
需要看看这个怎么实现的

使用特权

评论回复
12
zhuomuniao110| | 2018-6-23 23:40 | 只看该作者
了解一下arm的软件架构

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

180

主题

3135

帖子

2

粉丝