移植U-BOOT-2016.11到JZ2440详细教程(3)

让串口正常打印数据

一般在移植过程中,我们经常会碰到许多问题,这就需要让开发板给我们一些调试信息,最简单的调试信息就是通过点亮LED实现,但是LED就那么几个,能表示的调试信息少而且不直观,另一种更好的方法则是通过串口打印数据。所以我们在移植U-BOOT过程中一般都先实现串口能正常打印数据。

用过UART的人都知道,要想串口正常打印数据,最重要的两点就是时钟和波特率,所以在本章移植中,我们重点就是关注这两块的代码。

通过第一章的分析,我们知道按照程序的执行顺序,要想使用U-BOOT自带框架下的串口,要修改这几个部分:

  1. .globl reset(\arch\arm\cpu\arm920t\start.S)里设置了系统分频系数。
  2. board_early_init_f,(\common\board_f.c)初始化系统时钟
  3. init_baud_rate, serial_init,(\common\board_f.c)初始化串行通讯相关。

先看第一部分设置系统分频系数有没有问题。为了不在去计算时钟频率等,而直接使用韦东山老师后续的程序,我们修改一下分频系数,让FCLK:HCLK:PCLK = 1:4:8。然后根据datasheet说明,当HDIV设置为非0的时候,cpu总线模式要进行改变,默认情况下FCLK=HCLK,cpu工作在fast bus mode快速总线模式下,HDIV设置为非0后,FCLK和HCLK不再相等,要将cpu改为异步总线模式。然后为了让程序跑的更快,我们可以启用ICACHE,所以 代码修改如下:

\arch\arm\cpu\arm920t\start.S,第83行起


	///* FCLK:HCLK:PCLK = 1:2:4 */
	/* default FCLK is 120 MHz ! */
	ldr	r0, =CLKDIVN
	//mov	r1, #3
	mov	r1, #5		/* FCLK:HCLK:PCLK = 1:4:8 */
	str	r1, [r0]

	/* 如果HDIVN非0,CPU的总线模式应该从“fast bus mode”变为“asynchronous bus mode” */
	mrc p15, 0, r1, c1, c0, 0       /* 读出控制寄存器 */
	orr r1, r1, #0xc0000000         /* 设置为“asynchronous bus mode” */
	mcr p15, 0, r1, c1, c0, 0       /* 写入控制寄存器 */

	/* 启动ICACHE */
	mrc p15, 0, r0, c1, c0, 0   @ read control reg
	orr r0, r0, #(1<<12)
	mcr p15, 0, r0, c1, c0, 0   @ write it back

然后看第二部分,也就是系统时钟。关于时钟以及分频系数这块,现在不做过多讲解,需要的可以看这个这个链接: [https://blog.csdn.net/lee244868149/article/details/49962203].里面讲的非常详细,这边直接把修改的地方列出来,也就是简单的改了一下宏的值,让系统跑到400MHz:

\board\samsung\smdk2440\smdk2440.c,第19行起

#define FCLK_SPEED 1

#if (FCLK_SPEED == 0)		/* Fout = 203MHz, Fin = 12MHz for Audio */
#define M_MDIV	0xC3
#define M_PDIV	0x4
#define M_SDIV	0x1
#elif (FCLK_SPEED == 1)		/* Fout = 202.8MHz */
#define M_MDIV	0x5C
#define M_PDIV	0x1
#define M_SDIV	0x1
#endif

最后看修改串口配置这块,要修改的地方就一个,把头文件上的宏定义改一下就好了,修改后如下:

\include\configs\smdk2440.h,第16行起

/*
 * High Level Configuration Options
 * (easy to change)
 */
#define CONFIG_S3C24X0		/* This is a SAMSUNG S3C24x0-type SoC */
#define CONFIG_S3C2440		/* specifically a SAMSUNG S3C2440 SoC */
#define CONFIG_SMDK2410		/* on a SAMSUNG SMDK2410 Board */

#define CONFIG_SYS_TEXT_BASE	0x0

#define CONFIG_SYS_ARM_CACHE_WRITETHROUGH

虽然修改的地方少,但是这块的程序第一眼看还是有点复杂的,如果不想看分析的,可以跳过,直接配置编译,串口就能正常打印数据了。

\common\board_f.c,第909行起

  • init_baud_rate,
    • gd->baudrate = getenv_ulong(“baudrate”, 10, CONFIG_BAUDRATE);1
  • serial_init,2
    • get_current();(\drivers\serial\serial.c)
      • default_serial_console();
        • return &s3c24xx_serial0_device;(drivers\serial\serial_s3c24x0.c)3
          • #define INIT_S3C_SERIAL_STRUCTURE(port, __name) {
            .name = __name,
            .start = s3serial##port##_init,
            .stop = NULL,
            .setbrg = s3serial##port##_setbrg,
            .getc = s3serial##port##_getc,
            .tstc = s3serial##port##_tstc,
            .putc = s3serial##port##_putc,
            .puts = s3serial##port##_puts,
            }
    • start()4;(\drivers\serial\serial.c)
      • int s3serial##port##_init(void) \(drivers\serial\serial_s3c24x0.c)
        {
        return serial_init_dev(port);
        } \
        • _serial_setbrg(dev_index);
          • get_PCLK()(\arch\arm\cpu\arm920t\s3c24x0\speed.c)
            • get_HCLK()
              • #ifdef CONFIG_S3C24405
                switch (readl(&clk_power->clkdivn) & 0x6) {
                default:
                case 0:
                return get_FCLK();
                case 2:
                return get_FCLK() / 2;
                case 4:
                return (readl(&clk_power->camdivn) & (1 << 9)) ?
                get_FCLK() / 8 : get_FCLK() / 4;
                case 6:
                return (readl(&clk_power->camdivn) & (1 << 8)) ?
                get_FCLK() / 6 : get_FCLK() / 3;
                }
                #else
                return (readl(&clk_power->clkdivn) & 2) ? get_FCLK() / 2 : get_FCLK();
                #endif

