u-boot 和 rootfs/initramfs 启动 Raspberry Pi 4B-程序员宅基地

技术标签: Linux  u-boot  智能驾驶-BSP  

0. 概述

这篇文章的目的是了解嵌入式Linux的四个组成部分 —— 工具链、引导加载程序、内核、根文件系统 —— 通过使用最少的代码从头开始启动 Raspberry Pi 4 的命令。

1. 硬件要求

  • 用于编译源代码的 Linux 桌面计算机。我正在使用 Ubuntu 20.04。
  • 带有电源适配器的 Raspberry 4 型号 b。
  • SD 卡和读卡器。我使用的是 2GB SD 卡。

2. 准备工作

SD 卡将用于存储引导加载程序和根文件。所以,我们首先在其上创建两个分区 - boot(FAT32格式)和 root(ext4格式)。

2.1 查找TF卡设备名

将 SD 读卡器插入 Linux PC 后,从dmesg或mount找到其设备名称。

$ dmesg | tail
[19304.704047] usbcore: registered new interface driver uas
[19305.719653] scsi 33:0:0:0: Direct-Access     Mass     Storage Device   1.00 PQ: 0 ANSI: 0 CCS
[19305.720283] sd 33:0:0:0: Attached scsi generic sg2 type 0
[19305.725987] sd 33:0:0:0: [sdb] 3842048 512-byte logical blocks: (1.97 GB/1.83 GiB)
[19305.728140] sd 33:0:0:0: [sdb] Write Protect is off
[19305.728142] sd 33:0:0:0: [sdb] Mode Sense: 03 00 00 00
[19305.730188] sd 33:0:0:0: [sdb] No Caching mode page found
[19305.730750] sd 33:0:0:0: [sdb] Assuming drive cache: write through
[19305.757769]  sdb: sdb1 sdb2
[19305.788187] sd 33:0:0:0: [sdb] Attached SCSI removable disk

在本例中,设备名称为 sdb,并且它已经有两个分区sdb1和 sdb2。我将删除它们并重新分区 SD 卡。

2.2 删除现有分区

$ sudo fdisk /dev/sdb

Welcome to fdisk (util-linux 2.34).
Changes will remain in memory only, until you decide to write them.
Be careful before using the write command.


Command (m for help): d
Partition number (1,2, default 2):

Partition 2 has been deleted.

Command (m for help): d
Selected partition 1
Partition 1 has been deleted.

Command (m for help): p
Disk /dev/sdb: 1.85 GiB, 1967128576 bytes, 3842048 sectors
Disk model: Storage Device
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0x00000000

Command (m for help): w
The partition table has been altered.
Calling ioctl() to re-read partition table.
Syncing disks.

2.3 添加两个分区

添加一个 100MB 的boot分区和一个剩余大小的root分区。

$ sudo fdisk /dev/sdb

Welcome to fdisk (util-linux 2.34).
Changes will remain in memory only, until you decide to write them.
Be careful before using the write command.


Command (m for help): n
Partition type
   p   primary (0 primary, 0 extended, 4 free)
   e   extended (container for logical partitions)
Select (default p):

Using default response p.
Partition number (1-4, default 1):
First sector (2048-3842047, default 2048):
Last sector, +/-sectors or +/-size{K,M,G,T,P} (2048-3842047, default 3842047): +100M

Created a new partition 1 of type 'Linux' and of size 100 MiB.
Partition #1 contains a vfat signature.

Do you want to remove the signature? [Y]es/[N]o: Y

The signature will be removed by a write command.

Command (m for help): n
Partition type
   p   primary (1 primary, 0 extended, 3 free)
   e   extended (container for logical partitions)
Select (default p):

Using default response p.
Partition number (2-4, default 2):
First sector (206848-3842047, default 206848):
Last sector, +/-sectors or +/-size{K,M,G,T,P} (206848-3842047, default 3842047):

Created a new partition 2 of type 'Linux' and of size 1.8 GiB.
Partition #2 contains a ext4 signature.

Do you want to remove the signature? [Y]es/[N]o: Y

The signature will be removed by a write command.

Command (m for help): t
Partition number (1,2, default 2): 1
Hex code (type L to list all codes): b

Changed type of partition 'Linux' to 'W95 FAT32'.

