@liangdi 在 [Happy Hacking Nezha Board] 小孩子才做选择,我全都要 BOOT 中说:
下载
更快的下载地址
@liangdi 在 [Happy Hacking Nezha Board] 掌握 Device Tree Oerlay 的魔法 中说:
dtboverlay=pwm7-overlay
需要改为
dtoverlay=pwm7-overlay
Device Tree 是目前嵌入式 Linux 系统最常用的设备解耦工具, 所以要玩转嵌入式 Linux , 这个东西必须掌握.
在 tina sdk 代码中, 有 board.dts , sun20iw1p1.dtsi 这些文件, 这些就是 device tree 的源文件,或者说描述文件.
dts 通过 dtc 这个编译器可以编译成 dtb 以及后面我们要用到的 dtbo(dtb overlay) , 它们是二进制文件, Linux 和 uboot 可以使用.
Device Tree Compiler , dts 的编译工具, Linux 下面可以使用包管理工具按照
# redora
sudo dnf install dtc
# ubuntu
sudo apt install device-tree-compiler
windows 参考(未验证):
https://github.com/lbmeng/dtc
在之前给出的 demo 镜像中, pwm7 这个设备的驱动部分在 dts 中被注释了, 所以我们尝试用 dtb overlay 给他弄回来.
dtbo 的编译只要 dtc 就可以, 但是我们会看到 dtsi 中有 c 语言的 #include 宏,所以还会用到 c编译根据展开宏,如果没有 #include ,就不需要.
使用 #include 宏展开示例, include 里面都是 dt-bindings 目录下的文件,该目录在 tina 中的位置是: lichee/linux-5.4/include , 你要展开需要将 lichee/linux-5.4/include/dt-bindings 拷贝到 dts 目录中来
cpp -nostdinc -I. -undef -x assembler-with-cpp board.dts > board-with-include.dts
执行后, board-with-include.dts 就是 include 展开后的代码.
pwm7-overlay.dts 示例代码
/dts-v1/;
/plugin/;
/ {
fragment@0 {
target-path = "/soc/pinctrl@2000000";
__overlay__ {
pwm7_pin_a: pwm7@0 {
pins = "PD22";
function = "pwm7";
drive-strength = <10>;
bias-pull-up;
};
pwm7_pin_b: pwm7@1 {
pins = "PD22";
function = "gpio_in";
};
};
};
fragment@1 {
target-path = "/soc/pwm7@2000c17";
__overlay__ {
pinctrl-names = "active", "sleep";
pinctrl-0 = <&pwm7_pin_a>;
pinctrl-1 = <&pwm7_pin_b>;
status = "okay";
};
};
};
头部是作为一个 overlay 的描述, 和 dts 相比多了 /plugin/; 这个节点
/dts-v1/;
/plugin/;
后面同样有个 / 根节点, 然后需要覆盖的地方用 fragment@x 作为节点名称
fragment 里面 target-path 为需要覆盖的节点路径, 也可以用 target 使用的是引用.
后面 overlay 里面就是需要覆盖的属性.
dtc -I dts -O dtb -o ./pwm7-overlay.dtbo ./pwm7-overlay.dts
这样得到的 pwm7-overlay.dtbo 文件就是我们所要的. 可以用
fdtdump ./pwm7-overlay.dtbo 查看结果
一个测试程序 (pwm.sh, 来自 RVBoards) 和蜂鸣器扩展版
#!/bin/bash
#pwm 节点
pwmnode=pwm$1
#输出提示pwm$1 通道
echo /sys/class/pwm/pwmchip0/${pwmnode}
#申请pwm$1 通道
echo $1 > /sys/class/pwm/pwmchip0/export
#指定pwm$1 通道的频率
echo $2 > /sys/class/pwm/pwmchip0/${pwmnode}/period
#指定pwm$1 通道的占空比
echo $3 > /sys/class/pwm/pwmchip0/${pwmnode}/duty_cycle
#使能pwm$1 通道
echo 1 > /sys/class/pwm/pwmchip0/${pwmnode}/enable
#使能pwm$1 通道 工作时间10s
sleep 10
#失能pwm$1 通道
echo 0 > /sys/class/pwm/pwmchip0/${pwmnode}/enable
#等待pwm$1 通道 关闭
sleep 1
#注销pwm$1 通道
echo $1 > /sys/class/pwm/pwmchip0/unexport
在没有配置 dtb overlay 的系统中执行
# 设置 7 号 pwm 引脚数据
./pwm.sh 7 300000 150000
会出现错误
然后将 pwm7-overlay.dtbo 复制到 boot 分区的 overlay 目录下, 在编辑 config.txt 添加配置
dtboverlay=pwm7-overlay , 配置中不需要加 .dtbo, 系统会自动添加.
重启后, 在执行
./pwm.sh 7 300000 150000 , 蜂鸣器就会响了.
如果没有蜂鸣器扩展版,可以查看 /sys/class/pwm/ 里面的内容,进行前后对比,也会发现 dtb overlay 生效了.
dtb 以及相关技术需要去掌握,掌握了就可以把开发板玩的更溜了, 如果你去使用各种大的扩展板, 基本都会涉及到 dtb overlay.
前面完成引导 rt-smart 后, 开始继续其他功能的研究.
常规我们使用 raspberry pi 以及其他 Linux 系统的时候, 一般我们的 kernel 等信息都是放在 /boot 目录下的,大多数会格式化成独立的一个分区.
那么我们就照着这个方向去改造哪吒板子, 目前已经初见成效了, 我做了一个 demo 的 image , 供大家测试, 后续 RVBoards 会发布正式的 Debian 版本.
镜像前面几个 1-3 分区是全志的预留的几个分区,占用空间很间,这里不去动他.
第 4 分区是 vfat 格式的 boot 分区, kernel 和 dtbo 等文件存放在这里面
demo 镜像 boot 分区文件说明
overlay : 存放 dtb overlay 文件boot_debian.img debian 内核boot_tina.img tina 内核config.txt 引导配置文件rt-smart rt-smart 执行程序config.txt 配置说明
配置示例
# mode
# 0 boot bare metal bin
# 1 boot linux kernel
mode=1
bin=rt-smart
kernel=boot_debian.img
# dtb overlay
# load overlay/${dtoverlay}.dtbo
dtoverlay=test-overlay
# uboot vars
# for debian
mmc_root=/dev/mmcblk0p6
# for tina
#mmc_root=/dev/mmcblk0p5
详细说明
mode:
配置引导模式 0 为引导二进制程序 1 为引导linux 内核 (目前这个版本的 内核文件需要使用 mkbootimage 打包生成,就是 tina sdk 中 pack 命令生成的 boot.img 文化)
bin
mode=0 的时候引导的文件
kernel
mode=1 的时候引导的文件
dtoverlay
dtb overlay 配置, 将会加载 overlay/${dtoverlay}.dtbo 这个文件,后续将会支持多个文件加载
mmc_root
这个是作为 bootargs 中 root 参数传递给内核的,告诉内核 root 在什么分区,默认是 /dev/mmcblk0p5 , demo 镜像中有多个内核,多个 rootfs ,所以需要配置一下.
注意
主要就是利用 uboot 的 fatload 这个指令,从 mmc 中 vfat 文件系统中加载指定文件到内存中使用.
用到相关的指令 fatload, env import ,fdt 等
核心配置
# demo 镜像中的
bootcmd=run boot_check setargs_mmc boot_mmc
# 其中 setargs_mmc 是全志默认的,设置 mmc 加载的 bootrags 的指令
# boot_check 检测 mmc 是否启用,然后加载 config.txt 文件,再加载 dtbo 文件
boot_check=run card_init;mmcinfo;mmc part;fatload mmc ${mmc_dev}:${mmc_boot_part} 47000000 config.txt;env import -t 47000000 ${filesize}; test -n "$dtoverlay" && fatload mmc ${mmc_dev}:${mmc_boot_part} 48000000 overlay/${dtoverlay}.dtbo; fdt apply 48000000
# boot_mmc 就是根据 mode 引导不同系统了
boot_mmc=if test ${mode} -eq 0; then fatload mmc ${mmc_dev}:${mmc_boot_part} 45000000 ${bin}; go 45000000; else fatload mmc ${mmc_dev}:${mmc_boot_part} 45000000 ${kernel}; bootm 45000000; fi
原理和实现其实很简单, 后续还可以继续改进,支持多个 dtbo 加载, tftp 加载(方便快速调试) 等等.
完成多系统引导就这么简单了, 后续文章我会再写一个 dtb overlay 的 demo.
注意事项: demo 镜像中, debain 的 rootfs 大小太小,更大空间,需要自行处理一下.
@whycan https://gitlab.eduxiji.net/lizhirui/project325618-83178.git
用 riscv64-unknown-elf 的工具链就可以
每一个程序员都有一个 OS 梦!
- Liangdi
我属于提前批拿到哪吒开发板的,兴奋之余开始研究如何去运行自己的裸机程序,美其名曰:操作系统.
和 mcu 不一样, sbc 级别的 cpu 跑起来要复杂的多,不过好在系统级别的领域,不同的软件分工明确, 我们的裸机程序作为 kernel 部分,等着被引导就好.
尽管 sbc 的系统很复杂, 不过要跑起我们的小小的代码,我们刚开始关心的东西不必要很多.
走出第一步,才能看到后面的广阔天空.
由于没有自己的 OS , 这里用 rt-thread 的 rt-smart 来作为实验验证对象.
和我们接触的第一个对象就是 uboot , uboot 是哪吒开发板的 bootloader,所以我们要和他搞好关系,了解他,才能让他帮我们完成 kernel 的引导.
哪吒开发板的引导路径大致是这样: BROOM -> spl -> uboot -> [nand | mmc]
通过简单的把玩,发现以下规律, BROOM 中的一级 bootloader 会检测 mmc 和 nand 设备, 如果存在 mmc 设备就会 load mmc boot 分区中的 spl 继续工作, nand 同理.
开发板上有 256MB 的 nand flash, 可以有足够的空间存放我们的程序了, 所以就不考虑 mmc 了.
开发板,接上串口工具,上电,串口中就可以看到系统启动信息了, 如果你什么都不操作就会进入 tina 环境了, 所以开机的时候,连按 s 键盘(和 PC 开机按 F2或者 F10 一样吧) 就可以进入 uboot 环境
如下界面:
先用 mtdparts 查看 nand 信息
mdtparts default
mtdparts
# 输出如下:
device nand0 <nand>, # parts = 4
#: name size offset mask_flags
0: boot0 0x00100000 0x00000000 1
1: uboot 0x00300000 0x00100000 1
2: secure_storage 0x00100000 0x00400000 1
3: sys 0x0fb00000 0x00500000 0
active partition: nand0,0 - (boot0) 0x00100000 @ 0x00000000
defaults:
mtdids : nand0=nand
mtdparts: mtdparts=nand:1024k@0(boot0)ro,3072k@1048576(uboot)ro,1024k@4194304(secure_storage)ro,-(sys)
从上面可以看到, nand 有四个分区, 前面两个 bootloader , 第三 secure_storage 和我们也没有什么关系, 第四个分区 sys 就是保存用户 os 的地方, 目前就是 tina linux 系统.
查看一下 sys 中的信息
ubi part sys
ubi info l
# 输出如下:
Volume information dump:
vol_id 0
reserved_pebs 1
alignment 1
data_pad 0
vol_type 4
name_len 3
usable_leb_size 258048
used_ebs 1
used_bytes 258048
last_eb_bytes 258048
corrupted 0
upd_marker 0
name mbr
Volume information dump:
vol_id 1
reserved_pebs 1
alignment 1
data_pad 0
vol_type 3
name_len 13
usable_leb_size 258048
used_ebs 1
used_bytes 258048
last_eb_bytes 258048
corrupted 0
upd_marker 0
name boot-resource
Volume information dump:
vol_id 2
reserved_pebs 1
alignment 1
data_pad 0
vol_type 3
name_len 3
usable_leb_size 258048
used_ebs 1
used_bytes 258048
last_eb_bytes 258048
corrupted 0
upd_marker 0
name env
Volume information dump:
vol_id 3
reserved_pebs 1
alignment 1
data_pad 0
vol_type 3
name_len 10
usable_leb_size 258048
used_ebs 1
used_bytes 258048
last_eb_bytes 258048
corrupted 0
upd_marker 0
name env-redund
Volume information dump:
vol_id 4
reserved_pebs 29
alignment 1
data_pad 0
vol_type 3
name_len 4
usable_leb_size 258048
used_ebs 29
used_bytes 7483392
last_eb_bytes 258048
corrupted 0
upd_marker 0
name boot
...
我们看到了一些熟悉的信息,系统镜像的分区表, 就是 tina sdk 打包出来的产物.
那么 uboot 如何引导 nand 中的系统的呢?
使用 printenv 查看一下 uboot 的环境变量,下面列出重要的部分:
boot_normal=sunxi_flash read 45000000 ${boot_partition};bootm 45000000
boot_partition=boot
bootcmd=run setargs_nand_ubi boot_normal
partitions=mbr@ubi0_0:boot-resource@ubi0_1:env@ubi0_2:env-redund@ubi0_3:boot@ubi0_4:rootfs@ubi0_5:dsp0@ubi0_6:recovery@ubi0_7:UDISK@ubi0_8:
root_partition=rootfs
setargs_nand_ubi=setenv bootargs ubi.mtd=${mtd_name} ubi.block=0,${root_partition} earlyprintk=${earlyprintk} clk_ignore_unused initcall_debug=${initcall_debug} console=${console} loglevel=${loglevel} root=${nand_root} rootfstype=${rootfstype} init=${init} partitions=${partitions} cma=${cma} snum=${snum} mac_addr=${mac} wifi_mac=${wifi_mac} bt_mac=${bt_mac} specialstr=${specialstr} gpt=1
ubi_attach_mtdnum=3
就以上这几行就可以了, 对我们来说关键作用的只有前 3 行.
bootcmd 这个是 uboot 启动时候执行的变量, 内容是 run setargs_nand_ubi 和 boot_normal
其中 setargs_nand_ubi 是设置 bootargs 的, 是 Linux 关心的东西.
我们主要看 boot_normal
boot_normal 大致含义是 flash 工具读取 ${boot_partition}(解析后是 boot) 位置的数据到内存 0x45000000 的位置, 然后 bootm 引导 0x45000000 位置的内核.
所以,简单的方法就是我们把我们自己的 OS 程序,写入到 nand 中 boot 分区的位置,理论上就可以了.
起初本来想用 xboot 的 xfel 工具将数据写入 nand, 然后发现没有实现,所以先跳过, 等后续支持了就会更方便了.
tina sdk 中 device/config/chips/d1/configs/nezha_min/sys_partition.fex 这个文件是 pack 的配置信息 , 根据文件知道 pack 命令会把 boot.img 打包到 nand 的 boot 分区, 这个就是我们所要的,所以最简单的方法就是把我们自己的 bin 文件替换调 boot.img , 然后 tina sdk 中执行pack ,生成的产物tina_d1-nezha_min_uart0.img中就包含了我们的代码了.
然后用全志的工具,将 tina_d1-nezha_min_uart0.img 烧录到哪吒主板上.第一步就完成了.
这样就可以正常引导了么? 答案是否定的.
在前面 uboot 的引导指令用的是 bootm 45000000, bootm 是引导 linux kernel 的,包含了引导协议的一些东西, 我们作为一个裸机程序,我们可以使用 uboot 的 go 命令之间跳转到 0x45000000处运行, 将 boot_normal 改为 sunxi_flash read 45000000 ${boot_partition};go 45000000 即可, 但是目前 tina 默认的 uboot 没有编译 go 指令, 进入 lichee/brandy-2.0/u-boot-2018 目录, 执行 make menuconfig, 然后在 Command line interface --> Boot commands 中选中 go 指令,保存后,重新编译, 在打包一次就可以了.
tina uboot 默认的环境变量信息在文件 device/config/chips/d1/configs/nezha/env.cfg 里面,可以将 boot_normal 改好后再编译,就不用在 uboot 交互界面中修改环境变量了.

