蜂蜜加醋怎么喝减肥:S3C6410开发板实践记录

来源:百度文库 编辑:偶看新闻 时间:2024/04/26 20:29:12
----------------------------------------------------------------本文系作者原创, 欢迎大家转载!转载请注明出处:netwalker.blog.chinaunix.net---------------------------------------------------------------- 硬件平台:MainBoard:OK6410
CPU: S3C6410
RAM: 256M
FLASH:1G K9GAG08U0DKernel:Linux2.6.28BootLoader:Uboot1.1.6
 

1. LINUX FLASH分区问题

 

看到linux在启动的时候打印出:

 

  1. Creating 4 MTD partitions on "NAND 2GiB 3,3V 8-bit":

  2. 0x00000000-0x00040000 : "Bootloader"
  3. 0x00040000-0x00400000 : "Kernel"
  4. 0x00400000-0x05400000 : "Rootfs"
  5. 0x05400000-0x80000000 : "File System"

 

所以bootloader应该是0x00000000-0x00040000,大小256k,但是为何文档中升级uboot的时候更新的是0x100000大小的FLASH空间?

 

后在LINUX中dump出来/dev/mtdblock1和写入zImage比较发现前一部分是uboot的东西,之所以系统可以加载Rootfs成功是因为0x00400000-0x05400000和文档中的根文件系统是一致的。所以这里需要更改内核分区的大小,位于linux2.6.28/arch/arm/plat-s3c/include/ plat/partition.h

 

  1. struct mtd_partition s3c_partition_info[] =
  2. {
  3.   {
  4.     .name = "Bootloader",
  5.     .offset = 0,
  6.     .size = (SZ_1M),
  7.     .mask_flags = MTD_CAP_NANDFLASH,
  8.   },
  9.   {
  10.     .name = "Kernel",
  11.     .offset = (SZ_1M),
  12.     .size = (4*SZ_1M) - (SZ_1M),
  13.     .mask_flags = MTD_CAP_NANDFLASH,
  14.   },
  15. #if defined(CONFIG_SPLIT_ROOT_FILESYSTEM)
  16.   {
  17.     .name = "Rootfs",
  18.     .offset = (4*SZ_1M),
  19.     .size = (80*SZ_1M),//(48*SZ_1M),
  20.   },
  21. #endif
  22.   {
  23.     .name = "File System",
  24.     .offset = MTDPART_OFS_APPEND,
  25.     .size = MTDPART_SIZ_FULL,
  26.     }
  27. };

 

此时在内核启动时输出的信息与实际NAND Flash的分区情况是一致的了。

0x00000000-0x00100000 : "Bootloader"

0x00100000-0x00400000 : "Kernel"

0x00400000-0x05400000 : "Rootfs"

0x05400000-0x80000000 : "File System"

 

2.如何实现tftp uboot升级

 

这个网上有DM9000A的驱动,只要替换对应的driver/dm9000.c 和driver/dm9000.h即可,但是发现只有在使用ping命令(发送数据包)的时候,对应的网卡才会激活,而一旦ping结束后,那么网卡就会自动断掉,查看代码发现在do_ping时会做eth_init,而结束后会做eth_close动作

 

所以在start_armboot函数中加入初始化动作:eth_init(gd->bd); 并注释掉eth_close动作即可。

硬件平台:MainBoard:OK6410
CPU: S3C6410
RAM: 256M
FLASH:1G K9GAG08U0DKernel:Linux2.6.28BootLoader:Uboot1.1.6 3.使用TFTP升级Uboot 发现使用tftp升级uboot的时候无法成功,但是升级fs和kernel的时候可以。
后发现S3C6410启动的时候会通过NAND控制器将nandflash的头4K拷贝到SRAM中执行,这一步叫做steppingstone,SRAM的大小有8K,但是steppingstone只用了其中的4K,另外NAND控制器支持每FLASH页读取的大小为512B/2K,所以当flash页大小为4K的时候,它只能每块读取前2K,这里就需要在写Uboot的时候前面个PAGE每个只能写入2K,也即 
  1. nand erase 0 100000
  2. nand write 50008000 0 1000
  3. nand write 50008800 1000 1000
  4. nand write 50009000 2000 FF000
