【详解】【一】制作一个与x86平台标准printf()功能相同的arm平台裸机printf()函数——视频原版代码注解

/*x86平台标准输出printf()函数原型:int printf(const char * fmt, ...);

*测试平台:Ubuntu16.04(64位机)

*ARM平台:s3c2440A

*编译器:gcc

*程序功能:制作一个与x86平台标准printf()功能相同的arm平台printf()函数,当完全裸机时,依然可以实现printf()函数的部分功能,包括输出:字符,字符串,(不同数据格式的)整数,(暂无浮点数,结构体);

//===============================================================================================================================

一、arm平台上实现与printf()函数相同功能的my_printf()函数

难点:1、裸机UART0调试打印功能的程序与自动确定可变参数的标准printf()函数的结合问题
2、如何判断printf()函数中字符串和输出格式字符的区别,及输出格式字符后面数据格式,还有变量的输出问题
3、单项数值/变量输出还是多项输出,答:多项输出
4、系统对 buf[64] 的前几位没有数据的数组元素如何处理? 答:在buf[]中有指针s,指向数据n的最高位或正负符号位,输出s指向的字符串,即为数据n
方法:归根到底,把数据当做字符串来处理,已然确定是的

*/

/*
2018-05-30
File:	my_printf.h
功能:my_printf.c文件的函数集合声明
*/
#ifndef		_MY_PRINTF_H
#define		_MY_PRINTF_H

int printf(const char * fmt, ...);
int my_printf_test(void);

#endif
/*
2018-05-29
File:my_printf.c
功能:制作一个与x86平台printf()功能相同的arm平台printf()函数
难点:
1、裸机UART0调试打印功能的程序与自动确定可变参数的标准printf()函数的结合问题
2、如何判断printf()函数中字符串和输出格式字符的区别,及输出格式字符后面数据格式,还有变量的输出问题
3、单项数值/变量输出还是多项输出,答:多项输出
4、系统对 buf[64] 的前几位没有数据的数组元素如何处理?	答:在buf[]中有指针s,指向数据n的最高位或正负符号位,输出s指向的字符串,即为数据n
方法:归根到底,把数据当做字符串来处理,已然确定是的
*/
//#include "s3c2440_soc.h"
#include "uart.h"
#include "my_printf.h"

