嵌入式linux 如何烧写flash

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了嵌入式linux 如何烧写flash相关的知识,希望对你有一定的参考价值。

有flash烧写器的啊
1)在电脑上装好相应的驱动和烧写软件,
2)把flash芯片按正确的方向放在烧写器的卡座上
3)通过烧写软件控制flash芯片的擦除和烧写,对Nand flash,烧写时默认会跳过坏块。

如果你说的是固件升级,那么通过nand write 和nand read命令去操作,nand read是将写入flash的数据读出来与写之前的文件进行二进制比较,以确认烧写是成功的。
注意:要用write.e和read.e,这样才能跳过坏块。
参考技术A 不同的flash和CPU有不同的接口,如并行,SPI、I2C等,都有相应的驱动程序来访问它,一般有相应的DEMO程序提供开发使用,还有从PUDN、CSDN上也可下载到相关驱动。 参考技术B 对/dev/mtd 进行 erase/write/read
MTD (Memory Techology Device)是一个subsystem,用来简化底层的flash device (ROM/NAND/OneNAND/NOR)的driver。在MTD下,driver只需提供read/write/erase的功能,而不需知道其上是使用FTL (Flash Translation Layer),还是FFS (Flash File System)。

嵌入式Linux开发学习基于TFTP的通用代码烧写方式

文章目录

概述

对于从事嵌入式行业的人来说,进行代码升级一定不会陌生。相对于单片机相对单一的烧写方式,嵌入式Linux开发中的烧写方式就丰富了许多。常见的比如串口烧写,USB烧写(fastboot),sd卡烧写。

可以看出烧写方式是非常自由的,但是也带来了一些问题,不同厂家往往会有自己独特的固件烧写方式,当你手里头有非常多的板子的时候,各种各样的烧写方式并不方便,特别是有的SOC厂家提供的烧写方式并不人性化,往往需要手动输入很多很长的命令(fastboot),或者说需要你等待很长时间(串口烧写),这个时候就需要一种更为通用的烧写方法。

实现思路

我们知道的是uboot和kernel通常都会帮我们实现tftp协议

在uboot中我们可以通过tftp 将代码拷到内存中

在文件系统中我们可以通过busybox的tftp命令拷贝到内存中,于是方法也就产生了。

但是我们也发现升级的时候可能需要在文件系统中执行,所以直接在挂载块设备的文件系统中烧写自己的代码并不现实,所以在烧写的时候一定要是initramfs root=/dev/ram0的方式挂载文件系统。

升级UBOOT

这个最简单 ,我们只需要在uboot中 通过uboot的net_loop

接口读取uboot到内存中的一个位置,然后使用uboot读写flash的接口写入即可。

升级KERNEL ROOTFS

这个就会相对复杂,因为在这个过程中会涉及到格式化flash的情况,同时因为kernel rootfs以及一些应用会导致uboot在升级中出错。所以可以考虑在文件系统挂载之后使用类似于dd的命令烧写flash。

QEMU 仿真

代码实现

烧写代码肯定需要具有flash和可以通网,所以我们在使用qemu的时候命令这样设置

/usr/local/qemu/bin/qemu-system-arm  -M vexpress-a9 \\
	-kernel $(TOP)/publish/u-boot -nographic -m 512M \\
	-sd $(TOP)/publish/uboot.disk \\
    	-net nic -net tap,ifname=tap0,script=no,downscript=no $(linux_debug_para)

然后需要产生一块虚拟的disk盘 作为mmc 设备也就是上面的

uboot.disk

命令如下

	tar -cvf digicap.tar zImage vexpress-v2p-ca9.dtb rootfs.cpio.gz

	 dd if=/dev/zero of=uboot.disk bs=1024K count=1024
	sgdisk -n 0:0:+1M  -c 0:uboot uboot.disk
	sgdisk -n 0:0:+35M -c 0:digicap uboot.disk
	sgdisk -n 0:0:+100M -c 0:rootfs uboot.disk
	sgdisk -n 0:0:0	    -c 0:update uboot.disk
	sgdisk -p uboot.disk

	freeloop="$(shell losetup -f)";\\
	echo $$freeloop;\\
	sudo losetup $$freeloop uboot.disk;\\
	sudo partprobe $$freeloop;\\
	ls $$freeloop*;\\
	dd if=u-boot of=$$freeloopp1 bs=512 count=2048;\\
	dd if=digicap.tar of=$$freeloopp2 bs=512 count=71680;\\
	 sudo losetup -d $$freeloop;\\