飞凌提供的SD卡升级方式利用了SDRAM的全部大小,也即8K,所以它的写入步骤为:
  1. nand erase 0 100000
  2. nand write 50008000 0 1000
  3. nand write 50008800 1000 1000
  4. nand write 50009000 2000 1000
  5. nand write 50009800 3000 1000
  6. nand write 5000A000 4000 FE000
至此解决了TFTP升级Uboot问题。另外在do_nane中添加了命令nand write.uboot命令,可以直接一个命令搞定uboot升级,添加如下代码: 
  1. else if(!read && !strcmp(s, ".uboot"))
  2.         {
  3.             /* for Page 2048 then just do normal writing */
  4.             if(nand->writesize == 0x800)
  5.                 goto write;
  6.             else if(nand->writesize == 0x1000)
  7.             {
  8.                 /* try to simulate a 0x800 page size flash and do a remap */
  9.                 int blocks = 5, i = 0;
  10.                 
  11.                 for(; i < blocks; i++)
  12.                 {
  13.                     if(i == blocks - 1)
  14.                         size = 0xfe000;
  15.                     else
  16.                         size = 0x1000;
  17.                         
  18.                     printf("Write at Offset %x from Addr:%x with Size %x\n", off + i * 0x1000, addr + i * 0x800, size);
  19.                     ret = nand_write(nand, off + i * 0x1000, &size, (u_char *)(addr + i * 0x800));
  20.             
  21.                     if(ret == 0)
  22.                     {
  23.                         uint *magic = (uint*)(PHYS_SDRAM_1);
  24.                         if((0x24564236 == magic[0]) && (0x20764316 == magic[1]))
  25.                         magic[0] = 0x27051956;
  26.                     }
  27.                     
  28.                     if(ret)
  29.                     {
  30.                         printf(" 0x%x bytes %s to 0x%x %s\n", size, read ? "read" : "written", off + i * 0x1000, 0x1000,
  31.                                     ret ? "ERROR" : "OK");
  32.                         return 1;                
  33.                     }
  34.                 }
  35.                 
  36.                 printf("Uboot have been updated, please do reset!\n");
  37.                 return 0;
  38.             }
  39.             else
  40.             {
  41.                 printf("Only support page size 2K and 4K!\n");
  42.                 return 1;
  43.             }
  44.         }
硬件平台:MainBoard:OK6410
CPU: S3C6410
RAM: 256M
FLASH:1G K9GAG08U0DKernel:Linux2.6.28BootLoader:Uboot1.1.6 

4.BOOT SELECT

 OK6410底板原理图.pdf5页上的BOOT SELECT接线标记反了,如下图:

 

 

而在LINUX2.6.28使用手册中的拨码开关说明如下:

 

 

显然这里的Pin8SELNAND,也即是OK6410 BOOT SELECT电路图的PIN1

 

5.关于S3C6410启动模式

http://www.hzlitai.com.cn/article/ARM11/SYSTEM/S3C6410qd.html有详细的解释。

先读一下S3C6410 User Manual的第3章 System Controller,在123页列了一张表,如图:

从表中可以看出,S3C6410好像并不支持Nandflash启动,因为Boot Device中没有Nandflash设备。当OM[4:0]为0000x/0001x/0010x/0011x的时候,Boot Device是Reserved,我想这里是Samsung在6410中有意要隐瞒什么。如果你用过S3C6400,你应该知道在S3C6400 User Manual中也有这么一张表,描述了s3c6400的启动模式,具体S3C6400的启动模式也是在第3章 System Controller,在第107页的表,如图:

从表中可以看出,S3C6410好像并不支持Nandflash启动,因为Boot Device中没有Nandflash设备。当OM[4:0]为0000x/0001x/0010x/0011x的时候,Boot Device是Reserved,我想这里是Samsung在6410中有意要隐瞒什么。如果你用过S3C6400,你应该知道在S3C6400 User Manual中也有这么一张表,描述了s3c6400的启动模式,具体S3C6400的启动模式也是在第3章 System Controller,在第107页的表,如图:

 

OK6410提供了两种选择,也即PIN7/611时,OM[4:1]1111,此时为SD/MMC(CH0),当PIN7/600时,OM[4:1]0011,且XSELNAND永远为1,所以选择为Large PageAddrCycle=5(Flash的读写周期)。

硬件平台:MainBoard:OK6410
CPU: S3C6410
RAM: 256M
FLASH:1G K9GAG08U0DKernel:Linux2.6.28BootLoader:Uboot1.1.6 

6.关于NAND FLASH K9GAG08U0D的读写

 

K9GAG08U0D.pdf的第10页,提到它的操作时钟周期为5Cycle,另外页大小是4096Byte

 

6

 

 

uboot1.1.6_256M\cpu\s3c64xx\nand_cp.c72行,有如下代码:

  1. NFADDR_REG = 0; //Cycle 1



  2.         if (large_block)
  3.                  NFADDR_REG = 0; // //Cycle 2



  4.         NFADDR_REG = (addr) & 0xff; // //Cycle 3
  5.         NFADDR_REG = (addr >> 8) & 0xff; //Cycle 4
  6.         NFADDR_REG = (addr >> 16) & 0xff;////Cycle 5

 

显然这里使用了5Cycles,与文档是相符的。

 

如果对照本文件中的S3C2410的读FLASH的代码,那么对于NAND FLASH的读写将会有更深的理解。其实如果

你在本文件中改写了S3C6410读取的方法,那么也是可以成功使用tftp升级uboot的。

 

 

硬件平台:MainBoard:OK6410
CPU: S3C6410
RAM: 256M
FLASH:1G K9GAG08U0DKernel:Linux2.6.28BootLoader:Uboot1.1.6


7.Busybox 的编译

选择合适的Busybox版本,这里选择用2.6.28内核对应的busybox-1.16.2版本,然后做以下配置:

1)  make menuconfig

2)  Busybox Settings  --->  Build Options  ---> 


 

选择编译成静态库,这样就避免了繁琐的动态库安装,当然如果使用了动态库,那么需要将uclibc中的库so文件拷贝到lib文件夹中,这样其他的应用程序也可以使用动态库编译而不增加代码大小。

指定交叉编译工具链。

3)  Busybox Settings  --->  Installation Options  --->

 

   

选择Don't use /usr防止busybox安装到宿主机

指定安装路径为/home/red/forlinux/rootfs

 

  1. #make
  2. #make install

 

此时安装到rootfs的文件和目录如下:

 

 

  1. #ls /home/red/forlinux/rootfs
  2. drwxr-xr-x 2 root root 4.0K 2011-07-12 10:38 bin/
  3. lrwxrwxrwx 1 root root 11 2011-07-12 10:38 linuxrc -> bin/busybox*
  4. drwxr-xr-x 2 root root 4.0K 2011-07-12 10:38 sbin/

 

 创建一些系统启动必须的目录和文件

  1. #mkdir bin sbin dev etc lib proc tmp usr var sys mnt

 

创建设备节点,通常需要root权限才能使用mknod来创建,我们直接赋值已经创建好的设备节点即可,另外一种方法是使用mdev命令根据mdev.conf在启动时生成。

 

设备节点应该具有合适的权限,否则在使用该设备的时候会出问题。

 

etc下的配置文件:

1)  inittab :在busybox执行linuxrc或者init链接的时候解析并执行。

2)  init.d:是一个文件夹,一般放置rcS脚本

3)  profile:用来配置终端

 

export PS1='[\u@\h \W]\# ' 后发现终端提示符号不正确,如下显示:

[\u@\h \W]\#

 

busybox settings->busybox library tuning->username completion、fancy shell prompts选上

CONFIG_FEATURE_EDITING_FANCY_PROMPT=y

CONFIG_FEATURE_USERNAME_COMPLETION=y

 

重新编译即可。也只有开启了此两选线,/etc/profile才有意义。

 

8.关于nfs文件系统的挂载 