bingo!
少年, 下一步就开始在哪吒上运行你的 Dreeam OS 吧!
最后的补充注意事项: RISC-V 芯片运行在 SBI 环境, S Mode 下,所以如果裸机程序 M 模式的代码是无法正常运行的.
d1 的 tina 环境由于工具链比较老旧, 很多开发者的机器上的环境没法正常使用
所以,我写一篇使用 Docker 编译 tina 的文章,继续抛砖引玉,欢迎交流.
PS: 还是希望全志官方升级一下工具链版本
docker info 可以查看是否安装正常# 假设下载的 tina-sdk 目录是 /opt/tina-sdk
# 执行如下 docker 命令
docker run -v /opt/tina-sdk:/sdk -it liangdi/d1-tina-build-env /bin/bash
# 第一次执行的时候,需要下载镜像,会耗时比较久一点
# 执行完上面 docker 命令后, 会将本机 /opt/tina-sdk 映射到 docker 中的 /sdk 下,就可以在 docker 中编译 tina 了
cd /sdk
source ./build/envsetup.sh
# 选择编译内容
lunch
# 需要设置 FORCE_UNSAFE_CONFIGURE 变量解决 root 检测, 后面 -jx x为配置编译线程数量,可以根据 cpu 数量来配置
make FORCE_UNSAFE_CONFIGURE=1 -j8
# 备注: 第一次编译,会编译一批 host 的依赖比较慢,但是编译一次后,下次重新编译就会跳过了,速度会快很多
# 打包
pack
以上就可以正常编译 d1 的 tina 了, 编译完成后, 在本机 tina-sdk 目录下的 out 目录中就可以看到编译产物了, 采用路径映射的方式,本机更新代码后,再运行 docker , docker 里面就是最新的代码
# D1 tina build env
FROM ubuntu:14.04
RUN apt update
RUN apt install build-essential subversion git-core libncurses5-dev zlib1g-dev gawk flex quilt libssl-dev xsltproc libxml-parser-perl mercurial bzr ecj cvs unzip lib32z1 lib32z1-dev lib32stdc++6 libstdc++6 -y
RUN apt install bc busybox wget -y
可以自行修改构建, 完善这个 docker
单板机,上手比较烦人的就是要准备配套的鼠标键盘以及显示器,通过 SSH 或者 VNC 就可以在自己电脑上远程进行操作,更加方便.
系统默认配置禁用了 root 远程 ssh 登陆, 如果是普通权限用户没有这个问题.
开启 root ssh 远程登陆
编辑 /etc/ssh/sshd_config
将
#PermitRootLogin without-password修改为PermitRootLogin yes
systemctl restart sshd重启 ssh 服务即可
ssh 访问
使用
ssh root@ip就可以登陆访问了,默认密码是rvboards
使用
ssh-copy-id root@ip可以设置公钥访问,省掉密码输入
Linux 上有很多 vnc 服务程序,这里我们选择 tigervnc
apt update
apt install tigervnc-standalone-server -y
tigervnc server 常规使用方法
启动服务:
vncserver -localhost no -display :1
上述命令启动 vncserver 并且使用 :1 编号的显示器, :0 默认被启动的 xserver使用了, -localhost no 表示可以远程访问
第一次启用的时候会提示输入密码, 建议使用和 root 一样的密码,便于记忆, 同时可以配置使用 linux 系统认证, 这个哪吒玩家可以自己去查看相关资料.
查看服务:
vncserver -list
TigerVNC server sessions:
X DISPLAY # RFB PORT # PROCESS ID SERVER
:1 5901 647 Xtigervnc
停止服务
vncserver -kill
vncserver -kill :1
# 结束 :1 display 的 vnc 服务
配置分辨率, 使用
-geometry 1280x800参数
目前哪吒支持的分辨率
1920x1080 60.00
1600x1200 60.00
1680x1050 60.00
1400x1050 60.00
1360x768 60.00
1280x1024 60.00
1280x960 60.00
1280x720 60.00
1024x768 60.00
800x600 60.00
640x480 60.00
配置 VNC server 开机启动
开机启动最简单的方式是在 /etc/rc.local 中加入启动脚本,以下是示例
echo "start vnc server"
export HOME=/root
/usr/bin/vncserver -localhost no -display :1 -geometry 1280x800
echo "vnc server started"
# 这里需要先配置 HOME 环境变量, vncserver 需要
VNC 远程连接
VNC 有很多客户端, ReadVNC 的 VNC Viewer 推荐一下,并且有 Chrome 的插件, 输入ip和端口号就可以连接了,密码就是初次启动 vncserver 配置的密码