可以发现我们将digicap.tar zImage vexpress-v2p-ca9.dtb rootfs.cpio.gz

一起打了一个包,所以为了Uboot可以读取他们,我们在uboot中完成一个解tar包的程序。

然后我们需要修改Uboot的启动代码使得uboot可以正常解析tar包 并启动内核。

关键的函数如下

static int
do_hik_prep_boot(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])

    int len ,ret;
    if ( getenv("update_flag")!=NULL )
    
        hik_debug("digicap update !!! \\n");
        char cmdline[1024]= "root=/dev/ram0 rootfstype=ext2 rw console=ttyAMA0 rdinit=/linuxrc initrd=0x61000000,100M update_flag=1";
        setenv("bootargs",cmdline);
        return 1;
    

#ifdef CONFIG_HIK_NET_BOOT_MODE
    char cmdline[1024]= "root=/dev/ram0 rootfstype=ext2 rw  console=ttyAMA0 rdinit=/linuxrc initrd=0x61000000,100M ";
    hik_debug("run in net mode \\n");
    setenv("bootargs",cmdline);
    len = hik_tftp_get_file(hik_board_info.digicap_name,hik_board_info.update_load_addr,NULL );
    ret = hik_tar_file_get("zImage", len, hik_board_info.update_load_addr,hik_board_info.kernel_load_addr);
    if(ret < 0)
    
        hik_debug("read zImage failed\\n");
        return -1;
    
    ret = hik_tar_file_get("vexpress-v2p-ca9.dtb", len, hik_board_info.update_load_addr,hik_board_info.fdt_load_addr);
    if(ret < 0)
    
        hik_debug("read vexpress-v2p-ca9.dtb failed\\n");
        return -1;
    
    ret = hik_tar_file_get("rootfs.cpio.gz", len, hik_board_info.update_load_addr,hik_board_info.rootfs_load_addr);
    if(ret < 0)
    
        hik_debug("read rootfs.cpio.gz failed\\n");
        return -1;
    

#elif CONFIG_HIK_MMC_BOOT_MODE
    char cmdline[1024]= "root=/dev/mmcblk0p3 rw  rootwait earlycon console=ttyAMA0 init=/linuxrc";
    hik_debug("run in mmc mode \\n");
    setenv("bootargs",cmdline);
    len = hik_load_digicap();
    ret = hik_tar_file_get("zImage", len, hik_board_info.update_load_addr,hik_board_info.kernel_load_addr);
    if(ret < 0)
    
        hik_debug("read zImage failed\\n");
        return -1;
    
    ret = hik_tar_file_get("vexpress-v2p-ca9.dtb", len, hik_board_info.update_load_addr,hik_board_info.fdt_load_addr);
    if(ret < 0)
    
        hik_debug("read vexpress-v2p-ca9.dtb failed\\n");
        return -1;
    

#elif CONFIG_HIK_RAM_BOOT_MODE
    char cmdline[1024]= "root=/dev/ram0 rootfstype=ext2 rw console=ttyAMA0 rdinit=/linuxrc initrd=0x61000000,100M ";
    hik_debug("run in ram mode \\n");
    setenv("bootargs",cmdline);
    len = hik_load_digicap();
    hik_debug("load file success %d \\n",len);
    ret = hik_tar_file_get("zImage", len, hik_board_info.update_load_addr,hik_board_info.kernel_load_addr);
    if(ret < 0)
    
        hik_debug("read zImage failed\\n");
        return -1;
    
    hik_debug("read zImage success\\n");
    ret = hik_tar_file_get("vexpress-v2p-ca9.dtb", len, hik_board_info.update_load_addr,hik_board_info.fdt_load_addr);
    if(ret < 0)
    
        hik_debug("read vexpress-v2p-ca9.dtb failed\\n");
        return -1;
    
    hik_debug("read vexpress-v2p-ca9.dtb success\\n");
    ret = hik_tar_file_get("rootfs.cpio.gz", len, hik_board_info.update_load_addr,hik_board_info.rootfs_load_addr);
    if(ret < 0)
    
        hik_debug("read rootfs.cpio.gz failed\\n");
        return -1;
    
    hik_debug("read rootfs.cpio.gz success\\n");

#else
    hik_debug("run in flash mode \\n");
#endif
    return 0;


U_BOOT_CMD(
	hik_prep_boot, 1,	1,	do_hik_prep_boot,
	"set up en before boot",
	"hik_prep_boot \\n"
);

