打印

讨论一下KeilC51中printf库函数如何与串口中断共存

[复制链接]
34096|52
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
printf函数是极为方便的信息输出函数,能将程序中的各种变量的值快速格式化并输出到控制台,在程序调试和测试中无处不在,C语言教材,例子程序中十有**要用到它.
Keil C51的printf使用也极为方便,只要初始化串口后,就能使用printf直接将信号输出到串口,但在初始化时必段加上如下内容
TI=1;
在没有使用串口中断的情况下不要紧,但在串口必段以中断方式工作的情况下,因为TI=1导致频繁进入中断,主程序无法全速运行.
俺发现printf是调用putchar()底层函数来实现的,而putchar()函数是采用先查忙再发送的机制.伪码如下:
while(!TI);
TI=0
SBUF = ucSendChar;
Retrun;

俺现在的折中办法是,如果单片机的发送中断和接收中断可分开使能,则可以只开接收中断,发送不中断,比如LPC932分开了.如果发送与接收中断使能分不开的芯片,比如AT89S52,就只能使用自己的putchar()用如下写法
SBUF = ucSendChar;
while(!TI);
TI=0;
Return
再在Putchar基础上构造简单的调试函数,反正感觉不如Printf方便,有时也用Sprintf来变换后再输出到串口,但sprint占内存,也不好.

想听听大家调试时是否有好办法.
评分
参与人数 1威望 +3 收起 理由
16777216 + 3 这个帖子让我收获很大呀 谢谢楼主.

相关帖子

沙发
computer00| | 2009-9-18 12:58 | 只看该作者
本帖最后由 computer00 于 2009-9-18 12:59 编辑

简单啊……

volatile unsigned char UartIsSending;

void UartIsr(void)
{
if(TI) //发送中断
{
  UartIsSending=0;
  TI=0;
}
if(RI) //接收中断
{
RI=0;
}
}

void putchar(int x)
{
SBUF=x;
UartIsSending=1;
while(UartIsSending);
}

或者:
UartIsSending=0; //初始化好
void putchar(int x)  //发送最后一字节时可以干其它活
{
while(UartIsSending);
SBUF=x;
UartIsSending=1;
}

使用特权

评论回复
板凳
mohanwei| | 2009-9-18 13:18 | 只看该作者
以前发的:
第一步,改造putchar函数,把TI简单替换成Uart0_Send_Ready:
unsigned char Uart0_Send_Ready;//这个是中断方式使用printf的关键
/*
* putchar (basic version): expands '\n' into CR LF
*/
char putchar (char c)
{
    LED_TXD = light;//点亮发送指示灯
    if (c == '\n')
    {
        while (!Uart0_Send_Ready)
            ;
        Uart0_Send_Ready = 0;
        SBUF0 = 0x0d;                         /* output CR  */
    }
    while (!Uart0_Send_Ready)
        ;
    Uart0_Send_Ready = 0;
    return (SBUF0 = c);
}

第二步,在串口中断里,把TI的值传递给Uart0_Send_Ready:
void Uart0_Int(void) interrupt 4
{
    char ch;
    if(TI)
    {
        Uart0_Send_Ready=1;//把TI的值传递给Uart0_Send_Ready
        TI=0;//清TI
    }
    if(RI)
    {
        RI=0;
        ch = SBUF0;
        LED_RXD = light;//点亮接收LED
        //干该干的事……
    }
}

这样,printf就工作在中断方式下了,跟串行接收中断没有冲突。
实际项目中我还用了单工的RS485总线(那样还要继续改造putchar,主要是485的换向)……
适当改造一下putchar,还可以把printf的输出重定向到某个I/O口,LCD……编程就跟上位机一样方便。

使用特权

评论回复
地板
古道热肠|  楼主 | 2009-9-18 14:48 | 只看该作者
请教楼上网友,printf函数调用putch()底层,那如何控制它不调库,而调用我自己编写的这个替身呢?

使用特权

评论回复
5
mohanwei| | 2009-9-18 16:50 | 只看该作者
如5楼所述。

使用特权

评论回复
6
mohanwei| | 2009-9-18 16:50 | 只看该作者
在你的工程里放一个putchar函数就行了。

使用特权

评论回复
7
sjl2006| | 2009-9-18 21:03 | 只看该作者
C语言允许用与库函数同名的函数替代原库函数,称作interposition

使用特权

评论回复
8
原野之狼| | 2009-9-18 21:14 | 只看该作者
我一般自己写
void my_printf(const char *p,...)
{
        va_list va;
        char buf[64];

        va_start(va,p);
        vsprintf(buf,p,va);
        dev_write(h_dbg, buf, strlen(buf), 0);
}

使用特权