//==========================================
//#define		_out_putchar	putchar
#define		MAX_NUMBER_BYTES	64
//==========================================
typedef 	char * 			va_list;
#define		_INTSIZEOF(n)	((sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1))
#define		va_start(ap, v)	(ap = (va_list)&v + _INTSIZEOF(v))
#define 	va_arg(ap, t)	(*(t*)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)))
#define		va_end(ap)		(ap = (va_list)0)
//==========================================
unsigned char hex_tab[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};    //或   unsigned char hex_tab[] = "0123456789abcdef";  两种写法打印效果完全一样;注意:hex_tab为地址常量,不可改变和被赋值              
/*
函数说明:
功能:输出规定格式的数字n
方法:把数字n当做字符串处理
特征:static int out_num(参数列表)内部函数,只可在本文件调用
*/
static int out_num(long n, int base, char lead, int maxwidth)	
{
	unsigned long m = 0;
	char buf[MAX_NUMBER_BYTES], *s = buf + sizeof(buf);		//定义数组buf[64]作为盛放数字n的缓冲器,数字n最长8字节,二进制64位
															//防止数据输出格式为二进制?
	int count = 0, i = 0;
	*--s = '\0';			//s = s - 1; *s = '\0';先自减,后赋值; 可做左值
	if(n < 0)
		m = -n;
	else
		m = n;
	/*板块:do{...}while();
	*功能: 1、把数字n写到字符数组buf[]中,(假若n为十进制)其中,
			   '\0'→buf[63](末元素),n个位→buf[62],n十位→buf[61],n百位→buf[60] ..., 
			   n宽度最高位→*s(此时,s的值为本函数out_num()结束时s的值)
	*		2、数字n的实际有效位长度为count位
	*		3、数字n为base进制
	*/
	do
	{
		*--s = hex_tab[m%base];
		count++;
	}while((m /= base) != 0);
	/*板块:if(){...}
	*用法: 1、printf格式字符为%8d, %08d时进入if()
	*		2、printf格式字符为%d, %c, %f, %s, %u, %x时进入if()
	*功能:把数字n的规定格式宽度中,实际有效位前面的剩余位赋值为0
	*/
	if(maxwidth && count < maxwidth)
	{
		for(i = maxwidth - count; i; i--)
			*--s = lead;
	}
	/*板块:if(){...}
	*用法:当 n<0 时,进入if(){...};
	*说明:此时,在数组buf[]的存储空间中,指针s指向数字n的格式长度的的首位或符号(±)位。
	*此时,字符串s即printf()打印的数字n
	*/
	if(n < 0)
		*--s = '-';
	
	return puts(s);
}
/*
函数说明:
功能:制作一个与x86平台printf()功能相同的arm平台printf()函数
特征:static int my_printf(参数列表); 内部函数,只可在本文件调用
*/
static int my_printf(const char * fmt, va_list ap)		//此时,ap已经指向(fmt指向的)字符串后第一个可变参数
{
	char lead = ' ';
	int maxwidth = 0;
	for(; *fmt != '\0'; fmt++)
	{
		/*板块说明:
		*功能:打印printf()函数参数列表中%前面的字符
		*/
		if(*fmt != '%')
		{
			putchar(*fmt);
			continue;
		}
		//format: %08d, %8d, %d, %u, %c, %s, %f
		/*
		*板块功能:判断并确定数据的输出格式:前导码lead,数据宽度maxwidth
		*/
		fmt++;
		if(*fmt == '0')
		{
			lead = '0';
			fmt++;
		}
		lead = ' ';
		maxwidth = 0;
		while(*fmt >= '0' && *fmt <= '9')
		{
			maxwidth *= 10;
			maxwidth += (*fmt - '0');    //-'0';不可省去,否则会造成打印的数据宽度>>32位
			fmt++;
		}
		/*
		*板块功能:
		*打印规定格式的数据
		*/
		switch(*fmt)
		{
		case 'd':
				out_num(va_arg(ap, int), 10, lead, maxwidth);
				break;
		case 'o':
				out_num(va_arg(ap, unsigned int), 8, lead, maxwidth);
				break;
		case 'u':
				out_num(va_arg(ap, unsigned int), 10, lead, maxwidth);
				break;
		case 'x':
				out_num(va_arg(ap, unsigned int), 16, lead, maxwidth);
				break;
		case 'c':
				putchar(va_arg(ap, int));
				break;
		case 's':
				puts(va_arg(ap, char *));
				break;
		default:
				putchar(*fmt);
				break;
		}
	}
	
	return 0;
}

int printf(const char * fmt, ...)
{
	va_list ap;
	va_start(ap, fmt);
	my_printf(fmt, ap);
	va_end(ap);
	
	return 0;
}
/*测试函数*/
int my_printf_test(void)
{
	printf("This if www.100ask.org	my_printf_test\n\r");
	printf("Test char				= %c, %c\n\r",  'A', 'a');
	printf("Test decimal number		= %d\n\r", 		123456);
	printf("Test decimal number		= %d\n\r", 		-123456);
	printf("Test decimal number		= 0x%x\n\r", 	0x55aa56ff);
	printf("Test string				= %s\n\r",		"www.100ask.org");
	printf("num = %08d\n\r", 	12345);
	printf("num = %8d\n\r", 	12345);
	printf("num = 0x%08d\n\r", 	0x12345);
	printf("num = 0x%08d\n\r", 	0x12345);
	printf("num = %05d\n\r", 	12345);
	printf("num = %5d\n\r", 	12345);
	printf("num1 = %05d, num1 = %05d\n\r", 0x12, 0x23);
	
	return 0;
}
/*==========================================================
arm平台的开发板的程序处理结果通过UART0在PC机的打印结果为:
Wakakaka
This if www.100ask.org  my_printf_test
Test char                               = A, a
Test decimal number             = 123456
Test decimal number             = -123456
Test decimal number             = 0x55aa56ff
Test string                             = www.100ask.org
num =    12345
num =    12345
num = 0x   74565
num = 0x   74565
num = 12345
num = 12345
num1 =    18, num1 =    35
nihao
woaho
dajihao
结果:程序运行成功
========================================================
解析printf()函数:
1.未遇到%,直接输出'n', 'n', 'n', 'n', 
2.遇到%,处理格式字符:
                %08d           %8d
前导码:	        '0'            (空格)
最大宽度:        8位            8位
进制:           十进制          十进制
*/

