迪文屏幕T5L平台学习笔记四:C51使用printf或者sprintf注意事项

Posted 无痕幽雨

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了迪文屏幕T5L平台学习笔记四:C51使用printf或者sprintf注意事项相关的知识,希望对你有一定的参考价值。

今天用迪文的T5L平台的定时器2实现跑数(请看:迪文屏幕T5L平台学习笔记三:定时器使用),开始用sprintf转换后显示乱码,现在把调试过程记录下,希望能够帮助到你。

一、sprintf使用

sprintf

Summary
#include <stdio.h>

int sprintf (
  char *buffer,            /* storage buffer */
  const char *fmtstr       /* format string */
  <[>, arguments ...<]>);    /* additional arguments */
Description

The sprintf function formats a series of strings and numeric values and stores the resulting string in buffer. This function is similar to the printf routine, but it stores the formatted output in buffer rather than sending it to the output stream.

The fmtstr argument is a pointer to a format string which has the same form and function as the printf function's format string. The list of arguments are converted and output according to the corresponding format specifications in fmtstr.

Return Value

The sprintf function returns the number of characters actually written to buffer.

See Also

gets, puts, vprintf, vsprintf

Example
#include <stdio.h>

void tst_sprintf (void) 
  char buf [100];
  int n;
  int a,b;
  float pi;

  a = 123;
  b = 456;
  pi = 3.14159;

  n = sprintf (buf, "%f\\n", 1.1);
  n += sprintf (buf+n, "%d\\n", a);
  n += sprintf (buf+n, "%d %s %g", b, "---", pi);

  puts (buf);

 首先包含#include <stdio.h>

sprintf有三个参数,第一个是转换后的数据存储区,第二个是转换格式,第三个是要转换的数据,

转换成功则返回转换后数据区的数据长度,转换失败则返回负数。

使用如下:

                static int8_t    cData[10];
                int        i = 0;
                i = sprintf(cData,"%d\\0",s_wTimer);
                write_dgusii_vp(0x1000,cData,10);

显示结果不对,做如下测试:

                static int8_t    cData[10] = '1','0','0','\\0';
                int        i = 0;
                //i = sprintf(cData,"%d\\0",s_wTimer);
                write_dgusii_vp(0x1000,cData,10);

显示没有问题,那问题肯定是出在了sprintf上,难道C51不支持?

百度到如下:

(1)51中如果用sprintf()函数一定要多注意!

sprintf( (char *)d,"打印日期:%d-%02d-%02d %02d:%02d:%02d",

(int)SystemTime.year,

(int)SystemTime.month,

(int)SystemTime.day,

(int)SystemTime.hour,

(int)SystemTime.minute,

(int)SystemTime.second

);

如果不加强制转换,打印输出异常。51一定要注意,耽误了我一个上午的时间!!

(2)51中使用用sprintf()函数易出错的问题

1、头文件 #include "stdio.h"

2、sprintf( (char *)S,"%d", 1);

    结果 显示 256;

    正确的做法是sprintf( (char *)S,"%d", (int)1);

    输出的值可以随意,但是要将打印的值 进行强制类型转换。

试着强制转换下,没有问题,显示正常,难道只能用int类型?我感觉不对。

(3)keil51下使用sprintf问题

测试环境:keil c51 + STC89C52
说明:

1.keil的不定参数只有15个字节
也就是说sizeof(...) 加起来总共不能超过15字节,否则会出错

2.当不定参数中有常数时,你也会得不到你想要的结果,字符串除外

例:
 unsigned char count_sec=2, count_min=3, count_hour=4;
 xdata char buf[12] = "";
 sprintf(buf, "%2d:%2d:%2d", count_hour, count_min, count_sec);
则转换完的结果是不正确的。
解决方式1:
unsigned char count_sec=2, count_min=3, count_hour=4;
改成:
unsigned int count_sec=2, count_min=3, count_hour=4;
相同的代码在VC6.0下却是正常,为何?
printf("%d\\n",12);
printf("%d\\n",1234);
/****************KEIL C51**************************/
143: printf("%d\\n",12); 
C:0x098D 7BFF MOV R3,#0xFF
C:0x098F 7A0A MOV R2,#0x0A
C:0x0991 7902 MOV R1,#0x02
C:0x0993 75390C MOV 0x39,#0x0C
C:0x0996 12057D LCALL PRINTF(C:057D)
144: printf("%d\\n",1234); 
C:0x0999 7BFF MOV R3,#0xFF
C:0x099B 7A0A MOV R2,#0x0A
C:0x099D 7902 MOV R1,#0x02
C:0x099F 753904 MOV 0x39,#0x04
C:0x09A2 753AD2 MOV 0x3A,#0xD2
C:0x09A5 12057D LCALL PRINTF(C:057D)

