uboot第二阶段细节(环境变量和时钟)

我们的板文件是在include/configs/smdk2410.h,在该文件中有很多配置项
该文件被生成的include/config.h文件包含


在conmon/env_nand.c 中env_init()函数


int env_init(void)
{
#if defined(ENV_IS_EMBEDDED)  // 该宏我们不定义
size_t total;
int crc1_ok = 0, crc2_ok = 0;
env_t *tmp_env1, *tmp_env2;


total = CONFIG_ENV_SIZE;


tmp_env1 = env_ptr;
tmp_env2 = (env_t *)((ulong)env_ptr + CONFIG_ENV_SIZE);


crc1_ok = (crc32(0, tmp_env1->data, ENV_SIZE) == tmp_env1->crc);
crc2_ok = (crc32(0, tmp_env2->data, ENV_SIZE) == tmp_env2->crc);


if (!crc1_ok && !crc2_ok)
gd->env_valid = 0;
else if(crc1_ok && !crc2_ok)
gd->env_valid = 1;
else if(!crc1_ok && crc2_ok)
gd->env_valid = 2;
else {
/* both ok - check serial */
if(tmp_env1->flags == 255 && tmp_env2->flags == 0)
gd->env_valid = 2;
else if(tmp_env2->flags == 255 && tmp_env1->flags == 0)
gd->env_valid = 1;
else if(tmp_env1->flags > tmp_env2->flags)
gd->env_valid = 1;
else if(tmp_env2->flags > tmp_env1->flags)
gd->env_valid = 2;
else /* flags are equal - almost impossible */
gd->env_valid = 1;
}


if (gd->env_valid == 1)
env_ptr = tmp_env1;
else if (gd->env_valid == 2)
env_ptr = tmp_env2;

#else /* ENV_IS_EMBEDDED */  所以执行到此处
gd->env_addr  = (ulong)&default_environment[0]; 缺省的环境变量
gd->env_valid = 1;
#endif /* ENV_IS_EMBEDDED */


return (0);
}
default_environment数组在common/env_common.c中定义
有三个非常重要的环境参数bootargs,bootcmd,bootdelay


uchar default_environment[] = {
#ifdef CONFIG_BOOTARGS
"bootargs=" CONFIG_BOOTARGS "\0"       启动参数 重要
#endif
#ifdef CONFIG_BOOTCOMMAND
"bootcmd=" CONFIG_BOOTCOMMAND "\0"    启动命令 重要
#endif
#ifdef CONFIG_RAMBOOTCOMMAND
"ramboot=" CONFIG_RAMBOOTCOMMAND "\0"
#endif
#ifdef CONFIG_NFSBOOTCOMMAND
"nfsboot=" CONFIG_NFSBOOTCOMMAND "\0"
#endif
#if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)
"bootdelay=" MK_STR(CONFIG_BOOTDELAY) "\0"   延时时间 重要
#endif
#if defined(CONFIG_BAUDRATE) && (CONFIG_BAUDRATE >= 0)
"baudrate=" MK_STR(CONFIG_BAUDRATE) "\0"
#endif
#ifdef CONFIG_LOADS_ECHO
"loads_echo=" MK_STR(CONFIG_LOADS_ECHO) "\0"
#endif
#ifdef CONFIG_ETHADDR
"ethaddr=" MK_STR(CONFIG_ETHADDR) "\0"
#endif
#ifdef CONFIG_ETH1ADDR
"eth1addr=" MK_STR(CONFIG_ETH1ADDR) "\0"
#endif
#ifdef CONFIG_ETH2ADDR
"eth2addr=" MK_STR(CONFIG_ETH2ADDR) "\0"
#endif
#ifdef CONFIG_ETH3ADDR
"eth3addr=" MK_STR(CONFIG_ETH3ADDR) "\0"
#endif
#ifdef CONFIG_IPADDR
"ipaddr=" MK_STR(CONFIG_IPADDR) "\0"
#endif
#ifdef CONFIG_SERVERIP
"serverip=" MK_STR(CONFIG_SERVERIP) "\0"
#endif
#ifdef CFG_AUTOLOAD
"autoload=" CFG_AUTOLOAD "\0"
#endif
#ifdef CONFIG_PREBOOT
"preboot=" CONFIG_PREBOOT "\0"
#endif
#ifdef CONFIG_ROOTPATH
"rootpath=" MK_STR(CONFIG_ROOTPATH) "\0"
#endif
#ifdef CONFIG_GATEWAYIP
"gatewayip=" MK_STR(CONFIG_GATEWAYIP) "\0"
#endif
#ifdef CONFIG_NETMASK
"netmask=" MK_STR(CONFIG_NETMASK) "\0"
#endif
#ifdef CONFIG_HOSTNAME
"hostname=" MK_STR(CONFIG_HOSTNAME) "\0"
#endif
#ifdef CONFIG_BOOTFILE
"bootfile=" MK_STR(CONFIG_BOOTFILE) "\0"
#endif
#ifdef CONFIG_LOADADDR
"loadaddr=" MK_STR(CONFIG_LOADADDR) "\0"
#endif
#ifdef  CONFIG_CLOCKS_IN_MHZ
"clocks_in_mhz=1\0"
#endif
#if defined(CONFIG_PCI_BOOTDELAY) && (CONFIG_PCI_BOOTDELAY > 0)
"pcidelay=" MK_STR(CONFIG_PCI_BOOTDELAY) "\0"
#endif
#ifdef  CONFIG_EXTRA_ENV_SETTINGS
CONFIG_EXTRA_ENV_SETTINGS
#endif
"\0"
};












第二个重要的函数 在env_common.c
void env_relocate (void)
{
DEBUGF ("%s[%d] offset = 0x%lx\n", __FUNCTION__,__LINE__,
gd->reloc_off);


#ifdef CONFIG_AMIGAONEG3SE
enable_nvram();
#endif


#ifdef ENV_IS_EMBEDDED   // 不执行
/*
* The environment buffer is embedded with the text segment,
* just relocate the environment pointer
*/
env_ptr = (env_t *)((ulong)env_ptr + gd->reloc_off);
DEBUGF ("%s[%d] embedded ENV at %p\n", __FUNCTION__,__LINE__,env_ptr);
#else     执行
/*
* We must allocate a buffer for the environment
*/
env_ptr = (env_t *)malloc (CONFIG_ENV_SIZE);  
// 从堆空间中申请空间,地址存放在env_ptr里,该空间用于存放一些列环境参数


DEBUGF ("%s[%d] malloced ENV at %p\n", __FUNCTION__,__LINE__,env_ptr);
#endif


if (gd->env_valid == 0) { 此时gd->env_valid为1,所以不执行
#if defined(CONFIG_GTH) || defined(CONFIG_ENV_IS_NOWHERE) /* Environment not changable */
puts ("Using default environment\n\n");
#else
puts ("*** Warning - bad CRC, using default environment\n\n");
show_boot_progress (-60);
#endif
set_default_env();
}
else {   执行
env_relocate_spec();  将环境参数搬到新建的堆区
}
gd->env_addr = (ulong)&(env_ptr->data); // 重新定义环境变量的地址,此时应该是在堆区


#ifdef CONFIG_AMIGAONEG3SE
disable_nvram();
#endif
}












第三个重要函数env_relocate_spec  在env_nand.c中


void env_relocate_spec (void)
{
#if !defined(ENV_IS_EMBEDDED)
int ret;


ret = readenv(CONFIG_ENV_OFFSET, (u_char *) env_ptr); 
// 将环境参数从nand CONFIG_ENV_OFFSET地址处搬移到ram env_ptr处


if (ret)
return use_default();


if (crc32(0, env_ptr->data, ENV_SIZE) != env_ptr->crc)
return use_default();
#endif /* ! ENV_IS_EMBEDDED */
}




#endif /* CONFIG_ENV_OFFSET_REDUND */


#if !defined(ENV_IS_EMBEDDED)
static void use_default()
{
puts ("*** Warning - bad CRC or NAND, using default environment\n\n");
set_default_env();
}
#endif


int readenv (size_t offset, u_char * buf)
{
size_t end = offset + CONFIG_ENV_RANGE;
size_t amount_loaded = 0;
size_t blocksize, len;


u_char *char_ptr;


blocksize = nand_info[0].erasesize;
len = min(blocksize, CONFIG_ENV_SIZE);


while (amount_loaded < CONFIG_ENV_SIZE && offset < end) {
if (nand_block_isbad(&nand_info[0], offset)) {
offset += blocksize;
} else {
char_ptr = &buf[amount_loaded];
if (nand_read(&nand_info[0], offset, &len, char_ptr))
return 1;
offset += blocksize;
amount_loaded += len;
}
}
if (amount_loaded != CONFIG_ENV_SIZE)
return 1;


return 0;
}


void set_default_env(void)
{
if (sizeof(default_environment) > ENV_SIZE) {
puts ("*** Error - default environment is too large\n\n");
return;
}


memset(env_ptr, 0, sizeof(env_t));
memcpy(env_ptr->data, default_environment,
      sizeof(default_environment));
#ifdef CFG_REDUNDAND_ENVIRONMENT
env_ptr->flags = 0xFF;
#endif
env_crc_update ();
gd->env_valid = 1;
}
#############################################################################################################










执行序列:
在init_sequnse里执行env_init函数
env_init: 
           ;gd->env_addr  = (ulong)&default_environment[0]; 缺省的环境变量
        gd->env_valid = 1
接着执行env_relocate:  环境变量的重定位
         env_ptr = (env_t *)malloc (CONFIG_ENV_SIZE); //  从内存的堆里申请空间存放环境变量
         env_relocate_spec();
         gd->env_addr = (ulong)&(env_ptr->data); 
执行重定位env_relocate_spec
         ret = readenv(CONFIG_ENV_OFFSET, (u_char *) env_ptr); 
     // 将环境参数从nand CONFIG_ENV_OFFSET地址处搬移到ram env_ptr处
     
     if (ret)
    return use_default();


    if (crc32(0, env_ptr->data, ENV_SIZE) != env_ptr->crc) 第一次执行时,会出错
   return use_default();             执行到这里
   
上面的代码很清楚的表明了 env_relocate_spec 的意图,调用 nand_read 将环境变量从 CFG_ENV_OFFSET 处读出,
环境变量的大小为 CFG_ENV_SIZE 注意 CFG_ENV_OFFSET和 CFG_ENV_SIZE 要和 Nand Flash 的块/页边界对齐。
读出数据后再调用crc32 对env_ptr->data 进行校验并与保存在 env_ptr->crc 的校验码对比,看数据是否出错,
从这里也可以看出在系统第一次启动时,Nand Flash 里面没有存储任何环境变量,crc校验肯定回出错,当我们
保存环境变量后,接下来再启动板子u-boot就不会再报crc32出错了。


  接着执行
  static void use_default()   只执行一次
{
puts ("*** Warning - bad CRC or NAND, using default environment\n\n");
//  打印此段话,注意,只是第一次启动的时候打印
set_default_env();  设置缺省的环境变量
}
 
void set_default_env(void)
{
if (sizeof(default_environment) > ENV_SIZE) {
puts ("*** Error - default environment is too large\n\n");
return;
}


memset(env_ptr, 0, sizeof(env_t));
memcpy(env_ptr->data, default_environment,     设置缺省的环境变量
      sizeof(default_environment));
#ifdef CFG_REDUNDAND_ENVIRONMENT
env_ptr->flags = 0xFF;
#endif
env_crc_update ();
gd->env_valid = 1;
}










#########################################################################################################################
when uboot start,dispaly following info:


U-Boot 1.1.6 (Mar 19 2008 - 14:02:12)


DRAM: 32 MB
NAND: 1024 MiB
*** Warning - bad CRC or NAND, using default environment


so google and find ,the


*** Warning - bad CRC or NAND, using default environment


means nothing wrong ,but need some environment variables for uboot when booting.


you can just ignore this info ,and "The message will go away as soon as you save the envrionment variables using the saveenv command"








【后记】


具体解释一下*** Warning - bad CRC or NAND, using default environment的原因和解决办法:


【原因】


Uboot中的逻辑是,汇编执行完之后,掉转到C代码入口处,(此处是arm平台),此处是lib_arm\board.c中的start_armboot,
其在一系列的初始化后,会去调用


/* initialize environment */
env_relocate ();


去加载环境变量,在common\env_common.c中的env_relocate():


if (gd->env_valid == 0) {
#if defined(CONFIG_GTH) || defined(CONFIG_ENV_IS_NOWHERE) /* Environment not changable */
   puts ("Using default environment\n\n");
#else
   puts ("*** Warning - bad CRC, using default environment\n\n");
   show_boot_progress (-60);
#endif
   set_default_env();
}
else {
   env_relocate_spec ();
}


会去根据gd->env_valid 前面有没有被初始化,是否为1,而决定,


是直接调用默认环境变量,(我此处的uboot中定义的是#define CONFIG_ENV_IS_IN_NAND
,其在env_nand.c中的env_init()中已经初始化了 gd->env_valid = 1;)


还是去调用env_relocate_spec ()去重新(从你指定的设备,我这里的是之前指定的nand)装载你之前存储的环境变量。


而我这里,按照上面说明,就是去执行env_relocate_spec (),








在common\env_nand.c中


void env_relocate_spec (void)
{
#if !defined(ENV_IS_EMBEDDED)
int ret;

ret = readenv(CONFIG_ENV_OFFSET, (u_char *) env_ptr);
if (ret)
  return use_default();

if (crc32(0, env_ptr->data, ENV_SIZE) != env_ptr->crc)
  return use_default();
#endif /* ! ENV_IS_EMBEDDED */
}


static void use_default()
{
puts ("*** Warning - bad CRC or NAND, using default environment\n\n");
set_default_env();
}


可以很清楚的看到,如果是readenv读取环境变量失败,那么就调用use_default,使用默认环境变量。


如果即使数据读的对了,但是crc32校验失败,那么也是去调用use_default,使用默认环境变量。


而这两种情况下,调用use_default,就会打印*** Warning - bad CRC or NAND, using default environment,然后
使用你原先在编译Uboot的时候,在自己的头文件里面定义那些默认的值,比如对于常用到的启动参数和启动命令
来说,我代码里面的是:


/* read kernel from mtdblock2 no matter for 24/4K pagesize nand */
#define CONFIG_BOOTCOMMAND "nand read 0x40007FC0 0x100000 0x200000;bootm 0x40007FC0"
/* mmcblk0p2 -> rootfs Partition */
#define CONFIG_BOOTARGS "root=/dev/mtdblock2 rw init=/linuxrc console=ttyAMA1,115200 mem=64M rootfstype=yaffs2"
,反之,如果你上面从你指定的nand设备里面读取的环境变量是正常的话,那么就会用那些值,而不是你代码里
面写的值。


【解决办法】


知道了具体原因,解决起来就好办了:


1.正常的办法是,如果你确定你已经实现了saveenv,即,


已经在头文件中,定义了对应的值:


以我的此处的用nand存储env举例来说就是类似于这样的:


/* use nand to store env */
#define CONFIG_ENV_IS_IN_NAND
/*
current layout:
0-0x100000, uboot;
0x100000 - 0x800000, kernel;
0x800000 - 0x900000, env;
0x900000 - ~ , rootfs;
*/
#define CONFIG_ENV_OFFSET    0x800000
#define CONFIG_ENV_ADDR    CONFIG_ENV_OFFSET /* duplicate define */
#define CONFIG_ENV_SIZE    0x4000 /* 16KB is large enough */
/* set the block size to the max one: 128KB of 2K, 512KB of 4k pagesze nand */
#define CONFIG_ENV_SECT_SIZE   0x80000
#define CONFIG_SYS_ENV_OVERWRITE 1
#define CONFIG_CMD_SAVEENV             1


并且保证你的读取数据的代码是OK的:


即保证你的nand驱动相关操作函数等功能已经实现了,可以正常读取/写入数据,擦除块了。


那么,saveenv就应该可以正常工作了。


此时,你就可以在第一次启动后(第一次会有此警告),进入uboot(一般是按s键,前提是你代码里面设置了uboot等待
一定的时间,运行你进入uboot)后,执行saveenv,就可以把第一次,从代码里面获取的默认的那些env的值,
存储到nand里面了。此后,就不会出现这样的提示了,因为可以正常从指定的nand设备中读取和写入对应的env了。


2.如果你想特殊点,不想让别人改你的启动参数等env值,那么你可以去在头文件中,不要定义
CONFIG_SYS_ENV_OVERWRITE,然后也不要定义类似于这样的CONFIG_ENV_IS_IN_NAND定义。


然后别人无法修改这些环境变量,同时,也会每次都提示你


*** Warning - bad CRC or NAND, using default environment,此时,你就再直接把那行打印注释掉,就可以了。。。
虽然做法不雅,但是还是可以实现目的的。。。


【总结】


一句话,只要有源码,就没有解决不了的问题。一切问题,参考源码,都能找到根本原因。


此处对于arm+nand来说,具体涉及的文件和函数,总结如下:


1. lib_arm\board.c


void start_armboot (void)
{


。。。


for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
   if ((*init_fnc_ptr)() != 0) { //此处会去调用env_init()
    hang ();
   }
}


。。。


/* initialize environment */
env_relocate ();


。。。


}


2.对于env_init(),此处因为指定的是CONFIG_ENV_IS_IN_NAND,所以对应着:


common\env_nand.c:


int env_init(void)
{


。。。


gd->env_valid = 1;


。。。


}


3.common\env_common.c:


void env_relocate (void)
{


。。。


if (gd->env_valid == 0) {
#if defined(CONFIG_GTH) || defined(CONFIG_ENV_IS_NOWHERE) /* Environment not changable */
   puts ("Using default environment\n\n");
#else
   puts ("*** Warning - bad CRC, using default environment\n\n");
   show_boot_progress (-60);
#endif
   set_default_env();
}
else {
   env_relocate_spec ();
}
。。。


}


4.此处的env_relocate_spec,这里对应的是common\env_nand.c:


void env_relocate_spec (void)
{
#if !defined(ENV_IS_EMBEDDED)
int ret;


ret = readenv(CONFIG_ENV_OFFSET, (u_char *) env_ptr);
if (ret)
   return use_default();


if (crc32(0, env_ptr->data, ENV_SIZE) != env_ptr->crc)
   return use_default();
#endif /* ! ENV_IS_EMBEDDED */
}


5.对于数据读取有误或者读出来的数据crc32有误的话,都会调用common\env_nand.c:


static void use_default()
{
puts ("*** Warning - bad CRC or NAND, using default environment\n\n");
set_default_env();
}


因此才会提示有误。OK,总结完毕。有问题,最好自己跟踪代码,最后都是可以搞定的。


-----------------------------------------------------------------------------------------------
pwm定时器


 s3c2440芯片中一共有5个16位的定时器,其中有4个定时器(定时器0~定时器3)具有脉宽调制功能,即他们都有个输出
  引脚,可以通过定时器来控制引脚周期性的高低电平变化,定时器4没有输出引脚。上次脱机运行PWM测试程序
  实验的时候就用到了这块,所以这次将PWM和定时器放在一起来学习。


       定时器部件的时钟源为PCLK,首先通过两个8位预分频器降低频率,定时器0和1共用第一个预分频器,2,3,4共用
       第二个预分频器。预分频器输出接入第二级分频器,可以生成5种分频信号(1/2,1/4,1/8,1/16,TCLK),其中8位
       预分频器是可编程,根据装载值来分频PCLK,值储存在TCFG0和TCFG1中。


       定时器内部控制逻辑工作流程如下:


1.程序初始,先设置TCMPBn、TCNTBn这两个寄存器,分别表示定时器n的比较值和初始计数值。


       2.然后设置TCON寄存器启动定时器n,这时TCMPBn、TCNTBn值将被装入内部寄存器TCMPn、TCNTn。在定时器n的
       工作频率下,TCNTn开始减1计数,其值可以通过读取TCNTOn得知。


       3.当TCNTn值等于TCMPn值的时候,定时器n的输出管脚TOUTn反转;TCNTn继续减1计数。


       4.当TCNTn值为0,输出管脚TOUTn再次反转,并触发定时器n中断(中断使能)。


       5.当TCNTn值为0,如果在TCON寄存器中将定时器n设为自动加载,则TCMPBn、TCNTBn值将被自动装入内部
       寄存器TCMPn、TCNTn中,进入下一个计数流程。


       定时器n的输出管脚TOUTn初始状态为高电平,然后会两次反转,也可以通过TCON寄存器设定其初始电平,
       这样输出就完全反相了。通过设置TCMPBn、TCNTBn可以设置TOUTn输出信号的占空比,这样就是所谓的PWM。
       这里PWM的原理就不做介绍了。


下面就具体介绍如何实现PWM功能。


1、PWM是通过引脚TOUT0~TOUT3输出的,而这4个引脚是与GPB0~GPB3复用的,因此要实现PWM功能首先要把相应的
引脚配置成TOUT输出。


2、再设置定时器的输出时钟频率,它是以PCLK为基准,再除以用寄存器TCFG0配置的prescaler参数,和用寄存器
   TCFG1配置的divider参数。


3、然后设置脉冲的具体宽度,它的基本原理是通过寄存器TCNTBn来对寄存器TCNTn(内部寄存器)进行配置计数,
  TCNTn是递减的,如果减到零,则它又会重新装载TCNTBn里的数,重新开始计数,而寄存器TCMPBn作为比较寄存器
  与计数值进行比较,当TCNTn等于TCMPBn时,TOUTn输出的电平会翻转,而当TCNTn减为零时,电平会又翻转过来,就这样
  周而复始。因此这一步的关键是设置寄存器TCNTBn和TCMPBn,前者可以确定一个计数周期的时间长度,而后者可以
  确定方波的占空比。由于s3c2440的定时器具有双缓存,因此可以在定时器运行的状态下,改变这两个寄存器的值,
  它会在下个周期开始有效。


4、最后就是对PWM的控制,它是通过寄存器TCON来实现的,当不想计数了,可以使自动重载无效,这样在TCNTn减为零后,
  不会有新的数加载给它,那么TOUTn输出会始终保持一个电平(输出反转位为0时,是高电平输出;输出反转位为1时,
  是低电平输出),这样就没有PWM功能了,因此这一位可以用于停止PWM。


变相器的开和关决定了初始状态和维持TOUTn的电平的高低。


1    TCFG0寄存器


[7:0],[15:8]各8位分别被用于控制预分频器0,1,值为0~255。经过预分频器出来的时钟频率为:PCLK/(prescaler+1)。


 


 


2.      TCFG1寄存器


经过预分频器得到的时钟将进入2次分频,这个寄存器就是设置2次分频系数的。
这样定时器的工作频率为:PCLK/(prescaler+1)/(divider value),其中prescaler=0~255,divider value=2,4,6,8。


 


3.      TCON寄存器


TCON寄存器位[3:0]、[11:8]、[15:12]、[19:16]、[22:20]分别用于定时器0~4,位[4]为死区使能位,[7:5]为保留位。
除了定时器4没有输出反转位外,其他位功能相似,这里以定时器0为例加以说明。位[0]开启停止位:0停止定时器,
1开始定时器。[1]手动更新位:0无用,1将TCNTBn/TCMPBn寄存器的值装入内部寄存器TCNTn\TCMPn中。
[2]输出反转:0不反转,1反转。[3]自动加载:0不自动加载,1自动加载。














int timer_init(void)  将pwm4设为时钟周期为10ms
{
struct s3c24x0_timers *timers = s3c24x0_get_base_timers();  获取定时器寄存器的基地址
ulong tmr;


/* use PWM Timer 4 because it has no output */   为什么要选用定时器4作为时钟呢,因为定时器4不带pwm引脚
/* prescaler for Timer 4 is 16 */


writel(0x0f00, &timers->TCFG0);    第一次分频为预分频,系数为(15+1)分频  低8位用于定时器0和1,
高8位用于定时器2,3,4


if (timer_load_val == 0) {         
/*
* for 10 ms clock period @ PCLK with 4 bit divider = 1/2
* (default) and prescaler = 16. Should be 10390
* @33.25MHz and 15625 @ 50 MHz
*/
timer_load_val = get_PCLK() / (2 * 16 * 100);   
timer_clk = get_PCLK() / (2 * 16);
//  经过两次分频后的时钟频率,假设是62500HZ,表示每1秒,要数62500次,即用62500来衡量 所以,
10毫秒就用62500除以100,刚好吻合上面
}


/* load value for 10 ms timeout */
lastdec = timer_load_val;
writel(timer_load_val, &timers->TCNTB4);//  将10毫秒所对应的初始值赋给TCNTB4
/* auto load, manual update of Timer 4 */
tmr = (readl(&timers->TCON) & ~0x0700000) | 0x0600000; 
//将TCON寄存器的控制pwm4的第6个四位清零,然后设置自动重装


writel(tmr, &timers->TCON);
/* auto load, start Timer 4 */
tmr = (tmr & ~0x0700000) | 0x0500000; // 启动pwm4定时器
writel(tmr, &timers->TCON);
timestamp = 0;


return (0);
}
以下是对函数的详细分析:


 1、先看数据结构s3c24x0_timers


struct s3c24x0_timers {
u32 TCFG0;
u32 TCFG1;
u32 TCON;
struct s3c24x0_timer ch[4];
u32 TCNTB4;
u32 TCNTO4;
};有引出来了另一个结构s3c24x0_timer


struct s3c24x0_timer {
u32 TCNTB;
u32 TCMPB;
u32 TCNTO;
};看下这两个结构是不是涵盖了所有timer的寄存器,对照s3c2440的datasheet得出结论是:timer寄存器地址空间
从(0x51000000~0x51000040)依次对应的寄存器名是却是是上面的结构体中的寄存器,总共是17个寄存器.


为什么会有4个结构s3c24x0_timer呢?因为2440有5个timer,其中前四个timer0~timer3工作方式类似,都有外部引脚引出,
而第五个timer4不同与他们,第一个就是它没有TCMPB寄存器,具体的区别可以看看这个文档或是datasheet:


http://wenku.baidu.com/view/6d765ec42cc58bd63186bd24.html


2、s3c24x0_get_base_timers()函数返回的就是timer寄存器空间的起始地址0x51000000


static inline struct s3c24x0_timers *s3c24x0_get_base_timers(void)
{
return (struct s3c24x0_timers *)S3C24X0_TIMER_BASE;
}3、


 /* use PWM Timer 4 because it has no output */
