linux arm printf 函数与串口,11、串口实现printf()函数--s3c440

一、概要

由于以往的单片机实现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;

三、方法二Uart_SendString(char *pt)

void Uart_SendString(char *pt)

{

while (*pt)

putc(*pt++);

四、方法三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

{

putc(g_pcOutBuf[i]);

}

return len;

}

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

va_arg, va_end ,va_start就是三个宏

c122d728d4c4d9e3a1cfa16f6a948038.png

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)

{

returnvsnprintf(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

/*

*************************************************************************

*

* lcl bootloader

*

*************************************************************************

*/

.globl _start

_start:    b    start_code

HandleUndef:

b    HandleUndef @ 0x04: 指未定义指令终止模式的向量地址

HandleSWI:

b HandleSWI @ 0x08 指管理模式,通过SWI指令进入

HandlePerfetchAbort:

b HandlePerfetchAbort @ 0x0c: 指令预取终止导致的异常的向量地址

HandleDataAbort:

b HandleDataAbort @ 0x10: 数据访问终止导致的异常的向量地址

HandleNotUsed:

b HandleNotUsed         @ 0x14: 保留

b HandleIRQ @ 0x18: 中断模式的向量地址

HandleFIQ:

b    HandleFIQ             @ 0x1c: 快中断模式的向量地址

start_code:

bl clock_init

bl cpu_init_crit

msr cpsr_c,#0xd2 @进入中断模式

ldr sp,=0x33fffc00             @设置中断模式堆栈指针

msr cpsr_c,#0xdf     @进入系统模式

msr cpsr_c,#0x5f             @开IRQ中断

/*

*call c function .

*/

mov    fp, #0             // no previous frame, so fp=0

ldr sp, =4096

bl NandInit              //修改成用从Nand Flash中拷贝程序

bl ReadImageFromNand

/*

*run in sdram

*/

ldr sp, =0x34000000

ldr lr, =halt_loop          @返回地址

ldr pc, =    Main

halt_loop:

b halt_loop

HandleIRQ:

sub lr,lr,#4

stmdb sp!,{r0-r12,lr} @保存使用到的寄存器至中断堆栈区

ldr lr,=int_return @设置ISR的返回地址

ldr pc,=EINT_Handle         @调用中断服务函数

int_return:

ldmia sp!,{r0-r12,pc}^ @中断返回,恢复寄存器值,^表示将spsr的值复制到cpsr

.globl ReadPage512

ReadPage512: //读512字节

stmfd     {r2-r7}

mov    r2, #0x200

1:

ldr    r4, [r1] //取出32bit,按8bit字访问

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

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

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

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

ldr    r4, [r1]

ldr    r5, [r1]

ldr    r6, [r1]

ldr    r7, [r1]

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

ldr    r4, [r1]

ldr    r5, [r1]

ldr    r6, [r1]

ldr    r7, [r1]

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

ldr    r4, [r1]

ldr    r5, [r1]

ldr    r6, [r1]

ldr    r7, [r1]

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

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

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

ldmfd     {r2-r7}

mov    pc,lr //返回

clock_init:

#define pWTCON            0x53000000

#define CLKDIVN            0x4C000014     /* clock divisor register */

#define CLK_CTL_BASE    0x4C000000    /* clock base address */

#define MDIV_405        0x7f << 12    /* MDIV 0x7f*/

#define PSDIV_405        0x21        /* PDIV SDIV 0x2 0x1 */

/* turn off the watchdog */

ldr r0, =pWTCON

mov r1, #0x0

str r1, [r0]

/* FCLK:HCLK:PCLK = 1:4:8 */

ldr r0, =CLKDIVN

mov r1, #5                 //1:4:8

str r1, [r0]

mrc p15, 0, r1, c1, c0, 0    //切换到实时总线HCLK

orr r1, r1, #0xc0000000

mcr p15, 0, r1, c1, c0, 0

mov r1, #CLK_CTL_BASE

mov r2, #MDIV_405

add r2, r2, #PSDIV_405

str r2, [r1, #0x04]     /* MPLLCON address     Mpll=405MHZ */

mov    pc, lr

/*

*MMU and SDRAM initialize

*/

cpu_init_crit:

/*

* flush v4 I/D caches

*/

mov    r0, #0

mcr    p15, 0, r0, c7, c7, 0    /* flush v3/v4 cache */

mcr    p15, 0, r0, c8, c7, 0    /* flush v4 TLB */

/*

* disable MMU stuff and caches

*/

mrc    p15, 0, r0, c1, c0, 0

bic    r0, r0, #0x00002300    @ clear bits 13, 9:8 (--V- --RS)

bic    r0, r0, #0x00000087    @ clear bits 7, 2:0 (B--- -CAM)

orr    r0, r0, #0x00000002    @ set bit 2 (A) Align

orr    r0, r0, #0x00001000    @ set bit 12 (I) I-Cache

mcr    p15, 0, r0, c1, c0, 0

/*

* before relocating, we have to setup RAM timing

* because memory timing is board-dependend, you will

* find a lowlevel_init.S in your board directory.

*/

mov    ip, lr

bl memsetup     /* memory control configuration */

mov    lr, ip

mov    pc, lr

memsetup:

/* memory control configuration */

/* make r0 relative the current location so that it */

/* reads SMRDATA out of FLASH rather than memory ! */

#define MEM_CTL_BASE    0x48000000

mov r1,#MEM_CTL_BASE

adr r2,mem_cfg_val

add r3,r1,#52

1:

ldr     r4, [r2], #4

str     r4, [r1], #4

cmp     r1, r3

bne     1b

mov     pc,lr

.align 4

/* the literal pools origin */

mem_cfg_val:

.long        0x22011110

.long        0x00000700

.long        0x00000700

.long        0x00000700

.long        0x00000700

.long        0x00000700

.long        0x00000700    @BANK5

.long        0x00018005

.long        0x00018005

.long        0x00ac03f4    @REFRESH // 12MHZ 0x00ac07a3

.long        0x000000B1

.long        0x00000030

.long        0x00000030

修改uart0_init.h,当生成的静态库中需用到其中的函数。

uart0_init.h

#ifndef __UART0_INIT_H__

#define __UART0_INIT_H__

#define SERIAL_ECHO

void uart0_init();

void putc(unsigned char c);

unsigned char getc(void);

void Uart_SendString(char *pt);

#endif

printf.c

#include "vsprintf.h"

#include "string.h"

#include "printf.h"

extern void putc(unsigned char c);

extern unsigned char getc(void);

#define    OUTBUFSIZE    1024

#define    INBUFSIZE    1024

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

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

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

{

int i;

int len;

va_list args;

va_start(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;

}

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

{

int i = 0;

unsigned char c;

va_list args;

while(1)

{

c = getc();

if((c == 0x0d) || (c == 0x0a))

{

g_pcInBuf[i] = '\0';

break;

}

else

{

g_pcInBuf[i++] = c;

}

}

va_start(args,fmt);

i = vsscanf(g_pcInBuf,fmt,args);

va_end(args);

return i;

}

修改main.c函数,来验证我的们printf()函数。

main.c

/*

GPBCON 0x56000010 Port B control

GPBDAT 0x56000014 Port B data

GPBUP 0x56000018 Pull-up control B

LED1 GPB5 LED2 GPB6 LED3 GPB7 LED4 GPB8

*/

#include "uart0_init.h"

#include "s3c2440_addr.h"

#include "interrupt.h"

void leds_init()

{

/*GPBCON &= (GPX_mask(5) & GPX_mask(6) & GPX_mask(7) & GPX_mask(8));

GPBCON |= (GPB5_out | GPB6_out | GPB7_out | GPB8_out);

GPBUP = GPX_up;

GPBDAT |=( 0xf << 5 );*/

GPBCON = (GPB5_out | GPB6_out | GPB7_out | GPB8_out);

GPBUP     = GPX_up;

}

void buttons_eint_init()

{

GPGCON &= (GPX_mask(0) & GPX_mask(3) & GPX_mask(5) & GPX_mask(6));

GPGCON |= (GPG0_eint8 | GPG3_eint11 | GPG5_eint13 | GPG6_eint14);

}

int Main()

{

unsigned long dwDat;

int i=0;

char str[100];

leds_init();

buttons_eint_init();

uart0_init();

irq_init();

GPBDAT |=( 0xf << 5 );

Uart_SendString("\r wait interrupt. \r\n");

while (1)

{

GPBDAT |=( 0xf << 5 );

printf("\r\nEnter value: \n\r");

scanf("%s",str);

printf("Input char is %s\n\r",str); //\n换行,\r移到行首

sscanf(str,"%d",&i);

printf("print interger : %d, 0x%x\n\r", i, i);

}

return 0;

}

修改Makefile文件,可参考本博另一文章对此Makefile的分析《个人Makefile通用模板》

CC = arm-linux-gcc

LD = arm-linux-ld

AR = arm-linux-ar

OBJCOPY = arm-linux-objcopy

OBJDUMP = arm-linux-objdump

INCLUDEDIR     := $(shell pwd)/include

CFLAGS         := -Wall -O2

CPPFLAGS     := -nostdinc -I$(INCLUDEDIR)

export     CC LD OBJCOPY OBJDUMP INCLUDEDIR CFLAGS CPPFLAGS

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

stdio.bin: $(objs)

${LD} -Tmem.lds -o stdio_elf $^

${OBJCOPY} -O binary -S stdio_elf $@

${OBJDUMP} -D -m arm stdio_elf > memory.dis

${OBJDUMP} -dx stdio_elf > memory.map

rm -f stdio_elf *.o

.PHONY : lib/libc.a

lib/libc.a:

cd lib; make; cd ..

%.o:%.c

${CC} $(CPPFLAGS) $(CFLAGS) -c -o $@ $<

%.o:%.S

${CC} $(CPPFLAGS) $(CFLAGS) -c -o $@ $<

clean:

make clean -C lib

rm -f stdio.bin memory.dis memory.map

修改mem.lds,使其能在内存中正常运行。分析 可参考本博另一文《GCC-LD 连接脚本分析--uboot.lds》

mem.lds

/*

*lcl bootloader mem.lds.. AT((LOADADDR(.init)+SIZEOF(.init)+3)&~(0x03))

*/

OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")

OUTPUT_ARCH(arm)

ENTRY(_start)

SECTIONS {

. = 0x30000000;

. = ALIGN(4);

.init : AT(0) {start.o nand.o}

. = ALIGN(4);

. = 0x30001000;

.text : AT(4096) { *(.text) }

.rodata ALIGN(4) : AT((LOADADDR(.text)+SIZEOF(.text)+3)&~(0x03)) { *(.rodata*) }

.data ALIGN(4) : AT((LOADADDR(.rodata)+SIZEOF(.rodata)+3)&~(0x03)) { *(.data) }

. = ALIGN(4);

__bss_start = .;

.bss :{ *(.bss) *(COMMON)}

__bss_end = .;

}

全部源代码打包如下:密码为主页地址

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值