/******************VC++6.0***************************/
155: printf("%d\\n",12);
00401628 push 0Ch
0040162A push offset string "%d\\n" (00426034)
0040162F call printf (00411040)
00401634 add esp,8
156: printf("%d\\n",1234);
00401637 push 4D2h
0040163C push offset string "%d\\n" (00426034)
00401641 call printf (00411040)
00401646 add esp,8
/*************************************************/

这个时候你会发现,为什么keil c51会错了,因为它没有内存对齐
输入常数12的时候,占一个字节,他就给你分配一个字节,输入1234的时候占2个字节
他就给你分配两个字节
而VC++6.0就不一样了,你输入的常数起码都给分配4个字节,所以在你取数据的时候,
取四个字节就不会错了,keil呢,该取几个呢?不知道?所以数据会出错

没有办法了,继续看C51帮助手册。

二、printf说明

在C51帮助文档里面查看到这样几句话:

 

看到没有,d默认是int型(两个字节),如果是一个字节,要用bd,如果是四个字节,则要用ld。

修改:

                static int8_t    cData[10];
                int        i = 0;
                i = sprintf(cData,"%ld\\0",s_wTimer);
                write_dgusii_vp(0x1000,cData,10);

显示正常。

printf

Summary
#include <stdio.h>

int printf (
  const char *fmtstr       /* format string */
  <[>, arguments ... <]>);   /* additional arguments */
Description

The printf function formats a series of strings and numeric values and builds a string to write to the output stream using the putchar function. The fmtstr argument is a format string that may be composed of characters, escape sequences, and format specifications.

Ordinary characters and escape sequences are copied to the stream in the order in which they are interpreted. Format specifications always begin with a percent sign ('%') and require that additional arguments are included in the printf function call.

The format string is read from left to right. The first format specification encountered references the first argument after fmtstr and converts and outputs it using the format specification. The second format specification accesses the second argument after fmtstr, and so on. If there are more arguments than format specifications, extra arguments are ignored. Results are unpredictable if there are not enough arguments for the format specifications or if the argument types do not match those specified by fmtstr.

Format specifications have the following general format:

% <[>flags<]> <[>width<]> <[>.precision<]> <[>b|B|l|L<]> type

Each field in the format specification may be a single character or a number which specifies a particular format option.

The type field is a single character that specifies whether the argument is interpreted as a character, string, number, or pointer, as shown in the following table.

TypeArgument TypeInput Format
dintSigned decimal number.
uunsigned intUnsigned decimal number.
ounsigned intUnsigned octal number.
xunsigned intUnsigned hexadecimal number using "0123456789abcedf".
Xunsigned intUnsigned hexadecimal number using "0123456789ABCDEF".
ffloatFloating-point number formatted as
<[>-<]>dddd.dddd.
efloatFloating-point number formatted as
<[>-<]>d.dddde<[>-<]>dd.
EfloatFloating-point number formatted as
<[>-<]>d.ddddE<[>-<]>dd.
gfloatFloating-point number using either the e or f format, whichever is more compact for the specified value and precision.
GfloatFloating-point number using either the E or f format, whichever is more compact for the specified value and precision.
ccharA single character.
s*A string of characters terminated by a null character ('\\0').
p*A generic pointer formatted as t:aaaa where t is the memory type and aaaa is the hexadecimal address.

Note

  • The optional characters l or L may immediately precede the type character to respectively specify long types for d, i, u, o, x, and X.
  • The optional characters b or B may immediately precede the type character to respectively specify char types for d, i, u, o, x, and X.

Characters following a percent sign that are not recognized as a format specification are treated as ordinary characters. For example, "%%" writes a single percent sign to the output stream.

The flags field is a single character used to justify the output and to print +/- signs and blanks, decimal points, and octal and hexadecimal prefixes, as shown in the following table.

