在網上找資料時找到一篇介紹在keil中使用printf()函數的文章,copy到這里,作為備忘。
在keil中printf默認是向串口中發送數據的,所以,如果應用該函數,必須先初始化串口,否則可能引起死機的情況,並且在printf之前應該先將TI置位,摘抄原因如下:
1.printf函數是調用putchar函數輸入的,而putchar應該是先判斷ti是否為1,不為1則等待為1。如果為1則清0,然后送出一 個字符。因此你如果直接使用printf函數,你的程序就會在putchar函數中等待ti為1。這時你的程序就相當於直接死掉了。你可以通過改寫 putchar函數實現自己的目的的。
2.Keil的串口處理比較巧妙的,我的分析如下:
putchar.c里面,是先檢測TI再發送。這樣做的目的是把盡可能多的時間留給2次串口操作之間的程序,而不是把等待字節發送的時間白白空等待浪費掉。所以,在系統初始化的時候,一定要令TI=1; 就可以順暢的使用printf函數了。摟主sbuf=" "的辦法,其實就是令TI=1. 另外要特別注意,printf函數執行完畢后,最后一個字節並未發送完畢,例如在485通訊中,此時如果切換為收模式,會丟失最后一字節.
3.一般串口發送都是等TI(字節發送完標志)為1就馬上發送下一字節,由於不管是中斷還是查詢TI標志的方法,都會檢測TI,因此首次發送必須置 位TI標志,使串口開始發送你的“在程序的初始化部分往串口數據寄存器SBUF里放一個字符來起用終端顯示;”方法最終作用也就是把TI置1,改成 TI=1;來啟動發送也是一樣的(當然,不會發出那個' '字符了)。
4.<stdio.h>中定義,調用底層的putchar()來實現.底層發送數據到串口時,先查TI=1是否成立,死等直到TI=1時將新數據寫入SBUF,函數返回,所以要先將TI置1,啟動第一次傳輸操作.可查看反匯編相關代碼理解其工作機理!
下面舉一個簡單的例子:
//===========================
#include <reg51.h>
#include <stdio.h>
//-------------------------------
int main()
{
Uart_init(); //初始化串口,這里就不寫具體代碼了。
TI = 1; //keil 調用stdio.h中printf函數前要置位。
while(1)
{
printf("Hello world!\n");
delay_ms(800); //延時程序,這里也不寫具體代碼了。
}
return 0;
}
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 theputchar function. The fmtstrargument 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 additionalarguments are included in the printf function call. The format string is read from left to right. The first format specification encountered references the firstargument after fmtstr and converts and outputs it using the format specification. The second format specification accesses the secondargument 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 byfmtstr. 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.
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.
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. Theprecision field can cause truncation or rounding of the output value in the case of a floating-point number as specified in the following table.
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.
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
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); } |
C51: PRINTF GIVES WRONG VALUES
Information in this article applies to:
- C51 All Versions
QUESTION
In C51, I am using printf and sprintf with many arguments and it is printing out wrong values. What is the reason for this behavior?
ANSWER
There are any number of reasons why printf can output incorrect values.
Probably the most common reason is that the format specifier and the argument type do not match. To solve this kind of problem, explicitly type cast each argument to the desired type. Be sure that you accurately specify the correct format specifier for that type.
Another reason may be that the number of bytes you may pass to functions with variable-length argument lists is fixed (since arguments are not passed on the stack). In small and compact memory model, printf and sprintf only reserve 15 bytes for the argument list. In the large memory model 40 bytes are reserved. For example, 15 bytes allow you to pass up to five generic (3-byte) pointers, or one generic pointer and three long arguments. This parameter limitation is a compromise to the limited memory available in 8051 devices.
Eight arguments will not fit in the space reserved for small or compact memory models. So, instead of one sprintf call with many arguments you must perform two or three calls and distribute the arguments among the sprintf calls. For example:
The following code exceeds the parameter space limits since seven generic pointers (21 bytes) are passed to sprintf.
char str[] = "Any Text"; sprintf (buff_out, "%s %s %s %s %s", str, str, str, str, str);
The above example may be rewritten as follows:
char str[] = "Any Text"; int len; len = sprintf (buff_out, "%s %s %s ", str, str, str); sprintf (buff_out + len, "%s %s", str, str);
MORE INFORMATION
- Refer to printf in the Cx51 User's Guide.