天嵌 ARM开发社区

 找回密码
 注册
查看: 1634|回复: 0

TQ210裸机编程(9)——实现printf

[复制链接]
freewing 发表于 2014-6-8 01:05:11 | 显示全部楼层 |阅读模式
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/
您需要登录后才可以回帖 登录 | 注册

本版积分规则

关闭

i.MX8系列ARM cortex A53 M4 工控板上一条 /1 下一条

Archiver|手机版|小黑屋|天嵌 嵌入式开发社区 ( 粤ICP备11094220号 )

GMT+8, 2024-3-29 13:24 , Processed in 1.015619 second(s), 18 queries .

Powered by Discuz! X3.4

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表