可以看到虽然只要简单的需要一个宏,但是中间的代码分析还是蛮复杂的,推荐大家可以多看看这一块的程序,写的还是非常巧妙。

最后我们重新编译一下,烧到开发板上就可以看到如下的打印信息:
在这里插入图片描述

最后写我发现的一个小问题,在修改\include\configs\smdk2440.h文件的时候,我写成这样

/*
 * High Level Configuration Options
 * (easy to change)
 */
#define CONFIG_S3C24X0		/* This is a SAMSUNG S3C24x0-type SoC */
//#define CONFIG_S3C2410		/* specifically a SAMSUNG S3C2410 SoC */
#define CONFIG_S3C2440		/* specifically a SAMSUNG S3C2440 SoC */
#define CONFIG_SMDK2410		/* on a SAMSUNG SMDK2410 Board */

#define CONFIG_SYS_TEXT_BASE	0x0

#define CONFIG_SYS_ARM_CACHE_WRITETHROUGH

结果编译不通过打印这个错误信息:

LDS     u-boot.lds
LD      u-boot
arm-linux-ld:u-boot.lds:1: ignoring invalid character `#' in expression
arm-linux-ld:u-boot.lds:1: syntax error
Makefile:1018: recipe for target 'u-boot' failed
make: *** [u-boot] Error 1

查了一下才知道不能用//注释宏,得用#if 0…#endif,或者直接删除宏定义。特此记录一下。

下面附上补丁文件链接:
链接:https://pan.baidu.com/s/1v85_5VApVgmMG9IXrwgCiQ
提取码:bb6y

注意:这边的补丁文件只是相对于上一节的u-boot,直接下载下来的u-boot打这个补丁是没有用的。


  1. 环境变量里面读取波特率,如果没有设置,则返回默认值115200。 ↩︎

  2. 该函数关键在这句话get_current()->start(),总的来说,前半句用于确定使用哪个串口,后半句使用该串口的start函数。 ↩︎

  3. 因为我们定义了defined CONFIG_SERIAL1,所以返回struct serial_device s3c24xx_serial0_device这个结构体,而这个结构体又等于INIT_S3C_SERIAL_STRUCTURE(port, __name)这个宏定义,这个宏根据传进来的名字和端口,来确定结构体start,stop,setbrg等等对应的函数。 ↩︎

  4. 我们已经在get_current()里面确定了用哪个串口的结构体,那么这个函数就是调用这个结构体的.start。 ↩︎

  5. 可以看到如果定义了宏CONFIG_S3C2440,才能获取到正确的时钟频率。 ↩︎

猜你喜欢

转载自blog.csdn.net/yunlong654/article/details/83745324