LV060-串口格式化函数
一、概述
上一节中,我们实现了 UART1 基本的数据收发功能,虽然可以用来调试程序,但是功能太单一了,只能输出字符。如果需要输出数字的时候就需要我们自己先将数字转换为字符,非常的不方便。学习 STM32 串口的时候我们都会将 printf 函数映射到串口上,这样就可以使用 printf 函数来完成格式化输出了,使用非常方便。那 printf 这样的格式化函数能移植到 I.MX6U-ALPHA 开发板上吗?当然也可以啦。
格式化函数说的是 printf、 sprintf 和 scanf 这样的函数,分为格式化输入和格式化输出两类函数。学习 C 语言的时候常常通过 printf 函数在屏幕上显示字符串,通过 scanf 函数从键盘获取输入。这样就有了输入和输出了,实现了最基本的人机交互。在 I.MX6U-ALPHA 开发板上也可以将 printf 和 scanf 映射到串口上,这样就可以使用 MobaXterm 作为开发板的终端,完成与开发板的交互。也可以使用 printf 和 sprintf 来实现各种各样的格式化字符串,方便我们后续的开发。
二、printf 移植
1. 从 uboot 移植
这里直接用了正点原子的资料,他为我们提供了一个 stdio 文件夹

stdio 里面的文件其实是从 uboot 里面移植过来的。其实都是可以从 uboot 源码还有交叉编译工具链安装目录里面找出相应的文件,完成格式化函数的移植。这里要注意一点, stdio 中并没有实现完全版的格式化函数,比如 printf 函数并不支持浮点数,但是基本够我们使用了。 移植完成后,直接编译可能会报下边的警告:
In file included from project/main.c:5:0:
bsp/uart/bsp_uart.h:12:6: 警告: conflicting types for built-in function ‘putc’
void putc(unsigned char c);
^
bsp/uart/bsp_uart.h:13:6: 警告: conflicting types for built-in function ‘puts’
void puts(char *str);
^2
3
4
5
6
7
这个表示“putc”、“puts”这两个函数与内建函数冲突,在编译的时候加入选项“-fno-builtin”表示不使用内建函数就可以了。另外还需要添加在编译 C 文件的时候添加选项“-Wa,-mimplicit-it = thumb”,否则的话会有如下类似的错误提示:
thumb conditional instruction should be in IT block -- `addcs r5,r5,#65536移植完成后,可以看 gitee 仓库:02_UART/03_printf · sumumm/imx6ull-bare-demo
2. 自己实现一个?
自己实现的这个是参考的韦东山教程的逻辑开发教程,所以这里写一下基于的工程情况吧。可以看这里:02_UART/04_my_printf · sumumm/imx6ull-bare-demo。需要注意的是这里的 printf 后面的换行只能是 \n\r,换成 \r\n 就会报以下问题:

具体原因还不清楚,上面直接移植 uboot 中相关文件的版本倒是没问题,后面知道了再补充。
2.1 基础工程结构
2.1.1 目录结构

2.1.2 imx6ull.lds
SECTIONS {
. = 0x80100000;
. = ALIGN(4);
.text :
{
*(.text)
}
. = ALIGN(4);
.rodata : { *(.rodata) }
. = ALIGN(4);
.data : { *(.data) }
. = ALIGN(4);
__bss_start = .;
.bss : { *(.bss) *(.COMMON) }
__bss_end = .;
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2.1.3 main.c
#include "uart.h"
int main()
{
unsigned char cTestData ; /*用于测试发送的数据*/
Uart_Init() ;
PutStr("Hello, world!\n\r"); /*发送字符串*/
while(1)
{
cTestData = GetChar() ; /*等待从串口获取数据*/
if (cTestData == '\r') /*添加回到行首\r*/
{
PutChar('\n');
}
if (cTestData == '\n') /*换行\n*/
{
PutChar('\r');
}
PutChar(cTestData) ; /*从串口发送数据*/
}
return 0;
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
2.1.4 start.S
.text
.global _start
_start:
/* 设置栈 */
ldr sp,=0x80200000
/* 清除bss段 */
bl clean_bss
/* 跳转到主函数 */
bl main
halt:
b halt
clean_bss:
ldr r1, =__bss_start // 将链接脚本变量__bss_start变量保存于r1
ldr r2, =__bss_end // 将链接脚本变量__bss_end变量保存于r2
mov r3, #0
clean:
strb r3, [r1] // 将当前地址下的数据清零
add r1, r1, #1 // 将r1内存储的地址+1
cmp r1, r2 // 相等:清零操作结束;否则继续执行clean函数清零bss段
bne clean
mov pc, lr2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
2.1.5 uart.c
#include "uart.h"
static volatile unsigned int *IOMUXC_SW_MUX_CTL_PAD_UART1_TX_DATA ;
static volatile unsigned int *IOMUXC_SW_MUX_CTL_PAD_UART1_RX_DATA ;
static volatile unsigned int *IOMUXC_UART1_RX_DATA_SELECT_INPUT ;
void Uart_Init(void)
{
IOMUXC_SW_MUX_CTL_PAD_UART1_TX_DATA = (volatile unsigned int *)(0x20E0084);
IOMUXC_SW_MUX_CTL_PAD_UART1_RX_DATA = (volatile unsigned int *)(0x20E0088);
IOMUXC_UART1_RX_DATA_SELECT_INPUT = (volatile unsigned int *)(0x20E0624);
*IOMUXC_SW_MUX_CTL_PAD_UART1_RX_DATA = 0;
*IOMUXC_UART1_RX_DATA_SELECT_INPUT = 3;
*IOMUXC_SW_MUX_CTL_PAD_UART1_TX_DATA = 0;
UART1->UCR1 |= (1 << 0) ; /*关闭当前串口*/
/*
* 设置 UART 传输格式:
* UART1 中的 UCR2 寄存器关键 bit 如下
* [14]: 1:忽略RTS引脚
* [8] : 0: 关闭奇偶校验 默认为 0,无需设置
* [6] : 0: 停止位 1 位 默认为 0,无需设置
* [5] : 1: 数据长度 8 位
* [2] : 1: 发送数据使能
* [1] : 1: 接收数据使能
*/
UART1->UCR2 |= (1<<14) |(1<<5) |(1<<2)|(1<<1);
/*
* UART1 中的 UCR3 寄存器关键 bit 如下
* [2]: 1:根据官方文档表示,IM6ULL的UART用了这个MUXED模型,提示要设置
*/
UART1->UCR3 |= (1<<2);
/*
* 设置波特率
* 根据芯片手册得知波特率计算公式:
* Baud Rate = Ref Freq / (16 * (UBMR + 1)/(UBIR+1))
* 当我们需要设置 115200 的波特率
* UART1_UFCR [9:7] = 101,表示不分频,得到当前 UART 参考频率 Ref Freq :80M ,
* 带入公式:115200 = 80000000 /(16*(UBMR + 1)/(UBIR+1))
*
* 选取一组满足上式的参数:UBMR、UBIR 即可
*
* UART1_UBIR = 71
* UART1_UBMR = 3124
*/
UART1->UFCR = 5 << 7; /* Uart 的时钟 clk:80MHz */
UART1->UBIR = 71;
UART1->UBMR = 3124;
UART1->UCR1 |= (1 << 0); /*使能当前串口*/
}
void PutChar(int c)
{
while (!((UART1->USR2) & (1<<3))); /*等待上个字节发送完毕*/
UART1->UTXD = (unsigned char)c;
}
unsigned char GetChar(void)
{
while (!(UART1->USR2 & (1<<0))); /*等待接收数据*/
return (unsigned char)UART1->URXD;
}
void PutStr(const char *s)
{
while (*s)
{
PutChar(*s);
s++;
}
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
2.1.6 uart.h
#ifndef _UART_H_
#define _UART_H_
/*UART1 的寄存器的基地址*/
#define UART1_BASE (0x2020000u)
#define UART1 ((UART_Type *)UART1_BASE)
/*根据 IMX6ULL 芯片手册 <<55.15 UART Memory Map/Register Definition> > 的 3608 页,定义 UART 的结构体,*/
typedef struct {
volatile unsigned int URXD; /**< UART Receiver Register, offset: 0x0 串口接收寄存器,偏移地址 0x0 */
unsigned char RESERVED_0[60];
volatile unsigned int UTXD; /**< UART Transmitter Register, offset: 0x40 串口发送寄存器,偏移地址 0x40*/
unsigned char RESERVED_1[60];
volatile unsigned int UCR1; /**< UART Control Register 1, offset: 0x80 串口控制寄存器 1,偏移地址 0x80*/
volatile unsigned int UCR2; /**< UART Control Register 2, offset: 0x84 串口控制寄存器 2,偏移地址 0x84*/
volatile unsigned int UCR3; /**< UART Control Register 3, offset: 0x88 串口控制寄存器 3,偏移地址 0x88*/
volatile unsigned int UCR4; /**< UART Control Register 4, offset: 0x8C 串口控制寄存器 4,偏移地址 0x8C*/
volatile unsigned int UFCR; /**< UART FIFO Control Register, offset: 0x90 串口 FIFO 控制寄存器,偏移地址 0x90*/
volatile unsigned int USR1; /**< UART Status Register 1, offset: 0x94 串口状态寄存器 1,偏移地址 0x94*/
volatile unsigned int USR2; /**< UART Status Register 2, offset: 0x98 串口状态寄存器 2,偏移地址 0x98*/
volatile unsigned int UESC; /**< UART Escape Character Register, offset: 0x9C 串口转义字符寄存器,偏移地址 0x9C*/
volatile unsigned int UTIM; /**< UART Escape Timer Register, offset: 0xA0 串口转义定时器寄存器 偏移地址 0xA0*/
volatile unsigned int UBIR; /**< UART BRM Incremental Register, offset: 0xA4 串口二进制倍率增加寄存器 偏移地址 0xA4*/
volatile unsigned int UBMR; /**< UART BRM Modulator Register, offset: 0xA8 串口二进制倍率调节寄存器 偏移地址 0xA8*/
volatile unsigned int UBRC; /**< UART Baud Rate Count Register, offset: 0xAC 串口波特率计数寄存器 偏移地址 0xAC*/
volatile unsigned int ONEMS; /**< UART One Millisecond Register, offset: 0xB0 串口一毫秒寄存器 偏移地址 0xB0*/
volatile unsigned int UTS; /**< UART Test Register, offset: 0xB4 串口测试寄存器 偏移地址 0xB4*/
volatile unsigned int UMCR; /**< UART RS-485 Mode Control Register, offset: 0xB8 串口 485 模式控制寄存器 偏移地址 0xB8*/
} UART_Type;
void Uart_Init(void);
void PutChar(int c);
#endif2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
2.1.7 Makefile
PREFIX=arm-linux-gnueabihf-
CC=$(PREFIX)gcc
LD=$(PREFIX)ld
AR=$(PREFIX)ar
OBJCOPY=$(PREFIX)objcopy
OBJDUMP=$(PREFIX)objdump
uart.img : start.S uart.c main.c
$(CC) -nostdlib -g -c -o start.o start.S
$(CC) -nostdlib -g -c -o uart.o uart.c
$(CC) -nostdlib -g -c -o main.o main.c
$(LD) -T imx6ull.lds -g start.o uart.o main.o -o uart.elf
$(OBJCOPY) -O binary -S uart.elf uart.bin
$(OBJDUMP) -D -m arm uart.elf > uart.dis
./tools/mkimage -n ./tools/imximage.cfg.cfgtmp -T imximage -e 0x80100000 -d uart.bin uart.imx
dd if=/dev/zero of=1k.bin bs=1024 count=1
cat 1k.bin uart.imx > uart.img
clean:
rm -f uart.dis uart.bin uart.elf uart.imx uart.img *.o2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2.2 移植 printf
2.2.1 raise 函数
在 uart.c 中加入 raise 函数,用于防止编译失败。
int raise(int signum)/* raise 函数,防止编译报错 */
{
return 0;
}2
3
4
2.2.2 修改 Makefile
my_printf.c 中用到除法的求模运算,需要提供除法库。一般的交叉工具链里都提示有基本的数学运算,它们位于 libgcc.a 中。我们需要把 libgcc.a 也链接进程序里,需要修改 Makefile。
注意:链接指令中,每个“ -L”表示库在哪里,即它的目录;“ -l”表示哪个库,即库的名称, -lgcc 表示会链接“libgcc.a”库。
对 Makefile 作如下修改:
(1)增加 $(CC) -nostdlib -g -c -o my_printf.o my_printf.c
(2)在 $(LD) -T imx6ull.lds -g start.o uart.o main.o my_printf.o -o my_printf.elf 后 添加 -lgcc – L<libgcc.a 的路径>。
例如:
$(LD) -T imx6ull.lds -g start.o uart.o main.o my_printf.o -o my_printf.elf -lgcc -L/home/book/100ask_imx6ull-sdk/ToolChain/gcc-linaro-6.2.1-2016.11-x86_64_armlinux-gnueabihf/lib/gcc/arm-linux-gnueabihf/6.2.12.2.3 变参数函数移植
函数参数列表包括了字符串( format)和变参(…)组合而成, 在 vc6.0 的头文件 stdarg.h 中找到 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 )2
3
4
① _INTSIZEOF(n) 用于获取其中一个变参类型占用的空间长度。
② va_start(ap, v) 令 ap 指向第一个变参地址。
③ va_arg(ap, t) 取出一个变参,同时指针指向下一个变参。
④ va_end(ap) 将指针指向 NULL,防止野指针 。
我们移植以上的代码,编写一个属于自己的 printf。参考 int printf(const char *format, ...)库函数,实现 my_printf。我们创建 my_printf.c :
//reference : int printf(const char *format, ...);
int printf(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
my_vprintf(fmt, ap);
va_end(ap);
return 0;
}2
3
4
5
6
7
8
9
10
2.2.4 编写 my_vprintf(fmt, ap)
参考 int vprintf(const char *format, va_list ap)实现 my_vprintf ,在 my_printf.c 中添加:
/*reference : int vprintf(const char *format, va_list ap); */
static int my_vprintf(const char *fmt, va_list ap)
{
char lead=' ';
int maxwidth=0;
for(; *fmt != '\0'; fmt++)
{
if (*fmt != '%') {
outc(*fmt);
continue;
}
lead=' ';
maxwidth=0;
//format : %08d, %8d,%d,%u,%x,%f,%c,%s
fmt++;
if(*fmt == '0'){
lead = '0';
fmt++;
}
while(*fmt >= '0' && *fmt <= '9'){
maxwidth *=10;
maxwidth += (*fmt - '0');
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': outc(va_arg(ap, int )); break;
case 's': outs(va_arg(ap, char *)); break;
default:
outc(*fmt);
break;
}
}
return 0;
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
2.2.5 编写 out_c, outs 与 out_num 函数
- (1)利用之前我们实现的单字节打印函数 void PutChar(int c)实现 out_c,outs 与 out_num 函数 outc 用于格式化输出中的%c 的输出。
static int outc(int c)
{
PutChar(c);
return 0;
}2
3
4
5
- outs 用于格式化输出中的%s 的输出。
static int outs (const char *s)
{
while (*s != '\0')
PutChar(*s++);
return 0;
}2
3
4
5
6
- out_num 用于格式化输出中的%d, %o, %u, %x 的输出。
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);
int count=0,i=0;
*--s = '\0';
if (n < 0){
m = -n;
}
else{
m = n;
}
do{
*--s = hex_tab[m%base];
count++;
}while ((m /= base) != 0);
if( maxwidth && count < maxwidth){
for (i=maxwidth - count; i; i--)
*--s = lead;
}
if (n < 0)
*--s = '-';
return outs(s);
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
2.2.6 测试函数
int my_printf_test(void)
{
printf("This is 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 hex number =0x%x\n\r", 0x55aa55aa) ;
printf("test string =%s\n\r", "www.100ask.org") ;
printf("num=%08d\n\r", 12345);
printf("num=%8d\n\r", 12345);
printf("num=0x%08x\n\r", 0x12345);
printf("num=0x%8x\n\r", 0x12345);
printf("num=0x%02x\n\r", 0x1);
printf("num=0x%2x\n\r", 0x1);
printf("num=%05d\n\r", 0x1);
printf("num=%5d\n\r", 0x1);
return 0;
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2.3 移植后的工程结构
2.3.1 目录结构

2.3.2 imx6ull.lds
SECTIONS {
. = 0x80100000;
. = ALIGN(4);
.text :
{
*(.text)
}
. = ALIGN(4);
.rodata : { *(.rodata) }
. = ALIGN(4);
.data : { *(.data) }
. = ALIGN(4);
__bss_start = .;
.bss : { *(.bss) *(.COMMON) }
__bss_end = .;
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2.3.3 main.c
#include "my_printf.h"
#include "uart.h"
int main()
{ Uart_Init();
my_printf_test();
return 0;
}2
3
4
5
6
7
2.3.4 start.S
.text
.global _start
_start:
/* 设置栈 */
ldr sp,=0x80200000
/* 清除bss段 */
bl clean_bss
/* 跳转到主函数 */
bl main
halt:
b halt
clean_bss:
ldr r1, =__bss_start // 将链接脚本变量__bss_start变量保存于r1
ldr r2, =__bss_end // 将链接脚本变量__bss_end变量保存于r2
mov r3, #0
clean:
strb r3, [r1] // 将当前地址下的数据清零
add r1, r1, #1 // 将r1内存储的地址+1
cmp r1, r2 // 相等:清零操作结束;否则继续执行clean函数清零bss段
bne clean
mov pc, lr2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
2.3.5 uart.c
#include "uart.h"
static volatile unsigned int *IOMUXC_SW_MUX_CTL_PAD_UART1_TX_DATA ;
static volatile unsigned int *IOMUXC_SW_MUX_CTL_PAD_UART1_RX_DATA ;
static volatile unsigned int *IOMUXC_UART1_RX_DATA_SELECT_INPUT ;
void Uart_Init(void)
{
IOMUXC_SW_MUX_CTL_PAD_UART1_TX_DATA = (volatile unsigned int *)(0x20E0084);
IOMUXC_SW_MUX_CTL_PAD_UART1_RX_DATA = (volatile unsigned int *)(0x20E0088);
IOMUXC_UART1_RX_DATA_SELECT_INPUT = (volatile unsigned int *)(0x20E0624);
*IOMUXC_SW_MUX_CTL_PAD_UART1_RX_DATA = 0;
*IOMUXC_UART1_RX_DATA_SELECT_INPUT = 3;
*IOMUXC_SW_MUX_CTL_PAD_UART1_TX_DATA = 0;
UART1->UCR1 |= (1 << 0) ; /*关闭当前串口*/
/*
* 设置 UART 传输格式:
* UART1 中的 UCR2 寄存器关键 bit 如下
* [14]: 1:忽略RTS引脚
* [8] : 0: 关闭奇偶校验 默认为 0,无需设置
* [6] : 0: 停止位 1 位 默认为 0,无需设置
* [5] : 1: 数据长度 8 位
* [2] : 1: 发送数据使能
* [1] : 1: 接收数据使能
*/
UART1->UCR2 |= (1<<14) |(1<<5) |(1<<2)|(1<<1);
/*
* UART1 中的 UCR3 寄存器关键 bit 如下
* [2]: 1:根据官方文档表示,IM6ULL的UART用了这个MUXED模型,提示要设置
*/
UART1->UCR3 |= (1<<2);
/*
* 设置波特率
* 根据芯片手册得知波特率计算公式:
* Baud Rate = Ref Freq / (16 * (UBMR + 1)/(UBIR+1))
* 当我们需要设置 115200 的波特率
* UART1_UFCR [9:7] = 101,表示不分频,得到当前 UART 参考频率 Ref Freq :80M ,
* 带入公式:115200 = 80000000 /(16*(UBMR + 1)/(UBIR+1))
*
* 选取一组满足上式的参数:UBMR、UBIR 即可
*
* UART1_UBIR = 71
* UART1_UBMR = 3124
*/
UART1->UFCR = 5 << 7; /* Uart 的时钟 clk:80MHz */
UART1->UBIR = 71;
UART1->UBMR = 3124;
UART1->UCR1 |= (1 << 0); /*使能当前串口*/
}
void PutChar(int c)
{
while (!((UART1->USR2) & (1<<3))); /*等待上个字节发送完毕*/
UART1->UTXD = (unsigned char)c;
}
unsigned char GetChar(void)
{
while (!(UART1->USR2 & (1<<0))); /*等待接收数据*/
return (unsigned char)UART1->URXD;
}
void PutStr(const char *s)
{
while (*s)
{
PutChar(*s);
s++;
}
}
int raise(int signum)/* raise 函数,防止编译报错 */
{
return 0;
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
2.3.6 uart.h
#ifndef _UART_H_
#define _UART_H_
/*UART1 的寄存器的基地址*/
#define UART1_BASE (0x2020000u)
#define UART1 ((UART_Type *)UART1_BASE)
/*根据 IMX6ULL 芯片手册 <<55.15 UART Memory Map/Register Definition> > 的 3608 页,定义 UART 的结构体,*/
typedef struct {
volatile unsigned int URXD; /**< UART Receiver Register, offset: 0x0 串口接收寄存器,偏移地址 0x0 */
unsigned char RESERVED_0[60];
volatile unsigned int UTXD; /**< UART Transmitter Register, offset: 0x40 串口发送寄存器,偏移地址 0x40*/
unsigned char RESERVED_1[60];
volatile unsigned int UCR1; /**< UART Control Register 1, offset: 0x80 串口控制寄存器 1,偏移地址 0x80*/
volatile unsigned int UCR2; /**< UART Control Register 2, offset: 0x84 串口控制寄存器 2,偏移地址 0x84*/
volatile unsigned int UCR3; /**< UART Control Register 3, offset: 0x88 串口控制寄存器 3,偏移地址 0x88*/
volatile unsigned int UCR4; /**< UART Control Register 4, offset: 0x8C 串口控制寄存器 4,偏移地址 0x8C*/
volatile unsigned int UFCR; /**< UART FIFO Control Register, offset: 0x90 串口 FIFO 控制寄存器,偏移地址 0x90*/
volatile unsigned int USR1; /**< UART Status Register 1, offset: 0x94 串口状态寄存器 1,偏移地址 0x94*/
volatile unsigned int USR2; /**< UART Status Register 2, offset: 0x98 串口状态寄存器 2,偏移地址 0x98*/
volatile unsigned int UESC; /**< UART Escape Character Register, offset: 0x9C 串口转义字符寄存器,偏移地址 0x9C*/
volatile unsigned int UTIM; /**< UART Escape Timer Register, offset: 0xA0 串口转义定时器寄存器 偏移地址 0xA0*/
volatile unsigned int UBIR; /**< UART BRM Incremental Register, offset: 0xA4 串口二进制倍率增加寄存器 偏移地址 0xA4*/
volatile unsigned int UBMR; /**< UART BRM Modulator Register, offset: 0xA8 串口二进制倍率调节寄存器 偏移地址 0xA8*/
volatile unsigned int UBRC; /**< UART Baud Rate Count Register, offset: 0xAC 串口波特率计数寄存器 偏移地址 0xAC*/
volatile unsigned int ONEMS; /**< UART One Millisecond Register, offset: 0xB0 串口一毫秒寄存器 偏移地址 0xB0*/
volatile unsigned int UTS; /**< UART Test Register, offset: 0xB4 串口测试寄存器 偏移地址 0xB4*/
volatile unsigned int UMCR; /**< UART RS-485 Mode Control Register, offset: 0xB8 串口 485 模式控制寄存器 偏移地址 0xB8*/
} UART_Type;
void Uart_Init(void);
void PutChar(int c);
#endif2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
2.3.7 Makefile
PREFIX=arm-linux-gnueabihf-
CC=$(PREFIX)gcc
LD=$(PREFIX)ld
AR=$(PREFIX)ar
OBJCOPY=$(PREFIX)objcopy
OBJDUMP=$(PREFIX)objdump
my_printf.img : start.S uart.c main.c my_printf.c
$(CC) -nostdlib -g -c -o start.o start.S
$(CC) -nostdlib -g -c -o uart.o uart.c
$(CC) -nostdlib -g -c -o main.o main.c
$(CC) -nostdlib -g -c -o my_printf.o my_printf.c
$(LD) -T imx6ull.lds -g start.o uart.o main.o my_printf.o -o my_printf.elf -lgcc -L/home/book/100ask_imx6ull-sdk/ToolChain/gcc-linaro-6.2.1-2016.11-x86_64_arm-linux-gnueabihf/lib/gcc/arm-linux-gnueabihf/6.2.1
$(OBJCOPY) -O binary -S my_printf.elf my_printf.bin
$(OBJDUMP) -D -m arm my_printf.elf > my_printf.dis
./tools/mkimage -n ./tools/imximage.cfg.cfgtmp -T imximage -e 0x80100000 -d my_printf.bin my_printf.imx
dd if=/dev/zero of=1k.bin bs=1024 count=1
cat 1k.bin my_printf.imx > my_printf.img
clean:
rm -f my_printf.dis my_printf.bin my_printf.elf my_printf.imx my_printf.img *.o2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
2.3.8 mp_printf.c
#include "my_printf.h"
#include "uart.h"
//==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==
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_arg(ap,t) ( *(t *)( ap=ap + _INTSIZEOF(t), ap- _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'};
static int outc(int c)
{
PutChar(c);
return 0;
}
static int outs (const char *s)
{
while (*s != '\0')
PutChar(*s++);
return 0;
}
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);
int count=0,i=0;
*--s = '\0';
if (n < 0){
m = -n;
}
else{
m = n;
}
do{
*--s = hex_tab[m%base];
count++;
}while ((m /= base) != 0);
if( maxwidth && count < maxwidth){
for (i=maxwidth - count; i; i--)
*--s = lead;
}
if (n < 0)
*--s = '-';
return outs(s);
}
/*reference : int vprintf(const char *format, va_list ap); */
static int my_vprintf(const char *fmt, va_list ap)
{
char lead=' ';
int maxwidth=0;
for(; *fmt != '\0'; fmt++)
{
if (*fmt != '%') {
outc(*fmt);
continue;
}
lead=' ';
maxwidth=0;
//format : %08d, %8d,%d,%u,%x,%f,%c,%s
fmt++;
if(*fmt == '0'){
lead = '0';
fmt++;
}
while(*fmt >= '0' && *fmt <= '9'){
maxwidth *=10;
maxwidth += (*fmt - '0');
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': outc(va_arg(ap, int )); break;
case 's': outs(va_arg(ap, char *)); break;
default:
outc(*fmt);
break;
}
}
return 0;
}
//reference : int printf(const char *format, ...);
int printf(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
my_vprintf(fmt, ap);
va_end(ap);
return 0;
}
int my_printf_test(void)
{
printf("This is 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 hex number =0x%x\n\r", 0x55aa55aa) ;
printf("test string =%s\n\r", "www.100ask.org") ;
printf("num=%08d\n\r", 12345);
printf("num=%8d\n\r", 12345);
printf("num=0x%08x\n\r", 0x12345);
printf("num=0x%8x\n\r", 0x12345);
printf("num=0x%02x\n\r", 0x1);
printf("num=0x%2x\n\r", 0x1);
printf("num=%05d\n\r", 0x1);
printf("num=%5d\n\r", 0x1);
return 0;
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
2.3.9 my_printf.h
#ifndef _MY_PRINTF_H
#define _MY_PRINTF_H
#define MAX_NUMBER_BYTES 64
extern int my_printf_test(void);
int printf(const char *fmt, ...);
#endif /* _MY_PRINTF_H */2
3
4
5
6
7
8
2.4 测试效果
不出意外,一切顺利的话我们应该会得到以下输出:
This is www.100ask.org my_printf test
test char =A,a
test decimal number =123456
test decimal number =-123456
test hex number =0x55aa55aa
test string =www.100ask.org
num=00012345
num= 12345
num=0x00012345
num=0x 12345
num=0x01
num=0x 1
num=00001
num= 12
3
4
5
6
7
8
9
10
11
12
13
14
串口终端如果打印以上信息,证明实验成功!
2.5 地址打印
我们来看一下上面移植好后,对地址的打印情况:
#include "my_printf.h"
#include "uart.h"
char g_charA = 'A'; // 存储在 .data 段
const char g_charB = 'B'; // 存储在 .rodata 段
const char g_charC; // 存储在 .bss 段
int g_intA = 0; // 存储在 .bss 段
int g_intB; // 存储在 .bss 段
int main(int argc, const char * argv[])
{
int c = 9;
Uart_Init();
my_printf_test();
printf("\n\r");//反过来会报错,具体原因未知,还没有深究
printf("g_charA=%d &g_charA=0x%x\n\r", g_charA, &g_charA);
printf("g_charB=%d &g_charB=0x%x\n\r", g_charB, &g_charB);
printf("g_charC=%d &g_intA=0x%x\n\r", g_charC, &g_charC);
printf("g_intA=%d &g_intA=0x%x\n\r", g_intA, &g_intA);
printf("g_intB=%d &g_intB=0x%x\n\r", g_intB, &g_intB);
printf("c=%d &c=0x%x\n\r", c, &c);
return 0;
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
不出意外的话,我们会得到以下打印输出:

会发现全是错的,具体原因可能还是哪里移植的不太对,后面进行了修改,修改后的工程可以直接看这里:imx6ull-bare-demo/02_UART/04_my_printf。修改完后打印信息如下:
