@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 模块安装正常, 设备能识别
操作流程:
后续操作, 重新上电后,正常启动原先的系统