在ubuntu10.10上测试通过,参考了网上的一些文档,有些命令在ubuntu上需要修改

 

  1. #sudo apt-get install portmap
  2. #sudo apt-get install nfs-kernel-server
  3. #sudo vim /etc/exports

  4. 添加/forlinx *(rw,sync,no_root_squash)

  5. #sudo /etc/init.d/portmap restart //该命令应为service portmap stop; service portmap start
  6. #sudo /etc/init.d/nfs-kernel-server restart // 该命令为service nfs-kernel-server restart

 

另外查看rpcbind有没有运行。如果没有则rpcbind –w&

 

 

  1. #exportfs –rv // 这一步很关键
  2. #showmount -e // 显示共享出的目录

 

本机测试命令为:

  1. #sudo mount -t nfs localhost:/forlinux/root /mnt/tmp

 

嵌入式系统测试为:

  1. mount -t nfs localhost:/forlinux/root /mnt/tmp -o nolock // nolock 必须要添加

 

如果作为根系统启动,则

  1. #setenv bootargs "root=/dev/nfs nfsroot=10.255.21.240:/forlinux/root ip=10.255.21.213:10.255.21.240:10.255.21.254:255.255.255.0:hostname:eth0:off console=ttySAC0,115200"
  2. #saveenv
  3. #reset

10.255.21.240 PC 端Ubuntu 的IP

10.255.21.213开发板IP

10.255.21.254 网关

255.255.255.0 子网掩码

 

硬件平台:MainBoard:OK6410
CPU: S3C6410
RAM: 256M
FLASH:1G K9GAG08U0DKernel:Linux2.6.28BootLoader:Uboot1.1.6 9.LED灯驱动 这几天研究USB驱动,不想板子的USB坏了,所以闲着没事就看了看LED灯驱动.OK6410没有使用内核提供的LED驱动模板,而是直接通过寄存器虚拟地址进行操作,比起USB驱动来说要简单得多。 首先还是从电路图开始: 

 

User LED电路图

 

 

User LED CPU管脚接线图

 

从电路图文档中可以看出,LED是通过GPIOM端口实现的,其中分别用到了GPM0/1/2/3管脚。根据LED的接线图可以看出只有在NLED0/1/2/3为低电平时,才会导通点亮。M端口有6个管脚,三个对应的寄存器:

 

GPMCON是控制寄存器,控制管脚的功能:用作输入,输出等。

GPMDAT是数据寄存器,内容与管脚电平相一致,也即GPM0为高电平那么对应的0b11.

GPMPUD是用来控制功率输出的,通常在电平输出不符合要求是,实现电平的上拉和下拉。

 

                     相关寄存器文档(芯片手册300 pg.)

 