Command (m for help): p
Disk /dev/sdb: 1.85 GiB, 1967128576 bytes, 3842048 sectors
Disk model: Storage Device
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0x00000000

Device     Boot  Start     End Sectors  Size Id Type
/dev/sdb1         2048  206847  204800  100M  b W95 FAT32
/dev/sdb2       206848 3842047 3635200  1.8G 83 Linux

Command (m for help): w
The partition table has been altered.
Calling ioctl() to re-read partition table.
Syncing disks.

2.4 格式化分区

# FAT32 for boot partition
$ sudo mkfs.vfat -F 32 -n boot /dev/sdb1

# ext4 for root partition
$ sudo mkfs.ext4 -L root /dev/sdb2

2.5 挂载分区

挂载两个分区,以便我们可以写入它们。

$ sudo mount /dev/sdb1 /mnt/boot
$ sudo mount /dev/sdb2 /mnt/root

3. 工具链

首先,我们需要一个工具链来将源代码编译为运行在 树莓派 4。我们构建的工具链包括:

  • 交叉编译器
  • 二进制实用程序,如汇编程序和链接器,以及
  • 一些运行时库

需要交叉编译器,因为我们将编译在 Linux 台式计算机 (X86) 上的 Raspberry Pi 4 (ARM)。

我们可以按照 Linux 中的步骤从头开始构建一个完整的工具链从零开始[2]。但我会走捷径,使用 crosstool-NG。有关 构建工具链的过程,请参阅此处的精彩解释一文

3.1 下载crosstool-NG源码

$ git clone https://github.com/crosstool-ng/crosstool-ng
$ cd crosstool-ng/
# Switch to the latest release
$ git checkout crosstool-ng-1.24.0 -b 1.24.0

3.2 构建和安装crosstool-NG

安装 crosstool-NG 的完整文档可以在这里找到

$ ./bootstrap
$ ./configure --prefix=${PWD}
$ make
$ make install
$ export PATH="${PWD}/bin:${PATH}"

configures是可能会报 not found: libtool,需要额外安装 libtool-bin 解决。

3.3 配置crosstool-NG

在用crosstool-NG于构建工具链之前,我们需要首先对其进行配置。配置器的工作方式与配置 Linux 内核相同。

$ ct-ng menuconfig

还有一些示例配置,我们可以通过ct-ng list-samples命令获取。我们可以使用其中之一,然后使用ct-ng menuconfig。这里 我将不加修改地使用 aarch64-rpi4-linux-gnu。

# Basic information about this config
$ ct-ng show-aarch64-rpi4-linux-gnu
[G...]   aarch64-rpi4-linux-gnu
    Languages       : C,C++
    OS              : linux-4.20.8
    Binutils        : binutils-2.32
    Compiler        : gcc-8.3.0
    C library       : glibc-2.29
    Debug tools     : gdb-8.2.1
    Companion libs  : expat-2.2.6 gettext-0.19.8.1 gmp-6.1.2 isl-0.20 libiconv-1.15 mpc-1.1.0 mpfr-4.0.2 ncurses-6.1 zlib-1.2.11
    Companion tools :

# Use this config
$ ct-ng aarch64-rpi4-linux-gnu

注意:操作系统是 linux-4.20.8,意思是由工具链编译的二进制文件 应该能够在任何内核版本 >= 4.20.8 上运行。

3.4 构建工具链

要构建工具链,只需运行:

$ ct-ng build

注意:在撰写本文时,上述命令在尝试时失败 下载 ISL LIB,因为位置似乎已关闭。一个 可以在此处找到解决方法。isl.gforge.inria.fr

默认情况下,构建的工具链安装在 ~/x-tools/aarch64-rpi4-linux-gnu。

4. 引导加载程序

引导加载程序的工作是将系统设置到基本级别(例如,配置内存控制器以访问DRAM)并加载内核。 通常,启动顺序为:

  1. 存储在芯片上的 ROM 代码运行。它加载辅助程序加载程序 (SPL) 到静态随机存取存储器 (SRAM) 中,不需要存储器 控制器。SPL 可以是完整引导加载程序的精简版本,例如 U-启动。由于SRAM尺寸有限,因此需要它。
  2. SPL 设置内存控制器,以便可以访问 DRAM 并执行 其他一些硬件配置。然后,它将完整的引导加载程序加载到 DRAM。
  3. 然后,完整的引导加载程序加载内核,即扁平化设备树 (FDT) 以及可选的初始 RAM 磁盘 (initramfs) 到 DRAM 中。一旦内核是 加载后,引导加载程序会将控制权移交给它。