linux 生态下, 远程访问是比较容易的, SBC 级别的设备,大多比较精简,需要自己去安装配置,借此文抛砖引玉,欢迎一起交流.
吐槽一下目前系统层面对 D1 的显示驱动优化的比较差, 性能弱,使用 VNC 操作 gui 大大提升用户体验.
CanSat- - 易拉罐卫星,顾名思义,就是卫星的结构壳体是用最普通的易拉罐尺寸大小,卫星部组件和载荷全部按照相关标准放入易拉罐尺寸大小中,成为一颗具备卫星基本功能和易拉罐外形的Cansat。虽然看起来只是一个易拉罐,其实是真实卫星的模拟。

这一次,我们将使用全志在线D1开发板【哪吒】作为整个卫星的控制中枢。
开发板主控使用的是搭载阿里平头哥 C906 核心的RISC-V芯片——D1, 这是全球首颗量产的 RISC-V架构AIoT处理器。
我们的计划代号【风火轮】,因为开发板的代号正好是【哪吒】,寓意风火轮送哪吒上天!


这是一枚真正的火箭, 可以升空到1万米高度的探空火箭, 它将把我们的哪吒开发板送到2-3千米的高度。

在这个暑假,让我们相约海南文昌!
本次设计的卫星,最终将在海南文昌完成发射和回收任务.
时间:在2021年7月下旬,视天气决定

我叫吴亮弟, 来自一个叫 404 的工作室, 我们从事于产品研发和K12科创教育的工作.
感兴趣的朋友可以联系我们:
网站: http://404.net.cn
Email: wu@liangdi.me
“你承诺给我殖民火星,我却得到 Facebook!”——人类登月的第二人 Buzz Aldrin 曾经这样抱怨说。现在,要衷于我们儿时的梦想,奔向太空!只有天空,才是我们的极限!
本次活动致力于推广 RISC-V 在科创教育领域的应用, 推动 PBL 教育在该领域的发展, 所有设计和研发成果将在社区开源。
需要的 ,除非你只用汇编, bootloader 是个逻辑上的概念, 需要这部分程序对内存,io设备进行初始化,然后调用你的主程序(可以是系统kernel, 也可以是你自己写的简单的hello world)
@kunyao D1se-evb1 1080P解码 4K HDMI视频输出demo 固件.img 这个固件,我帕是中文问题,改了名字
系统: Fedora 33 , 5.11.17-200.fc33.x86_64
主板来源: RVBoards 已经刷 Debian
LiveSuit : http://linux-sunxi.org/LiveSuit 模块安装正常, 设备能识别
操作流程:

后续操作, 重新上电后,正常启动原先的系统