/*
2018-05-27
FIle:	main.c
功能:主函数
1、输出字符串
2、完成与printf()功能相同的的arm平台printf()函数的制作
3、用UART0作调试打印功能串口,并输入输出字符
*/
#include "s3c2440_soc.h"
#include "uart.h"
#include "my_printf.h"

int main(void)
{
	unsigned char c;
	led_test();
	uart0_init();

	puts("Wakakaka\n\r");
	my_printf_test();
	while(1)
	{
		c = getchar();
		if(c == '\n')
			putchar('\r');
		if(c == '\r')
			putchar('\n');
		putchar(c);
	}

	return 0;
}
/*
2018-05-27
File:	uart0_2>uart.c
功能:
把UART0设置成开发板和PC机的串口工具间的调试打印工具
步骤:
1、设置输入输出引脚GPH2,3,波特率,数据格式
2、设置字符输出函数,字符输入函数,字符串输出函数
*/
#include "s3c2440_soc.h"

/*115200, 8N1*/
void uart0_init(void)
{
	//设置输入输出引脚
	GPHCON &= ~((3<<6) | (3<<4));
	GPHCON |= ((2<<6) | (2<<4));
	GPHUP &= ~((1<<3)|(1<<2));
	//波特率UCON0 = 0B0101,UBRDIVn = (int)( UART 时钟 / ( 波特率 × 16) ) –1
	UCON0 = 0X00000005;
	UBRDIV0 = 26;
	//数据格式
	ULCON0 = 0X00000003;
}

/*字符输出函数*/
int putchar(int c)
{
	while(!(UTRSTAT0 & (1<<2)));

	UTXH0 = (unsigned char)c;
}
/*字符输入函数*/
int getchar(void)
{
	while(!(UTRSTAT0 & (1<<0)));

	return URXH0;
}
/*字符串输出函数*/
int puts(const char * s)
{
	while(*s)
	{
		putchar(*s);
		s++;
	}
}

/*

程序lib1funcs.S, 略

*/

/*
2018-05-26
File:	led.c
功能:
流水灯
GPF4,5,6
*/
#include "s3c2440_soc.h"

void delay(volatile int k)
{
	volatile int i, j;
	for(i = 0; i < k; i++)
		for(j = 0; j < 110; j++);
}
void led_test(void)
{
	int i = 30;
	int l = 4;
	GPFCON &= ~((3<<12)|(3<<10)|(3<<8));
	GPFCON |= ((1<<12)|(1<<10)|(1<<8));
	while(i)
	{
		GPFDAT |= (7<<4);
		GPFDAT &= ~(1<<l);
		l++;
		i--;
		delay(2000);
		if(l > 6)
			l = 4;
	}
}
/*
2018-05-27
File:	uart0_2>start.S
功能:
1、程序开始的地方
2、关闭看门狗
3、设置时钟频率
4、判断启动方式并设置栈地址
*/
.text
.global _start