4.1 下载u-boot源码

$ git clone git://git.denx.de/u-boot.git
$ cd u-boot
$ git checkout v2021.10 -b v2021.10

4.2 配置u-boot

因为引导加载程序是特定于设备的,所以我们需要在构建它之前对其进行配置。与 crosstool-NG类似,有几个位于configs/下的 sample/default 配置。我们可以在configs/rpi_4_defconfig下 找到一个用于Raspberry Pi 4的。那么我们只需要运行 make rpi_4_defconfig。在此之前,我们还需要设置 CROSS_COMPILE 环境变量。

$ export PATH=${HOME}/x-tools/aarch64-rpi4-linux-gnu/bin/:$PATH
$ export CROSS_COMPILE=aarch64-rpi4-linux-gnu-
$ make rpi_4_defconfig

4.3 构建u-boot

$ make

4.4 安装u-boot

我们只需要将最后一步编译的二进制文件 u-boot.bin 复制到 SD 卡上的 boot 分区中即可。

$ sudo cp u-boot.bin /mnt/boot

注意:Raspberry Pi 有自己专有的引导加载程序,由 ROM代码,并且能够加载内核。但是,既然我想 使用开源,我需要配置树莓派启动 loader 加载,然后让内核加载。u-bootu-bootu-boot

从4B的官方镜像中拷贝出 bootcode.bin, start4.elf 和 fixup4.dat 到 /mnt/boot 中。

再手动写一份 config.txt:

# Let Raspberry Pi 4 bootloader load u-boot
$ cat << EOF > config.txt
enable_uart=1
arm_64bit=1
kernel=u-boot.bin
EOF
$ sudo mv config.txt /mnt/boot/

介绍下rpi4的启动过程,其启动分区采用的 fat32 fs,并采用三级启动方式:

  1. 板载VideoCore GPU启动固化在rpi4中的ROM,该阶段非常简单,主要支持读取TF卡中的fat32 fs的第2级启动程序;
  2. 板载VideoCore GPU加载并执行启动分区(/boot)中的bootcode.bin,该文件的主要功能是解析elf格式文件,再加载并解析同目录下的start4.elf;
  3. 运行start4.elf,读取并解析config.txt的配置文件,再加载并执行真正的u-boot程序。

5. 内核

接下来,我们编译 Linux 内核。

5.1 下载内核源码

虽然原来的 Linux 内核应该可以工作,但使用 Raspberry Pi 的分支 更稳定。另请注意,内核版本必须高于 为工具链配置的内核版本。

$ git clone --depth=1 -b rpi-5.10.y https://github.com/raspberrypi/linux.git
$ cd linux

5.2 配置和构建内核

我们只使用 Raspberry Pi 4 的默认配置。有关 Raspberry Pi 4 型号 b 规格,请参阅此处

$ make ARCH=arm64 CROSS_COMPILE=aarch64-rpi4-linux-gnu- bcm2711_defconfig
$ make -j$(nproc) ARCH=arm64 CROSS_COMPILE=aarch64-rpi4-linux-gnu-

5.3 安装内核和设备树

现在我们将内核映像和设备树二进制文件(*.dtb)复制到SD卡上的boot分区中。

$ sudo cp arch/arm64/boot/Image /mnt/boot
$ sudo cp arch/arm64/boot/dts/broadcom/bcm2711-rpi-4-b.dtb /mnt/boot/

6. 根文件系统

请参阅文件系统层次结构标准配置,更多 有关 Linux 系统基本目录布局的详细信息。

6.1 创建目录

$ mkdir rootfs
$ cd rootfs
$ mkdir {bin,dev,etc,home,lib64,proc,sbin,sys,tmp,usr,var}
$ mkdir usr/{bin,lib,sbin}
$ mkdir var/log

# Create a symbolink lib pointing to lib64
$ ln -s lib64 lib

