ARM-LINUX基础
+ -

SD卡烧写脚本解释

2025-08-04 10 0

这个脚本是用于制作 I.MX6 处理器的 SD 卡启动系统的 Shell 脚本。


1. 脚本头

#! /bin/sh
#I.MX6 SD卡启动系统烧写脚本
#version v1.1
#Author QQ1252699831
#company 广州星翼电子科技有限公司
  • #!/bin/sh:指定使用 sh(Bourne Shell)解释器执行脚本。
  • 注释说明脚本用途、版本、作者和公司信息。

2. 版本变更记录

#version v1.0 2019.10.26
#version v1.1 2020.11.7 1.添加判断文件系统是否存在/lib/modules目录的功能,再解压模块
VERSION="1.1"
  • 记录脚本的版本变更历史。
  • VERSION="1.1":定义脚本版本变量。

3. usage() 函数(打印帮助信息)

usage ()
{
  echo "
用法: `basename $1` [选项] <(必选)-device> <(可选)-flash> <(可选)-ddrsize>
用法示例:
sudo ./imx6mksdboot.sh -device /dev/sdd
sudo ./imx6mksdboot.sh -device /dev/sdd -flash emmc -ddrsize 512 
命令选项:
  -device              SD卡块设备节点 (例如/dev/sdx)
  -flash           请选择开发板Flash类型(emmc | nand)
  -ddrsize           请选择DDR大小 (512 | 256) 
可选选项:
  --version             打印版本信息.
  --help                打印帮助信息.
"
  exit 1
}
  • 定义 usage() 函数,用于打印脚本的使用方法。
  • basename $1:提取脚本名称(去掉路径)。
  • exit 1:退出脚本,返回错误码 1

4. 默认 U-Boot 镜像

Uboot='u-boot-imx6ull-14x14-ddr512-emmc.imx'
  • 默认使用 DDR512 版本的 U-Boot 镜像。

5. execute() 函数(执行命令并检查错误)

execute ()
{
    $* >/dev/null  # 执行命令并丢弃输出
    if [ $? -ne 0 ]; then  # 检查上一条命令的返回值
        echo
        echo "错误: 执行 $*"
        echo
        exit 1  # 如果命令执行失败,退出脚本
    fi
}
  • $*:执行传入的所有参数(即命令)。
  • $?:获取上一条命令的返回值,0 表示成功,非 0 表示失败。
  • 如果命令执行失败,打印错误信息并退出脚本。

6. version() 函数(打印版本信息)

version ()
{
  echo
  echo "`basename $1` version $VERSION"
  echo "I.MX6 SD卡制卡脚本"
  echo
  exit 0
}
  • 打印脚本版本信息并退出。

7. 参数解析

arg=$#
if [ $arg -ne 6 ];then
number=1
while [ $# -gt 0 ]; do
  case $1 in
    --help | -h) usage $0 ;;
    -device) shift; device=$1; shift; ;;
    --version) version $0;;
    *) copy="$copy $1"; shift; ;;
  esac
done
test -z $device && usage $0  # 如果未提供 -device 参数,打印帮助信息
echo ""
echo "根据下面的提示,补全缺省的参数-flash -ddrsize"
read -p "请选择开发板参数,输入数字1~4,按Enter键确认
1.-flash emmc,-ddrsize 512
2.-flash emmc,-ddrsize 256
3.-flash nand,-ddrsize 512
4.-flash nand,-ddrsize 256 
输入数字1~4(default 1): " number
if [ -z $number ];then
  echo "使用默认参数:EMMC版本,DDR大小为512MB"
else
  case $number in
    1) echo '您已经选择开发板参数为:EMMC版本,DDR大小为512MB'
       Uboot='u-boot-imx6ull-14x14-ddr512-emmc.imx' ;;
    2) echo '您已经选择开发板参数为:EMMC版本,DDR大小为256MB'
       Uboot='u-boot-imx6ull-14x14-ddr256-emmc.imx' ;;
    3) echo '您已经选择开发板参数为:NAND FLASH版本,DDR大小为512MB'
       Uboot='u-boot-imx6ull-14x14-ddr512-nand-sd.imx' ;;
    4) echo '您已经选择开发板参数为:NAND FLASH版本,DDR大小为256MB'
       Uboot='u-boot-imx6ull-14x14-ddr256-nand-sd.imx' ;;
    *) echo '输入的参数有误,退出制卡'; exit ;;
  esac