_start:
	/*关闭看门狗*/
	ldr r0, = 0x53000000
	mov r1, #0
	str r1, [r0]
	
	/*设置时钟频率*/
	//设置锁定时间计数寄存器LOCKTIME(0X4C000000)
	ldr r0, = 0x4c000000
	ldr r1, = 0xffffffff
	str r1, [r0]
	//设置CLKDIVN = 0b101,使得T(fclk) : T(hclk) : T(pclk) = 1 : 4 : 8
	ldr r0, = 0x4c000014
	ldr r1, = 5
	str r1, [r0]
	/*
	*此时HDIVN 不为 0,CPU 总线模式应该使用以下指令使其从快总线模式改变为异步总线模式(S3C2440
	*不支持同步总线模式)。
	*/
	MRC  p15, 0,  r0,  c1,  c0,  0
	ORR  r0,  r0,  #0xc0000000
	MCR  p15, 0,  r0,  c1,  c0,  0
	//设置多层锁相环寄存器MPLL,使得FCLK = 400MHz
	ldr r0, = 0x4c000004
	ldr r1, =((92<<12)|(1<<4)|(1<<0))
	str r1, [r0]
	
	/*判断启动方式并设置栈地址*/
	mov r0, #0
	ldr r1, [r0]
	str r0, [r0]
	ldr r2, [r0]
	cmp r0, r2
	//先假设Norflash启动
	ldr sp, = 0x40000000 + 4096
	//再假设Norflash启动
	moveq sp, #4096
	streq r1, [r0]

	bl main
halt:
	b halt


/*
2018-05-27
File:	uart0_2>uart.h
功能:
函数声明集合
*/
#ifndef		_UART_H
#define 	_UART_H

void delay(volatile int k);
void led_test(void);

void uart0_init(void);
int putchar(int c);
int getchar(void);
int puts(const char * s);

int push_test(const char * format, ...);
int uart0_test(void);

#endif
/*
2018-05-31
File:	my_printf.h
功能:自制printf()函数的函数声明集合
*/
#ifndef		_MY_PRINTF_H
#define		_MY_PRINTF_H

int printf(const char * format, ...);
int my_printf_test(void);

#endif



all:
	arm-linux-gcc -c -o led.o led.c
	arm-linux-gcc -c -o start.o start.S
	arm-linux-gcc -c -o lib1funcs.o lib1funcs.S
	arm-linux-gcc -c -o my_printf.o my_printf.c
	arm-linux-gcc -c -o main.o main.c
	arm-linux-gcc -c -o uart.o uart.c
	arm-linux-ld -Ttext 0 -Tdata 0xe80 start.o led.o  uart.o  lib1funcs.o my_printf.o  main.o -o uart.elf
	arm-linux-objcopy -O binary -S uart.elf uart.bin
	arm-linux-objdump -D uart.elf > uart.dis
clean:
	rm *.o  *.bin  *.dis  *.elf

//==========================================================================

arm平台的开发板的程序处理结果通过UART0在PC机的打印结果为:
Wakakaka
This if www.100ask.org  my_printf_test
Test char                               = A, a
Test decimal number             = 123456
Test decimal number             = -123456
Test decimal number             = 0x55aa56ff
Test string                             = www.100ask.org
num =    12345
num =    12345
num = 0x   74565
num = 0x   74565
num = 12345
num = 12345
num1 =    18, num1 =    35
nihao
woaahuo
dajiahao
结果:程序运行成功

//==========================================================================

补充:

/*
2018-04-23
FIle:s3c2440_soc.h
功能:宏定义头文件
*/
#ifndef		_S3C2440_SOC_H
#define 	_S3C2440_SOC_H

#define 	_REG(x)					(*(volatile unsigned int *)(x))		//地址[0xx]所对应的寄存器(即内存空间)
#define 	_REG_BYTE(x)			(*(volatile unsigned char *)(x))	