$ tree -d
.
├── bin
├── dev
├── etc
├── home
├── lib -> lib64
├── lib64
├── proc
├── sbin
├── sys
├── tmp
├── usr
│   ├── bin
│   ├── lib
│   └── sbin
└── var
    └── log

16 directories

# Change the owner of the directories to be root
# Because current user doesn't exist on target device
$ sudo chown -R root:root *

6.2 构建和安装Busybox

我们将 Busybox 用于基本的 Linux 实用程序,例如 shell。所以,我们需要 将其安装到刚刚创建的rootfs目录中。

# Download the source code
$ wget https://busybox.net/downloads/busybox-1.33.2.tar.bz2
$ tar xf busybox-1.33.2.tar.bz2
$ cd busybox-1.33.2/

# Config
$ CROSS_COMPILE=${HOME}/x-tools/aarch64-rpi4-linux-gnu/bin/aarch64-rpi4-linux-gnu-
$ make CROSS_COMPILE="$CROSS_COMPILE" defconfig
# Change the install directory to be the one just created
$ sed -i 's%^CONFIG_PREFIX=.*$%CONFIG_PREFIX="/home/hechaol/rootfs"%' .config

# Build
$ make CROSS_COMPILE="$CROSS_COMPILE"

# Install
# Use sudo because the directory is now owned by root
$ sudo make CROSS_COMPILE="$CROSS_COMPILE" install

6.3 安装所需的库

接下来,我们安装一些 Busybox 需要的共享库。我们可以找到那些库:

$ readelf -a ~/rootfs/bin/busybox | grep -E "(program interpreter)|(Shared library)"
      [Requesting program interpreter: /lib/ld-linux-aarch64.so.1]
 0x0000000000000001 (NEEDED)             Shared library: [libm.so.6]
 0x0000000000000001 (NEEDED)             Shared library: [libresolv.so.2]
 0x0000000000000001 (NEEDED)             Shared library: [libc.so.6]

我们需要将这些文件从工具链的sysroot目录复制到rootfs/lib目录。

$ export SYSROOT=$(aarch64-rpi4-linux-gnu-gcc -print-sysroot)
$ sudo cp -L ${SYSROOT}/lib64/{ld-linux-aarch64.so.1,libm.so.6,libresolv.so.2,libc.so.6} ~/rootfs/lib64/

6.4 创建设备节点

Busybox 需要两个设备节点。

$ cd ~/rootfs
$ sudo mknod -m 666 dev/null c 1 3
$ sudo mknod -m 600 dev/console c 5 1

7. 启动开发板

最后,准备好所有组件后,我们就可以启动电路板了。有两个根文件系统的选项。我们可以将其用作 initramfs 它可以在以后挂载一个真正的根文件系统或将其用作永久根文件系统。

7.1 选项 1:使用 initramfs 引导

什么时候是 initramfs 需要?根据 Linux From Scratch [2],只有四个主要 在 LFS 环境中使用 initramfs 的原因:

  • 从网络加载 rootfs。
  • 从 LVM 逻辑卷加载它。
  • 有一个加密的rootfs,其中需要密码。
  • 为了方便将 rootfs 指定为 LABEL 或 UUID。

除了使用 initramfs,我们还可以将根文件系统直接放入 SD 卡上的分区中。在这种情况下,我们需要配置 内核命令行从引导加载程序传递到内核。root

7.1.1 构建 initramfs

initramfs 是一个压缩的存档,它是一个旧的 Unix 存档 格式类似于 和 。cpiotarzip

$ cd ~/rootfs
$ find . | cpio -H newc -ov --owner root:root -F ../initramfs.cpio
$ cd ..
$ gzip initramfs.cpio
$ ~/u-boot/tools/mkimage -A arm64 -O linux -T ramdisk -d initramfs.cpio.gz uRamdisk

# Copy the initramffs to boot partition
$ sudo cp uRamdisk /mnt/boot/
7.1.2 配置u-boot

我们需要配置 u-boot,以便它可以通过正确的内核 命令行和设备树二进制到内核。为简单起见,我将使用 Busybox shell 作为init程序。在现实生活中,如果使用 initramfs,那么 init 程序应负责挂载永久根目录文件系统。