评论回复
9
computer00| | 2009-9-19 00:13 | 只看该作者
你打开stdio.h文件看看,里面就有一个putchar函数,你按照这个格式,自己写一个函数放在某个.c文件里,当调用printf时,它就会自动调用到它拉~~~~你可以把输出数据定向到任何设备~~~~如果你不写这个putchar函数,那么就会给你链接到一个默认的串口发送数据的函数去~~~~

使用特权

评论回复
10
yewuyi| | 2009-9-19 08:36 | 只看该作者
呵呵,从来不用printf的人飘过。。。

使用特权

评论回复
11
HWM| | 2009-9-19 08:39 | 只看该作者
有C++支持,一般都用“<<”,相当方便。不过那个“流”得自己做。

使用特权

评论回复
12
古道热肠|  楼主 | 2009-9-19 09:50 | 只看该作者
呵呵,从来不用printf的人飘过。。。
yewuyi 发表于 2009-9-19 08:36

不会吧,printf用起来很方便的,没有仿真器联调的系统,我全靠它了.

使用特权

评论回复
13
古道热肠|  楼主 | 2009-9-19 09:52 | 只看该作者
谢谢大家的建议,俺去试试,先去自已改个的putchar().

使用特权

评论回复
14
古道热肠|  楼主 | 2009-9-19 09:54 | 只看该作者
9# 原野之狼
自已写不及用printf爽,俺过去也用您类似的用法.

使用特权

评论回复
15
yewuyi| | 2009-9-19 09:55 | 只看该作者
呵呵,俺都用眼睛看。。。


俺代码少,有个万儿八千行就差不多了,眼睛看看就知道结果了,实在不行就直接看寄存器数值也就OK了。

使用特权

评论回复
16
冷漠| | 2009-9-19 12:16 | 只看该作者
这个是书上改好的配合Printf( )使用的相关程序。供参考。它的初始化程序中没有TI=1;如果没有它的
char putchar (char c),Printf( )不能输出。


/****************************************************************************/
/*                                                                          */
/*       SERIAL.C:  Interrupt Controlled Serial Interface                   */
/*                                                                          */
/****************************************************************************/

#pragma CODE DEBUG OBJECTEXTEND

#include <reg52.h>                    /* special function register 8052     */


#define  OLEN  8                      /* size of serial transmission buffer */
unsigned char  ostart;                /* transmission buffer start index    */
unsigned char  oend;                  /* transmission buffer end index      */
idata    char  outbuf[OLEN];          /* storage for transmission buffer    */
//unsigned char  otask = 0xff;          /* task number of output task         */

#define  ILEN  8                      /* size of serial receiving buffer    */
unsigned char  istart;                /* receiving buffer start index       */
unsigned char  iend;                  /* receiving buffer end index         */
idata    char  inbuf[ILEN];           /* storage for receiving buffer       */
//unsigned char  itask = 0xff;          /* task number of output task         */

#define   CTRL_Q  0x11                /* Control+Q character code           */
#define   CTRL_S  0x13                /* Control+S character code           */

bit   sendfull;                       /* flag: marks transmit buffer full   */
bit   sendactive;                     /* flag: marks transmitter active     */
bit   sendstop;                       /* flag: marks XOFF character         */

/****************************************************************************/
/*       putbuf:  write a character to SBUF or transmission buffer          */
/****************************************************************************/
putbuf (char c)  {
  if (!sendfull)  {                   /* transmit only if buffer not full   */
    if (!sendactive && !sendstop)  {  /* if transmitter not active:         */
      sendactive = 1;                 /* transfer the first character direct*/
      SBUF = c;                       /* to SBUF to start transmission      */
    }
    else  {                           /* otherwize:                         */
      outbuf[oend++ & (OLEN-1)] = c;  /* transfer char to transmission buffr*/
      if (((oend ^ ostart) & (OLEN-1)) == 0)  sendfull = 1;
    }                                 /* set flag if buffer is full         */
  }
}


/****************************************************************************/
/*       putchar:  interrupt controlled putchar function                    */
/****************************************************************************/
char putchar (char c)  {
  if (c == '\n')  {                   /* expand new line character:         */
      putbuf (0x0D);                    /* send CR before LF for <new line>   */
  }

  putbuf (c);                         /* send character                     */
  return (c);                         /* return character: ANSI requirement */
}


/****************************************************************************/
/*       _getkey:  interrupt controlled _getkey                             */
/****************************************************************************/
char _getkey (void)  {

  return (inbuf[istart++ & (ILEN-1)]);
}