/* prescaler for Timer 4 is 16 */这个注释是不是有问题呢,我怎么查不到timer4预分频器是16位的,都是8位。是不是
要表达计数器是16位的呢?


4、writel(0x0ff,&timers->TCFG0)的作用是什么呢?在arch/arm/include/asm/io.h文件中既然有3处定义了这个函数,
考察了一下只有下面被编译:


#define writel(v,a) __arch_putl(v,a)#define __arch_putl(v,a) (*(volatile unsigned int *)(a) = (v))分析:就是往
a寄存器写入v.




将timer0、1的预分频器的值写ff就是255,


        其值N为: 0~255
       输出频率为:PCLK ÷(N+1)


这时候N = 255,结合这篇文档http://blog.csdn.net/sonbai/article/details/8687858知道PCLK的频率是101.25MHz,输出
频率就出来了,这时候最低,同时没有用到他们,所以不考虑了。t只考虑imer4,这时候N = 0,输出频率最大就是PCLK。
为什么要设最大,先放着


5、timer_load_val是一个全局变量,顾名思义它是装入TCNTB4寄存器的值。看下面的注释:


 /*
* for 10 ms clock period @ PCLK with 4 bit divider = 1/2
* (default) and prescaler = 16. Should be 10390
* @33.25MHz and 15625 @ 50 MHz
*/它的意思是如果要将timer4的时钟周期设置为10ms的话,要通过PCLK、分配器divider默认的值1/2和prescaler = 16来
  设置如何设置(?)来看一下下面这个函数


/* return PCLK frequency */
ulong get_PCLK(void)
{
struct s3c24x0_clock_power *clk_power = s3c24x0_get_base_clock_power();


return (readl(&clk_power->CLKDIVN) & 1) ? get_HCLK() / 2 : get_HCLK();
}看看结构s3c24x0_clock_power


/* CLOCK & POWER MANAGEMENT (see S3C2400 manual chapter 6) */
/* (see S3C2410 manual chapter 7) */
struct s3c24x0_clock_power {
u32 LOCKTIME;
u32 MPLLCON;
u32 UPLLCON;
u32 CLKCON;
u32 CLKSLOW;
u32 CLKDIVN;
#if defined(CONFIG_S3C2440)
U32 CAMDIVN;
#endif
};


涵盖了所有时钟设置的寄存器,s3c24x0_get_base_clock_power();返回的是(struct s3c24x0_clock_power *)0x4C000000
也就是时钟设置寄存器的起始地址


return (readl(&clk_power->CLKDIVN) & 1) ? get_HCLK() / 2 : get_HCLK();判断一下CLKDIVN是不是被设置成了1,如果
是说明PCLK = 1/2HCLK,不在去看get_HCLK了。前面我已经说过PCLK = 101.25MHz,至于
timer_load_val = 101.25MHz / (2*16*100);这个公式,分析如下:


我们要设置timer4的时钟周期是T = 10ms;


又  T = [1 /(PCLK / (预分频器的值(prescaler)*分频器的值(div)))] *timer_load_val


已知T、PCLK、perscaler、div,就可以求的


timer_load_val = PCLK / (perscaler * div)


但是这里为什么会有100呢?(暂时不清楚,望大家给我一个解释)
timer_clk下面暂时没有遇到,先不考虑。


6、


 /* load value for 10 ms timeout */
    lastdec = timer_load_val;
    writel(timer_load_val, &timers->TCNTB4);然后将timer_load_val的值写入TCNTB4寄存器中


7、下面是要配置TCON寄存器,也就是timer控制寄存器


 /* auto load, manual update of Timer 4 */
tmr = (readl(&timers->TCON) & ~0x0700000) | 0x0600000;
writel(tmr, &timers->TCON);





你可能感兴趣的:(uboot)