$ cat << EOF > boot_cmd.txt
fatload mmc 0:1 \${kernel_addr_r} Image
fatload mmc 0:1 \${ramdisk_addr_r} uRamdisk
setenv bootargs "console=serial0,115200 console=tty1 rdinit=/bin/sh"
booti \${kernel_addr_r} \${ramdisk_addr_r} \${fdt_addr}
EOF
$ ~/u-boot/tools/mkimage -A arm64 -O linux -T script -C none -d boot_cmd.txt boot.scr

# Copy the compiled boot script to boot partition
$ sudo cp boot.scr /mnt/boot/

引导命令的含义:

  • 将内核映像从分区1( boot partition) 加载到内存中。
  • 将 initramfs 从分区1( boot partition) 加载到内存中。
  • 设置内核命令行。
  • 使用给定的内核、设备树二进制文件和 initramfs 启动。

注意:在最后一行中,最后一个参数 fdt_addr 与其他两个参数不同。起初,我使用 fdt_addr 无法启动开发板。发现这个后我意识到了错误 在树莓上发帖 Raspberry Pi论坛。此外,根据其中一个回复,当前的 U-boot 已经从固件继承 DTB,将其地址放入 {fdt_addr}。所以我们不需要在 U-Boot 中加载 dtb 文件。

7.1.3 启动它!

最后,所有四个组件都准备就绪。我们现在可以尝试启动它。靴子 分区现在包含以下文件:

$ tree /mnt/boot/
/mnt/boot/
├── bcm2711-rpi-4-b.dtb
├── bootcode.bin
├── boot.scr
├── config.txt
├── Image
├── start4.elf
├── uRamdisk
└── u-boot.bin

0 directories, 7 files

现在我们卸载分区并将 SD 卡插入 Raspberry Pi 4。

$ sudo umount /dev/sdb1
$ sudo umount /dev/sdb2

启动 Raspberry Pi 4 后,如果成功,我们应该会得到一个Busybox shell。

7.2 选项 2:直接使用永久 rootfs 引导

或者,我们可以在 root 分区作为根文件系统的情况下直接启动。为此,请按照以下步骤操作。

7.2.1 将rootfs复制到SD卡上的root分区

将 SD 卡插入读卡器,然后将读卡器插入 Linux 桌面。