/****************************************************************************/
/*       serial:  serial receiver / transmitter interrupt                   */
/****************************************************************************/
serial () interrupt 4 using 1  {     /* use registerbank 1 for interrupt    */
  unsigned char c;
  bit   start_trans = 0;

  if (RI)  {                         /* if receiver interrupt               */
    c = SBUF;                        /* read character                      */
    RI = 0;                          /* clear interrupt request flag        */
    switch (c)  {                    /* process character                   */
      case CTRL_S:
        sendstop = 1;                /* if Control+S stop transmission      */
        break;

      case CTRL_Q:
        start_trans = sendstop;      /* if Control+Q start transmission     */
        sendstop = 0;
        break;

      default:                       /* read all other characters into inbuf*/
        if (istart + ILEN != iend)  {
          inbuf[iend++ & (ILEN-1)] = c;
        }
                                   

        break;
    }
  }

  if (TI || start_trans)  {          /* if transmitter interrupt            */
    TI = 0;                          /* clear interrupt request flag        */
    if (ostart != oend)  {           /* if characters in buffer and         */
      if (!sendstop)  {              /* if not Control+S received           */
        SBUF = outbuf[ostart++ & (OLEN-1)];      /* transmit character      */
        sendfull = 0;                /* clear 'sendfull' flag               */
                                     /* if task waiting: signal ready       */
//        if (otask != 0xFF)  isr_send_signal (otask);
      }
    }
    else sendactive = 0;             /* if all transmitted clear 'sendactive*/
  }

}


/****************************************************************************/
/*       serial_init: initialize serial interface                           */
/****************************************************************************/
serial_init ()  {
  SCON  = 0x50;                      /* mode 1: 8-bit UART, enable receiver */
  TMOD |= 0x20;                      /* timer 1 mode 2: 8-Bit reload        */
  TH1   = 0xf3;                      /* reload value 2400 baud              */
  TR1   = 1;                         /* timer 1 run                         */
  ES=1;

使用特权

评论回复
评分
参与人数 1威望 +2 收起 理由
古道热肠 + 2
17
原野之狼| | 2009-9-19 16:59 | 只看该作者
Be care of that function "printf" is not thread safe,but function "vsprintf" is thread safe.

使用特权

评论回复
18
古道热肠|  楼主 | 2009-9-20 11:07 | 只看该作者
经测试,2楼和3楼两位网友提出的方法完全可行,再次感谢!
贴上测试代码

/****************************************************************************/
/*                                                                          */
/*       SERIAL.C:  Interrupt Controlled Serial Interface                   */
/*                                                                          */
/****************************************************************************/
//采用中断接收串口数据
//发送数据到串口可使用<stdio.h>定义的函数
//有疑义可访问bbs.21ic.com进行讨论。
//测试:古道热肠
#include <reg52.h>
#include <stdio.h>
volatile unsigned char UartIsSending;

char putchar (char ucWriteData)
{
SBUF=ucWriteData;
UartIsSending=1;
while(UartIsSending);
return (ucWriteData);
}

volatile unsigned char UartIsSending;
void UartIsr(void) interrupt 4
{
if(TI) //发送中断
{
    UartIsSending=0;
    TI=0;
}
if(RI) //接收中断
{
  RI=0;
}
}
/****************************************************************************/
/*       serial_init: initialize serial interface                           */
/****************************************************************************/
serial_init ()  
{
  SCON  = 0x50;                      /* mode 1: 8-bit UART, enable receiver */
  TMOD |= 0x20;                      /* timer 1 mode 2: 8-Bit reload        */
  TH1   = 0xf3;                      /* reload value 2400 baud              */
  TR1   = 1;                         /* timer 1 run                         */
  ES=1;
}
void Delay(void)
{
unsigned char ucDelayCount;
for(ucDelayCount=0; ucDelayCount<0xFF; ucDelayCount++)
{
  ;
}
}

void main(void)
{
serial_init();
EA = 1;
while(1)
{
  putchar('8');
  putchar('\n');
  Delay();
  printf("welcome to www.21ic.com\n");
}
}





程序文件压缩包,可直接导入keil c51进行测试
testPutchar.rar (765 Bytes)

使用特权

评论回复
19
古道热肠|  楼主 | 2009-9-20 11:39 | 只看该作者
17# 冷漠

冷漠网友给的这个架构也不错,经测试好用,有虚拟设备的概念在里面,可提高MCU的效率.

哈哈,让MCU死等的确有些浪费,只有采用中断驱动底层,并接合数据缓冲池,才能将MCU解放出来.

使用特权

评论回复
20
myfaith| | 2009-9-20 11:50 | 只看该作者
21IC文字过滤太强悍了,经常看到贴子里很多莫名其妙的"**"符号,这里热肠一句"十有八 9"的后两个字都被屏蔽了,现在这世道从一数到九都不行了!!!还让不让人说话了

使用特权

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

本版积分规则

个人签名:以VS1003B和山景SOC芯片为背景,倾心研制数字化语音录放产品. 排忧邮箱:xg_2004_sy@126.com 得意之作是做了个AVR高压编程器,用起来爽歪歪, 串口MP3录放音模块,全面进入数字录放音时代

284

主题

6411

帖子

16

粉丝