fi
else
while [ $# -gt 0 ]; do
  case $1 in
    --help | -h) usage $0 ;;
    -device) shift; device=$1; shift; ;;
    -flash) shift; flash=$1; shift; ;;
    -ddrsize) shift; ddrsize=$1; shift; ;;
    --version) version $0;;
    *) copy="$copy $1"; shift; ;;
  esac
done
  if [ $flash = "emmc" -a $ddrsize = "512" ];then
    Uboot='u-boot-imx6ull-14x14-ddr512-emmc.imx'
    echo '您已经选择开发板参数为:EMMC版本,DDR大小为512MB'
  elif [ $flash = "emmc" -a $ddrsize = "256" ];then
    Uboot='u-boot-imx6ull-14x14-ddr256-emmc.imx'
    echo '您已经选择开发板参数为:EMMC版本,DDR大小为256MB'
  elif [ $flash = "nand" -a $ddrsize = "512" ];then
    Uboot='u-boot-imx6ull-14x14-ddr512-nand-sd.imx'
    echo '您已经选择开发板参数为:NAND FLASH版本,DDR大小为512MB'
  elif [ $flash = "nand" -a $ddrsize = "256" ];then
    Uboot='u-boot-imx6ull-14x14-ddr256-nand-sd.imx'
    echo '您的开发板参数为:NAND FLASH版本,DDR大小为256MB'
  else
    echo '参数有误!'
    usage $0
  fi 
fi
  • 检查参数数量:
    • 如果参数不足 6 个,提示用户选择 Flash 类型和 DDR 大小。
    • 如果参数完整,直接解析 -device-flash-ddrsize 参数。
  • case 语句用于匹配不同的选项。

8. 检查必要文件

sdkdir=$PWD
if [ ! -d $sdkdir ]; then
    echo "错误: $sdkdir目录不存在"
    exit 1