$ sudo mount /dev/sdb1 /mnt/boot
$ sudo mount /dev/sdb2 /mnt/root
$ cp -r ~/rootfs/* /mnt/root/

7.2.2 更改引导命令

我们不再需要 initramfs。

$ cat << EOF > boot_cmd.txt
fatload mmc 0:1 \${kernel_addr_r} Image
setenv bootargs "console=serial0,115200 console=tty1 root=/dev/mmcblk0p2 rw rootwait init=/bin/sh"
booti \${kernel_addr_r} - \${fdt_addr}
EOF
$ ~/u-boot/tools/mkimage -A arm64 -O linux -T script -C none -d boot_cmd.txt boot.scr
$ sudo cp boot.scr /mnt/boot/

# Remove the initramfs as it's not needed
$ sudo rm -f /mnt/boot/uRamdisk

7.2.3 启动它!

现在我们卸载分区并将 SD 卡插入 Raspberry Pi 4。

$ sudo umount /dev/sdb1
$ sudo umount /dev/sdb2

启动 Raspberry Pi 4 后,如果出现以下情况,我们应该会得到一个 shell 成功的。与上面的 -only 情况不同,在这种情况下,无论什么 我们对根文件系统所做的更改将被保留。Busyboxinitramfs。

如下是我的 rootfs 方式下的 U-Boot 串口启动日志:

欢迎使用 minicom 2.8

选项: I18n 
通信端口 /dev/ttyUSB0, 14:09:08

按 CTRL-A Z 说明特殊键 



U-Boot 2024.04-rc4 (Mar 14 2024 - 11:38:29 +0800)

DRAM:  948 MiB (effective 7.9 GiB)
RPI 4 Model B (0xd03115)
Core:  211 devices, 16 uclasses, devicetree: board
MMC:   mmcnr@7e300000: 1, mmc@7e340000: 0
Loading Environment from FAT... Unable to read "uboot.env" from mmc0:1... 
In:    serial,usbkbd
Out:   serial,vidconsole
Err:   serial,vidconsole
Net:   eth0: ethernet@7d580000
PCIe BRCM: link up, 5.0 Gbps x1 (SSC)
starting USB...
Bus xhci_pci: Register 5000420 NbrPorts 5                                       
Starting the controller                                                         
USB XHCI 1.00                                                                   
scanning bus xhci_pci for devices... 3 USB Device(s) found                      
       scanning usb for storage devices... 0 Storage Device(s) found            
Hit any key to stop autoboot:  0                                                
Card did not respond to voltage select! : -110                                  
** Booting bootflow '[email protected]_1' with script                   
25463296 bytes read in 1227 ms (19.8 MiB/s)                                     
Moving Image from 0x80000 to 0x200000, end=1b60000                              
## Flattened Device Tree blob at 2eff2500                                       
   Booting using the fdt blob at 0x2eff2500                                     
Working FDT set to 2eff2500                                                     
   Using Device Tree in place at 000000002eff2500, end 000000002f002fa2         
Working FDT set to 2eff2500                                                     
                                                                                
Starting kernel ... 

资源

[1] 掌握嵌入式 Linux 编程 - 第三版
[2] Linux 从零开始
[3] 如何构建工具链

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/yiyu20180729/article/details/137565754

智能推荐

分布式光纤传感器的全球与中国市场2022-2028年:技术、参与者、趋势、市场规模及占有率研究报告_预计2026年中国分布式传感器市场规模有多大-程序员宅基地

文章浏览阅读3.2k次。本文研究全球与中国市场分布式光纤传感器的发展现状及未来发展趋势,分别从生产和消费的角度分析分布式光纤传感器的主要生产地区、主要消费地区以及主要的生产商。重点分析全球与中国市场的主要厂商产品特点、产品规格、不同规格产品的价格、产量、产值及全球和中国市场主要生产商的市场份额。主要生产商包括:FISO TechnologiesBrugg KabelSensor HighwayOmnisensAFL GlobalQinetiQ GroupLockheed MartinOSENSA Innovati_预计2026年中国分布式传感器市场规模有多大

07_08 常用组合逻辑电路结构——为IC设计的延时估计铺垫_基4布斯算法代码-程序员宅基地

文章浏览阅读1.1k次,点赞2次,收藏12次。常用组合逻辑电路结构——为IC设计的延时估计铺垫学习目的:估计模块间的delay,确保写的代码的timing 综合能给到多少HZ,以满足需求!_基4布斯算法代码

OpenAI Manager助手(基于SpringBoot和Vue)_chatgpt网页版-程序员宅基地

文章浏览阅读3.3k次,点赞3次,收藏5次。OpenAI Manager助手(基于SpringBoot和Vue)_chatgpt网页版

关于美国计算机奥赛USACO,你想知道的都在这_usaco可以多次提交吗-程序员宅基地

文章浏览阅读2.2k次。USACO自1992年举办,到目前为止已经举办了27届,目的是为了帮助美国信息学国家队选拔IOI的队员,目前逐渐发展为全球热门的线上赛事,成为美国大学申请条件下,含金量相当高的官方竞赛。USACO的比赛成绩可以助力计算机专业留学,越来越多的学生进入了康奈尔,麻省理工,普林斯顿,哈佛和耶鲁等大学,这些同学的共同点是他们都参加了美国计算机科学竞赛(USACO),并且取得过非常好的成绩。适合参赛人群USACO适合国内在读学生有意向申请美国大学的或者想锻炼自己编程能力的同学,高三学生也可以参加12月的第_usaco可以多次提交吗

MySQL存储过程和自定义函数_mysql自定义函数和存储过程-程序员宅基地

文章浏览阅读394次。1.1 存储程序1.2 创建存储过程1.3 创建自定义函数1.3.1 示例1.4 自定义函数和存储过程的区别1.5 变量的使用1.6 定义条件和处理程序1.6.1 定义条件1.6.1.1 示例1.6.2 定义处理程序1.6.2.1 示例1.7 光标的使用1.7.1 声明光标1.7.2 打开光标1.7.3 使用光标1.7.4 关闭光标1.8 流程控制的使用1.8.1 IF语句1.8.2 CASE语句1.8.3 LOOP语句1.8.4 LEAVE语句1.8.5 ITERATE语句1.8.6 REPEAT语句。_mysql自定义函数和存储过程

半导体基础知识与PN结_本征半导体电流为0-程序员宅基地

文章浏览阅读188次。半导体二极管——集成电路最小组成单元。_本征半导体电流为0

随便推点

【Unity3d Shader】水面和岩浆效果_unity 岩浆shader-程序员宅基地

文章浏览阅读2.8k次,点赞3次,收藏18次。游戏水面特效实现方式太多。咱们这边介绍的是一最简单的UV动画(无顶点位移),整个mesh由4个顶点构成。实现了水面效果(左图),不动代码稍微修改下参数和贴图可以实现岩浆效果(右图)。有要思路是1,uv按时间去做正弦波移动2,在1的基础上加个凹凸图混合uv3,在1、2的基础上加个水流方向4,加上对雾效的支持,如没必要请自行删除雾效代码(把包含fog的几行代码删除)S..._unity 岩浆shader

广义线性模型——Logistic回归模型(1)_广义线性回归模型-程序员宅基地

文章浏览阅读5k次。广义线性模型是线性模型的扩展,它通过连接函数建立响应变量的数学期望值与线性组合的预测变量之间的关系。广义线性模型拟合的形式为:其中g(μY)是条件均值的函数(称为连接函数)。另外,你可放松Y为正态分布的假设,改为Y 服从指数分布族中的一种分布即可。设定好连接函数和概率分布后,便可以通过最大似然估计的多次迭代推导出各参数值。在大部分情况下,线性模型就可以通过一系列连续型或类别型预测变量来预测正态分布的响应变量的工作。但是,有时候我们要进行非正态因变量的分析,例如:(1)类别型.._广义线性回归模型

HTML+CSS大作业 环境网页设计与实现(垃圾分类) web前端开发技术 web课程设计 网页规划与设计_垃圾分类网页设计目标怎么写-程序员宅基地

文章浏览阅读69次。环境保护、 保护地球、 校园环保、垃圾分类、绿色家园、等网站的设计与制作。 总结了一些学生网页制作的经验:一般的网页需要融入以下知识点:div+css布局、浮动、定位、高级css、表格、表单及验证、js轮播图、音频 视频 Flash的应用、ul li、下拉导航栏、鼠标划过效果等知识点,网页的风格主题也很全面:如爱好、风景、校园、美食、动漫、游戏、咖啡、音乐、家乡、电影、名人、商城以及个人主页等主题,学生、新手可参考下方页面的布局和设计和HTML源码(有用点赞△) 一套A+的网_垃圾分类网页设计目标怎么写

C# .Net 发布后,把dll全部放在一个文件夹中,让软件目录更整洁_.net dll 全局目录-程序员宅基地

文章浏览阅读614次,点赞7次,收藏11次。之前找到一个修改 exe 中 DLL地址 的方法, 不太好使,虽然能正确启动, 但无法改变 exe 的工作目录,这就影响了.Net 中很多获取 exe 执行目录来拼接的地址 ( 相对路径 ),比如 wwwroot 和 代码中相对目录还有一些复制到目录的普通文件 等等,它们的地址都会指向原来 exe 的目录, 而不是自定义的 “lib” 目录,根本原因就是没有修改 exe 的工作目录这次来搞一个启动程序,把 .net 的所有东西都放在一个文件夹,在文件夹同级的目录制作一个 exe._.net dll 全局目录

BRIEF特征点描述算法_breif description calculation 特征点-程序员宅基地

文章浏览阅读1.5k次。本文为转载,原博客地址:http://blog.csdn.net/hujingshuang/article/details/46910259简介 BRIEF是2010年的一篇名为《BRIEF:Binary Robust Independent Elementary Features》的文章中提出,BRIEF是对已检测到的特征点进行描述,它是一种二进制编码的描述子,摈弃了利用区域灰度..._breif description calculation 特征点

房屋租赁管理系统的设计和实现,SpringBoot计算机毕业设计论文_基于spring boot的房屋租赁系统论文-程序员宅基地

文章浏览阅读4.1k次,点赞21次,收藏79次。本文是《基于SpringBoot的房屋租赁管理系统》的配套原创说明文档,可以给应届毕业生提供格式撰写参考,也可以给开发类似系统的朋友们提供功能业务设计思路。_基于spring boot的房屋租赁系统论文