我们只需要在bootcmd中添加hik_prep_boot命令就可以完成启动前的所有准备工作

我的bootcmd是这样的

#define CONFIG_BOOTCOMMAND  "hik_prep_boot ; bootz 0x60003000 - 0x60500000 ; "

至于升级uboot 和digicap的命令是这样实现的

static int
do_updateb(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])

    struct mmc *mmc;
    int ret;
    int blk_count;
    if(argc == 1)
        ret = hik_tftp_get_file(hik_board_info.uboot_name , hik_board_info.update_load_addr , NULL);
    else
        ret = hik_tftp_get_file(argv[1] , hik_board_info.update_load_addr , NULL);
    if(ret < 0 )
    
        hik_debug("hik_tftp_get_file failed \\n");
        do_reset(NULL, 0, 0, NULL);
        return -1;
    
    else
    
        mmc = hik_init_mmc_device(hik_board_info.flash_dev , false);
        if (!mmc)
            hik_debug("mmc init error\\n");
            return -1;
        
        if (mmc_getwp(mmc) == 1) 
            hik_debug("Error: card is write protected!\\n");
            return -1;
        

        blk_count=(ret/MMC_BLK_SIZE)+1;
        ret = mmc->block_dev.block_write(hik_board_info.flash_dev , hik_board_info.uboot_start_blk , blk_count ,(const void *)hik_board_info.update_load_addr);
    
    if( ret == blk_count)
    
        hik_debug("uboot update success \\n");
    else
        hik_debug("uboot update failed %d\\\\%d\\n",ret,blk_count);
    

    do_reset(NULL, 0, 0, NULL);

    return 0;//never run to here



U_BOOT_CMD(
	updateb, 2,	1,	do_updateb,
	"updateb uboot from tftp server",
	"updateb filename\\n"
);



static int
do_updatek(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])

    char *s;
    int ret,len;
    if(argc == 1)
        len = hik_tftp_get_file(hik_board_info.digicap_name , hik_board_info.update_load_addr , NULL);
    else
        len = hik_tftp_get_file(argv[1] , HIK_UPDATE_LOAD_ADDR , NULL);
    if(len < 0 )
    
        hik_debug("hik_tftp_get_file failed \\n");
        do_reset(NULL, 0, 0, NULL);
        return -1;
    
    ret = hik_tar_file_get("zImage", len, hik_board_info.update_load_addr,hik_board_info.kernel_load_addr);
    if(ret < 0)
    
        hik_debug("read zImage failed\\n");
        return -2;
    
    ret = hik_tar_file_get("vexpress-v2p-ca9.dtb", len, hik_board_info.update_load_addr,hik_board_info.fdt_load_addr);
    if(ret < 0)
    
        hik_debug("read vexpress-v2p-ca9.dtb failed\\n");
        return -2;
    
    ret = hik_tar_file_get("rootfs.cpio.gz", len, hik_board_info.update_load_addr,hik_board_info.rootfs_load_addr);
    if(ret < 0)
    
        hik_debug("read rootfs.cpio.gz failed\\n");
        return -2;
    
    setenv("update_flag","1");
    s = getenv("bootcmd");
    autoboot_command(s);
    return 0;//never run to here



U_BOOT_CMD(
	updatek, 2,	1,	do_updatek,
	"updatek kernel and rootfs from tftp server",
	"updatek filename\\n"
);

最后我们需要在启动脚本中添加如下代码

for file in /sbin/hik_scripts/include/*
do
    if [ -f $file ];then
        echo include $file
        source $file
    fi
done
update_flag=$(cmdline_exist update_flag)
echo update_flag="$update_flag"
if [ "$update_flag" == "0" ];then
    mkdir /mnt/update
    cd /mnt/update;\\
    tftp -g 192.168.27.129 -r digicap.tar
    dd if=digicap.tar of=mmcblk0p2 bs=512 count=71680
    reboot
fi

升级效果

以上是关于嵌入式linux 如何烧写flash的主要内容,如果未能解决你的问题,请参考以下文章

嵌入式bootloader和内核在flash上分布问题

嵌入式Linux之旅——环境搭建篇之烧写整个系统

嵌入式中烧写程序的基础知识有哪些

嵌入式Linux开发学习基于TFTP的通用代码烧写方式

嵌入式Linux开发学习基于TFTP的通用代码烧写方式

嵌入式Linux4.U-Boot入门