/*Memory Controllers*/
#define     BWSCON                   _REG(0x48000000)   //Bus width & wait status control   
#define     BANKCON0                 _REG(0x48000004)   //Boot ROM control                  
#define     BANKCON1                 _REG(0x48000008)   //BANK1 control                     
#define     BANKCON2                 _REG(0x4800000C)   //BANK2 control                     
#define     BANKCON3                 _REG(0x48000010)   //BANK3 control                     
#define     BANKCON4                 _REG(0x48000014)   //BANK4 control                     
#define     BANKCON5                 _REG(0x48000018)   //BANK5 control                     
#define     BANKCON6                 _REG(0x4800001C)   //BANK6 control                     
#define     BANKCON7                 _REG(0x48000020)   //BANK7 control                     
#define     REFRESH                  _REG(0x48000024)   //DRAM/SDRAM refresh control        
#define     BANKSIZE                 _REG(0x48000028)   //Flexible bank size                
#define     MRSRB6                   _REG(0x4800002C)   //Mode register set for SDRAM BANK6 
#define     MRSRB7                   _REG(0x48000030)   //Mode register set for SDRAM BANK7 

/*WATDOG Timer register*/
#define 	WATCON 					 _REG(0x53000000)					//WATDOG Timer control register

/*I/O Points*/
#define     GPACON                   _REG(0x56000000)  //Port A control                             
#define     GPADAT                   _REG(0x56000004)  //Port A data                                        
#define     GPBCON                   _REG(0x56000010)  //Port B control                                     
#define     GPBDAT                   _REG(0x56000014)  //Port B data                                        
#define     GPBUP                    _REG(0x56000018)  //Pull-up control B                                  
#define     GPCCON                   _REG(0x56000020)  //Port C control                                     
#define     GPCDAT                   _REG(0x56000024)  //Port C data                                        
#define     GPCUP                    _REG(0x56000028)  //Pull-up control C                                  
#define     GPDCON                   _REG(0x56000030)  //Port D control                                     
#define     GPDDA1T                  _REG(0x56000034)  //Port D data                                        
#define     GPDUP                    _REG(0x56000038)  //Pull-up control D                                  
#define     GPECON                   _REG(0x56000040)  //Port E control                                     
#define     GPEDAT                   _REG(0x56000044)  //Port E data                                        
#define     GPEUP                    _REG(0x56000048)  //Pull-up control E                                  
#define     GPFCON                   _REG(0x56000050)  //Port F control                                     
#define     GPFDAT                   _REG(0x56000054)  //Port F data                                        
#define     GPFUP                    _REG(0x56000058)  //Pull-up control F                                  
#define     GPGCON                   _REG(0x56000060)  //Port G control                                     
#define     GPGDAT                   _REG(0x56000064)  //Port G data                                        
#define     GPGUP                    _REG(0x56000068)  //Pull-up control G                                  
#define     GPHCON                   _REG(0x56000070)  //Port H control                                     
#define     GPHDAT                   _REG(0x56000074)  //Port H data                                        
#define     GPHUP                    _REG(0x56000078)  //Pull-up control H                                  
#define     GPJCON                   _REG(0x560000D0)  //Port J control                                     
#define     GPJDAT                   _REG(0x560000D4)  //Port J data                                        
#define     GPJUP                    _REG(0x560000D8)  //Pull-up control J                                  
#define     MISCCR                   _REG(0x56000080)  //Miscellaneous control                              
#define     DCLKCON                  _REG(0x56000084)  //DCLK0/1 control                                    
#define     EXTINT0                  _REG(0x56000088)  //External interrupt control register 0              
#define     EXTINT1                  _REG(0x5600008C)  //External interrupt control register 1              
#define     EXTINT2                  _REG(0x56000090)  //External interrupt control register 2              
#define     EINTFLT0                 _REG(0x56000094)  //? W R/W Reserved                                   
#define     EINTFLT1                 _REG(0x56000098)  //Reserved                                           
#define     EINTFLT2                 _REG(0x5600009C)  //External interrupt filter control register 2       
#define     EINTFLT3                 _REG(0x560000A0)  //External interrupt filter control register 3       
#define     EINTMASK                 _REG(0x560000A4)  //External interrupt mask                            
#define     EINTPEND                 _REG(0x560000A8)  //External interrupt pending                         
#define     GSTATUS0                 _REG(0x560000AC)  //R External pin status                              
#define     GSTATUS1                 _REG(0x560000B0)  //R/W Chip ID                                        
#define     GSTATUS2                 _REG(0x560000B4)  //Reset status                                       
#define     GSTATUS3                 _REG(0x560000B8)  //Inform register                                    
#define     GSTATUS4                 _REG(0x560000BC)  //Inform register                                    
#define     MSLCON                   _REG(0x560000CC)  //Memory sleep control register    