FlagDescription
-Left justify the output in the specified field width.
+Prefix the output value with a + or - sign if the output is a signed type.
blank (' ')Prefix the output value with a blank if it is a signed positive value. Otherwise, no blank is prefixed.
#Prefixes a non-zero output value with 0, 0x, or 0X when used with o, x, and X field types, respectively.

When used with the e, E, f, g, and G field types, the # flag forces the output value to include a decimal point.

The # flag is ignored in all other cases.

The width field is a non-negative number that specifies the minimum number of characters printed. If the number of characters in the output value is less than width, blanks are added on the left (by default) or right (when the - flag is specified) to pad to the minimum width. If width is prefixed with a '0', zeros are padded instead of blanks. The width field never truncates the output. If the length of the output value exceeds the specified width, all characters are output.

The width field may be an asterisk ('*'), in which case an int argument from the argument list provides the width value. Specifying a 'b' in front of the asterisk specifies that the argument is an unsigned char.

The precision field is a non-negative number that specifies the number of characters to print, the number of significant digits, or the number of decimal places. The precision field can cause truncation or rounding of the output value in the case of a floating-point number as specified in the following table.

TypePrecision Field Meaning
d,u,o,x,XThe precision field specifies the minimum number of digits that are included in the output value. Digits are not truncated if the number of digits in the argument exceeds that defined in the precision field. If the number of digits in the argument is less than the precision field, the output value is padded on the left with zeros.
fThe precision field specifies the number of digits to the right of the decimal point. The last digit is rounded.
e,EThe precision field specifies the number of digits to the right of the decimal point. The last digit is rounded.
g,GThe precision field specifies the maximum number of significant digits in the output value.
sThe precision field specifies the maximum number of characters in the output value. Excess characters are not output.
c,pThe precision field has no effect on these field types.

The precision field may be an asterisk ('*'), in which case an int argument from the argument list provides the value. Specifying a 'b' in front of the asterisk specifies that the argument is an unsigned char.

Note

  • You must ensure that the argument type matches that of the format specification. You may use type casts to ensure that the proper type is passed to printf.
  • This function is implementation-specific and is based on the operation of the _getkey and putchar functions. These functions, as provided in the standard library, read and write characters using the microcontroller's serial port. Custom functions may use other I/O devices.
  • The total number of bytes that may be passed to this function is limited due to the memory restrictions imposed by the 8051. A maximum of 15 bytes may be passed in SMALL or COMPACT model. A maximum of 40 bytes may be passed in LARGE model.
Return Value

The printf function returns the number of characters actually written to the output stream.

See Also

gets, printf517, puts, scanf, scanf517, sprintf, sprintf517, sscanf, sscanf517, vprintf, vsprintf

Example
#include <stdio.h>

void tst_printf (void) 
  char a = 1;
  int b  = 12365;
  long c = 0x7FFFFFFF;

  unsigned char x = 'A';
  unsigned int y  = 54321;
  unsigned long z = 0x4A6F6E00;

  float f = 10.0;
  float g = 22.95;

  char buf [] = "Test String";
  char *p = buf;

  printf ("char %bd int %d long %ld\\n",a,b,c);
  printf ("Uchar %bu Uint %u Ulong %lu\\n",x,y,z);
  printf ("xchar %bx xint %x xlong %lx\\n",x,y,z);
  printf ("String %s is at address %p\\n",buf,p);
  printf ("%f != %g\\n", f, g);
  printf ("%*f != %*g\\n", (int)8, f, (int)8, g);

 造成sprintf或者printf显示乱码,确实是字节对齐问题,是我们没有给函数对应的参数,ARM内核,则没有这些限制。

三、在Keil C51 中使用printf ,首先需要重新实现 putchar(char c)函数。此函数在

char putchar (char c)   
        
    ES=0;        
    SBUF = c;        
    while(TI==0);        
    TI=0;        
    ES=1;        
    return 0;

这个下篇讲C51的串口再详细讲。

以上是关于迪文屏幕T5L平台学习笔记四:C51使用printf或者sprintf注意事项的主要内容,如果未能解决你的问题,请参考以下文章

迪文屏幕T5L平台学习笔记二:第一个C51C程序Demo

迪文屏幕T5L平台学习笔记五:C51使用UART2打印log

迪文屏幕T5L平台学习笔记三:定时器使用

迪文屏幕T5L平台学习笔记七:RS485测试

迪文屏幕T5L平台学习笔记零:KEIL环境搭建

迪文屏幕T5L平台学习笔记一:开发环境搭建注意事项