Bootloader中对应GPM初始化的代码位于board/samsung/smdk6410/lowlevel_init.S

  1. /* LED on only #8 */
  2. ldr r0, =ELFIN_GPIO_BASE
  3. ldr r1, =0x00111111
  4. str r1, [r0, #GPMCON_OFFSET]

  5. ldr r1, =0x00000555
  6. str r1, [r0, #GPMPUD_OFFSET]

  7. ldr r1, =0x002a
  8. str r1, [r0, #GPMDAT_OFFSET]

 

相关寄存器定义在include/s3c6410.h

 

  1. #define ELFIN_GPIO_BASE 0x7f008000
  2. #define GPMCON_OFFSET 0x820
  3. #define GPMDAT_OFFSET 0x824
  4. #define GPMPUD_OFFSET 0x828

ldr指令用来将立即数装载如寄存器,strr1中的值写入间址GPMDAT_OFFSET+r0ELFIN_GPIO_BASE定义的就是实际的物理地址,不难看出bootloader是实地址寻址的。

 

3行写入0x00111111GPMCON寄存器,也即配置GPM0-5为输出。

接着2行写入0x00000555GPMPUD寄存器,配置GPM0-5内部电阻下拉使能。这是因为如果要点亮LED,那么需要低电平,下拉可以保证下拉电阻接地产生低电平。

最后2行写入0x002aGPMDATA寄存器,也即0b101010,所以对应的GPM0/2/4是低电平,GPM1/3/5是高电平,与此相连的LED1/3点亮,LED2/4熄灭,这也就是为何开发板上电后LED1/3点亮的原因。

 

硬件平台:MainBoard:OK6410
CPU: S3C6410
RAM: 256M
FLASH:1G K9GAG08U0DKernel:Linux2.6.28BootLoader:Uboot1.1.6 

11. LINUX下led驱动解析

Linux2.6.28中的LED驱动位于drivers/char/s3c6410_leds.c。

 

  1. static int __init s3c6410_leds_init(void)
  2. {
  3.     int ret = 0;
  4.   unsigned long tmp;
  5.     dev_t devno;
  6.   
  7.   printk(KERN_NOTICE "enter s3c6410_leds_init\n");

  8.   devno = MKDEV(LED_MAJOR,0);
  9.     
  10.      //申请设备号资源,对应到内核的HASH表chrdevs    
  11.      ret = register_chrdev_region(devno,1,DEVICE_NAME);     
  12.      if(ret<0)    
  13.      {
  14.      printk(KERN_NOTICE "can not register led device");
  15.      return ret;    
  16.      }
  17.     
  18.      //初始化字符设备cdev_leds,并安装操作函数    
  19.      cdev_init(&cdev_leds,&s3c6410_leds_fops);    
  20.      cdev_leds.owner = THIS_MODULE;
  21.          
  22.      //通告内核,这样就可以在/proc/devices下看到231 leds设备了    
  23.      ret =cdev_add(&cdev_leds,devno,1);    
  24.      if(ret)    
  25.      {
  26.             printk(KERN_NOTICE "can not add leds device");
  27.      return ret;
  28.      }
  29.     
  30.      //创建/sys下的my_class类文件    
  31.      my_class = class_create(THIS_MODULE,"my_class");    
  32.      if(IS_ERR(my_class))    
  33.      {
  34.              printk("Err: Failed in creating class\n");
  35.      return -1;    
  36.      }
  37.     
  38.      // 创建/dev/leds文件,提供用户空间访问的接口    
  39.      device_create(my_class,NULL,MKDEV(LED_MAJOR,0),NULL,DEVICE_NAME);

  40.    //与lowlevel_init.S初始化类似的操作,但是这里的地址都是虚拟地址    
  41.      //gpm0-3 pull up    
  42.      tmp = __raw_readl(S3C64XX_GPMPUD);     
  43.      tmp &= (~0xFF);    
  44.      tmp |= 0xaa;    
  45.      __raw_writel(tmp,S3C64XX_GPMPUD);    
  46.     
  47.      //gpm0-3 output mode    
  48.      tmp = __raw_readl(S3C64XX_GPMCON);    
  49.      tmp &= (~0xFFFF);    
  50.      tmp |= 0x1111;    
  51.      __raw_writel(tmp,S3C64XX_GPMCON);
  52.     
  53.      //gpm0-3 output 0    
  54.      tmp = __raw_readl(S3C64XX_GPMDAT);    
  55.      tmp |= 0x10;
  56.      __raw_writel(tmp,S3C64XX_GPMDAT);
  57.     
  58.      printk(DEVICE_NAME " initialized\n");
  59.     
  60.      return 0;
  61. }


相关的寄存器虚地址定义在arch/arm/plat-s3c64xx/include/plat/gpio-bank-m.h

  1. #define S3C64XX_GPMCON (S3C64XX_GPM_BASE + 0x00)
  2. #define S3C64XX_GPMDAT (S3C64XX_GPM_BASE + 0x04)
  3. #define S3C64XX_GPMPUD (S3C64XX_GPM_BASE + 0x08)

GPIO M端口的基地址定义在arch/arm/plat-s3c64xx/include/plat/regs-gpio.h

  1. #define S3C64XX_GPM_BASE (S3C64XX_VA_GPIO + 0x0820)

GPIO的虚地址定义在arch/arm/mach-s3c6400/include/mach/map.h

  1. #define S3C64XX_VA_GPIO S3C_ADDR(0x00500000)


GPIO的虚地址是有全局虚地址S3C_ADDR_BASE计算出来的,void __iomem __force *作用是强制转化为地址。arch/arm/plat-s3c/include/plat/map.h

 

  1. #define S3C_ADDR_BASE (0xF4000000)

  2. #ifndef __ASSEMBLY__
  3. #define S3C_ADDR(x) ((void __iomem __force *)S3C_ADDR_BASE + (x))
  4. #else
  5. #define S3C_ADDR(x) (S3C_ADDR_BASE + (x))
  6. #endif

由此可以得到GPM寄存器对应的虚地址分别为:

  1. S3C64XX_GPMCON 0xF4500820
  2. S3C64XX_GPMDAT 0xF4500824
  3. S3C64XX_GPMPUD 0xF4500828


驱动中的接口通过ioctl来提供,

  1. static struct file_operations s3c6410_leds_fops = {
  2.    .owner = THIS_MODULE,
  3.    .ioctl = s3c6410_leds_ioctl,
  4. };

  5. static int s3c6410_leds_ioctl( struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
  6. {
  7.  unsigned long tmp;
  8.  switch(cmd)
  9.  {
  10.   case 0: //熄灭
  11.   case 1:// 点亮

  12.   if(arg > 4)
  13.    return -EINVAL;

  14.   tmp = __raw_readl(S3C64XX_GPMDAT);
  15.   if(cmd) //注意cmd为1时点亮动作,对应的位置0
  16.      tmp &= (~(1<
  17.     else
  18.       tmp |= (1<
  19.     
  20.     __raw_writel(tmp,S3C64XX_GPMDAT);
  21.     return 0;

  22.  default:
  23.     return -EINVAL;
  24.  }
  25. }

尽管该驱动在编译进内核时,运行led或者led-player可以正常工作,但是当你把选项CONFIG_TE6410_LEDS=y改为CONFIG_TE6410_LEDS=m时,问题出现了。 

进行insmode s3c6410_leds.ko没有问题,而进行rmmod s3c6410_leds时则会提示:

rmmod: chdir(/lib/modules): No such file or directory,关于此问题网上有很多解决方法,比如http://www.cnblogs.com/junmao/articles/1991495.html

 

真正的问题不在于卸载,而在于卸载后再次insmod时提示加载失败,分析源码发现,驱动的卸载载函数存

在问题:

 

  1. static void __exit s3c6410_leds_exit(void)
  2. {
  3.     /* added by lli_njupt */
  4.     device_destroy(my_class, MKDEV(LED_MAJOR,0));
  5.     class_destroy(my_class);

  6.     cdev_del(&cdev_leds);

  7.     unregister_chrdev_region(MKDEV(LED_MAJOR,0),1);

  8.     printk(KERN_NOTICE "s3c2440_leds_exit\n");
  9. }

注意红色部分由笔者添加,加载失败的原因就在于没有从/dev/下注销leds,另外/sys下的class文件也需要注销。如此天下太平了。

硬件平台:MainBoard:OK6410
CPU: S3C6410
RAM: 256M
FLASH:1G K9GAG08U0DKernel:Linux2.6.28BootLoader:Uboot1.1.6 

12.用户空间的跑马灯

 

  1. int main(void)
  2. {
  3.     int on=1;
  4.     int led;
  5.     int fd;
  6.     
  7.     fd = open("/dev/leds",0);
  8.     if(fd<0)
  9.     {
  10.         perror("open device leds");
  11.         exit(1);
  12.     }

  13.     printf("leds test show.press ctrl+c to exit\n");
  14.     while(1)
  15.     {
  16.         for(led = 0; led < 4; led++)
  17.         {
  18.             // 这个实现倒是很简洁,每隔60000微秒切换一次
  19.             ioctl(fd, on, led);
  20.             usleep(60000);
  21.         }
  22.         on = !on;
  23.     }
  24.     
  25.     close(fd);
  26.     return 0;
  27. }
为何可以通过fopen获取的描述符fd来访问设备,涉及到linux字符cdev和Inode的关系,这个chinaunix上有一篇很好的帖子http://www.chinaunix.net/jh/4/1027719.html,由于字符设备的驱动比较简单,相关的数据结构这个帖子里讲解的很清楚了,不再赘述!