打印
[51单片机]

用了printf函数之后为什么数字扫描会变慢

[复制链接]
3031|15
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
Edwardyue|  楼主 | 2018-3-18 17:21 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
      我是一名刚开始学单片机的小白,今天在单片机的学习上遇到一些问题,在网上搜到了这个神奇网站,这是我第一次发帖,还有点小激动呢,希望能在这里得到帮助。
      这个代码功能是通过上机向单片机以十六进制传输0至65535的任意数,然后单片机向上机返该回数,同时在数码管上显示该数
问题
      1.在用printf返回数的时候,数字扫描会变慢,还有就是不能返回65535,输入ffff时返回的是-1。
      2.在用SBUF=wan;
               while(!TI);
               TI=0;
         逐字返回时,不会影响扫描速度,但返回上机的值只能用十六进制显示,例如输入ffff 则返回06 05 05 03 05,不用十六进制则返回五个正方形,而且还不能换行。



#include <reg52.h>
#include <stdio.h>
#define uchar unsigned char
#define uint unsigned int
sbit dula=P2^6;
sbit wela=P2^7;
uchar flag,wan,qian,bai,shi,ge;
uchar k,x;
uint c;
uchar code table[]={
0x3f,0x06,0x5b,0x4f,
0x66,0x6d,0x7d,0x07,
0x7f,0x6f,0x77,0x7c,
0x39,0x5e,0x79,0x71};
uint temp[2]={0,0};                                       
void delay(uint z)   //延时控制时间
{
    uint x,y;
for(x=z;x>0;x--)
  for(y=110;y>0;y--);
}
void init()
{
TMOD=0x20;
TH1=0xfd;
TL1=0xfd;
TR1=1;
SM0=0;
SM1=1;
REN=1;
EA=1;
ES=1;
k=0;
x=0;
}
void display(uchar aa,uchar bb,uchar cc,uchar dd,uchar ee) //数字扫描
{
wela=1;
P0=0xfe;
wela=0;
P0=0xff;
dula=1;
P0=table[aa];
dula=0;
delay(2);
wela=1;
P0=0xfd;
wela=0;
P0=0xff;
dula=1;
P0=table[bb];
dula=0;
delay(2);
wela=1;
P0=0xfb;
wela=0;
P0=0xff;
dula=1;
P0=table[cc];
dula=0;
delay(2);
wela=1;
P0=0xf7;
wela=0;
P0=0xff;
dula=1;
P0=table[dd];
dula=0;
delay(2);
wela=1;
P0=0xef;
wela=0;
P0=0xff;
dula=1;
P0=table[ee];
dula=0;
delay(2);
}
void main()
{
init();
while(1)
{
  if(flag==1)
  {
     wan=c/10000;
     qian=c%10000/1000;
     bai=c%1000/100;
     shi=c%100/10;
     ge=c%10;
     flag=0;
     ES=0;
   TI=1;
   printf("%d\n",c);
   TI=0;
     /*SBUF=wan;
     while(!TI);
     TI=0;
     SBUF=qian;
     while(!TI);
     TI=0;
     SBUF=bai;
     while(!TI);
     TI=0;
     SBUF=shi;
     while(!TI);
     TI=0;
     SBUF=ge;
     while(!TI);
     TI=0;*/
   //TI=1;
     // printf("\n");
     // TI=0;
     ES=1;
     x=0;      
     k=0;
  }

    display(wan,qian,bai,shi,ge);
}
}
void ser() interrupt 4
{
while(RI==1)
{
  if(k==2)
   k=0;
  temp[k]=SBUF;
  RI=0;
  k++;
  x++;
  if(x==2)
  c=(temp[0]*256)+temp[1];
  if(x==1)
  c=temp[0];
  flag=1;
}
}

相关帖子

沙发
makesoft| | 2018-3-18 18:17 | 只看该作者
你可以跟踪下printf耗用时间啊

使用特权

