Chinaunix首页 | 论坛 | 博客
  • 博客访问: 306331
  • 博文数量: 63
  • 博客积分: 1482
  • 博客等级: 上尉
  • 技术积分: 1185
  • 用 户 组: 普通用户
  • 注册时间: 2011-03-12 19:06
个人简介

hello world!

文章分类

全部博文(63)

分类: LINUX

2011-03-20 16:09:02

一、概要

由于以往的单片机实现printf函数成本太高,基本上不会其上实现printf功能,随着ARM芯片的发展,printf调试受到了极大的欢迎。对于嵌入式软件的开发人员而言,“printf调试(printf-debugging)”这个术语描述了将调试字符串从嵌入式目标空闲的串口压出,并在运行于宿主工作站的终端模拟器上显示结果的常见方法。出于这个目的,许多程序员更喜欢使用有名的printf() C语言库函数,因为它在将文本输出和数据组合成单个函数调用上具有灵活性。

在嵌入式系统中,串口常用来辅助调试输出一些调试信息。所以有必要实现串口的格式化输出功能,这可以由3种方法实现.

1)构建串口的输入输出putc getc .

2)使用数组构建字符串输出.函数Uart_SendString(char *pt)

3)写printf()函数.

二、方法一putc(), getc()

void putc(char data)

{

while (!(UTRSTAT0 & 0x4)) ; //Wait until THR is empty.

UTXH0 = data;

}

unsigned char getc(void)

{

while (!(UTRSTAT0 & (1))) ; //Wait until THx is empty.

    return URXH0;

}//可参考本博另文章《4、UART通用异步接收器-s3c2440

三、方法二Uart_SendString(char *pt)

void Uart_SendString(char *pt)

{

while (*pt)

putc(*pt++);

}//可参考本博另文章《4、UART通用异步接收器-s3c2440

四、方法三printf()函数

为了减少难度,提高效率,故继承前人的知识,在第三种方法来在自己的ARM9芯片上基于C语言实现串口Printf函数。

1、printf函数体

static unsigned char g_pcOutBuf[1024];

static unsigned char g_pcInBuf[1024];

int printf(const char *fmt, ...)

{

int i;

int len;

va_list args;

va_sart(args, fmt);

len = vsprintf(g_pcOutBuf,fmt,args);

va_end(args);

for (i = 0; i < strlen(g_pcOutBuf); i++)

{

putc(g_pcOutBuf[i]);

}

return len;

}

va_list就一个指针类型的封闭typedef char *va_list;

va_arg, va_end ,va_start就是三个宏

va_start(ap,A):很明显它先得到第一个参数内存地址,然后又加上这个参数的内存大小,就是下个参数的内存地址。

_bnd(X,bnd):对于任何编译器,每个栈单元的大小都是sizeof(int), 而函数的每个参数都至少要占一个栈单元大小。其中#define  _AUPBND  (sizeof (int) - 1) .

va_arg:第一次调用时,它得到是参数列表中的第二个参数的值。

va_end:把指针归0。

ap:va_linst的一个变量arg;T:是要取得的参数类型fmt的格式;A:是一个参数。 

vsprintf:返回写入buf的字符个数。

int vsprintf(char *buf, const char *fmt, va_list args)

{

return vsnprintf(buf, (~0U)>>1, fmt, args);

}

vsnprintf(...):格式化。它接受确定输出格式的格式字符串fmt。用格式字符串对个数变化的参数进行格式化,产生格式化输出,此函数比较复杂,以后分析。

五、些特殊规定字符 

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

字符         作用

—————————————————————————— 

\n           换行

\f           清屏并换页

\r           回车

\t           Tab符 

—————————————————————————— 

六、源程序

     由于比较多,只列出几个比较重要点。在我的另一文章的《》

 
的基础上做出的修改,及增加。
     由于生成的二进制文件比较大,故要用到Nand Flash的驱动来拷贝程序到内存中可参考下文,
3、NAND Flash控制器--s3c2440》 。在start.S中修改了启动时的拷贝代码部分,增加nand.h nand.c文件。
 
