|
printf的原型如下
int printf(const char *format, ...);
它带有一个固定的参数format,紧接着是可选参数,所谓可选参数就是参数个数和类型都不固定,也可以没有。
C语言传参是从最后一个参数开始压栈的。
比如在ARM Linux系统下,ATPCS规定为递减堆栈,假设栈顶SP=0x24000000
假设有这么一个函数void foo(int a, int b, int c);
则参数a先压入栈,地址为SP-4=0x23FFFFFC;
紧接着b压入栈,地址为SP-4=0x23FFFFF8;
紧接着c压入栈,地址为SP-4=0x23FFFFF4;
标准C为我们提供了相关的宏来处理可选参数,这些宏在stdarg.h中定义。
在Linux发行版系统(比如ubuntu)中执行man stdarg可查看相关帮助信息。
root@zjh:/mnt/hgfs/E/cloud/embedded/my_code/tq210# man stdarg
#include <stdarg.h>
void va_start(va_list ap, last);
type va_arg(va_list ap, type);
void va_end(va_list ap);
void va_copy(va_list dest, va_list src);
…………………
…………………
va_list ap:实际上就是int *ap;
va_start :初始化ap,使ap指向第一个可选参数,last就是调用函数的固定参数;
va_arg :解析出一个可变参数,并使ap指向下一个可选参数;
va_end :是ap指针无效
注意:在同一个函数中,每次对va_start的调用都必须有对应的va_end调用与之匹配。
通过man stdarg查询的帮助信息中有一个例子可以参考:
[cpp] view plaincopy
01.#include <stdio.h>
02.#include <stdarg.h>
03.
04.void
05.foo(char *fmt, ...)
06.{
07. va_list ap;
08. int d;
09. char c, *s;
10.
11. va_start(ap, fmt);
12. while (*fmt)
13. switch (*fmt++) {
14. case 's': /* string */
15. s = va_arg(ap, char *);
16. printf("string %s\n", s);
17. break;
18. case 'd': /* int */
19. d = va_arg(ap, int);
20. printf("int %d\n", d);
21. break;
22. case 'c': /* char */
23. /* need a cast here since va_arg only
24. takes fully promoted types */
25. c = (char) va_arg(ap, int);
26. printf("char %c\n", c);
27. break;
28. }
29. va_end(ap);
30.}
下面实现自己的printf函数
start.S
[cpp] view plaincopy
01..global _start /* 声明一个全局的标号 */
02._start:
03. bl clock_init /* 时钟初始化 */
04. bl uart_init /* 串口初始化 */
05. bl main /* 跳转到C函数去执行 */
06.halt:
07. b halt
clock.c
[cpp] view plaincopy
01.#define APLLCON0 *((volatile unsigned int *)0xE0100100)
02.#define MPLLCON *((volatile unsigned int *)0xE0100108)
03.#define EPLLCON0 *((volatile unsigned int *)0xE0100110)
04.#define VPLLCON *((volatile unsigned int *)0xE0100120)
05.#define CLK_SRC0 *((volatile unsigned int *)0xE0100200)
06.#define CLK_DIV0 *((volatile unsigned int *)0xE0100300)
07.#define CLK_DIV1 *((volatile unsigned int *)0xE0100304)
08.#define CLK_DIV2 *((volatile unsigned int *)0xE0100308)
09.#define CLK_DIV3 *((volatile unsigned int *)0xE010030C)
10.
11.void clock_init()
12.{
13. /* 1、设置PLL_LOCK寄存器(这里使用默认值) */
14. /* 2、设置PLL_CON寄存器(使用芯片手册推荐的值) */
15. APLLCON0 = (1 << 0) | (3 << 8) | (125 << 16) | (1 << 31); /* FOUTAPLL = 1000MHz */
16. MPLLCON = (1 << 0) | (12 << 8) | (667 << 16) | (1 << 31); /* FOUTMPLL = 667MHz */
17. EPLLCON0 = (1 << 0) | (12 << 8) | (667 << 16) | (1 << 31); /* FOUTEPLL = 96MHz */
18. VPLLCON = (3 << 0) | (6 << 8) | (108 << 16) | (1 << 31); /* FOUTVPLL = 54MHz */
19.
20. /* 3、选择PLL为时钟输出 */
21. /* MOUT_MSYS = SCLKAPLL = 1000MHz
22. ** MOUT_DSYS = SCLKMPLL = 667MHz
23. ** MOUT_PSYS = SCLKMPLL = 667MHz
24. */
25. CLK_SRC0 = (1 << 0) | (1 << 4) | (1 << 8) | (1 << 12);
26.
27. /* 4、设置系统时钟分频值 */
28. /* freq(ARMCLK) = MOUT_MSYS / (APLL_RATIO + 1) = 1000MHz / (0 + 1) = 1000MHz
29. ** freq(HCLK_MSYS) = ARMCLK / (HCLK_MSYS_RATIO + 1) = 1000MHz / (4 + 1) = 200MHz
30. ** freq(PCLK_MSYS) = HCLK_MSYS / (PCLK_MSYS_RATIO + 1) = 200MHz / (1 + 1) = 100MHz
31. ** freq(HCLK_DSYS) = MOUT_DSYS / (HCLK_DSYS_RATIO + 1) = 667 / (3 + 1) = 166MHz
32. ** freq(PCLK_DSYS) = HCLK_DSYS / (PCLK_DSYS_RATIO + 1) = 166 / (1 + 1) = 83MHz
33. ** freq(HCLK_PSYS) = MOUT_PSYS / (HCLK_PSYS_RATIO + 1) = 667 / (4 + 1) = 133MHz
34. ** freq(PCLK_PSYS) = HCLK_PSYS / (PCLK_PSYS_RATIO + 1) = 133 / (1 + 1) = 66MHz
35. */
36. CLK_DIV0 = (0 << 0) | (4 << 8) | (1 << 12) | (3 << 16) | (1 << 20) | (4 << 24) | (1 << 28);
37.}
types.h
[cpp] view plaincopy
01.#ifndef TYPES_H_
02.#define TYPES_H_
03.
04.typedef volatile char s8;
05.typedef volatile short s16;
06.typedef volatile int s32;
07.
08.typedef volatile unsigned char u8;
09.typedef volatile unsigned short u16;
10.typedef volatile unsigned int u32;
11.
12.#endif
uart.h
[cpp] view plaincopy
01.#ifndef UART_H_
02.#define UART_H_
03.
04.#include "types.h"
05.
06.void putchar(u8 c);
07.void puts(s8 *s);
08.void put_int(u32 v);
09.void put_hex(u8 v, u8 small);
10.void put_int_hex(u32 v, u8 small);
11.int printf(const char *fmt, ...);
12.
13.#endif
uart.c
[cpp] view plaincopy
01.#include <stdarg.h>
02.#include "types.h"
03.
04.#define GPA0CON *((volatile unsigned int *)0xE0200000)
05.#define ULCON0 *((volatile unsigned int *)0xE2900000)
06.#define UCON0 *((volatile unsigned int *)0xE2900004)
07.#define UFCON0 *((volatile unsigned int *)0xE2900008)
08.#define UTRSTAT0 *((volatile unsigned int *)0xE2900010)
09.#define UTXH0 *((volatile unsigned int *)0xE2900020)
10.#define URXH0 *((volatile unsigned int *)0xE2900024)
11.#define UBRDIV0 *((volatile unsigned int *)0xE2900028)
12.#define UDIVSLOT0 *((volatile unsigned int *)0xE290002C)
13.
14./*
15.** UART0初始化
16.*/
17.void uart_init()
18.{
19. /*
20. ** 配置GPA0_0为UART_0_RXD
21. ** 配置GPA0_1为UART_0_TXD
22. */
23. GPA0CON &= ~0xFF;
24. GPA0CON |= 0x22;
25.
26. /* 8-bits/One stop bit/No parity/Normal mode operation */
27. ULCON0 = 0x3 | (0 << 2) | (0 << 3) | (0 << 6);
28.
29. /* Interrupt request or polling mode/Normal transmit/Normal operation/PCLK/*/
30. UCON0 = 1 | (1 << 2) | (0 << 10);
31.
32. /* 静止FIFO */
33. UFCON0 = 0;
34.
35. /*
36. ** 波特率计算:115200bps
37. ** PCLK = 66MHz
38. ** DIV_VAL = (66000000/(115200 x 16))-1 = 35.8 - 1 = 34.8
39. ** UBRDIV0 = 34(DIV_VAL的整数部分)
40. ** (num of 1's in UDIVSLOTn)/16 = 0.8
41. ** (num of 1's in UDIVSLOTn) = 12
42. ** UDIVSLOT0 = 0xDDDD (查表)
43. */
44. UBRDIV0 = 34;
45. UDIVSLOT0 = 0xDDDD;
46.}
47.
48./* 输出一字节的数据到终端 */
49.void uart_send_byte(u8 byte)
50.{
51. while (!(UTRSTAT0 & (1 << 2))); /* 等待发送缓冲区为空 */
52. UTXH0 = byte; /* 发送一字节数据 */
53.}
54.
55./* 从终端接收一字节数据 */
56.u8 uart_recv_byte()
57.{
58. while (!(UTRSTAT0 & 1)); /* 等待接收缓冲区有数据可读 */
59. return URXH0; /* 接收一字节数据 */
60.}
61.
62./* 打印字符c到终端 */
63.void putchar(u8 c)
64.{
65. uart_send_byte(c);
66. if (c == '\n')
67. uart_send_byte('\r');
68.}
69.
70./* 打印字符串s到终端 */
71.void puts(s8 *s)
72.{
73. s8 *p = s;
74. while (*p)
75. putchar(*p++);
76.}
77.
78./* 打印整数v到终端 */
79.void put_int(u32 v)
80.{
81. int i;
82. u8 a[10];
83. u8 cnt = 0;
84.
85. if (v == 0)
86. {
87. putchar('0');
88. return;
89. }
90.
91. while (v)
92. {
93. a[cnt++] = v % 10;
94. v /= 10;
95. }
96.
97. for (i = cnt - 1; i >= 0; i--)
98. putchar(a + 0x30);
99. /*
100. ** 整数0-9的ASCII分别为0x30-0x39
101. */
102.}
103.
104./* 将一字节十六进制数按十六进制显示打印到终端 */
105.void put_hex(u8 v, u8 small)
106.{
107. /* 注意:必须用volatile修饰,否则会出错 */
108. u8 h, l; /* 高4位和第4位(这里按二进制算) */
109. char *hex1 = "0123456789abcdef"; /* 这里放在数据段中 */
110. char *hex2 = "0123456789ABCDEF";
111.
112. h = v >> 4;
113. l = v & 0x0F;
114.
115. if (small) /* 小写 */
116. {
117. putchar(hex1[h]); /* 高4位 */
118. putchar(hex1[l]); /* 低4位 */
119. }
120. else /* 大写 */
121. {
122. putchar(hex2[h]); /* 高4位 */
123. putchar(hex2[l]); /* 低4位 */
124. }
125.}
126.
127./* 将int型整数按16进制打印到终端 */
128.void put_int_hex(u32 v, u8 small)
129.{
130. if (v >> 24)
131. {
132. put_hex(v >> 24, small);
133. put_hex((v >> 16) & 0xFF, small);
134. put_hex((v >> 8) & 0xFF, small);
135. put_hex(v & 0xFF, small);
136. }
137. else if ((v >> 16) & 0xFF)
138. {
139. put_hex((v >> 16) & 0xFF, small);
140. put_hex((v >> 8) & 0xFF, small);
141. put_hex(v & 0xFF, small);
142. }
143. else if ((v >> 8) & 0xFF)
144. {
145. put_hex((v >> 8) & 0xFF, small);
146. put_hex(v & 0xFF, small);
147. }
148. else
149. put_hex(v & 0xFF, small);
150.}
151.
152./* 格式化输出到终端 */
153.int printf(const char *fmt, ...)
154.{
155. va_list ap;
156. s8 c;
157. s8 *s;
158. u32 d;
159. u8 small;
160.
161. va_start(ap, fmt);
162. while (*fmt)
163. {
164. small = 0;
165. c = *fmt++;
166. if (c == '%')
167. {
168. switch (*fmt++)
169. {
170. case 'c': /* char */
171. c = (char) va_arg(ap, int);
172. putchar(c);
173. break;
174. case 's': /* string */
175. s = va_arg(ap, char *);
176. puts(s);
177. break;
178. case 'd': /* int */
179. case 'u':
180. d = va_arg(ap, int);
181. put_int(d);
182. break;
183. case 'x':
184. small = 1; // small
185. case 'X':
186. d = va_arg(ap, int);
187. put_int_hex(d, small);
188. break;
189. }
190. }
191. else
192. putchar(c);
193. }
194. va_end(ap);
195.}
main.c
[cpp] view plaincopy
01.#include "uart.h"
02.#include "types.h"
03.
04.void delay(u32 t)
05.{
06. u32 t2 = 0xFFFF;
07. while (t--)
08. for (; t2; t2--);
09.}
10.
11.int main()
12.{
13. u32 t = 0;
14. printf("\nUart Test for printf\n");
15. while (1)
16. {
17. printf("t = %d\n", t++);
18. delay(500000);
19. }
20. return 0;
21.}
Makefile
[cpp] view plaincopy
01.uart.bin: start.o clock.o uart.o main.o
02. arm-linux-ld -Ttext 0xD0020010 -o uart.elf $^
03. arm-linux-objcopy -O binary uart.elf $@
04. arm-linux-objdump -D uart.elf > uart.dis
05.
06.%.o : %.c
07. arm-linux-gcc -c $< -o $@
08.%.o : %.S
09. arm-linux-gcc -c $< -o $@
10.
11.clean:
12. rm *.o *.elf *.bin *.dis
实验效果如下:
Uart Test for printft =
t = 1
t = 2
t = 3
t = 4
t = 5
t = 6
t = 7
t = 8
t = 9
t = 10
烧写过程见《TQ210裸机编程(5)——系统时钟配置》
转载请注明来源:http://blog.csdn.net/zjhsucceed_329/
|
|