评论回复
板凳
dirtwillfly| | 2018-3-18 20:48 | 只看该作者
printf要占用很多时钟周期的

使用特权

评论回复
地板
dirtwillfly| | 2018-3-18 20:49 | 只看该作者
输入ffff时返回的是-1。
这个问题是上位机的问题,估计是不支持无符号数。你用HEX模式接收就好了

使用特权

评论回复
5
linqing171| | 2018-3-18 20:59 | 只看该作者
while(!TI); 修改为
while(!TI)
{
display(wan,qian,bai,shi,ge);
}
TI=0;
SBUF=wan+'0' ;       // '1' = 0x31 代表 '1'的ASCII码
... ...

这样就相当于把printf给拆分了,自己写了个,就不会在printf的情况下影响显示了。


换行用 SBUF= '\n'; 回车用 SBUF='\r';
小方块是0xFF;

有无符号的整数问题,原谅它吧。

使用特权

评论回复
6
Edwardyue|  楼主 | 2018-3-18 22:37 | 只看该作者
linqing171 发表于 2018-3-18 20:59
while(!TI); 修改为
while(!TI)
{

谢谢老师,按这个跑确实能正常跑出来,
但我不太能理解这个原理,我测试了一下,把SBUF=0x36,发现那一位就只显示6了,
但把SBUF=0x06,那一未就是空白的小方块,我查了一下ascll码表,0x06对应的不是小方块,
为什么他会显示0xff的小方块呢?
还有什么是无符号的整数问题,能否讲具体一些,不是很懂。。
还有就是板子在的时候返回给上机的值有时候会出错,我测试了一下
连续200次左右从上机给单片机给值,一直给ffff,总共出错了18次
其中
00235有6次
00255有2次
00535有7次
05535有3次
其余均为65535正常显示,但用printf的话不会出现这个问题,请问这是个什么原因

使用特权

评论回复
7
Edwardyue|  楼主 | 2018-3-18 23:17 | 只看该作者
dirtwillfly 发表于 2018-3-18 20:48
printf要占用很多时钟周期的

谢谢老师,我不太懂为什么printf会占用很多时钟周期,所以我上网搜了一下51单片机printf的函数,
果然,我看不懂。。。。但我看到了printf中有这个初始函数
void USART_Init(void)
{
AUXR=0x40;//定时器1的速度是传统的8051的12倍 不分频   不可以位寻址
TMOD=0x00;//设置定时器1为16位自动重装寄存器 不可以位寻址
SCON=0x50;//设置为8位波特率可变 并允许串口接收使能
PCON=0x00;//用定时器1模式0时于PCON无关了

TL1=(65536-(FOSC/4/BAUD));
TH1=(65536-(FOSC/4/BAUD))>>8;
TI=1;//用系统自带的printf函数,TI必须等于1,否则无法发送,不能
TR1=1;//使能定时器1
ES=1; //串口中断使能
EA=1; //全局中断使能
}
是不是可以理解为printf改变了原本的TH1和TL1,于是我在printf函数完后又重新设置了一遍初值
发现,依然是没有用的.....数码管的扫描还是慢了很多,请问为什么printf会占用很多时钟周期,
而且我在网上搜的时候,有些帖子也有说printf运行会比puts慢,那以后是不是要考虑printf函数的使用。

使用特权

评论回复
8
dirtwillfly| | 2018-3-19 08:15 | 只看该作者
Edwardyue 发表于 2018-3-18 23:17
谢谢老师,我不太懂为什么printf会占用很多时钟周期,所以我上网搜了一下51单片机printf的函数,
果然, ...

stdio库中的printf执行速度很慢,而且很占用内存资源。主频低、内存小的mcu不要用stdio

使用特权

评论回复
9
Edwardyue|  楼主 | 2018-3-19 12:48 | 只看该作者
dirtwillfly 发表于 2018-3-19 08:15
stdio库中的printf执行速度很慢,而且很占用内存资源。主频低、内存小的mcu不要用stdio ...

谢谢老师

使用特权

评论回复
10
gujiamao12345| | 2018-3-19 13:30 | 只看该作者
明显是printf函数比较占用时间,你可以自己写个Myprintf,实现十进制或者十六进制打印即可

使用特权

评论回复
11
linqing171| | 2018-3-19 15:36 | 只看该作者
这段代码前后关中断,因为它用了c,在中断中也在用,所以c是临界资源,存在数据完整性问题。

关总中断或者串口中断
     wan=c/10000;
     qian=c%10000/1000;
     bai=c%1000/100;
     shi=c%100/10;
     ge=c%10;
打开关闭了的中断

使用特权

评论回复
12
linqing171| | 2018-3-19 15:40 | 只看该作者
EA=0;
c_bak=c;
EA=1;
后面用本线程的 c_bak ,不再使用中断里面会改的 c;

对于你的串口终端,把不可见字符显示成什么,是你的电脑程序的特点。

printf的主要时间,是花在了它内部的 while(!TI); 这句话上。 tasking等8051编译器带的双缓冲的putchar的例子,使用中断驱动,不轮询TI,速度就快的多。


使用特权

评论回复
13
Edwardyue|  楼主 | 2018-3-19 21:57 | 只看该作者
linqing171 发表于 2018-3-19 15:36
这段代码前后关中断,因为它用了c,在中断中也在用,所以c是临界资源,存在数据完整性问题。

关总中断或者 ...

确实,跟踪了一下发现每次到这里都会跳到中断函数中走一遍

使用特权

评论回复
14
Edwardyue|  楼主 | 2018-3-19 22:32 | 只看该作者

谢谢老师,这个确实可以避免再打开中断,但主要问题不是这个,因为把这个写上去之后错误的频率反而更高了,思考了一番之后我终于找到了问题的所在,是我的中断函数的问题,因为这个中断函数接收一个字节就会触发中断,如果在第二次中断触发前c的值已经附给了wan,qian,bai,shi,ge,或者把中断关闭了,就会出问题,所以我在主函数中添加了一点延时,给了第二次中断足够的时间,这样的话就能保证传输双字节时第二次中断能够出来
if(flag==1)
{
     delay(1);
}

还有老师说的小方块问题确实是串口调试工具的问题,我换了个串口调试工具显示的就不是小方块了,
谢谢老师,非常感谢,虽然printf还是一知半解,因为我刚刚开始学,但我会在以后的学习中去弄懂它的。
再次感谢。

使用特权

评论回复
15
linqing171| | 2018-3-19 22:46 | 只看该作者
本帖最后由 linqing171 于 2018-3-19 22:48 编辑

k=0的时候,flag才为1就可以了。
对两个线程能想的这明白,不像新手。

Keil安装目录 \C51\LIB\PUTCHAR.C
把这个加到你的项目里,你的项目里有 putchar这个函数了,keil就不再链接函数库里的了。printf最终调用的就是这个putchar。如果你要打印到数码管上,可以改这个putchar函数。
keil函数库里的那个是不带软件流控的,这个增加了XON/XOFF软件流控。



使用特权

评论回复
16
Edwardyue|  楼主 | 2018-3-20 23:24 | 只看该作者
linqing171 发表于 2018-3-19 22:46
k=0的时候,flag才为1就可以了。
对两个线程能想的这明白,不像新手。

考虑过让中断函数累计两次中断后再让flag为1,但这样就不能显示一个字节了,如第一次输入ff是没有值出来的,然后第二次输入0101就会出来65281.然后后面再连续输入两字节就是错误值了,所以干脆加了个延时
那个putchar带软件流控的对我来说看起来还有点困难,不过那两个不带软件流控的对我来说挺值得借鉴的。
感谢老师。

使用特权

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

本版积分规则

2

主题

17

帖子

0

粉丝