start.S
  1. /*
  2.  *************************************************************************
  3.  *
  4.  * lcl bootloader
  5.  *
  6.  *************************************************************************
  7.  */
  8. .globl _start
  9. _start:    b    start_code

  10. HandleUndef:
  11.      b    HandleUndef @ 0x04: 指未定义指令终止模式的向量地址
  12.             
  13. HandleSWI:
  14.         b HandleSWI @ 0x08 指管理模式,通过SWI指令进入
  15.             
  16. HandlePerfetchAbort:
  17.         b HandlePerfetchAbort @ 0x0c: 指令预取终止导致的异常的向量地址
  18.             
  19. HandleDataAbort:
  20.         b HandleDataAbort @ 0x10: 数据访问终止导致的异常的向量地址
  21.             
  22. HandleNotUsed:
  23.         b HandleNotUsed         @ 0x14: 保留

  24.         b HandleIRQ @ 0x18: 中断模式的向量地址
  25.         
  26. HandleFIQ:
  27.         b    HandleFIQ             @ 0x1c: 快中断模式的向量地址

  28. start_code:

  29.     bl clock_init
  30.     bl cpu_init_crit    
  31.     
  32.     msr cpsr_c,#0xd2 @进入中断模式
  33.     ldr sp,=0x33fffc00             @设置中断模式堆栈指针

  34.     msr cpsr_c,#0xdf     @进入系统模式
  35.     msr cpsr_c,#0x5f             @开IRQ中断    
  36.     
  37.     /*
  38.      *call c function .
  39.      */
  40.     mov    fp, #0             // no previous frame, so fp=0    

  41.     ldr sp, =4096
  42.     bl NandInit              //修改成用从Nand Flash中拷贝程序
  43.     bl ReadImageFromNand
  44.     
  45.     /*
  46.      *run in sdram
  47.      */

  48.     ldr sp, =0x34000000
  49.     ldr lr, =halt_loop          @返回地址
  50.     ldr pc, =    Main        

  51. halt_loop:
  52.    b halt_loop
  53.    
  54. HandleIRQ:            
  55.     sub lr,lr,#4
  56.     stmdb sp!,{r0-r12,lr} @保存使用到的寄存器至中断堆栈区
  57.     ldr lr,=int_return @设置ISR的返回地址
  58.     ldr pc,=EINT_Handle         @调用中断服务函数
  59. int_return:
  60.     ldmia sp!,{r0-r12,pc}^ @中断返回,恢复寄存器值,^表示将spsr的值复制到cpsr

  61. .globl ReadPage512
  62. ReadPage512: //读512字节

  63.     stmfd     {r2-r7}
  64.     mov    r2, #0x200
  65. 1:
  66.     ldr    r4, [r1] //取出32bit,按8bit字访问

  67.     ldr    r5, [r1] //取出32bit

  68.     ldr    r6, [r1] //取出32bit

  69.     ldr    r7, [r1] //取出32bit

  70.     stmia     {r4-r7} //存 64bit 到buffer中

  71.     ldr    r4, [r1]
  72.     ldr    r5, [r1]
  73.     ldr    r6, [r1]
  74.     ldr    r7, [r1]
  75.     stmia     {r4-r7} //存 64bit 到buffer中

  76.     ldr    r4, [r1]
  77.     ldr    r5, [r1]
  78.     ldr    r6, [r1]
  79.     ldr    r7, [r1]
  80.     stmia     {r4-r7} //存 64bit 到buffer中

  81.     ldr    r4, [r1]
  82.     ldr    r5, [r1]
  83.     ldr    r6, [r1]
  84.     ldr    r7, [r1]
  85.     stmia     {r4-r7} //存 64bit 到buffer中

  86.     subs    r2, r2, #64 //减去已读出的64byte

  87.     bne    1b; //循环读出直到512byte

  88.     ldmfd     {r2-r7}
  89.     mov    pc,lr //返回


  90.     
  91. clock_init:
  92. #define pWTCON            0x53000000
  93. #define CLKDIVN            0x4C000014     /* clock divisor register */
  94. #define CLK_CTL_BASE    0x4C000000    /* clock base address */
  95.         
  96. #define MDIV_405        0x7f << 12    /* MDIV 0x7f*/
  97. #define PSDIV_405        0x21        /* PDIV SDIV 0x2 0x1 */
  98.         
  99.     /* turn off the watchdog */
  100.         
  101.         ldr r0, =pWTCON
  102.         mov r1, #0x0
  103.         str r1, [r0]
  104.         
  105.         /* FCLK:HCLK:PCLK = 1:4:8 */
  106.         ldr r0, =CLKDIVN
  107.         mov r1, #5                 //1:4:8

  108.         str r1, [r0]
  109.             
  110.         mrc p15, 0, r1, c1, c0, 0    //切换到实时总线HCLK

  111.         orr r1, r1, #0xc0000000     
  112.         mcr p15, 0, r1, c1, c0, 0    
  113.             
  114.             
  115.         mov r1, #CLK_CTL_BASE    
  116.         mov r2, #MDIV_405    
  117.         add r2, r2, #PSDIV_405    
  118.         str r2, [r1, #0x04]     /* MPLLCON address     Mpll=405MHZ */    
  119.         mov    pc, lr
  120.     /*
  121.      *MMU and SDRAM initialize
  122.      */
  123.      
  124. cpu_init_crit:
  125.     /*
  126.      * flush v4 I/D caches
  127.      */
  128.     mov    r0, #0
  129.     mcr    p15, 0, r0, c7, c7, 0    /* flush v3/v4 cache */
  130.     mcr    p15, 0, r0, c8, c7, 0    /* flush v4 TLB */

  131.     /*
  132.      * disable MMU stuff and caches
  133.      */
  134.     mrc    p15, 0, r0, c1, c0, 0
  135.     bic    r0, r0, #0x00002300    @ clear bits 13, 9:8 (--V- --RS)
  136.     bic    r0, r0, #0x00000087    @ clear bits 7, 2:0 (B--- -CAM)
  137.     orr    r0, r0, #0x00000002    @ set bit 2 (A) Align
  138.     orr    r0, r0, #0x00001000    @ set bit 12 (I) I-Cache
  139.     mcr    p15, 0, r0, c1, c0, 0

  140.     /*
  141.      * before relocating, we have to setup RAM timing
  142.      * because memory timing is board-dependend, you will
  143.      * find a lowlevel_init.S in your board directory.
  144.      */
  145.     mov    ip, lr

  146.     bl memsetup     /* memory control configuration */

  147.     mov    lr, ip
  148.     mov    pc, lr

  149. memsetup:
  150.     /* memory control configuration */
  151.     /* make r0 relative the current location so that it */
  152.     /* reads SMRDATA out of FLASH rather than memory ! */

  153. #define MEM_CTL_BASE    0x48000000

  154.     mov r1,#MEM_CTL_BASE
  155.     adr r2,mem_cfg_val
  156.     add r3,r1,#52
  157. 1:
  158.     ldr     r4, [r2], #4
  159.     str     r4, [r1], #4
  160.     cmp     r1, r3
  161.     bne     1b
  162.     mov     pc,lr    

  163. .align 4
  164.         /* the literal pools origin */    
  165. mem_cfg_val:
  166.     .long        0x22011110
  167.     .long        0x00000700
  168.     .long        0x00000700
  169.     .long        0x00000700
  170.     .long        0x00000700
  171.     .long        0x00000700
  172.     .long        0x00000700    @BANK5
  173.     .long        0x00018005
  174.     .long        0x00018005    
  175.     .long        0x00ac03f4    @REFRESH // 12MHZ 0x00ac07a3

  176.     .long        0x000000B1
  177.     .long        0x00000030
  178.     .long        0x00000030
      修改uart0_init.h,当生成的静态库中需用到其中的函数。
uart0_init.h
  1. #ifndef __UART0_INIT_H__
  2. #define __UART0_INIT_H__

  3. #define SERIAL_ECHO

  4. void uart0_init();
  5. void putc(unsigned char c);
  6. unsigned char getc(void);
  7. void Uart_SendString(char *pt);

  8. #endif
printf.c
  1. #include "vsprintf.h"
  2. #include "string.h"
  3. #include "printf.h"

  4. extern void putc(unsigned char c);
  5. extern unsigned char getc(void);

  6. #define    OUTBUFSIZE    1024
  7. #define    INBUFSIZE    1024


  8. static unsigned char g_pcOutBuf[OUTBUFSIZE];//输出缓冲

  9. static unsigned char g_pcInBuf[INBUFSIZE]; //输入缓冲



  10. int printf(const char *fmt, ...)
  11. {
  12.     int i;
  13.     int len;
  14.     va_list args;

  15.     va_start(args, fmt);
  16.     len = vsprintf(g_pcOutBuf,fmt,args);
  17.     va_end(args);
  18.     for (i = 0; i < strlen(g_pcOutBuf); i++)
  19.     {
  20.         putc(g_pcOutBuf[i]);
  21.     }
  22.     return len;
  23. }



  24. int scanf(const char * fmt, ...)
  25. {
  26.     int i = 0;
  27.     unsigned char c;
  28.     va_list args;
  29.     
  30.     while(1)
  31.     {
  32.         c = getc();
  33.         if((c == 0x0d) || (c == 0x0a))
  34.         {
  35.             g_pcInBuf[i] = '\0';
  36.             break;
  37.         }
  38.         else
  39.         {
  40.             g_pcInBuf[i++] = c;
  41.         }
  42.     }
  43.     
  44.     va_start(args,fmt);
  45.     i = vsscanf(g_pcInBuf,fmt,args);
  46.     va_end(args);

  47.     return i;
  48. }
修改main.c函数,来验证我的们printf()函数。
main.c
  1. /*
  2. GPBCON 0x56000010 Port B control
  3. GPBDAT 0x56000014 Port B data
  4. GPBUP 0x56000018 Pull-up control B

  5. LED1 GPB5 LED2 GPB6 LED3 GPB7 LED4 GPB8

  6. */

  7. #include "uart0_init.h"
  8. #include "s3c2440_addr.h"
  9. #include "interrupt.h"

  10. void leds_init()
  11. {
  12.     /*GPBCON &= (GPX_mask(5) & GPX_mask(6) & GPX_mask(7) & GPX_mask(8));
  13.     GPBCON |= (GPB5_out | GPB6_out | GPB7_out | GPB8_out);
  14.     GPBUP = GPX_up;
  15.     GPBDAT |=( 0xf << 5 );*/
  16.     GPBCON = (GPB5_out | GPB6_out | GPB7_out | GPB8_out);
  17.     GPBUP     = GPX_up;
  18. }
  19. void buttons_eint_init()
  20. {
  21.     GPGCON &= (GPX_mask(0) & GPX_mask(3) & GPX_mask(5) & GPX_mask(6));
  22.     GPGCON |= (GPG0_eint8 | GPG3_eint11 | GPG5_eint13 | GPG6_eint14);
  23. }

  24. int Main()
  25. {
  26.     unsigned long dwDat;
  27.     int i=0;
  28.     char str[100];
  29.     
  30.     leds_init();
  31.     buttons_eint_init();
  32.     uart0_init();
  33.     irq_init();
  34.         
  35.     GPBDAT |=( 0xf << 5 );
  36.     
  37.     Uart_SendString("\r wait interrupt. \r\n");    

  38.     while (1)
  39.     {
  40.         GPBDAT |=( 0xf << 5 );
  41.         printf("\r\nEnter value: \n\r");
  42.         scanf("%s",str);
  43.         printf("Input char is %s\n\r",str); //\n换行,\r移到行首

  44.         sscanf(str,"%d",&i);
  45.         printf("print interger : %d, 0x%x\n\r", i, i);
  46.     }

  47.     return 0;
  48. }
修改Makefile文件,可参考本博另一文章对此Makefile的分析《个人Makefile通用模板
  1. CC = arm-linux-gcc
  2. LD = arm-linux-ld
  3. AR = arm-linux-ar
  4. OBJCOPY = arm-linux-objcopy
  5. OBJDUMP = arm-linux-objdump

  6. INCLUDEDIR     := $(shell pwd)/include
  7. CFLAGS         := -Wall -O2
  8. CPPFLAGS     := -nostdinc -I$(INCLUDEDIR)

  9. export     CC LD OBJCOPY OBJDUMP INCLUDEDIR CFLAGS CPPFLAGS

  10. objs := start.o main.o uart0_init.o interrupt.o nand.o lib/libc.a

  11. stdio.bin: $(objs)
  12.     ${LD} -Tmem.lds -o stdio_elf $^
  13.     ${OBJCOPY} -O binary -S stdio_elf $@
  14.     ${OBJDUMP} -D -m arm stdio_elf > memory.dis
  15.     ${OBJDUMP} -dx stdio_elf > memory.map
  16.     rm -f stdio_elf *.o

  17. .PHONY : lib/libc.a
  18. lib/libc.a:
  19.     cd lib; make; cd ..
  20.     
  21. %.o:%.c
  22.     ${CC} $(CPPFLAGS) $(CFLAGS) -c -o $@ $<

  23. %.o:%.S
  24.     ${CC} $(CPPFLAGS) $(CFLAGS) -c -o $@ $<

  25. clean:
  26.     make clean -C lib
  27.     rm -f stdio.bin memory.dis memory.map
修改mem.lds,使其能在内存中正常运行。分析 可参考本博另一文《GCC-LD 连接脚本分析--uboot.lds
mem.lds
  1. /*
  2.  *lcl bootloader mem.lds.. AT((LOADADDR(.init)+SIZEOF(.init)+3)&~(0x03))
  3.  */

  4. OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
  5. OUTPUT_ARCH(arm)
  6. ENTRY(_start)

  7. SECTIONS {
  8.   . = 0x30000000;

  9.   . = ALIGN(4);
  10.   .init : AT(0) {start.o nand.o}

  11.   . = ALIGN(4);
  12.   . = 0x30001000;
  13.   .text : AT(4096) { *(.text) }
  14.     
  15.   .rodata ALIGN(4) : AT((LOADADDR(.text)+SIZEOF(.text)+3)&~(0x03)) { *(.rodata*) }

  16.   .data ALIGN(4) : AT((LOADADDR(.rodata)+SIZEOF(.rodata)+3)&~(0x03)) { *(.data) }
  17.   
  18.   . = ALIGN(4);
  19.   __bss_start = .;
  20.   .bss :{ *(.bss) *(COMMON)}
  21.   __bss_end = .;
  22. }
全部源代码打包如下:密码为主页地址
阅读(3615) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~