fi
if [ ! -f $sdkdir/filesystem/*.tar.* ]; then
    echo "错误: $sdkdir/filesystem/下找不到文件系统压缩包"
    exit 1
fi
if [ ! -f $sdkdir/boot/zImage ]; then
    echo "错误: $sdkdir/boot/下找不到zImage"
    exit 1
fi
if [ ! -b $device ]; then
    echo "错误: $device 不是一个块设备文件"
    exit 1
fi
if [ $device = '/dev/sda' ];then
    echo "请不要选择sda设备,/dev/sda通常是您的Ubuntu硬盘!
继续操作你的系统将会受到影响!脚本已自动退出"
    exit 1 
fi
  • $PWD:当前工作目录。
  • 检查:
    • 当前目录是否存在。
    • filesystem/ 下是否有文件系统压缩包。
    • boot/ 下是否有 zImage(内核镜像)。
    • $device 是否是块设备(-b 检查)。
    • 防止误操作 /dev/sda(系统硬盘)。

9. 警告信息

echo "即将进行制作SD系统启动卡,大约花费几分钟时间,请耐心等待!"
echo "************************************************************"
echo "*         注意:这将会清除$device所有的数据               *"
echo "*         在脚本执行时请不要将$device拔出                 *"
echo "*             请按<Enter>确认继续                          *"
echo "************************************************************"
read enter
  • 提示用户确认操作,防止误操作。

10. 卸载已有分区

for i in `ls -1 $device?`; do
    echo "卸载 device '$i'"
    umount $i 2>/dev/null
done
  • $device?:匹配 sdd1sdd2 等分区。
  • umount:卸载已挂载的分区,防止后续操作冲突。

11. 清空 SD 卡

execute "dd if=/dev/zero of=$device bs=1024 count=1024"
  • dd if=/dev/zero:用 0 填充设备的前 1024KB,清空分区表。

12. 使用 fdisk 分区

cat << END | fdisk -H 255 -S 63 $device
n  # 新建分区
p  # 主分区
1  # 分区号 1
   # 起始扇区(默认)
+64M  # 分区大小 64MB
n  # 新建第二个分区
p  # 主分区
2  # 分区号 2
   # 起始扇区(默认)
   # 结束扇区(默认,使用剩余空间)
t  # 修改分区类型
1  # 选择分区 1
c  # FAT32 (LBA)
a  # 设置可启动标志
1  # 选择分区 1
w  # 写入分区表
END
  • fdisk -H 255 -S 63:指定磁头数 255 和扇区数 63(传统磁盘参数)。
  • 创建:
    • 第一个分区:64MBFAT32,用于存放 zImage.dtb
    • 第二个分区:剩余空间,ext3,用于存放根文件系统。

13. 格式化分区

PARTITION1=${device}1
if [ ! -b ${PARTITION1} ]; then
    PARTITION1=${device}1
fi
PARTITION2=${device}2
if [ ! -b ${PARTITION2} ]; then
    PARTITION2=${device}2
fi
echo "格式化 ${device}1 ..."
if [ -b ${PARTITION1} ]; then
    mkfs.vfat -F 32 -n "boot" ${PARTITION1}
else
    echo "错误: /dev下找不到 SD卡 boot分区"
fi
echo "格式化${device}2 ..."
if [ -b ${PARITION2} ]; then
    mkfs.ext3 -F -L "rootfs" ${PARTITION2}
else
    echo "错误: /dev下找不到 SD卡 rootfs分区"
fi
  • mkfs.vfat -F 32:格式化 p1FAT32,卷标 boot
  • mkfs.ext3 -F:格式化 p2ext3,卷标 rootfs

14. 烧写 U-Boot

echo "正在烧写${Uboot}到${device}"
execute "dd if=$sdkdir/boot/$Uboot of=$device bs=1024 seek=1 conv=fsync"
sync
  • dd if=... of=$device:将 U-Boot 烧写到 SD 卡。
  • seek=1:跳过 1KB,确保正确写入启动位置。

15. 挂载 p1 并复制内核和设备树

execute "mkdir -p /tmp/sdk/$$"
while [ ! -e ${device}1 ]
do
    sleep 1
    echo "wait for ${device}1 appear"
done
execute "mount ${device}1 /tmp/sdk/$$"
execute "cp -r $sdkdir/boot/*${flash}*.dtb /tmp/sdk/$$/"
execute "cp -r $sdkdir/boot/zImage /tmp/sdk/$$/"
sync
  • /tmp/sdk/$$:临时挂载点($$ 是当前进程 ID,防止冲突)。
  • mount:挂载 p1 到临时目录。
  • cp:复制 zImage.dtb 文件。

16. 挂载 p2 并解压文件系统

execute "mkdir -p /tmp/sdk/$$"
execute "mount ${device}2 /tmp/sdk/$$"
echo "正在解压文件系统到${device}2 ,请稍候..."
rootfs=`ls -1 filesystem/*.tar.*`
execute "tar jxfm $rootfs -C /tmp/sdk/$$"
sync
  • tar jxfm:解压 bz2 格式的文件系统到 p2

17. 检查并解压内核模块

if [ ! -e "/tmp/sdk//lib/modules/" ];then
    mkdir -p /tmp/sdk/lib/modules/
fi
echo "正在解压模块到${device}2/lib/modules/ ,请稍候..."
modules=`ls -1 modules/*.tar.*`
execute "tar jxfm $modules -C /tmp/sdk/$$/lib/modules/"
sync
  • 如果 /lib/modules 不存在,则创建。
  • 解压内核模块到该目录。

18. 卸载分区并清理

echo "卸载${device}2"
execute "umount /tmp/sdk/$$"
execute "rm -rf /tmp/sdk/$$"
sync
echo "SD卡启动系统烧写完成!"
  • umount:卸载分区。
  • rm -rf:删除临时目录。
  • sync:确保数据写入完成。

挂载点原理

  1. /dev/sdx:SD 卡设备节点。
  2. /dev/sdx1FAT32 分区,存放 zImage.dtb(内核启动所需)。
  3. /dev/sdx2ext3 分区,存放根文件系统。
  4. /tmp/sdk/$$:临时挂载点,用于操作分区内容。

挂载的作用

  • 挂载后,可以通过文件系统访问设备内容(如 cptar)。
  • 卸载后,确保数据写入完成,避免损坏。

总结

该脚本完成了:

  1. 分区FAT32 + ext3)。
  2. 烧写 U-Boot(到 SD 卡)。
  3. 复制内核和设备树(到 p1)。
  4. 解压文件系统和模块(到 p2)。

适用于 I.MX6 平台的 SD 卡系统烧写。

0 篇笔记 写笔记

关注公众号
取消
感谢您的支持,我会继续努力的!
扫码支持
扫码打赏,你说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

您的支持,是我们前进的动力!