/*UART*/  
#define     ULCON0                   _REG(0x50000000)  //UART 0 line control        
#define     UCON0                    _REG(0x50000004)  //UART 0 control             
#define     UFCON0                   _REG(0x50000008)  //UART 0 FIFO control        
#define     UMCON0                   _REG(0x5000000C)  //UART 0 modem control       
#define     UTRSTAT0                 _REG(0x50000010)  //UART 0 Tx/Rx status        
#define     UERSTAT0                 _REG(0x50000014)  //UART 0 Rx error status     
#define     UFSTAT0                  _REG(0x50000018)  //UART 0 FIFO status         
#define     UMSTAT0                  _REG(0x5000001C)  //UART 0 modem status      
#define     UTXH0                    _REG_BYTE(0x50000020)  //UART 0 transmission hold, 小端模式-高字节高地址,低字节低地址
#define     URXH0                    _REG_BYTE(0x50000024)  //UART 0 receive buffer, 小端模式-高字节高地址,低字节低地址      
#define     UBRDIV0                  _REG(0x50000028)  //UART 0 baud rate divisor   
#define     ULCON1                   _REG(0x50004000)  //UART 1 line control        
#define     UCON1                    _REG(0x50004004)  //UART 1 control             
#define     UFCON1                   _REG(0x50004008)  //UART 1 FIFO control        
#define     UMCON1                   _REG(0x5000400C)  //UART 1 modem control       
#define     UTRSTAT1                 _REG(0x50004010)  //UART 1 Tx/Rx status        
#define     UERSTAT1                 _REG(0x50004014)  //UART 1 Rx error status     
#define     UFSTAT1                  _REG(0x50004018)  //UART 1 FIFO status         
#define     UMSTAT1                  _REG(0x5000401C)  //UART 1 modem status          
#define     UTXH1                    _REG_BYTE(0x50004020)  //UART 1 transmission hold,小端模式-高字节高地址,低字节低地址   
#define     URXH1                    _REG_BYTE(0x50004024)  //UART 1 receive buffer,小端模式-高字节高地址,低字节低地址     
#define     UBRDIV1                  _REG(0x50004028)  //UART 1 baud rate divisor   
#define     ULCON2                   _REG(0x50008000)  //UART 2 line control        
#define     UCON2                    _REG(0x50008004)  //UART 2 control             
#define     UFCON2                   _REG(0x50008008)  //UART 2 FIFO control         
#define     UTRSTAT2                 _REG(0x50008010)  //UART 2 Tx/Rx status        
#define     UERSTAT2                 _REG(0x50008014)  //UART 2 Rx error status     
#define     UFSTAT2                  _REG(0x50008018)  //UART 2 FIFO status         
#define     UTXH2                    _REG_BYTE(0x50008020)  //UART 2 transmission hold,小端模式-高字节高地址,低字节低地址   
#define     URXH2                    _REG_BYTE(0x50008024)  //UART 2 receive buffer,小端模式-高字节高地址,低字节低地址       
#define     UBRDIV2                  _REG(0x50008028)  //UART 2 baud rate divisor  

#endif

猜你喜欢

转载自blog.csdn.net/weixin_39420903/article/details/80509344