您尚未登录。

#1 Re: 技术人生/软件使用技巧/破解经验/技术吐槽/灌水 » 【开源项目】兼容部分Zlib接口的轻量压缩/解压库:muzic » 2022-07-20 18:53:29

aozima 说:

相比zlib,程序体积,压缩率,性能 这块主要差异是啥?

体积和内存占用对比数据已经更新到了GitHub仓库的readme上。在最小的示例程序上二进制体积节省1倍,峰值内存占用节省6倍以上。

性能和压缩率还没有测试。这个测起来会比较复杂,和压缩数据的特性、Zlib采用的压缩比率都会有关系;不同压缩比率下的内存占用又肯定不一样了。以后有时间补上尽量完善的对比吧。

#2 技术人生/软件使用技巧/破解经验/技术吐槽/灌水 » 【开源项目】兼容部分Zlib接口的轻量压缩/解压库:muzic » 2022-07-19 18:33:05

SdtElectronics
回复: 2

今年非常有幸地入选为Google summer of code 2022活动的贡献者。我参与的项目是将一个RPC库移植到开源RTOS Zephyr上。这个库依赖Zlib来压缩和解压RPC消息,但是Zlib的实现对于嵌入式平台来说太重了,因此我需要找到一个替代的轻量级库。

Zlib产生的压缩格式是遵从RFC-1950 ZLIB或者RFC-1951 DEFLATE标准的(RFC-1950比RFC-1951格式多一层封装),所以只要是能压缩和解压DEFLATE标准数据的库都可以用——我一开始是这么想的。但DEFLATE这个格式另有玄机:它支持流式操作,也就是说用于通信的时,发送方可以边压缩边发送,接收方可以边接受边解压,而不必等到一帧数据全部压缩完两边再开始通信。虽然Github上支持DEFLATE压缩和解压的库已经有很多了,但支持流式操作的却很少,并且各自都使用的是不同的API。

因为是移植工作,我希望尽量不要改变上游库的原有代码,同时这个库还需要流式操作的支持,我就对Github上原有的两个DEFLATE压缩和解压库进行封装和改写,从而暴露出和Zlib部分兼容的API。我想在移植其他的库或者程序到嵌入式平台的时候,Zlib也是一个很常见的依赖,而这个库允许开发者在几乎不改动原库的前提下把Zlib替换成一个轻量实现,因此应该还会有更多人用到,故为它建立了开源仓库:
https://github.com/SdtElectronics/muzic

#4 Re: 全志 SOC » 带WiFi的【开源D1s开发板】来了,Xassette-Asterisk升级完成 » 2022-03-09 15:43:04

shxyke 说:

我打算自制一个,看到你发的图里两个晶振的匹配电容都没有焊接,请问是不需要吗?

实验结果来看不焊也能正常启动,可能会导致时钟轻微偏快

#6 Qt/MSVC/MINGW/C++/MFC/GTK+/Delphi/BCB » 简便地追踪C++中任意类对象的复制,移动和析构 » 2022-02-06 18:47:17

SdtElectronics
回复: 1

对象生命周期管理是C++里最麻烦的事之一,虽然在C++11引入智能指针之后资源的回收变得简单了很多,但并不是所有时候我们都想把对象丢到堆上;除了对象的析构以外,我们也还有其他希望知道的事,比如在预期对象会被移动的地方是否真的发生了移动(而不是拷贝),或者一些编译器能把拷贝优化掉(e.g. RVO)的地方,是否真的发生了优化。对于我们自己定义的类,可以通过给构造函数、析构函数加个断点或者打印来追踪对象的生命周期,但是对于依赖库的类,尤其是标准库容器,这种侵入式的做法就没那么容易实施了。我最近想到了一个小trick可以跟踪任意类对象的生命周期:利用C++里被继承的类也可以被模板化的特性,写一个定义了构造函数和析构函数的装饰器类,这样被装饰器包装的类型可以直接替代原来的类型,同时用户可以给构造函数和析构函数附加自定义的行为。一个小demo展示一下效果:
HKkhp8.md.png

我把这个过程抽象成了一个header-only的小工具,代码可以在这里下载到:
https://github.com/SdtElectronics/ACI/tree/master/Logoo

用法:比如希望追踪std::string实例的生命周期,只用把原来的std::string换成Logoo<std::string>:

// std::string str{"a"};
Logoo<std::string> str{"a"};

更多细节可以参考上面的链接,仓库里面提供了两个例子,也可以参考我写的这篇博客,里面详细讲述了这个工具是怎么被静态装饰器实现的,以及怎么自定义装饰器的行为:
Track Lifetime of Objects in C++

#7 Re: DIY/综合/Arduino/写字机/3D打印机/智能小车/平衡车/四轴飞行/MQTT/物联网 » 简单、操作友好的面包板/通用多电压电源,基于PD诱骗IC CH224K » 2022-01-19 21:35:05

codetez 说:

请教大佬,看到有些诱骗IC除了电压档,还能设置PD输出的电流档;

这个CH224K只看到有电压档,那么电流是按默认5A输出的吗?

支持限流的应该只有PD3.0 PPS,CH224不支持PPS协议。另外PD2.0只有20V这一档的最大电流是5A,其余是3A。

#8 Re: 全志 SOC » 带WiFi的【开源D1s开发板】来了,Xassette-Asterisk升级完成 » 2021-12-14 11:49:54

@XIVN1987
非常感谢这位坛友的指正,之前一直没有测试过使用PD电源供电,昨天实测后发现使用C-C线连接PD电源时确实存在问题:只有特定的一面朝上时才能被识别。好在不算大的毛病,已经打了板的坛友遇到这种情况直接把线翻个面插即可。修正过的原理图和PCB也已经在GitHub更新了。

#9 Re: 全志 SOC » 带WiFi的【开源D1s开发板】来了,Xassette-Asterisk升级完成 » 2021-12-13 11:30:51

mine_3216 说:

直接用楼主文件打板学习,可以不?

可以,两版PCB设计都经过验证了。板子的Gerber也发在了release里:
https://github.com/SdtElectronics/Xassette-Asterisk/releases/tag/hw-v0.2
焊接前请仔细阅读readme以及原理图上的notes

#10 Re: 全志 SOC » 带WiFi的【开源D1s开发板】来了,Xassette-Asterisk升级完成 » 2021-12-12 10:17:52

sy373466062 说:

@SdtElectronics

考虑做一个网口的扩展板吗?

已经新建文件夹了(

#11 Re: 全志 SOC » 带WiFi的【开源D1s开发板】来了,Xassette-Asterisk升级完成 » 2021-12-11 11:12:11

touchey 说:

@SdtElectronics
太及时了,镜像直接使用tina吗?

使用BSP编译时要支持sdc2作为sdio接口(也就是在本版上启用WiFi)需改三个地方:
板级设备树把sdc2启用(status 设为 okay),去掉no-sdio属性
SoC设备树去掉sdc2的no-sdio属性
env.conf中把setargs_mmc变量改为setenv  bootargs earlyprintk=${earlyprintk} clk_ignore_unused initcall_debug=${initcall_debug} console=${console} loglevel=${loglevel} root=/dev/mmcblk1p5 init=${init} partitions=${partitions} cma=${cma} snum=${snum} mac_addr=${mac} wifi_mac=${wifi_mac} bt_mac=${bt_mac} specialstr=${specialstr} gpt=1

可以直接dd进sd卡的镜像已经发布在了GitHub上,设备树已经配置好并且带编译好的WiFi驱动module(8189ftv),地址:
https://github.com/SdtElectronics/Xassette-Asterisk/releases/tag/fw-v0.2

通过

insmod 8189fs

即可加载驱动,正常的话能看到一长串打印(如1楼图2)之后就可以愉快玩耍了

#12 全志 SOC » 带WiFi的【开源D1s开发板】来了,Xassette-Asterisk升级完成 » 2021-12-09 21:31:37

SdtElectronics
回复: 21

第一版开源D1s开发板Xassette-Asterisk自公开已经过去一个多月了,在此期间,这个项目在国内外爱好者和科技媒体间颇引起了一些关注,我也非常高兴地看到有数位来自国内外的爱好者成功进行了仿制,充分印证了这个设计很好地达到了易自制的目标。为了不负大家的关注与期待,我在几周前开始了第一版基础上的改进版的设计与验证工作,现在终于可以把成果公开:
o4ZMhd.jpg
这一版的主要改进有:

  • 增加了WiFi模块的焊盘,使用sdc2接口。(*注)

  • 增加了FEL按钮

  • 增加了USB和3.5mm音频接口的ESD保护

  • 优化部分走线

*因板子面积紧张,未板载天线,尽管焊盘与多数sdio WiFi模块兼容,建议使用搭载ipex接口的模块,如RTL8189FTV。

RTL8189FTV驱动检测到模块:
o4nW4g.png

目前第二版的设计已经更新到GitHub仓库:
https://github.com/SdtElectronics/Xassette-Asterisk

tina BSP需要修改设备树才可以把sdc2作为SDIO使用,目前我编译的固件遇到了一点小问题(Linux把mmc识别成了mmcblk1,修改bootarg不生效,需要在u-boot中手动修改env变量才能正常boot)。后面会继续更新固件的支持。

#13 Re: 全志 SOC » 适合自制的全开源D1s/F133开发板_Open Source EVB For RISC-V SoC D1s/F133 » 2021-12-07 19:14:07

mfkiwl 说:
SdtElectronics 说:
mfkiwl 说:

支持一个,这个非常适合DIY.
KICAD哪个版本设计的?

5.1.6。坐等6 stable这个月发布

收到. DVP摄像头接口测试了么

暂时没条件测试(手上没DVP摄像头)

#15 Re: 全志 SOC » 适合自制的全开源D1s/F133开发板_Open Source EVB For RISC-V SoC D1s/F133 » 2021-12-04 10:31:16

mfkiwl 说:

支持一个,这个非常适合DIY.
KICAD哪个版本设计的?

5.1.6。坐等6 stable这个月发布

#16 技术人生/软件使用技巧/破解经验/技术吐槽/灌水 » 【带你设计开发板】今晚七点半,在蘑菇云直播间讲述一款开源D1s开发板的故事 » 2021-12-02 09:29:00

SdtElectronics
回复: 5

oYdye1.png

10月底的时候我开源了一个自己的D1s开发板设计,当时在论坛的贴子没有太多关注,但是在国外开源硬件圈却小火了一段时间,有hackster.io,RISC-V Open Hours等平台进行了转载。后来国内的创客平台蘑菇云创客空间找到了我,邀请我在星期四晚上的开放夜分享一下这块开发板的故事。我倍感荣幸,精心准备了从开发板介绍,到开发板设计和制作的过程和经验等内容:

oYdhSe.jpg

除此之外,该项目的第一次重要升级也会在分享中披露(能找到图中的WiFi模块吗)

oYwZ6J.png

如果你也想自己制作,或者自己设计一块D1s的开发板,这次分享的内容说不定能帮你跳过一些天坑哦(


直播间链接:
https://live.bilibili.com/h5/21864541

#17 Re: 技术人生/软件使用技巧/破解经验/技术吐槽/灌水 » 一键生成可定制的Debian rootfs,支持riscv64 » 2021-11-14 19:14:26

tigger 说:

强大!

对 qemu-user-static 版本有要求吗? 有些低版本的Ubuntu没有 riscv。

没有专门对多个qemu版本进行测试,但已知Ubuntu16.04源中的qemu在模拟aarch64平台的时候有bug,部分程序运行时会报非法指令然后退出。最好用高一点的版本。

#18 Re: 技术人生/软件使用技巧/破解经验/技术吐槽/灌水 » 一键生成可定制的Debian rootfs,支持riscv64 » 2021-11-13 17:21:26

一些进阶用法:
更改预装包:
修改multistrap.conf中的packages字段即可。注意要生成riscv64的rootfs,需要修改的是multistrap_debian-ports.conf。
更改版本:
修改multistrap.conf中的suite字段即可。合法的值可以是版本名,或者是stable, testing和sid。注意要生成riscv64的rootfs,需要修改的是multistrap_debian-ports.conf。
异构chroot:
异构chroot所需的qemu等依赖,脚本都已经自动装好,直接chroot ./ 即可。

#19 技术人生/软件使用技巧/破解经验/技术吐槽/灌水 » 一键生成可定制的Debian rootfs,支持riscv64 » 2021-11-13 17:13:18

SdtElectronics
回复: 7

论坛之前已经有一个使用debootstrap生成Debian rootfs的教程了,但我一直使用的是一个Github上的自动生成脚本来生成,优势主要是:

  • 更方便,跑一个脚本生成直接可用的rootfs,无需更多设置

  • 包括版本和预装包都完全可定制

  • 生成的rootfs可以直接chroot进去,所需的依赖自动安装

最近给D1s定制rootfs,为了支持riscv64,我对原作者的脚本进行了一定的魔改,在此分享一下使用方法:

首先clone仓库到本地:

git clone https://github.com/SdtElectronics/debian-rootfs.git

然后安装相关依赖

apt-get install multistrap binfmt-support qemu-user-static

最后运行一条命令即可生成:

./make-rootfs.sh ARCHITECTURE

支持的ARCHITECTURE有:

amd64
arm64
armel
armhf
i386
mips
mipsel
powerpc
powerpcspe
ppc64el
s390x
riscv64

注意要是想生成riscv64的rootfs,需要改用如下命令(因为risc-v目前还在Debian port源下):

sudo ./make-rootfs.sh riscv64 multistrap_debian-ports.conf

生成的rootfs在build/目录下。
若脚本运行时出现类似错误信息:

The following signatures couldn't be verified because the public key is not available

需要手动给multistrap的文件打上这个补丁

更多信息,可以参考原仓库:
https://github.com/SdtElectronics/debian-rootfs

#20 Re: Openwrt/LEDE/AR9331/MT7688/RT5350 » 给某宝15的MT7628模块画了个底板,双网口+USB+SD+37个IO » 2021-11-13 16:54:00

统一回复:这么香的车肯定早就开走了,不过还是欢迎大家加群,避免错过下次发车(XD

#21 Re: 全志 SOC » D1s/F133目前的一些玩法:跑Ubuntu rootfs,显示图片,播放音频 » 2021-11-07 22:01:09

最后晒一张自己板子的靓照,第一张板子上的芯片被磨掉了丝印,这次拍个帅气点的XD
I3VyAP.jpg

#22 Re: 全志 SOC » D1s/F133目前的一些玩法:跑Ubuntu rootfs,显示图片,播放音频 » 2021-11-07 21:48:28

播放音频:
其实要是只想知道板子的音频输出正不正常的话,tina开关机的时候是有提示音可以验证的。tina也内置了音频播放器:

aplay PATH_TO_WAV

这玩意好像支支持wav的声音文件,给MP3的话只能播出噪声。

#23 Re: 全志 SOC » D1s/F133目前的一些玩法:跑Ubuntu rootfs,显示图片,播放音频 » 2021-11-07 21:43:54

显示图片:
播放视频很多人都演示过了,我这就不重复了,利用tina自带的tplayerdemo就可以。这里主要说一下如何显示图片,其实tina也是有内置的demo的:

dfbshow PATH_TO_IMAGE

PATH_TO_IMAGE换成图片路径即可,目前试了bmp和jpg都支持。效果:
dfbshow

#24 全志 SOC » D1s/F133目前的一些玩法:跑Ubuntu rootfs,显示图片,播放音频 » 2021-11-07 21:39:07

SdtElectronics
回复: 8

非常感谢晕哥在我开源自己的D1s PCB设计之后慷慨赠送的芯片,让我在第一时间玩上了这颗rv64 SoC。目前D1的主线适配工作还在进行,D1和D1s暂且可以通过全志的tina Linux来体验和开发。Tina的资料比较少,但不影响我们探索有趣的玩法。运行各种软件是很基础的需求,而有一个包管理器可以免去构建和安装软件包的诸多麻烦。目前据我所知支持rv64软件源的主流发行版只有Debian和Ubuntu。首先就来通过chroot在D1s上启动一个带apt的Ubuntu环境!

Ubuntu提供了预构建好的rootfs tarball, 出于D1s的资源考虑这里选择最小的Ubuntu base作为演示。也可以使用debootstrap定制自己的rootfs。
rootfs 官方下载链接
之后在烧好tina镜像的卡上新建一个ext4分区,然后把下载好的rootfs解压到此分区。
插卡启动后,挂载该分区,我这里是mmcblk0p8。

mkdir /mnt/sdd
mount -t ext4 /dev/mmcblk0p8 /mnt/sdd
chroot /mnt/sdd/

chroot之后就在Ubuntu环境里了:
I3AnEV.png
不知道怎么回事,chroot之后有些转义字符被打印出来了。现在假设有网络连接的话已经可以愉快地装包了~

#25 Re: 全志 SOC » 适合自制的全开源D1s/F133开发板_Open Source EVB For RISC-V SoC D1s/F133 » 2021-11-04 21:26:10

可以点LCD了
ImtBbd.jpg
这板子公开之后在本坛不温不火,倒是在英文社区有了点热度,不过大多数人还是更关心能否买到成品。本坛里已经开始量产的板子估计以后要大卖😂

#27 全志 SOC » 适合自制的全开源D1s/F133开发板_Open Source EVB For RISC-V SoC D1s/F133 » 2021-10-28 10:03:17

SdtElectronics
回复: 20

For whom the first language is English: Don't bother go through the rest of the post. The repository below has an English README ad contains all necessary info.

https://github.com/SdtElectronics/Xassette-Asterisk

先上仓库链接 ↑ 目前包含kicad格式原理图和PCB。后续会上传原理图图片并再release中发布Gerber。
5bXp2F.jpg
5bXQrd.jpg
板子的功能不算丰富,主要是为自制优化的:

  • 能塞得下的元件都尽量塞在了正面

  • 所有密脚的元件都在正面

  • 最小的贴片元件封装为0603/SOD-323

  • 全部采用最常规的物料

  • 56*56mm双面板,可以在最小的LED加热台上焊接

另外,板子还配备了

  • TF卡,nand flash

  • 3.5mm音频输出和输入

  • 标准40pin RGB LCD接口、24pin DVP接口(待验证)

  • IO和外设全部引出,包括板载SD、Flash的IO以及模拟外设

  • IO电源选择和Boot跳线

目前板子已经成功启动了Tina Linux,其他外设的功能尚待进一步验证。

#28 全志 SOC » 使用全志官方平台下载sdk等资料(D1s SDK) » 2021-10-18 16:44:17

SdtElectronics
回复: 1

折腾D1的时候发现全志官方提供了tina sdk的下载方式,这个平台还提供像R329等其他一些SoC的sdk的下载。官方给出的下载教程页面:
https://d1.docs.aw-ol.com/study/study_2getsdk/
写得可以说是相当简略了,我操作过程中还遇到一些小坑,记录一下补充原来的教程。

添加公钥

这个公钥是用来后续ssh登录git时认证用的,名称可以乱填,但公钥不能乱填,不然之后拉取仓库时认证会不通过。已经生成过ssh密钥的可以直接复制~/.ssh/id_rsa.pub中的密钥,还没有生成过的(也即~/.ssh/下没有id_rsa.pub这个文件的)要先通过

ssh-keygen -o

生成密钥,然后复制~/.ssh/id_rsa.pub中的全部内容,到全志那个添加密钥的界面里密钥那个框中。

安装repo引导脚本

这一步应该没啥问题,按着上面那个链接里给的步骤来就行了。遇到mkdir或者chmod权限不够的sudo一下,不多提。

下载代码

按上面那个链接里给的步骤,运行以下命令

$ repo init -u ssh://xxxxxx@sdk.allwinnertech.com/git_repo/D1_Tina_Open/manifest.git -b master -m tina-d1-open.xml 
#xxxxxx替换为您的全志客户服务平台账号
$ repo sync 
$ repo start product-smartx-d1-tina-v1.0-release --all # 全部下载完成之后,创建分支

报错,提示

warning: Python 3 support is currently experimental. YMMV.
Please use Python 2.6 - 2.7 instead.

应该是repo这个脚本只能用python2运行,没装python2的去装一个,正常发行版应该都装了。
手动指定python的版本来运行上面的命令:

$ python2 $PATH_TO_REPO$ init -u ssh://xxxxxx@sdk.allwinnertech.com/git_repo/D1_Tina_Open/manifest.git -b master -m tina-d1-open.xml 
#xxxxxx替换为您的全志客户服务平台账号
$ python2 $PATH_TO_REPO$ sync 
$ python2 $PATH_TO_REPO$ start product-smartx-d1-tina-v1.0-release --all # 全部下载完成之后,创建分支

其中$PATH_TO_REPO$替换成之前clone下来的repo脚本的路径,比如/mnt/sda/downloads/repo/repo
之后应该就能愉快地下载了

#29 Re: Openwrt/LEDE/AR9331/MT7688/RT5350 » 给某宝15的MT7628模块画了个底板,双网口+USB+SD+37个IO » 2021-08-19 16:03:12

关于固件:
板子上的固件是基于Linux2.6的,非常老而且(意料之中地)命令很少。不过自带的u-boot可以直接用来刷固件,开机按2就行了。但是这板子又没有把复位脚引出来,串口转USB一上电就开始启动了,因此不想挑战手速的话比较舒适的办法是先进原来的系统,reboot,然后按住2。注意原来系统的u-boot和Linux console都是57600bps的。之后就可以按这篇文章来烧固件。注意最好不要在u-boot里面输固件的文件名,会很蛋疼,直接把要烧的固件重命名成fw.bin更方便。
然后是固件本身,其实本来这个u-boot可以烧大多数openwrt releases里面7628的sysupgrade固件,但是前面说了,核心板没把UART0引出来,console是走UART1的,而大多数固件的console是UART0。所以随便选一个烧完你会发现Linux boot到一半串口没打印了,如果那个固件碰巧还没默认配置网口的话,板子就变成了半块砖。后来我找到一个console走UART1的固件刷了进去。
其实自己编译固件,然后在设备树里把bootargs改了应该也是可以的,但是我clone了widora的固件改完设备树之后没效果,尽管log上显示切换到ttyS1了打印还是中断了Orz。直接用主线的源码应该不会这样,我还没试过。
openwrt固件启动:
fbM0SS.png
对了,主线的固件默认波特率是115200,而自带的u-boot前面说了是57600的,所以直接刷releases里的固件启动过程总有一部分会是乱码的……
最后一点就是,据说每个flash里都保存了SoC的出厂无限校准数据,虽然按上面说的方法刷机并不会覆盖这个数据,但以防万一最好还是备份一下,方法可以参照我的GitHub仓库里的教程或者这篇文章

#30 Openwrt/LEDE/AR9331/MT7688/RT5350 » 给某宝15的MT7628模块画了个底板,双网口+USB+SD+37个IO » 2021-08-19 15:40:04

SdtElectronics
回复: 7

main
7628an不少人都很熟悉了不多说,最近在某宝上看到一个这颗片子的核心板模块卖的很便宜,还不小心把资料搞到了,就给他画了个底板
fbeLHP.jpg
按顺序引出了模块全部的IO,共37个,不过模块并没有把SoC的IO全部引出,尤其是UART0没引出来,这点非常坑,后面会再提到。板载USB母座,两个百兆口,SD自弹座以及USB转串口。网口带状态灯。
fbmrUf.jpg
盈盈一握,身形小巧

项目仓库,PCB Gerber和文档都在里面:
https://github.com/SdtElectronics/Xassette-IoT
QQ交流群:
1164615798

#31 Re: 全志 SOC » 主线内核上的下一代GPIO API简介及其C++包装库的一点使用经验 » 2021-06-20 16:17:24

关于中断
libgpiod的一个特色是可以简便地在用户空间使用外部中断,具体的实现机制我并不清楚,但应该和sysfs的polling有很大区别,因为申请用作中断的引脚必须是有硬件中断支持的。尝试为没有连接到中断控制器的引脚请求中断的话会抛出异常std::system_error:
what():  error requesting GPIO lines: Unknown error 517
和多数中断注册API一样,libgpiod可以监视三种中断:上升沿,下降沿和双边沿。以监视PH00上的下降沿为例:

PH00.request({"", gpiod::line_request::EVENT_FALLING_EDGE, 0});

之后就可以通过gpiod::line::event_wait()来等待中断了,这个调用会一直阻塞到中断发生为止或超时才返回,参数是一个const std::chrono::nanoseconds&,正是设置超时时间的,例如等待PH00上的中断,1小时后超时:

PH00.event_wait(std::chrono::hours(1));

假设一个下降沿在PH00上产生,上一个调用返回了。这时再调用一次line::event_wait(),这个调用不会等到下一个下降沿来临,而是会直接返回,就像单片机上的中断标志位没被清除一样。这是因为上一次产生中断的事件没有被读取,调用一次line::event_read就可以清除了:

PH00.event_read();

另外line::event_read返回上次中断的事件类型,这样监视双边沿的时候可以利用它来获知到底产生中断的是什么边沿。
line::event_read只能清除一个中断事件,如果此次event_wait之前产生了多个中断,调用还是会立即返回。line::event_read_multiple则可以一次读取、清除所有此前的中断事件。注意这两个read调用前没有产生中断的话都会被阻塞到中断产生。
最后一点就是get_value不止可以用在被配置为DIRECTION_INPUT的line上,对于配置为输出和中断的line也是能用的。这个可以用来判断中断之后io的电平,来实现消抖之类的功能。

#32 Re: 全志 SOC » 主线内核上的下一代GPIO API简介及其C++包装库的一点使用经验 » 2021-06-20 15:55:09

libgpiod C++ binding 概述

libgpiod C++ binding,以下简称libgpiod,主要提供了三个对象来实现对IO的操作,分别是

  • gpiod::chip代表一个GPIO芯片,对应/dev下的一个gpiochipN文件

  • gpiod::line代表一个GPIO引脚

  • gpiod::line_bulk代表多个GPIO引脚,开头说的同时操作多个IO就是依此实现的

操作一个IO的步骤是:
1,使用gpiod::chip构造函数实例化一个chip对象,参数是character device的文件名,比如gpiochip0:

gpiod::chip chip("gpiochip0");

2,使用gpiod::chip::get_line或者get_lines来获得一个line或者line_bulk对象
对于gpiod::chip::get_line,参数是那个引脚对应的偏移量,比如主线内核里一个port一般是32个脚,PH00就是224:

gpiod::line PH00 = chip.get_line(224);

对于gpiod::chip::get_lines,参数是包含那一组引脚对应的偏移量的一个std::vector<unsigned int>,利用C++11列表初始化可以很方便地写在一行:

gpiod::line_bulk PH00_01 = get_lines(std::vector<unsigned int>{224, 225});

需要注意的是到这一步line和line_bulk还不能用,如果直接调用其上的方法会抛出一个std::system_error异常:
what():  error setting GPIO line values: Operation not permitted
必须首先通过gpiod::line::request或者gpiod::line_bulk::request来配置这个引脚的方向和偏置(上下拉或开漏/开源),参数是一个gpiod::line_request结构体。这个结构体有三个成员,分别是consumer名字的string,io的用途(输入或输出或中断),以及一个标志位的bitset,用于配置极性,输入上下拉或者输出开漏或开源,利用C++11列表初始化也可以很方便地写在一行:

PH00.request({"", gpiod::line_request::DIRECTION_OUTPUT, 0})

其中io的用途是一个匿名枚举,可以是以下的值:

		DIRECTION_AS_IS = 1,
		/**< Request for values, don't change the direction. */
		DIRECTION_INPUT,
		/**< Request for reading line values. */
		DIRECTION_OUTPUT,
		/**< Request for driving the GPIO lines. */
		EVENT_FALLING_EDGE,
		/**< Listen for falling edge events. */
		EVENT_RISING_EDGE,
		/**< Listen for rising edge events. */
		EVENT_BOTH_EDGES,
		/**< Listen for all types of events. */

每个值的含义配合名字和注释应该很容易理解,不赘述了,多提一句,需要使用这个匿名枚举的类型名的时候怎么办?可以利用C++11的decltype给他指派一个名字:

using reqType = decltype(glReq::EVENT_BOTH_EDGES);

标志位可以是以下几个预定义的值,配合名字和注释也很容易理解,不赘述:

FLAG_ACTIVE_LOW;
/**< Set the active state to 'low' (high is the default). */
FLAG_OPEN_SOURCE;
/**< The line is an open-source port. */
FLAG_OPEN_DRAIN;
/**< The line is an open-drain port. */
FLAG_BIAS_DISABLED;
/**< The line has neither pull-up nor pull-down resistor enabled. */
FLAG_BIAS_PULL_DOWN;
/**< The line has a configurable pull-down resistor enabled. */
FLAG_BIAS_PULL_UP;
/**< The line has a configurable pull-up resistor enabled. */

操作line的函数主要就是get_value()读取和set_value(int val)设置,line_bulk对应的的get_values()和set_values(const ::std::vector<int>& values)就是把返回值和参数换成vector,更多的成员函数可以在libgpiod头文件的源码里查阅,不多说。
已经request过的line/lines要作他用时必须先调用release(),否则会抛出std::system_error异常:
what():  error requesting GPIO lines: Device or resource busy
最后需要注意的是,编译的时候需带上选项 -lgpiodcxx 以链接对应的库。当然,编译平台上还必须安装了libgpiod的包。

#33 全志 SOC » 主线内核上的下一代GPIO API简介及其C++包装库的一点使用经验 » 2021-06-20 15:07:50

SdtElectronics
回复: 2

Linux上利用sysfs接口操作GPIO大家都已不陌生,但在Linux 4.8以后这个API就已经deprecated了,其后继者是GPIO character device API,暴露到用户空间的设备文件位置由/sys/class/gpio变动到/dev/gpiochipN(虽然只要编译内核时没有取消sysfs GPIO接口的支持,sysfs API仍然可以用)。新接口的设备文件没法像之前的sysfs接口一样,通过直接对data和direction等文件的读写来简便地操作IO,但相比sysfs接口增加了数个重大的功能支持:

  • 为设置IO的上下拉提供了统一的界面。此前虽然部分厂家的BSP内核通过私有的界面实现了sysfs中对IO上下拉的配置,但主线内核里是没有支持的

  • 提供了用户空间中断的原生支持。此前如果想利用sysfs实现类似中断的效果,只能利用select/poll来模拟

  • 可以在一个系统调用的时间内同时读取/设置多个IO的值。此前操作多个IO必须分别对每个IO对应的设备文件进行read/write,相比之下新的接口不但节省了开销,更可以对多个IO电平变化的时机进行精确的同步

新接口对于IO的操作主要是通过作用在设备文件上的一系列ioctl实现的,比较底层而不方便使用,一个建立在其上的封装库libgpiod应运而生。这个库本身是纯C的,不过提供了对于python和C++两种语言的封装,官方称其为python/C++ binding。由于纯C的界面我并没有直接使用过,下文主要介绍一下C++ binding的使用方法。libgpiod项目本身还在开发中,文档并不完善,而且文档采用的是和kernel一致的嵌入在代码注释里的风格,有不少地方的描述欠详,一些要点只能依靠上下文和源码猜测,如果有错误之处敬请指出。

#34 Re: 技术人生/软件使用技巧/破解经验/技术吐槽/灌水 » 一种通过debugfs获取sys_config(FEX)内容的方法 » 2021-06-10 18:37:57

Yogo 说:

我手上有十几块全志A20的板子 ZK-A201AD200全志A20.pdf
可以提供给你两块 把折腾的过程记录下来就可以了 冒昧了

谢谢你的好意,但我近期较忙没空弄这些了,过段时间再说吧

#35 Re: 技术人生/软件使用技巧/破解经验/技术吐槽/灌水 » 捡垃圾上瘾,35元双SATA口海思方案nvr拆解,破密码,固件dump » 2021-05-20 15:39:23

Blueskull 说:

Winbond SOP16难道不是ROM?

看了一下还真是,W25Q256。之前没遇到过这种封装的flash,加上离电池比较近,我看都没看就当他是RTC了

#36 Re: 技术人生/软件使用技巧/破解经验/技术吐槽/灌水 » 捡垃圾上瘾,35元双SATA口海思方案nvr拆解,破密码,固件dump » 2021-05-20 15:37:06

最后提一下这颗比较迷的主控。objdump了一下固件里的bin,可以确认是armv7的ISA,也就是说它一定不是它标记的型号,HI3515。但是它和系统检测出的型号HI3520又不能完全对上,首先HI3520的手册显示它并没有配备HDMI输出,然后前文中提到的似乎被集成到了片内的flash亦没有被提及。诡异的是HI3520在手册中的封装是BGA,但网上查到的HI3520图片却几乎都是lqfp封装的,而且部分还有HDMI输出。难道是我搜索手册时的型号不对?

#37 Re: 技术人生/软件使用技巧/破解经验/技术吐槽/灌水 » 捡垃圾上瘾,35元双SATA口海思方案nvr拆解,破密码,固件dump » 2021-05-20 15:15:59

固件dump:
本来从SPI flash里dump固件是很简单的,编程器一连就ok了。不过这奇葩板子上到处找不到flash,难道是集成到片上了?好在自带的u-boot仁慈地留了1s的autoboot延时,可以用sf工具dump。
启动后按任意键终止boot,进入u-boot命令行。
sf probe看一下flash:

hisilicon # sf probe 0
32768 KiB hi_sfc at 0:0 is now current device

32M的,对应的16进制byte数是0x200000。
设置一下本机IP和tftp上位机(运行tftp服务器的主机)地址:

setenv serverip 192.168.x.x
setenv ipaddr 192.168.x.y

将flash内容读入内存0x82000000处,然后tftp上传:

sf read 0x82000000 0x0 0x200000
tftp 0x82000000 flash.bin 0x200000

得到固件后binwalk了一下,暂时没发现什么有意思的东西,就当做个备份以防日后玩坏了吧。
固件上传到了我建的一个交流群里:1164615798
板子自带的kernel版本是3.0.8,libC是libuClibc版本0.9.32.1,估计大多程序没法直接在这个上面跑,得重新编译链接到uClibc的版本。这颗IC的资料也找不到,更别说更新的内核了,想玩起来还是有点难度,不过自带系统里有nfs,做个文件服务器应该还是可以的。

#38 技术人生/软件使用技巧/破解经验/技术吐槽/灌水 » 捡垃圾上瘾,35元双SATA口海思方案nvr拆解,破密码,固件dump » 2021-05-20 13:17:11

SdtElectronics
回复: 13

自从当上电子垃圾佬,房里的吸尘器越堆越多,可惜每次都管不住这手啊。这次败来的是一台海思方案的硬盘录像机,到手后由于太急,没有拍遗照便惨遭我肢解,只好配上x宝卖家的图让大家一睹其生前芳容:
tb0
tb1
可以看出配置了两个SATA口,其中一个标准母座适配2.5寸硬盘,一个eSATA可以连接外置硬盘;两个USB2.0,一个VGA,一个HDMI,一个(百兆)网口。买家的图显得有点脏,我实际到手的比照片看起来新。
整个外壳是全卡扣设计,没有一根螺丝,直接上大力就拆开了。需要注意的是外壳多处形状较为尖锐,谨防割伤。楼主在拆时由于用力过猛擦破了点皮,好在没流血。
拆开一看我的心就凉了半截,里面就一张绿到你发慌的板子,上面一个256M的DDR,一个LQFP的主控,上书"HI3515",还有一个IC应该是RTC:
PCBf
非常奇葩的是供电走的是microUSB,而且满板子找不着ROM,那颗SOP8被磨了型号,连编程器也读不出,排除是SPI flash。再一查这颗主控的信息,心直接凉透了,ARM926 400MHz,想自己配文件系统都麻烦,垃圾中的战斗机,可以考虑挂咸鱼上找接盘侠了。
死心归死心,流程还是要走完。接上debug串口,擦腚揩鸡:

Linux version 3.0.8 (root@sqy-desktop) (gcc version 4.4.1 (Hisilicon_v100(gcc4.4-290+uclibc_0.9.32.1+eabi+linuxpthread)) ) #81 Fri Oct 17 16:20:03 CST 2014
CPU: ARMv7 Processor [414fc091] revision 1 (ARMv7), cr=10c53c7f
CPU: VIPT nonaliasing data cache, VIPT aliasing instruction cache
Machine: hi3520d

嗯?log报的是ARMv7的架构而非ARM9,并且型号也和芯片的标对不上,是HI3520。查了一下这颗主控的信息,是600MHz的cortex A9。可能还有得玩?
ps一下,看到有telnetd。默认IP是192.168.1.188,和我路由器的网段对不上,ifconfig改一下,连上了,但是要密码,试了几个常见弱口令都不对。passwd重置密码,报错:

/etc/passwd: Read-only file system

重新挂载 / 为rw,报错依旧。mount一下:

rootfs on / type rootfs (rw)
/dev/root on / type squashfs (ro,relatime)
proc on /proc type proc (rw,relatime)
sysfs on /sys type sysfs (rw,relatime)
tmpfs on /dev type tmpfs (rw,relatime)
tmpfs on /tmp type tmpfs (rw,relatime)
devpts on /dev/pts type devpts (rw,relatime,mode=600,ptmxmode=000)
/dev/mtdblock3 on /root type jffs2 (rw,relatime)
/dev/mtdblock3 on /update type jffs2 (rw,relatime)
/dev/mtdblock4 on /usr/etc type jffs2 (rw,relatime)
usbfs on /proc/bus/usb type usbfs (rw,relatime)

原来 / 是squashfs,只读的。查了一下这种情况重置密码该咋办,基本上都是说要提取squashfs,解压,修改,再重新生成新的fs写回去。这样做一来是麻烦,二来是我手上有锤子,哪能放过/etc/passwd这颗钉子。下面有请锤子出场:
连上我(好吧,其实是学校的)双路银标E5 32个框框的服务器,上传/etc/passwd,生死看淡,不服就干,john the ripper开始爆破:

john --fork=32 passwd

结果是一个很弱的口令:antslq。看来是大炮轰蚊子了。
telnet
输入爆破得到的口令,telnet成功登录。
(待续)

#39 Re: DIY/综合/Arduino/写字机/3D打印机/智能小车/平衡车/四轴飞行/MQTT/物联网 » 简单、操作友好的面包板/通用多电压电源,基于PD诱骗IC CH224K » 2021-05-02 13:13:14

tpu 说:

很好的项目,但。。。我不仅要多电压,我还想要多输出!

多路隔离输出肯定是做不到的,但是板子上除了排针以外也预留了一个5.08mm连接器的焊盘,在左上角,输出诱骗的电压。另外右上角的外置稳压器焊盘如果不用的话也能引出一路3.3V

#40 Re: DIY/综合/Arduino/写字机/3D打印机/智能小车/平衡车/四轴飞行/MQTT/物联网 » 简单、操作友好的面包板/通用多电压电源,基于PD诱骗IC CH224K » 2021-05-02 13:09:30

iamseer 说:

MPM3620 看起来是个好芯片,请问LZ在哪买的?大概多少钱?真的如深水宝上几块钱就能拿下吗?
另外建议Github里放一下电路图的图片或者PDF文件,方便预览,也方便没装软件的人/。

我的MPM3620是在一家垃圾店卖的板子上拆下来的,均价5毛 XD
淘宝上其他的卖家不清楚,不过我想这个IC集成了电感应该算先进封装技术了,造假怕是成本有点高,水应该不会太深吧。
昨天急着想写个简介出来,GitHub上的readme都还没上传,现在已经更新了,下次更新的时候把电路图片加一下

#41 Re: DIY/综合/Arduino/写字机/3D打印机/智能小车/平衡车/四轴飞行/MQTT/物联网 » 简单、操作友好的面包板/通用多电压电源,基于PD诱骗IC CH224K » 2021-04-30 15:25:53

现在PD充电器已经比较普及了,很适合在空间不大的桌面上或者非实验室环境当作一个多电压电源的替代品。不过大多数PD sink项目要么太简单,使用拨码开关这种不是很直观的操作方式;要么稍显复杂,需要使用单片机来实现交互。我这个设计的目标就是,既要尽量提供简便直观的操作方式,又不用到单片机等需编程的元件,简单易制低成本,于是就有了这个板子,使用CH224K作为诱骗芯片,单击式的电压选择由一片74HC32和一片74LVC161实现,并有LED作为视觉提示(表示请求电压而非实际电压)。
左侧的排针和5.08mm间距连接器提供5V / 9V / 12V / 15V / 20V可选输出,右边的排针提供3.3V固定输出。总共有三种可选的方式生成3.3V:使用集成电感的MPM3620同步降压IC(也是图示PCB使用的方式);使用LM1117兼容的SOT-223封装LDO(但不能用LM1117,因为最高可请求的20V超过了耐压,可考虑用NCP1117替代),或者使用右上角给外置降压器预留的TO-220脚位焊盘。
PCB的布局考虑了焊接的简便性,所有密脚的元件全部在背面,方便糊好锡膏后上铁板烧或风枪焊接。另外除PD外,CH224K似乎还支持BC1.2协议,我没太听说过,据说是包括高通的QC等一些厂家私有的标准的一个共同基础。我试了一下我的诺基亚7Plus的QC充电器也能给诱骗出5V / 9V / 12V三档电压。
目前的一个小问题是,上电后请求的电压是不确定的,虽然大概率是9V。我设计了一个RC脉冲电路试图让电路上电自动设置到5V,但实测C1太小时不能触发逻辑电路,C1太大时可以触发但5V那一档不能保持了,可能是和触发器的建立时间有关系。然后我懒得在这样一个小功能上下太多心思,后来直接把C1空焊了,先把坑丢这里看以后会不会填吧(
PCB文件已发布在GitHub上:
https://github.com/SdtElectronics/Biscuits/tree/master/bbSink
带CH224K小板的文件是ch224baseProdt.kicad_pcb。目前没什么制作的说明,等以后有空再补,不过就PCB和原理图上的信息也应该足够仿制一个了。

#43 Re: 全志 SOC » 全志平台上通过configfs(libcomposite)配置RNDIS Gadget(u_ether)及Windows下的驱动 » 2021-03-30 16:02:07

Windows下的驱动

原生Linux连接gadget我还没试,据说是直接就能认的,我这里Windows10还认不了,设备管理器里会显示一个感叹号:
devmgmt
有两个办法解决,第一个是去这里下一个驱动,然后右键那个RNDIS点更新驱动程序,手动查找和安装,选择上面下载下来的文件解压出的目录就行了。
第二个方法,我后来查了一下,其实Windows10里有那两个id的INF,但是不知道为啥还是认不出来。那只好手动指定一下驱动了,还是右键那个RNDIS点更新驱动程序,手动查找和安装,但是之后选择“从计算机的可用驱动程序列表中选取”。然后选择“网络适配器”,之后的窗口里把“显示兼容硬件”的勾点掉。下面厂商里选择Microsoft,型号拖到最后,选“远程NDIS兼容设备”。这样也能装上,就是稍微有点麻烦,不过不用下载别的东西。

#44 Re: 全志 SOC » 全志平台上通过configfs(libcomposite)配置RNDIS Gadget(u_ether)及Windows下的驱动 » 2021-03-30 15:51:52

下面都是在设备上的操作了,正常使用gadget前要先加载模块再配置。

加载内核模块

配置gadget前要加载这些模块:

modprobe sunxi
modprobe configfs
modprobe libcomposite
modprobe u_ether
modprobe usb_f_rndis

懒得每次开机都手动加载的可以把模块的名字写到/etc/modules里。

configfs里的配置
cd /sys/kernel/config/usb_gadget
mkdir g1
cd g1
echo "0x0502" > idVendor
echo "0x3235" > idProduct
mkdir functions/rndis.rn0
mkdir configs/c1.1
ln -s functions/rndis.rn0 configs/c1.1/

这里idVendor和idProduct是让host的操作系统识别gadget的,不过我的Windows10直接认不了这两个id,所以后面还要解决驱动问题。按说把这两个id改成Windows的RNDIS驱动兼容的就不用另外搞驱动了,但我懒得去找Windows自带的驱动兼容哪些id了。
配置完之后终于可以开启gadget了:

echo <udc name> > UDC

<udc name>要换成

ls /sys/class/udc/

看到的文件名。我这里的名称是musb-hdrc.1.auto。这步做完,host电脑上应该能识别出来一个新设备了,识别正不正确另说。
要是不想启用gadget功能了还能这样关掉:

echo "" > UDC
配置网络

把以下内容加入到/etc/network/interfaces

iface usb0 inet static
        address 192.168.137.2
        netmask 255.255.255.0
        network 192.168.137.0
        broadcast 192.168.137.255
        gateway 192.168.137.1

选择192.168.137.x网段是因为Windows网卡间共享网络就是在这个网段,你可以根据自己需求修改。然后

ifconfig usb0 up

这时候再ifconfig一下应该能看到usb0这个interface了。

#45 Re: 全志 SOC » 全志平台上通过configfs(libcomposite)配置RNDIS Gadget(u_ether)及Windows下的驱动 » 2021-03-30 15:37:43

内核配置和编译

要开启configfs RNDIS Gadget功能需要在menuconfig中配置如下选项:

    Device Drivers  --->
        [*] USB support  --->
        <M> Inventra Highspeed Dual Role Controller (TI, ADI, AW, ...)
                MUSB Mode Selection (Dual Role mode)  --->
                *** Platform Glue Layer ***
            <M> Allwinner (sunxi)
                *** MUSB DMA mode ***
            [*] Disable DMA (always use PIO)
        USB Physical Layer drivers  --->
            <M> NOP USB Transceiver Driver
        <M>   USB Gadget Support  --->
            <M>   USB Gadget functions configurable through configfs
            [*]     RNDIS

有人说还要开启libcomposite和configfs的支持,不过我这里貌似默认已经勾选了,没做额外操作也可以。
在全志全平台通用的defconfig基础上配置好的Kconfig我发布在了Github上,懒得自己配置的也可以直接用这个:
https://github.com/SdtElectronics/sun7i-std-dvr/blob/master/src/.config
然后重新编译内核和模块就行了。不知道怎么编译的可以参考sunxi Wiki上的Kernel Compilation
编译好之后把内核放到boot分区,然后把对应内核的模块目录放到/lib/modules下。

#46 全志 SOC » 全志平台上通过configfs(libcomposite)配置RNDIS Gadget(u_ether)及Windows下的驱动 » 2021-03-30 15:24:49

SdtElectronics
回复: 5

目前网上配置RNDIS Gadget的教程大部分是通过Precomposed Configurations (g_ether)的,通过configfs来配置gadget相对于Precomposed Configurations要更加麻烦,但稍微灵活一点,可以在用户空间动态设置设备的信息和功能啥的。昨晚我把configfs配置RNDIS gadget的方法添加到了Linux sunxi的USB Gadget/Ethernet Wiki页面上,不过是英文的。这贴里用中文简单介绍一下,并补充在Windows下驱动RNDIS gadget的方法。

#47 Re: DIY/综合/Arduino/写字机/3D打印机/智能小车/平衡车/四轴飞行/MQTT/物联网 » Linux串口实时接收二进制数据流的探讨 » 2021-03-25 21:52:53

实时性再探

借助termios的力量,我们成功让读取一帧的时间开销从线性(对帧长)倍减到了常数,可喜可贺!于是我们可以收工睡觉去了吗?且慢,再设想一下我们的程序被系统调度起来的情形。虽然Linux内核是非实时的,但尤其是当帧率不高时,可以预见有不小的几率我们的程序能及时响应连续的两帧,这时前述方法的一个问题显现了出来:由于每次读取(大概率)没有和帧对齐,我们从接收数据尾部搜寻到一帧时,实际上就把之后的数据,也就是下一帧的前半截丢掉了!我们当然可以在每次读取时加一个判断,确保搜寻到的是完整的一帧,但当连续的两帧原本都能被及时处理时,后一帧却被浪费了,他的前半截被上一次处理丢弃,后半截则被这一次处理无视。这会对我们应用的实时性造成一定影响,尤其是当帧率不高而使每一帧都比较重要时。解决办法是显而易见的:加一个生命周期在每帧处理函数之外的缓存以保存后一帧的前部,然后在后一次处理时取出来。但不那么显而易见的是,这个缓存该如何实现。

最粗暴的办法是直接把后一帧的前部拷贝到那个外部的缓存里,下次处理时首先就把缓存里的内容拷贝回来。不过当帧较长时,拷贝的开销就比较可观了,这不是一个scalable的方法。一些人可能马上会想到适于这个工作的一种数据结构:环形缓存。每次处理直接把数据读取到一个大小为帧长的环形缓存里,下一次读取到的帧的尾部会自动和残留在缓存里的前部拼接起来。理想是美好的,然而read并没有提供针对环形缓存的接口,因此在一次读取多个字节时可能发生越界。于是我们又面临一个两难的抉择:要么舍弃来之不易的一次读取多个字节的能力,让read逐字节地写入到环形缓存上,要么再引入一个复杂的中间层以对接read和环形缓存。

我没有太多数据结构相关的知识,针对这个困难,我姑且提出一种折衷的办法,来平衡复杂度、实时性和开销。它不一定是最好的方案,仅供大家讨论:把之前4kB的buffer再扩大一点(好吧,我个人的选择是扩大到8k,似乎不能说是“一点”了),然后每次读取直接写入到buffer上次读取的数据的末尾。这样一直写下去当然是要越界的,于是在每次读取前加一个判断,如果缓存剩余的空间已不足4k(为什么是4k?因为为了清空串口缓冲区,每次可能读取的最多字节数就是4k),就把此次读取的位置指向buffer的头部。这时后一帧的前部还是被丢掉了,但可以想见,把buffer设定得越大,这种情况发生的频率就越低,实际上是一个空间开销对实时性的tradeoff:

unsigned char buffer[8192];
unsigned char *buftop = buffer + 8192;
unsigned char *bufesp = buffer;

const unsigned char* getFrame(){
    if(bufesp + 4096 > buftop){
        bufesp = buffer;
    }
    bufesp += read(_sensorFd, bufesp, 4096);
    return serachFrame(bufesp);
}

考虑到简洁以及不同协议帧格式的差异,这里搜寻帧的操作用一行伪码serachFrame()代替。我在自己的应用中对上述机制的完整实现发布在了GitHub上:

https://github.com/SdtElectronics/yarn-fw/blob/master/src/drivers/lStream.cpp

楼主不是计科专业出身,以上内容均为Web搜索、查询手册加上个人的一些胡思乱想得来的。如果有什么贻笑大方的地方,还请坛友不吝赐教。

参考资料:

[1]: https://stackoverflow.com/a/66740261/10627291

[2]: https://stackoverflow.com/a/32632249/10627291

[3]: https://www.cmrr.umn.edu/~strupp/serial.html#4_2

#48 Re: DIY/综合/Arduino/写字机/3D打印机/智能小车/平衡车/四轴飞行/MQTT/物联网 » Linux串口实时接收二进制数据流的探讨 » 2021-03-25 21:48:13

Termios与Canonical Mode

上述的性能问题的实质是,每次read读取的数据量太小,使得读完一帧的开销变大。我们希望有控制使read阻塞的字节数的方法,而之前提到的count参数并不能做到这点。操作系统的设计者当然对此有所考虑,在Linux部分实现的POSIX规范中就通过termios提供了对于串口的更为细化的控制。
termios将对串口的读写分为两类:Canonical ModeRaw Mode。历史上,串口被广泛用于和终端交互,Canonical Mode就是提供终端的命令发送和回显功能的模式。这种模式下,termios通过预先规定的分隔符将输入分隔为行,这样可以使read阻塞到收完一行为止。我们发现似乎可以利用这个模式来满足我们的需求:将标志包尾的字节设为分隔符,内核就会让read阻塞到读完一帧为止。问题解决了吗?再仔细审视一下这个方法,我们会发现仍然有许多问题:

  • 分隔符的出现不一定标志着读完了一帧。因为没有控制read读取的最小字节数,读取到的可能是某一帧的尾部,或是数据载荷中的某一字节恰好取到了分隔符的值,导致对帧结尾的误判

  • 这种机制并不能保证我们读到的是最新的数据,可能串口的缓冲区里已经积压了太多的过时的帧,而read总是从最早接收的字节开始读取的

  • 阅读termios的文档可知,Canonical Mode还规定了一些控制字符,这意味着数据载荷取到ASCII可打印字符以外的值时,可能会产生意料外的结果

不管怎样,Canonical Mode至少解决了频繁的系统调用带来的性能问题,却同时带来了更大的麻烦。你大可以通过调整termios的各种设置以及加入更多的条件判断来解决这些问题,但不应忘了简洁是程序设计的最高原则之一。既然利用Canonical Mode有如此诸多麻烦,不妨转而了解一下termios提供的另一个模式,Raw Mode。

Raw Mode是termios专为二进制数据流制定的模式,并且通过一个宏VMIN实现了我们之前苦苦追寻的对read读取最小字节数的控制。现在可以通过在程序中设置termios结构体来确保能一次读完一帧了:[3]

struct termios sssConf;
//get the current options
tcgetattr(_sensorFd, &sssConf);
//set raw input, 1 second timeout
cfmakeraw(&sssConf);
sssConf.c_cc[VMIN] = FRAME_SIZE;
sssConf.c_cc[VTIME] = 10;
cfsetispeed(&sssConf, B19200);
//set the options 
tcsetattr(_sensorFd, TCSANOW, &sssConf);

但这才是解决问题的第一步,上面的代码既不能保证帧头和buffer的首字节是对齐的,也不能保证读取到的帧是最新的。对于前一个问题,我们可以通过搜寻帧的起始或终止字节来对齐一帧,并通过包的校验和来验证帧的有效性。对于后一个问题,我们可以把readcount设为串口缓冲区的大小,从而获取到最新的数据帧,并从read到的数据末尾开始搜寻一帧。这个方法看起来已经足够好了,除了buffer似乎有点大——4kB,也即内核默认的串口缓冲区大小。实际上内核实现的串口驱动提供了一个ioctl的request来控制串口缓冲区的大小,但不同版本的内核上ioctl的语义无法保证一致;另一方面,4kB的buffer和内核给一个线程设定的默认栈空间上限——8M相比,似乎又显得微不足道了,因此在内存资源并不紧张时,优化buffer大小显得并无必要。

#49 Re: DIY/综合/Arduino/写字机/3D打印机/智能小车/平衡车/四轴飞行/MQTT/物联网 » Linux串口实时接收二进制数据流的探讨 » 2021-03-25 21:42:59

简易的read方法

Linux遵循Unix“一切皆文件”的设计哲学,将串口抽象为一个设备文件。读取串口和读取文件的流程一样,先通过open系统调用打开串口,然后通过read系统调用读取:

#define FRAME_SIZE 8
unsigned char buf[FRAME_SIZE];
int sensorFd = open(sensorPath, O_RDWR | O_NOCTTY);
read(sensorFd, &buf, FRAME_SIZE);

open默认以阻塞方式打开文件,因此read会使线程挂起,直到串口接收到可用的数据。read可以通过第二个参数count设置读取到的字节数,我们把count设为一帧的长度,希望read会阻塞线程到串口接收完一帧为止。但read的行为果真如此吗?阅读read的manual,其对第二个参数count的描述是“最多读取的字节数”,也就是说,read不一定会读取到count个字节。实际上,对于串口的情形,默认只要有一个字节被接收到,read就会立即返回,其返回值是实际接收到的字节数。因此,通过设置count来保证read能读完一帧是不可行的。
于是我们在第二次尝试中让read循环读取一个字节,然后把这个字节写到一个buffer中,直到读取了一个完整的帧:[2]

ssize_t read2(int fd, void *buf, size_t count) {
    ssize_t read_length = 0;
    while (read_length < count) {
        ssize_t delta = read(fd, buf + read_length, count - read_length);
        if (delta == -1) return -1;
        read_length += delta;
    }
    return read_length;
}

这个方法是可行的,但并非完美的:read是一个系统调用,执行时会导致内核陷入,因此开销比一般的过程调用要更加高昂。当然,因为进行阻塞的read时,执行的频率等于串口的波特率,而串口的波特率相对于处理器主频而言一般是很低的,这里系统调用的overhead不一定会造成性能瓶颈。但我们还是要问,比起每次读取一字节,有没有更好的方式呢?

#50 DIY/综合/Arduino/写字机/3D打印机/智能小车/平衡车/四轴飞行/MQTT/物联网 » Linux串口实时接收二进制数据流的探讨 » 2021-03-25 21:38:05

SdtElectronics
回复: 6

楼主最近的一个项目需要在Linux下用串口接收来自一个传感器的数据流,本以为很简单的任务在探究一番之后却发现有不少名堂,特把自己的一些心得记录下来和大家分享。

实时场景下的Linux串口数据帧流接收

在广泛被采用的传感器与上位机的接口中,串口是因缺少I2C、SPI等协议所有的硬帧同步机制,而显得较为特殊的一种。这类传感器(或其他传输数据有Burst特性的设备)发送的数据流一般有如下特点:

  • 传输的突发性
        传感器不连续按波特率发送数据,而是按某一周期重复发送,或在某事件触发时发送数据

  • 数据的时效性
        应用只关心传感器最新发送的一个或数个数据,此前的数据因已过时而不再重要了

  • 数据帧格式固定
        传感器产生的数据帧有固定的格式,通常可能包括起始字节,中止字节,校验和,帧的长度也一般是固定的;但是数据载荷可能是二进制格式,从而可能正好为起始字节或终止字节的值,由此造成帧对齐的误判

这些特性让数据帧的解析变得复杂。下面从比较简单的接收方法开始分析,看看会遇到什么问题。

#51 Re: VMWare/Linux/Ubuntu/Fedora/CentOS/U-BOOT » 单片机程序移植到Linux下,串口接收什么方式比较好? » 2021-03-25 18:50:06

前几天我正好也遇到这样一个需求,感觉每次read 1byte的系统调用overhead太大了,于是在StackOverflow上问了这个问题,得到了一个相当详尽的解答,结论是有办法控制阻塞模式下串口一次read的最小数据量。答者还指出使用select()或poll()+非阻塞模式是非常不可取的方法:

If your program uses non-blocking mode but does not perform useful work while waiting for data, and instead uses select() or poll() (or even worse calls sleep()), then your program is unnecessarily complex and ineffecient.

不想读大段的英文也没关系,我正准备写一篇文章来分享自己读完那个答案之后的心得

#52 Re: 全志 SOC » 新版本主线内核上的并行RGB LCD适配(解决启动过程中屏幕变白问题) » 2021-02-23 20:56:09

我在4楼曾提到

至于选择哪个panel是否重要,还尚待验证,因为有人指出设备树的液晶屏配置在启动时会被u-boot修改成defconfig文件描述的配置。

今天在一块800x480上的屏幕上实测后,发现在设备树里选择配置和实际屏幕相符的panel型号是必须的,也就是说,新版本内核上u-boot的defconfig和设备树对于配置屏幕而言是各司其职的,二者都要被正确配置才能使boot和系统正常显示。

#53 Re: 技术人生/软件使用技巧/破解经验/技术吐槽/灌水 » 使用C++解释器动态调试你的嵌入式C程序:cling the C++ interpreter on ARM » 2021-02-19 18:35:20

C++解释器的意义,特别是对嵌入式开发领域而言,我已经在1楼说得很清楚了。当然,缺少脚本语言或者和硬件交互的开发经历的人可能不太好理解。

补充几个特性:
其实cling也支持tab补全,但他的match机制还不是很完善,现在不太好用。

cling支持自动的多行模式,当输入一个 { 而不在本行闭合它的时候,就会自动进入多行模式。多行模式的提示符会多一个'?'

.L 命令除了支持加载C/C++源码,还支持直接加载动态库,前提是先导入了对应的头文件,才能使用动态库里的符号。

来一个展示上面两个特性的例子,用cling和内核新的GPIO接口libgpiod交互:

[cling]$ #include <gpiod.hpp>
[cling]$ 
[cling]$ .L /usr/lib/arm-linux-gnueabihf/libgpiod.so.2
[cling]$ 
[cling]$ .L /usr/lib/arm-linux-gnueabihf/libgpiodcxx.so
[cling]$ 
[cling]$ gpiod::chip chip("/dev/gpiochip0")
(gpiod::chip &) @0xb6fa100c
[cling]$ 
[cling]$ auto lines = chip.get_line(236)
(gpiod::line &) @0xb6fa1014
[cling]$ 
[cling]$ lines.request({.consumer = std::string("tst"),
[cling]$ ?      
[cling]$ ?      .request_type = gpiod::line_request::DIRECTION_OUTPUT,
[cling]$ ?      
[cling]$ ?      .flags = gpiod::line_request::FLAG_OPEN_DRAIN})
[cling]$

#54 Re: 技术人生/软件使用技巧/破解经验/技术吐槽/灌水 » 使用C++解释器动态调试你的嵌入式C程序:cling the C++ interpreter on ARM » 2021-02-18 18:44:53

novice 说:

C++本地编译的速度都那么慢了,解释C++的速度能让人满意吗?解释型C脚本的速度都比js/lua慢那么多,何况是C++?

你说的很对,总体来说解释C++的开销是很大的,但是否让人满意要看实际场景了。一方面,C++有模板、constexpr之类的编译开销很大的特性,大量使用这些特性的代码会很慢。另一方面,cling宣称自己是jit解释器,这意味着热路径上的代码不会有重复解释的开销,一定程度上优化了性能。cling的目的不是代替编译好的程序,而是在原型验证阶段提供一个强大的武器:写过脚本语言的人都知道,有一个REPL对于开发阶段来说是多大的帮助,这种交互式的开发体验是一般的编译型语言难以得到的,而cling为C/C++提供了这个宝贵的能力

#55 Re: 技术人生/软件使用技巧/破解经验/技术吐槽/灌水 » 使用C++解释器动态调试你的嵌入式C程序:cling the C++ interpreter on ARM » 2021-02-18 17:36:55

还想了解更多?
令人哭笑不得的是,Root团队好像全然没有意识到他们搞出了个什么神仙玩意,他们的主要精力投入在了Root本体的开发上,而cling则一直被当成一个附属项目对待。因此,想探索cling的更多功能,最好的地方是CERN Root的官网,以及CERN Root的官方community。Root包括cling至今还在被高度活跃的社区不断地开发和完善中,在其组织的一些会议中的幻灯片展示了将来的Roadmap,还会有更多的黑魔法被加入到其中,最受关注的一些特性包括C++20的modules。

#56 Re: 技术人生/软件使用技巧/破解经验/技术吐槽/灌水 » 使用C++解释器动态调试你的嵌入式C程序:cling the C++ interpreter on ARM » 2021-02-18 17:25:57

cling进阶:
除了标准的C++语法,cling还支持一些内置的命令。我们来看的第一条命令,是可以列出其他命令用法的:

.help
[cling]$ .help

 Cling (C/C++ interpreter) meta commands usage
 All commands must be preceded by a '.', except
 for the evaluation statement { }
 ==============================================================================
 Syntax: .Command [arg0 arg1 ... argN]

   .L <filename>                - Load the given file or library

   .(x|X) <filename>[args]      - Same as .L and runs a function with
                                  signature: ret_type filename(args)

   .> <filename>                - Redirect command to a given file
      '>' or '1>'               - Redirects the stdout stream only
      '2>'                      - Redirects the stderr stream only
      '&>' (or '2>&1')          - Redirects both stdout and stderr
      '>>'                      - Appends to the given file

   .undo [n]                    - Unloads the last 'n' inputs lines

   .U <filename>                - Unloads the given file

   .I [path]                    - Shows the include path. If a path is given -
                                  adds the path to the include paths

   .O <level>                   - Sets the optimization level (0-3)
                                  (not yet implemented)

   .class <name>                - Prints out class <name> in a CINT-like style

   .files                       - Prints out some CINT-like file statistics

   .fileEx                      - Prints out some file statistics

   .g                           - Prints out information about global variable
                                  'name' - if no name is given, print them all

   .@                           - Cancels and ignores the multiline input

   .rawInput [0|1]              - Toggle wrapping and printing the
                                  execution results of the input

   .dynamicExtensions [0|1]     - Toggles the use of the dynamic scopes and the
                                  late binding

   .printDebug [0|1]            - Toggles the printing of input's corresponding
                                  state changes

   .storeState <filename>       - Store the interpreter's state to a given file

   .compareState <filename>     - Compare the interpreter's state with the one
                                  saved in a given file

   .stats [name]                - Show stats for internal data structures
                                  'ast'  abstract syntax tree stats
                                  'asttree [filter]'  abstract syntax tree layout
                                  'decl' dump ast declarations
                                  'undo' show undo stack

   .help                        - Shows this information

   .q                           - Exit the program

英文看得头晕转向?没事,常用的命令只有几个:
.q, 退出cling。等效于Ctrl+D。

.L, 加载文件,之后这个文件里的符号就可以被调用了。

# echo 'int val = 666;' > tst.cpp
# ./cling
****************** CLING ******************
* Type C++ code and press enter to run it *
*             Type .q to exit             *
*******************************************
[cling]$ 
[cling]$ .L tst.cpp
[cling]$ val
(int) 666

注意,如果你加载的文件还依赖别的文件中的符号,你需要把依赖的文件也通过.L加载。cling没法帮你处理依赖关系,毕竟它不知道依赖的文件在哪。

类似的命令还有一个.x,他会加载文件之后执行文件中和文件同名的那个函数:

# echo 'int tst(){return 666;}' > tst.cpp
# ./cling
****************** CLING ******************
* Type C++ code and press enter to run it *
*             Type .q to exit             *
*******************************************
[cling]$ 
[cling]$ .x tst.cpp
(int) 666

.I 显示或添加包含路径

[cling]$ .I
-cxx-isystem
/usr/include/c++/10
-cxx-isystem
/usr/include/arm-linux-gnueabihf/c++/10
-cxx-isystem
/usr/include/c++/10/backward
-isystem
/usr/local/include
-isystem
/home/cling-src/release/bin/../lib/clang/5.0.0/include
-extern-c-isystem
/usr/include/arm-linux-gnueabihf
-extern-c-isystem
/include
-extern-c-isystem
/usr/include
-I
/home/cling-src/release/include
-I
/home/cling-src/llvm/tools/cling/include
-I
.
-resource-dir
/home/cling-src/release/bin/../lib/clang/5.0.0
-nostdinc++

.class, 非常强大的命令,打印出某个class的layout:

[cling]$ #include<limits>
[cling]$ 
[cling]$ .class std::numeric_limits<char>
===========================================================================
struct std::numeric_limits<char>
SIZE: 1 FILE: limits LINE: 453
List of member variables --------------------------------------------------
limits          455 0x0      public: static constexpr _Bool is_specialized
limits          468 0x0      public: static constexpr int digits
limits          469 0x0      public: static constexpr int digits10
limits          471 0x0      public: static constexpr int max_digits10
limits          473 0x0      public: static constexpr _Bool is_signed
limits          474 0x0      public: static constexpr _Bool is_integer
limits          475 0x0      public: static constexpr _Bool is_exact
limits          476 0x0      public: static constexpr int radix
limits          484 0x0      public: static constexpr int min_exponent
limits          485 0x0      public: static constexpr int min_exponent10
limits          486 0x0      public: static constexpr int max_exponent
limits          487 0x0      public: static constexpr int max_exponent10
limits          489 0x0      public: static constexpr _Bool has_infinity
limits          490 0x0      public: static constexpr _Bool has_quiet_NaN
limits          491 0x0      public: static constexpr _Bool has_signaling_NaN
limits          492 0x0      public: static constexpr enum std::float_denorm_style has_denorm
limits          494 0x0      public: static constexpr _Bool has_denorm_loss
limits          508 0x0      public: static constexpr _Bool is_iec559
limits          509 0x0      public: static constexpr _Bool is_bounded
limits          510 0x0      public: static constexpr _Bool is_modulo
limits          512 0x0      public: static constexpr _Bool traps
limits          513 0x0      public: static constexpr _Bool tinyness_before
limits          514 0x0      public: static constexpr enum std::float_round_style round_style
List of member functions :---------------------------------------------------
filename     line:size busy function type and name
(compiled)     (NA):(NA) 0 public: static constexpr char min() noexcept;
(compiled)     (NA):(NA) 0 public: static constexpr char max() noexcept;
(compiled)     (NA):(NA) 0 public: static constexpr char lowest() noexcept;
(compiled)     (NA):(NA) 0 public: static constexpr char epsilon() noexcept;
(compiled)     (NA):(NA) 0 public: static constexpr char round_error() noexcept;
(compiled)     (NA):(NA) 0 public: static constexpr char infinity() noexcept;
(compiled)     (NA):(NA) 0 public: static constexpr char quiet_NaN() noexcept;
(compiled)     (NA):(NA) 0 public: static constexpr char signaling_NaN() noexcept;
(compiled)     (NA):(NA) 0 public: static constexpr char denorm_min() noexcept;

#57 Re: 技术人生/软件使用技巧/破解经验/技术吐槽/灌水 » 使用C++解释器动态调试你的嵌入式C程序:cling the C++ interpreter on ARM » 2021-02-18 17:02:53

cling快速入门:

hello world:

[cling]$ #include <stdio.h>
[cling]$ 
[cling]$ printf("hello world\n");
hello world

cling可以直接include在系统搜索路径里的头文件,包括kernel headers,其中声明的函数就能立即使用了

获取表达式的值:
表达式的末尾不写分号会怎么样?报错?不,cling会回显该表达式的值。写过matlab的人应该对此不陌生:语句末带分号,静默语句的值在console中的输出,反之则不静默:

[cling]$ __cplusplus
(long) 201703

cling可以语义化地回显一些对象和对象引用表达式的值,这些对象基本是stl容器:

[cling]$ #include <string>
[cling]$ 
[cling]$ std::string {"str"}
(std::string) "str"
[cling]$ 
[cling]$ #include <vector>
[cling]$ 
[cling]$ std::vector<char> ve{'c', 'h', 'a', 'r'} 
(std::vector<char> &) { 'c', 'h', 'a', 'r' }
[cling]$ 
[cling]$ #include <map>
[cling]$ 
[cling]$ std::map<int, char> mp{std::pair<int,char>()}
(std::map<int, char> &) { 0 => '0x00' }

函数名也是表达式。输入函数名会怎么样呢?

[cling]$ atoi
(int (*)(const char *) throw()) Function @0xb6c7bcd1
  at /usr/include/stdlib.h:104:
extern int atoi (const char *__nptr)
     __THROW __attribute_pure__ __nonnull ((1))

函数签名被打印了出来!
我编译的版本开启了RTTI,因此还可以通过typeid动态审查变量的类型:

[cling]$ auto b = std::string ("typed")
(std::basic_string<char, std::char_traits<char>, std::allocator<char> > &) "typed"
[cling]$ typeid(b).name()
(const char *) "NSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE"

#58 Re: 技术人生/软件使用技巧/破解经验/技术吐槽/灌水 » 使用C++解释器动态调试你的嵌入式C程序:cling the C++ interpreter on ARM » 2021-02-18 12:50:24

一些Note

nb的功能,nb的开销
和cling强大的功能相称的是其巨大的体积(我编译出来的版本整个文件夹达到了400+M,这个大小包括了一个完整的LLVM和clang,因此可能有可以精简的文件)以及相对低的解释执行性能,所以尽量不要在一个配置过低的平台上运行它。我在一张A20(dual a7@1G)+128M的板子上可以正常运行,一条简单语句的解释时间接近1s。

从源码编译
如果你想从源码编译,有一些要注意的地方:
依赖:libxml2 cmake git python2 libffi(libffi-dev on Debian-based distros)
系统配置:编译llvm是一项极其消耗计算资源的工作,我在i5-8300H平台上的QEMU下开j4进行编译,消耗五个半小时左右。之所以不开j8是因为编译的空间开销也很大,线程过多容易爆内存。注意编译脚本默认是开

$nproc

个线程的,如果要指定线程数,可以传一个参数给脚本:

./build.sh 4

网络:整个项目的源码体积在100+M,建议shell开代理,不然会很慢。

串口设备上的问题
cing似乎会打印一些和串口不兼容的字符,如果你在使用串口终端的设备上遇到问题,可以尝试用以下命令运行cling:

script -qc ./cling | cat 

#59 技术人生/软件使用技巧/破解经验/技术吐槽/灌水 » 使用C++解释器动态调试你的嵌入式C程序:cling the C++ interpreter on ARM » 2021-02-18 12:30:49

SdtElectronics
回复: 9

众所周知,C/C++是编译型语言,但若有人向你提及C++的解释器,你也不必认为他在痴人说梦——确实有一个C++的解释器项目存在,这就是cling。

什么是Cling

Cling是一个基于LLVM和Clang的现代C++解释器,作为CERN基于C++的数据分析框架Root的一部分被开发。CERN就是用大型强子对撞机(aka LHC)证实了希格斯玻色子存在的那个CERN,Root是负责对包括这项工作在内的LHC产生的实验数据进行分析的套件。Cling是用户和Root动态交互的界面,不同于那些prove-of-concept的玩具,cling实现了stack trace,variable shadowing(新声明的变量覆盖已有的同名变量)和语义化的对象打印等脚本语言解释器的功能。Cling是我见过的最具想象力的程序之一,因为兼容C ABI,他可以直接执行系统调用,这基本上意味着你可以利用他以一种动态的、交互式的模式来做任何事!

Cling on ARM

令人稍感困惑的是这么nb的一个程序似乎并不太为人所知。除开别的应用不谈,对于嵌入式开发来说,一个C/C++解释器应该有非比寻常的意义:对SoC上各个外设的控制本质上是通过kernel提供的系统调用实现的,而通常开发这种C程序的流程是:写代码—编译—debug—再编译—debug……当项目很大时,编译这一步会很耗时,无法敏捷地迭代真正感兴趣的那部分代码。如果有一个C++解释器的话,你可以动态地执行外设的API,写完一行代码敲下回车就能实时获得外设的响应。这和microPython之类的项目的概念有些类似,但因为这里你直接面对的是最底层的C API,就不会受限于只能调用别的框架封装好的API。就像上面提到的,你可以动态地做任何事!
遗憾的是,CERN Root官网只提供了X86/64平台上预编译的压缩包下载。为了能让cling运行在arm平台上,我在一个chroot环境下从源码编译了cling可独立运行的二进制,和编译脚本一起发布在GitHub上:
仓库链接
目前只编译了armhf(armv7)的版本,可以直接进入release下载,也可以clone仓库后运行build.sh自己编译。 cling的可执行文件是

./release/bin/cling

#60 Re: 全志 SOC » 新版本主线内核上的并行RGB LCD适配(解决启动过程中屏幕变白问题) » 2021-01-18 13:06:41

完成以上修改后,重新编译u-boot然后替换原来的SPL和dtb的二进制。如果一切正常的话,屏幕应该可以正常显示了:
demo

#61 Re: 全志 SOC » 新版本主线内核上的并行RGB LCD适配(解决启动过程中屏幕变白问题) » 2021-01-18 12:52:27

以上的修改都是对SoC对应的dtsi文件的修改,下面是需要对板子对应的dts做出的修改:
确保在dts文件开头引入了pwm.h:

#include <dt-bindings/pwm/pwm.h>

添加背光控制设备节点:

/ {
	...
	//这是要添加的内容
	backlight: backlight {
		compatible = "pwm-backlight";
		pwms = <&pwm 0 50000 PWM_POLARITY_INVERTED>;
		brightness-levels = <0 10 20 30 40 50 60 70 80 90 100>;
		default-brightness-level = <6>;
		enable-gpios = <&pio 7 7 GPIO_ACTIVE_HIGH>; /* PH7 */
	};
	//结束
}

这样你就能在/sys/class/backlight下对背光进行调节。为了方便阅读,这个例子只配置了11个背光级别,你可以根据需要细分更多的背光级别。
如果你的设备支持屏幕电源使能,添加电源使能节点:

/ {
	...
	//这是要添加的内容
	panel_power: panel_power {
		compatible = "regulator-fixed";
		regulator-name = "panel-power";
		regulator-min-microvolt = <10400000>;
		regulator-max-microvolt = <10400000>;
		//这里要和uboot配置里的CONFIG_VIDEO_LCD_POWER相一致
		gpio = <&pio 7 8 GPIO_ACTIVE_LOW>; /* PH08 */
		enable-active-low;
		regulator-boot-on;
	};
	//结束
}

配置tcon,display engine,panel设备节点:

//这是要添加的内容
&de {
	status = "okay";
};

&tcon0 {
	pinctrl-names = "default";
	pinctrl-0 = <&lcd0_rgb888_pins>;
	status = "okay";
};

&panel {
	compatible = "nec,nl4827hc19-05b";
	power-supply = <&panel_power>;
	backlight = <&backlight>;
};
//结束

重点讲一下panel节点的compatible属性,它标记了该屏幕是Linux kernel源码树下的/drivers/gpu/drm/panel/panel-simple.c中描述的哪个panel。比方说我这里选择了nec_nl4827hc19_05b这个panel,他在panel-simple.c里的描述是这样的:

static const struct drm_display_mode nec_nl4827hc19_05b_mode = {
	.clock = 10870,
	.hdisplay = 480,
	.hsync_start = 480 + 2,
	.hsync_end = 480 + 2 + 41,
	.htotal = 480 + 2 + 41 + 2,
	.vdisplay = 272,
	.vsync_start = 272 + 2,
	.vsync_end = 272 + 2 + 4,
	.vtotal = 272 + 2 + 4 + 2,
	.flags = DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_NHSYNC,
};

我们能看出来这是一个480x272的屏幕。我们就根据他的名字,给panel节点设置compatible = "nec,nl4827hc19-05b";。至于选择哪个panel是否重要,还尚待验证,因为有人指出设备树的液晶屏配置在启动时会被u-boot修改成defconfig文件描述的配置。我这里选了panel-simple.c里的一块分辨率和实际使用的屏幕相一致的panel,可以正常驱动。
你可以在我的GitHub仓库里找到我为我的A20板子和480x272的LCD修改好的设备树作为参考。

#62 Re: 全志 SOC » 新版本主线内核上的并行RGB LCD适配(解决启动过程中屏幕变白问题) » 2021-01-18 12:50:55

然后是修改设备树。首先检查你的SoC对应的dtsi文件,比如我的是A20的SoC的话对应的就是sun7i-a20.dtsi,其中的tcon(一个SoC可能有多个tcon,比如A20有两个,那么设备树里会有tcon0和tcon1两个节点)节点的ports的tcon0_out: port@1属性下有没有tcon0_out_lcd属性,没有的话,添加如下内容:

/ {
	...
	soc {
		...
		tcon0: lcd-controller@1c0c000 {
			...
			ports {
				...
				tcon0_out: port@1 {
					...
					//这是要添加的内容
					tcon0_out_lcd: endpoint@0 {
                        			reg = <0>;
                        			remote-endpoint = <&lcd_in_tcon0>;
                   			};
					//结束
				}
			}
		}
	}
}

打省略号的部分只是为了方便大家定位到要添加代码的位置,两段注释中间的代码才是要添加的。之后贴出的代码同理。
添加一个panel节点:

/ {
	...
	soc {
		...
		//这是要添加的内容
		panel: panel {
            		#address-cells = <1>;
            		#size-cells = <0>;
            		port {
                		#address-cells = <1>;
                		#size-cells = <0>;
                		lcd_in_tcon0: endpoint {
					//这里要和上面加的那部分一致
					remote-endpoint = <&tcon0_out_lcd>;
				};
			};
		};
		//结束
	}
}

添加lcd的pins:

/ {
	...
	soc {
		...
		pio: pinctrl@1c20800 {
            		...
            		//这是要添加的内容
            		lcd0_rgb888_pins: lcd0-rgb888 {
				pins = "PD0", "PD1", "PD2", "PD3",
				       "PD4", "PD5", "PD6", "PD7",
				       "PD8", "PD9", "PD10", "PD11",
				       "PD12", "PD13", "PD14", "PD15",
				       "PD16", "PD17", "PD18", "PD19",
				       "PD20", "PD21", "PD22", "PD23",
				       "PD24", "PD25", "PD26", "PD27";
				function = "lcd0";
			};
			//结束
		};
	}
}

注意这个要根据你实际使用的SoC的lcd输出使用的GPIO修改,这个例子使用的是A20的lcd0输出。

#63 Re: 全志 SOC » 新版本主线内核上的并行RGB LCD适配(解决启动过程中屏幕变白问题) » 2021-01-18 11:51:21

首先是u-boot下的配置,修改u-boot代码树的configs/目录下你的板子对应的defconfig文件,添加如下配置:
1,CONFIG_VIDEO_LCD_MODE字段,这是存储液晶屏的分辨率,扫描模式等配置的字段,具体每个键值对的含义和计算方法在Linux-sunxi wiki上的LCD页面上有。下面是一个给480x272的LCD使用的配置样例:

CONFIG_VIDEO_LCD_MODE="x:480,y:272,depth:24,pclk_khz:10000,hs:1,vs:1,le:42,ri:8,up:11,lo:4,sync:3,vmode:0"

2,CONFIG_VIDEO_LCD_BL_EN字段,这是标记驱动液晶屏的使能信号的GPIO的字段,根据你板子的实际情况填写,例如:

CONFIG_VIDEO_LCD_BL_EN="PH7"

3,CONFIG_VIDEO_LCD_BL_PWM字段,这是标记控制背光亮度pwm信号的GPIO的字段,根据你板子的实际情况填写,例如:

CONFIG_VIDEO_LCD_BL_PWM="PB2"

4,CONFIG_VIDEO_LCD_POWER字段,这是标记液晶电源使能信号的GPIO的字段,根据你板子的实际情况填写,例如:

CONFIG_VIDEO_LCD_POWER="PH8"

如果你的设备没有液晶电源使能功能,即液晶电源是常开的,此段可以略去。
5,CONFIG_VIDEO_LCD_DCLK_PHASE字段,指明LCD时钟极性,一般值为0:

CONFIG_VIDEO_LCD_DCLK_PHASE=0

所以为了驱动一块480x272的LCD,我的板子最终需要在defconfig中添加如下配置:

CONFIG_VIDEO_LCD_MODE="x:480,y:272,depth:24,pclk_khz:10000,hs:1,vs:1,le:42,ri:8,up:11,lo:4,sync:3,vmode:0"
CONFIG_VIDEO_LCD_DCLK_PHASE=0
CONFIG_VIDEO_LCD_POWER="PH8"
CONFIG_VIDEO_LCD_BL_EN="PH7"
CONFIG_VIDEO_LCD_BL_PWM="PB2"

#64 全志 SOC » 新版本主线内核上的并行RGB LCD适配(解决启动过程中屏幕变白问题) » 2021-01-18 11:35:36

SdtElectronics
回复: 13

大概一个月前尝试用一块A20板子点LCD的时候,遇到了屏幕在u-boot和启动开始阶段能正常显示,但之后屏幕开始变白的问题。症状类似下图:
white screen
同时可以在log上看到这样的输出:

sun4i-drm display-engine: NO panel or bridge found... RGB output disabled

后来一番Google后找到armbian论坛上的一个贴子给出了有效的解决方法:如log所提示的信息,这是设备树中缺少panel节点造成的,需要修改设备树解决。
前些天貌似也有坛友遇到了相同问题发贴提问,回贴中坛友jimmy提到可能是linux 4.14后显示架构改了。我索性写一贴来讲述一下在主线的u-boot、Linux kernel上如何进行并行RGB液晶屏的配置。

#65 技术人生/软件使用技巧/破解经验/技术吐槽/灌水 » [胡思乱想] 一种基于命名管道的高度解耦的、语言中立的、事件驱动的多层协议栈实现方法 » 2020-12-02 13:05:00

SdtElectronics
回复: 1

==============以下是废话,可以跳过==============
这个想法起源于楼主参加校队的电赛集训时遇到的一个练习题,需求大致是从ADC获取信息,处理之后发给电脑或者手机上的客户端软件。
我最开始粗略构想的架构是,写一个c程序从zynq的XADC暴露到iio子系统的文件读取数据,然后通过管道和一个web服务器IPC,web服务器再和手机或电脑上的客户端通讯。
电赛这几年测量题的一个趋势好像就是逐渐向这种物联网的方向发展。做电赛的人都知道,实际比赛的时间非常紧张,正式比赛时一般是把平时准备好的硬件模块拼起来。为了节省时间,软件层面上也应该采取同样的方法。软件的模块化,一提起来很多人就想到库。但除非是都写成c库然后供其他带FFI的语言调用,不然不同语言之间想重用模块是比较困难的。但面对电赛现在的趋势,软件能用多种语言构建才更为理想:性能要求高的数据处理部分用C写,物联网服务器的部分用更高级的语言实现。那么有没有一种办法能让不同语言写成的模块像拼硬件模块一样任意组合呢?

==================进入正题==================
先说一下命名管道这个东西。管道大家都不陌生,它能把上一个命令的标准输出重定向到下一个命令的标准输入上。命名管道和它差不多,不过你可以给他起个名字,然后他就像一个文件一样以那个名字存在在他被创建的目录下面。比如说在一个终端里输入

mkpipe tst

创建了一个名字叫tst的管道。然后打开另一个终端,cd到前一个终端打开的目录下,就能看到一个叫tst的文件,然后输入

cat < tst

把tst这个管道重定向给cat命令的标准输入,这时候回到上一个终端,输入

echo "t" > tst

这时候第二个终端会显示一个t。
几乎任何语言写的程序都能访问标准输入和输出,只要预先约定好一种协议和编码,就能利用命名管道实现不同语言构建的程序之间的通信。此所谓语言中立。当然这个事普通的管道操作符也能干,而且这样实现的通信是一条链,没法分叉。如果我想根据协议约定好的某个字段,把信息送到不同的程序那里呢?

有计算机网络知识的人都知道有个5层(或者7层)模型,数据从底层往上层传,每经一层丢掉一个报头。那一层也就是根据这个报头知道这个数据包该往后一层的哪个地方(哪个MAC,哪个IP,哪个port)传。我们也可以效仿这种做法,在传输的数据中加入报头。
为了解析报头,需要编写一个新的程序,我姑且称之为路由器(router)。这个程序,也是个普通的命令,接受两个参数。第一个参数 l 表示这一层的报头有多长,第二个参数是变长参数,或者说剩余参数,是一列命名管道的名字。每一层的报头存储的就是这列名字中的其中一个,router把报头读出来,然后把丢掉报头的信息发到匹配的那个命名管道里。这些命名管道连接着这一层待选择的不同的程序。router的输入也是标准输入,于是它本身也能被命名管道和上一层的程序连起来。
这样的一个多层的体系看起来是这样的:
数据生产者
命名管道
路由器
命名管道1 ... 命名管道i
1层中间件1 1层中间件2 ... 1层中间件i
命名管道
路由器
命名管道1 ... 命名管道j
2层中间件1 2层中间件2 ... 2层中间件j
命名管道
...
路由器
命名管道1 ... 命名管道k
n层中间件1 n层中间件2 ... n层中间件k
命名管道
路由器
命名管道1 ... 命名管道h
数据消费者1 ... 数据消费者h

比方说你写了个很复杂的通讯程序,生产者接收到一个字符串,需要过滤内容,压缩大小,编码之后再用从一个web服务器发走。过滤器有很多种,压缩算法有很多种,编码方式也有很多种,假定这些都是生产者知道的,于是它产生这样一段数据:
[过滤器id[压缩器id[编码器id[web服务器id[字符串载荷]]]]]
你可能希望用python来写几个不同的过滤程序,调用bzip2或者zstd等不同的压缩程序,用C++写几个不同的编码器。这都没有问题,只用创建一系列名字和那一层的中间件的种类对应的命名管道,然后每一层的路由器会根据报头把数据传给对应的中间件,最后传给服务器。

每次读完管道中的内容以后,操作系统就会把读端的程序挂起,直到有新数据写入管道。也就是说只有生产者产生了新信息,后续的程序才会动作。此所谓事件驱动。

命名管道的通讯是单向的。一般而言,上行和下行数据的处理方式是不一样的,所以需要为两个方向上的数据流分别实现这样一个结构。

不知道这种想法有没有已经在应用了,或者有更好的办法实现相同的目的,就先发出来供大家拍砖讨论。

#66 Re: 全志 SOC » 淘宝20元A20行车记录仪主板运行主线u-boot & 主线Linux(Q群 1164615798) » 2020-11-15 16:59:07

qinxiongxu 说:

楼主,请问这个主线的linux支持声卡吗?

A20的Audio CODEC有主线支持,但是这个板子上能用的输出应该只有一个喇叭。麦倒是有两个

#67 Re: 全志 SOC » 淘宝20元A20行车记录仪主板运行主线u-boot & 主线Linux(Q群 1164615798) » 2020-11-04 11:31:13

ubuntufull 说:

编译提示spl部分爆内存了。。。头大
https://whycan.com/files/members/4503/none_20201104-1020.png

请使用我发的linaro预编译的工具链编译试试。或者加群有编译好的bootloader在群文件分享。

#68 Re: 全志 SOC » 淘宝20元A20行车记录仪主板运行主线u-boot & 主线Linux(Q群 1164615798) » 2020-11-02 22:24:02

四、boot分区和文件系统
1, boot分区
刚刚划分好的第一个FAT32分区作为boot分区。boot分区中需存放以下文件:
(1)12楼中编译kernel产生的zImage(在arch/arm/boot/下)
(2)12楼中编译u-boot产生的sun7i-a20-std-dvr.dtb
(3)boot.scr。boot.scr是指引u-boot加载内核的文件,启动时向内核传递的参数也在此定义。下面介绍如何由boot.scr的源文件boot.cmd编译产生boot.scr。
新建一个名为boot.cmd的文件,编辑内容为:

setenv bootargs console=ttyS0,115200 root=/dev/mmcblk0p2 rootwait panic=10
load mmc 0:1 0x43000000 sun7i-a20-std-dvr.dtb || load mmc 0:1 0x43000000 boot/sun7i-a20-std-dvr.dtb
load mmc 0:1 0x42000000 zImage || load mmc 0:1 0x42000000 boot/zImage
bootz 0x42000000 - 0x43000000

然后在当前目录下执行

mkimage -C none -A arm -T script -d boot.cmd boot.scr

然后将当前目录下生成的boot.scr拷入boot分区。

2, 文件系统
将你准备好的文件系统拷贝到刚刚划分好的ext4分区,就是后面那个分区。如何准备rootfs请参考Linux-sunxi的Wiki
一楼展示的终端使用的rootfs是LinuxContainers提供的arch Linux文件系统,下载后可以直接解压到ext4分区里。ext4分区下现在应该有这些目录:

bin   dev  home  mnt  proc  run   srv  tmp  var
boot  etc  lib   opt  root  sbin  sys  usr

大功告成!你现在可以插上SD卡尝试boot了。出现问题欢迎在楼里提出,也可以进群讨论。
(全文完)

#69 Re: 全志 SOC » 淘宝20元A20行车记录仪主板运行主线u-boot & 主线Linux(Q群 1164615798) » 2020-11-02 22:06:49

三、烧写u-boot和SD卡分区
1, 烧写u-boot
可以用Linux下的dd工具,也可以用Windows下git带的MinGW中的dd。以下${card}全部为sd卡的设备路径
清理SD卡:

dd if=/dev/zero of=${card} bs=1M count=1

烧写u-boot:

dd if=u-boot-sunxi-with-spl.bin of=${card} bs=1024 seek=8

2, 分区:
想用什么工具都行,这里以Windows下的DiskGenuis为例。
partition
首先划分一个FAT32分区用于存放内核和其他boot需要的文件,大小为16MB,分区前部保留1MB的空间;
然后划分一个ext3或者ext4的分区,用于存放rootfs,大小足够即可,或者让他直接占满剩余空间。

#70 Re: 全志 SOC » 淘宝20元A20行车记录仪主板运行主线u-boot & 主线Linux(Q群 1164615798) » 2020-11-01 10:13:24

二、构建u-boot和kernel
1, 编译u-boot(产生bootloader和设备树)
每次编译之前,别忘了确认工具链目录是否已被导入PATH(见10楼)。缺少工具链时make会产生找不到arm-linux-gnueabihf-gcc之类的报错。
进入clone完成的u-boot源码目录,

make CROSS_COMPILE=arm-linux-gnueabihf- A20_std_dvr_defconfig

如果你想微调一些设置的话:

make CROSS_COMPILE=arm-linux-gnueabihf- menuconfig

不想调整可以跳过这一步,保留默认配置。
编译:

make CROSS_COMPILE=arm-linux-gnueabihf- -j$(nproc)

产生的bootloader是./u-boot-sunxi-with-spl.bin
产生的dtb是./arch/arm/dts/sun7i-a20-std-dvr.dtb

2, 编译kernel
获取kernel源码

git clone https://github.com/torvalds/linux.git --depth=1

进入clone完成的kernel源码目录,

make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- menuconfig

如果你想微调一些设置的话:

make CROSS_COMPILE=arm-linux-gnueabihf- menuconfig

不想调整可以跳过这一步,保留默认配置。
编译:

ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- make -j$(nproc) zImage

如果一切顺利的话,到现在为止产生的二进制文件已经足够让板子boot进入内核了。如果你配置了别的内核模块或者希望在板子上运行编译器,可能还想要构建内核模块和头文件,请参考Linux-sunxi的Wiki
(待续)

#71 Re: 全志 SOC » 淘宝20元A20行车记录仪主板运行主线u-boot & 主线Linux(Q群 1164615798) » 2020-11-01 09:33:18

之前找到一份疑似是该板原理图的文件,已上传QQ群文件和Github信息仓库。注意,尽管这份原理图和板子的吻合程度较高,但在部分地方还是有不同的,仅供参考用途。

#72 Re: 全志 SOC » 淘宝20元A20行车记录仪主板运行主线u-boot & 主线Linux(Q群 1164615798) » 2020-10-31 13:12:48

开始缓更构建教程。提醒一下其实编译出u-boot以及编译u-boot产生的设备树就已经可以正常引导sunxi_defconfig的主线内核进入rootfs了,构建u-boot的过程在二楼,老手编译完就能直接用了。以下教程是相对详细的构建过程。

一、准备
0, 关于系统要求
是个软件包都还比较新的Linux就行,推荐进行下面步骤之前用包管理器进行一次软件更新(Debian系发行版是apt-get update; apt-get upgrade)。实际上我感觉自己的构建环境(Arch Linux文件系统的WSL)可能是最邪门的,因为严格来说它压根就不是Linux……因此如果你是WSL用户的话,你甚至可以全程在Windows下完成构建。

1, 获取工具链
建议直接下载Linaro预先编译好的工具链。如果你的电脑是x86或x64平台(一般都是这个平台,除非你准备在Arm平台上构建)的,下载链接里的gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf.tar.xz即可。
接下来解压工具链的压缩包到你想要的位置。貌似较新版的tar命令能直接

tar -xf 文件名

来解压xz的压缩包了。如果你的系统上不行,请自行搜索xz格式压缩包的解压方法。
解压完成后

export PATH=$PATH:/home/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf/bin

把工具链导入环境变量。/home/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf/需要换成你解压出的工具链所在的路径。你也可以把该命令写入你使用的shell对应的rc文件(比如.bashrc)里,这样可以避免每次手动导入PATH。

2, 安装必要的依赖
需要的依赖和依赖对应的包名可能在不同发行版下略有不同。Debian系Linux下的依赖可通过如下命令安装:

apt-get install build-essential libncurses5-dev u-boot-tools qemu-user-static debootstrap git binfmt-support libusb-1.0-0-dev pkg-config

我用的Arch上貌似只要安装u-boot-tools flex bison就行了。

3, 获取u-boot源码
进入工作路径,

git clone https://github.com/SdtElectronics/u-boot-sun7i-std-dvr.git

至此构建的准备工作已经完成。下面可以正式开始编译了(待续)

#73 Re: 全志 SOC » 淘宝20元A20行车记录仪主板运行主线u-boot & 主线Linux(Q群 1164615798) » 2020-10-31 12:00:05

qinxiongxu 说:

另外,请问下A10的可以跑吗?我还买了一块A10主芯片的记录仪

肯定不行的。A10是sun4i,A20是sun7i,u-boot和dts基于的架构不一样几乎肯定不能兼容。不过你可以在导出fex后根据我发的教程自己适配u-boot试试,或者在群里求助

#75 Re: 技术人生/软件使用技巧/破解经验/技术吐槽/灌水 » 不拆SoC,不用X光机,利用已知IO探测未知的IO连接情况 » 2020-10-31 10:24:22

对了,还有一点补充:这样得出来的GPIO标号是gpioxxx的格式,xxx要换算成PB00这种格式要模32得出后面的数字偏移量以及除以32取整得到port。批量手动换算的话麻烦还易错,我写了段JavaScript来做这个工作:

(n => (['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H'][(n - (n%32))/32] + Number(n%32).toString()))(num)

用法:
浏览器下F12打开devtools,把以上代码粘贴到console,num改成gpio号,回车,例如

(n => (['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H'][(n - (n%32))/32] + Number(n%32).toString()))(45)

返回B13,即PB13。

#76 Re: 全志 SOC » 为新设备构建主线u-boot » 2020-10-30 16:24:40

四、补充
本贴正文的内容应该到此结束了的,因为标题是u-boot的构建嘛,上面已经是全部过程了。但只是构建出来一个bootloader没啥用,毕竟他引导的系统才是我们真正关注的东西。而且在低内存的设备上,主线u-boot还有个坑,解决方法也在此节记录。
1, boot.cmd和boot.scr
boot.scr是指引u-boot加载内核的文件,启动时向内核传递的参数也在此定义。boot.cmd是boot.scr的源文件,编译后生成boot.scr。
下面是一段示例的boot.cmd:

setenv bootargs console=ttyS0,115200 root=/dev/mmcblk0p2 rootwait panic=10
load mmc 0:1 0x43000000 ${fdtfile} || load mmc 0:1 0x43000000 boot/${fdtfile}
load mmc 0:1 0x42000000 zImage || load mmc 0:1 0x42000000 boot/zImage
bootz 0x42000000 - 0x43000000

其中,/dev/mmcblk0p2是rootfs所在的分区,${fdtfile}是设备树文件的名字,zImage是编译完的压缩后的内核binary,也即make zImage命令生成的内核。如果希望加载的内核是uImage,需要把以上内容中的zImage改为uImage,bootz改为bootm。
编辑完毕保存后,

mkimage -C none -A arm -T script -d boot.cmd boot.scr

产生对应的boot.scr。

2, 烧写u-boot以及为可引导的SD分区
Linux-sunxi的Wiki上有详细步骤,这里简单描述一下,以下${card}全部为sd卡的设备路径:
清理SD卡:

dd if=/dev/zero of=${card} bs=1M count=1

烧写u-boot:

dd if=u-boot-sunxi-with-spl.bin of=${card} bs=1024 seek=8

分区:
想用什么工具都行,你甚至可以在Windows下用DiskGenuis来做。
首先划分一个FAT32分区用于存放内核和其他boot需要的文件,大小为16MB,分区前部保留1MB的空间;
然后划分一个ext3或者ext4的分区,用于存放rootfs,大小足够即可,或者让他直接占满剩余空间。
复制文件:
把前面编译好的内核,boot.scr,设备树全放进刚刚分好的FAT32分区。把解压好的rootfs放进刚刚分好的ext分区。在哪找rootfs超出本贴范围太多,给个参考链接自己看吧Linux-sunxi的Wiki
然后插上sd卡祈祷他能正常boot吧。一次就能成功可以去买彩票了,如果你是巨佬当我没说。遇到问题可以Google出错时的log,或者来论坛问。
本节内容仅适用于制作可启动的SD卡。制作可启动的nand或者spi flash请自行查阅sunxi的Wiki。

3, 主线u-boot在低内存设备上的坑
本节内容主要来自参考[4]
在内存较少的设备上运行u-boot可能会遇到如下报错:

** Reading file would overwrite reserved memory **

根据参考[4],这和uboot的内存分配有关,需要对源码中的./include/configs/sunxi-common.h做如下修改:
第一处:

- #if  (!defined CONFIG_MACH_SUN8I_V3S) 
- /* 64MB of malloc() pool */
- #define CONFIG_SYS_MALLOC_LEN		(CONFIG_ENV_SIZE + (64 << 20))
- #else
/* 2MB of malloc() pool */
#define CONFIG_SYS_MALLOC_LEN		(CONFIG_ENV_SIZE + (2 << 20))
- #endif

第二处:

#else
/*
 * 160M RAM (256M minimum minus 64MB heap + 32MB for u-boot, stack, fb, etc.
 * 32M uncompressed kernel, 16M compressed kernel, 1M fdt,
 * 1M script, 1M pxe and the ramdisk at the end.
 */
- #if (!defined CONFIG_MACH_SUN8I_V3S) 
- #define BOOTM_SIZE     __stringify(0xa000000)
- #define KERNEL_ADDR_R  __stringify(SDRAM_OFFSET(2000000))
- #define FDT_ADDR_R     __stringify(SDRAM_OFFSET(3000000))
- #define SCRIPT_ADDR_R  __stringify(SDRAM_OFFSET(3100000))
- #define PXEFILE_ADDR_R __stringify(SDRAM_OFFSET(3200000))
- #define RAMDISK_ADDR_R __stringify(SDRAM_OFFSET(3300000))
- #else
/*
 * 64M RAM minus 2MB heap + 16MB for u-boot, stack, fb, etc.
 * 16M uncompressed kernel, 8M compressed kernel, 1M fdt,
 * 1M script, 1M pxe and the ramdisk at the end.
 */
#define BOOTM_SIZE     __stringify(0x2e00000)
#define KERNEL_ADDR_R  __stringify(SDRAM_OFFSET(1000000))
#define FDT_ADDR_R     __stringify(SDRAM_OFFSET(1800000))
#define SCRIPT_ADDR_R  __stringify(SDRAM_OFFSET(1900000))
#define PXEFILE_ADDR_R __stringify(SDRAM_OFFSET(1A00000))
#define RAMDISK_ADDR_R __stringify(SDRAM_OFFSET(1B00000))
- #endif
#endif

开头标记"-"的行需在源码中删去。
(全文完)

#77 Re: 技术人生/软件使用技巧/破解经验/技术吐槽/灌水 » 不拆SoC,不用X光机,利用已知IO探测未知的IO连接情况 » 2020-10-30 15:39:10

需要注意的一点是,如果输入引脚悬空,读取的电平极易受干扰而改变,导致脚本误认为已经搜索到引脚了。解决方法是给探测脚加一个下拉电阻,或者同一个引脚多测几次看看结果是否一致。

#78 Re: 全志 SOC » 淘宝20元A20行车记录仪主板运行主线u-boot & 主线Linux(Q群 1164615798) » 2020-10-30 15:36:32

今天上午没有更贴,去折腾另一个有意思的问题了:获取板子上引出IO的标号。我摸索出了一种不需要对板子下毒手就能获知IO连接的办法:
不拆SoC,不用X光机,利用已知IO探测未知的IO连接情况
这是初步探测出来的IO连接情况:
IO
还有一些没测出来的,不知道什么原因,可能是引脚不在遍历范围内,可能是连接到部分引脚的电阻空焊了,也可能是因为我懒得焊线去探测,而是直接用杜邦线抵着焊盘探测,导致接触不良。

我为这个板子建了个交流群:1164615798

群里会分享这个板子和其他一些便宜的ARM Linux板子的资料。当然,教程还是会在本贴更新,群主要是供交流以及疑难解答之用。也欢迎大家进行其他Linux相关的讨论,或者开一些便宜板子的车。

#79 技术人生/软件使用技巧/破解经验/技术吐槽/灌水 » 不拆SoC,不用X光机,利用已知IO探测未知的IO连接情况 » 2020-10-30 15:25:15

SdtElectronics
回复: 20

在淘宝或闲鱼上买到广告机或者导航仪之类的板子后,如何获知板子上引出的IO和SoC上IO编号的对应关系是个棘手的问题。除非走运能拿到板子的资料,否则比较便捷的方法就只有在板子自带系统上的fex文件之类的配置信息中搜寻了,但这些信息可能不全。本贴给出一种方法,只要你能通过猜测等手段得出一个已知的IO编号,其余的一般能很轻易地获知。

核心思想是,将那个已知IO连接到你想知道编号的未知IO上,然后在sysfs中遍历更改某范围IO的输出电平。如果已知IO检测到了电平变化,证明遍历到的那个IO编号正是未知IO的编号了。
我为这个方法编写了一段shell script,其中known是你已经知道的IO的编号,for循环中的区间需要自行修改成合适的范围,因为有些IO对系统正常运行是必要的(比如mmc,nand,reset),修改后将导致系统崩溃。

#!/bin/bash  

known=10

echo ${known} > /sys/class/gpio/export
echo in > /sys/class/gpio/gpio${known}/direction

cd /sys/class/gpio/
for i in $(seq 0 9) $(seq 11 17);
do   
	echo "${i}" > ./export
	echo out > "gpio${i}"/direction
	echo 1 > "gpio${i}"/value
	pre=($(cat ./gpio${known}/value))
	echo 0 > "gpio${i}"/value
	pos=($(cat ./gpio${known}/value))
	if [ ! $pre == $pos ]; then
		echo "gpio${i}"
		echo "${i}" > ./unexport
        exit
	fi
	echo "${i}" > ./unexport
done  

上面的代码在主线Linux上测试可用,但部分厂商定制的系统可能有不同的将gpio暴露到用户空间的方式,也可能缺少运行该脚本必要的依赖(GNU coreutils seq)。你可能需要下载合适平台的busybox到设备再借助busybox带有的seq来运行该脚本。

#80 Re: 全志 SOC » 为新设备构建主线u-boot » 2020-10-30 13:07:08

三、编译!
foreplay做足,终于可以开始正戏了。但开始编译之前,别忘了确认工具链目录是否已被导入PATH(见2楼)。
进入clone完成的源码目录,

make CROSS_COMPILE=arm-linux-gnueabihf- <board_name>_defconfig

<board_name>_defconfig是上一步中新建的Kconfig文件的文件名。
如果你想微调一些配置,

make CROSS_COMPILE=arm-linux-gnueabihf- menuconfig

当然如果不想的话可以跳过这一步,使用默认的Kconfig。
然后正式开始编译:

make CROSS_COMPILE=arm-linux-gnueabihf- -j$(nproc)

如果你的机器性能尚可,十来分钟左右应该就能完成编译。
以全志平台为例,编译产生的bootloader是 ./u-boot-sunxi-with-spl.bin
编译产生的dtb在 ./arch/arm/dts/ 下,名称是在第二步中新建的dts文件的文件名,后缀换成dtb,例如sun7i-a20-std-dvr.dtb

note1:
上述编译过程仅适用于armhf架构的SoC,也就是32位的。适用于64位的Aarch架构的编译过程请参考Linux-sunxi的Wiki

#81 Re: 全志 SOC » 淘宝20元A20行车记录仪主板运行主线u-boot & 主线Linux(Q群 1164615798) » 2020-10-29 21:51:35

适用于该板的u-boot分支仓库:u-boot-sun7i-std-dvr
构建方法:

make CROSS_COMPILE=arm-linux-gnueabihf- A20_std_dvr_defconfig

如果你想微调一些设置的话:

make CROSS_COMPILE=arm-linux-gnueabihf- menuconfig

编译:

make CROSS_COMPILE=arm-linux-gnueabihf- -j$(nproc)

产生的bootloader是./u-boot-sunxi-with-spl.bin
产生的dtb是./arch/arm/dts/sun7i-a20-std-dvr.dtb
配合编译好的主线内核、boot.scr和rootfs就能boot了。以上简略过程是给老手看的,之后我u-boot那贴完成后会更详细步骤。

#82 全志 SOC » 淘宝20元A20行车记录仪主板运行主线u-boot & 主线Linux(Q群 1164615798) » 2020-10-29 20:35:02

SdtElectronics
回复: 22

论坛上之前已经有人玩过这块板子了:淘了一个a20行车记录仪主板,并编译lichee
我早在9月初左右就买了几块这个板子,不过开始的工作着重在导出fex和分析pin assignment上,断断续续折腾到今天才完全让主线uboot带起主线Linux进入文件系统。
本来准备先写一贴讲如何从源码构建u-boot的,不过写到一半发现稍微有点累人,所以先发一贴展示一下板子跑起主线Linux的效果:
u-boot
rootfs
其实为u-boot编写完板级配置并编译后,主要的移植工作就已经完成了。u-boot在这个板子上还有个小坑,或者坦白一点说对我而言是个大坑,之后会在u-boot那贴里详述。主线内核并没有什么移植工作要做,编译好之后和u-boot,设备树和rootfs一起烧进卡里就行了。放心,u-boot的坑并不用各位手动去填,我已经fork了主线u-boot的repo,晚些时候会连坑的patch带初步写好的板级配置一起上传,需要的坛友直接下载编译即可。
当然,目前的板级配置还比较粗糙,外设的功能还有待验证,板上的加速度传感器还没驱动起来。这些都还需要后续工作完善,坛友有精力的话也希望能contribute一下。

#83 Re: 全志 SOC » 为新设备构建主线u-boot » 2020-10-29 20:10:36

2, 编写Kconfig
Kconfig是保存u-boot和内核配置选项的文件。和编写设备树时一样,由于主线中一般有为其他相同SoC的设备编写好的Kconfig,不必深入理解他的编写规则,只需要从其他Kconfig中复制需要的配置到新建的Kconfig然后按你的设备的情况修改即可。
Kconfig文件在主线u-boot的source tree的configs/目录下。在此为你的设备新建一个Kconfig文件然后按上述方法编写,保存。

note1:
要想让lcd屏在主线Linux上工作,须在Kconfig中配置如下设置:

CONFIG_VIDEO_LCD_MODE
CONFIG_VIDEO_LCD_BL_EN
CONFIG_VIDEO_LCD_BL_PWM

示例配置(来自参考[3]):

CONFIG_VIDEO_LCD_MODE="x:800,y:480,depth:24,pclk_khz:30000,le:40,ri:40,up:29,lo:13,hs:48,vs:3,sync:3,vmode:0"
CONFIG_VIDEO_LCD_POWER="PH12"
CONFIG_VIDEO_LCD_BL_EN="PH8"
CONFIG_VIDEO_LCD_BL_PWM="PB2"

实际配置要参考你的设备情况和屏幕的datasheet编写。详见Linux-sunxi wiki上的LCD页面

#84 Re: 全志 SOC » 为新设备构建主线u-boot » 2020-10-29 16:05:38

二、编写板级配置

1, 编写设备树
设备树是主线u-boot和主线Linux中用于描述板级硬件配置信息的文件。网上关于设备树的各种文章已经很多了,可以自行阅读;但要是嫌麻烦不想读也是完全可以的,因为设备树描述文件本身是一种可读性很高的格式,主线Linux中一般又有各个SoC对应的一些设备已经写好的设备树。一般而言不会用到其余设备都没有支持的外设,所以直接参考主线中相同SoC的设备的设备树来编写你自己的就行了。当然,还是有一些基本的概念要知道:

&i2c0 {
	status = "okay";

	axp209: pmic@34 {
		reg = <0x34>;
		interrupt-parent = <&nmi_intc>;
		interrupts = <0 IRQ_TYPE_LEVEL_LOW>;
	};
};

后缀为dts的文件是设备树的源代码文件,其中有很多上面这样的结构。一个大括号构成的这样的结构叫一个node,上面这个node是描述一个i2c控制器的。一个node下面有一些属性,上面这个node还有一个child-node,描述了一个PMU。注意这个PMU child-node,他在pmic之后跟了一个@,后面有一个数字34。他还有一个属性reg,值也是34。34是PMU在这个i2c总线上的地址。在其他结构中也能找到类似的描述设备在总线上或内存中的地址的值,你需要根据你设备已知的信息进行对应的修改。
在主线u-boot的source tree中,arm设备的设备树文件在arch/arm/dts/下。你可以为你的设备新建一个dts文件,然后从其他同SoC的设备的dts文件中复制你的设备上有的设备的node过来,也可以直接复制一份其他同SoC的设备的dts文件重命名,然后根据你的设备做对应修改。这里,以一个A20 SoC的板子为例,我给他的设备树命名sun7i-a20-std-dvr.dts,参照sun7i-a20-cubietruck.dts进行编辑:
dts edit
编辑完成后,保存。但这时进行编译并不会生成新dts文件对应的dtb文件,必须要在同目录下的Makefile文件下对应的MACH段下添加你新建的dts文件的文件名,后缀改为dtb。同样还是上面A20板子的例子,A20的MACH为sun7i,添加sun7i-a20-std-dvr.dtb到Makefile的CONFIG_MACH_SUN7I段下:
Makefile edit
现在你新添加的dts文件在编译时就可以生成设备树了。

note1:
dts文件最后一行有一个换行不能省略,否则不能通过编译。也就是说dts文件最后一行一定是一个空行。

note2:
要想让lcd屏在主线Linux上工作,设备树中必须添加对pwm的支持,例如:

&pwm {
      pinctrl-names = "default";
      pinctrl-0 = <&pwm0_pin>, <&pwm1_pin>;
      status = "okay";
};

具体属性的值要根据你的设备来配置。详见参考[3]。
(待续)

#85 Re: 全志 SOC » 为新设备构建主线u-boot » 2020-10-29 13:07:36

一、准备

1, 获取设备对应的工具链
一些主流发行版上的工具链安装可以参考Linux-sunxi wiki上的Toolchain页面
更为简便的方式是,直接下载Linaro预先编译好的工具链,你需要从Armv7,32bit Armv8和64bit Armv8中选择你设备对应的那个下载然后解压。注意,如果选择使用预先编译好的工具链,你需要在每次打开用于构建的shell后,将解压后的工具链下的bin目录导入环境变量。以gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf为例:

export PATH=$PATH:/home/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf/bin

记得把路径换成你解压工具链的位置。另外,你也可以把该命令写入你使用的shell对应的rc文件(比如.bashrc)里,这样可以避免每次手动导入PATH。

2, 安装必要的依赖
需要的依赖和依赖对应的包名可能在不同发行版下略有不同。根据参考[1],Debian下的依赖可通过如下命令安装:

apt-get install swig python-dev

我的环境是Arch发行版的wsl,可能是因为之前做过有关内核编译的任务已经装上依赖了,没有执行此步也能正常构建。

3, 获取u-boot源码
进入工作路径,

git clone https://github.com/u-boot/u-boot.git

至此构建的准备工作已经完成。下面要为你的设备编写对应的板级配置(待续)

#86 全志 SOC » 为新设备构建主线u-boot » 2020-10-29 12:46:20

SdtElectronics
回复: 19

本贴主要适用于:
1、新设备的SoC及PMU等关键外设在主线中已有支持;
2、主线中缺少对新设备的板级配置的描述;
3、但目前已经可以通过其他途径(如设备自带legacy kernel的fex等)得到设备的板级硬件配置信息的情况。

满足以上条件的情况一般是你拿到了如广告机、平板之类的缺少主线支持的设备,或者自己为某SoC新设计了板子。本贴描述如何从u-boot主线源码开始为这些新设备构建bootloader.

参考资料:
[1] Linux-sunxi wiki上的U-Boot页面
[2] Porting U-Boot and Linux on new ARM boards: a step-by-step guide
[3] Enabling LCD in u-boot Kernel 4.7.2
[4] 工厂废品小爱同学mini的重生(3)——— Uboot和硬改SD卡

#87 Re: 全志 SOC » 淘了一个a20行车记录仪主板,并编译lichee » 2020-10-29 10:02:45

已通过主线u-boot带起主线内核。稍晚一些时候发贴说过程,先发一贴说适配u-boot

#88 Re: 技术人生/软件使用技巧/破解经验/技术吐槽/灌水 » script.bin文件头特征以及直接从flash镜像文件中提取的方法 » 2020-10-23 10:01:38

qianfan 说:

sunxi-tools里面有个工具script_extractor.c也可以提取, 前提是能够进入目标机器并能运行文件.

全志的代码默认将sys_config加载到43000000这个地址, 参考代码里面的配置:

```c
➜  linux-3.4 git:(r16-v2.1.y) grep -nr SYS_CONFIG_MEMBASE arch/arm/mach-sunxi
arch/arm/mach-sunxi/sun8i.c:114:unsigned int sys_config_addr = SYS_CONFIG_MEMBASE;
arch/arm/mach-sunxi/include/mach/sun8i/memory-sun8iw7p1.h:27:#define SYS_CONFIG_MEMBASE       (PLAT_PHYS_OFFSET + SZ_32M + SZ_16M)           /* 0x43000000 */
arch/arm/mach-sunxi/include/mach/sun8i/memory-sun8iw1p1.h:23:#define SYS_CONFIG_MEMBASE       (PLAT_PHYS_OFFSET + SZ_32M + SZ_16M)           /* 0x43000000 */
arch/arm/mach-sunxi/include/mach/sun8i/memory-sun8iw5p1.h:27:#define SYS_CONFIG_MEMBASE       (PLAT_PHYS_OFFSET + SZ_32M + SZ_16M)           /* 0x43000000 */
arch/arm/mach-sunxi/include/mach/sun8i/memory-sun8iw6p1.h:27:#define SYS_CONFIG_MEMBASE       (PLAT_PHYS_OFFSET + SZ_32M + SZ_16M)           /* 0x43000000 */
arch/arm/mach-sunxi/include/mach/sun8i/memory-sun8iw3p1.h:27:#define SYS_CONFIG_MEMBASE       (PLAT_PHYS_OFFSET + SZ_32M + SZ_16M)           /* 0x43000000 */
arch/arm/mach-sunxi/include/mach/sun8i/memory-sun8iw8p1.h:25:#define SYS_CONFIG_MEMBASE       (PLAT_PHYS_OFFSET + SZ_32M + SZ_16M)           /* 0x43000000 */
arch/arm/mach-sunxi/include/mach/sun8i/memory-sun8iw9p1.h:27:#define SYS_CONFIG_MEMBASE       (PLAT_PHYS_OFFSET + SZ_32M + SZ_16M)           /* 0x43000000 */
```

这个工具使用memmap读取物理地址的数据, 可以使用devmem简单的看下地址是否正确:

busybox devmem 0x43000000

之后提取出128K的文件并使用sunxi-fexc转换就行了.

实际上在尝试其他的偏门方法之前,我已经试过包括这个在内的sunxi的Wiki上提供的所有方法了。非常古怪的是,从0x43000000到0x4300fff0读出来的数据全是0xaaaaaaaa,但另一个工具,meminfo却能正常使用,不过这个只能导出dram_para section的内容。

#89 全志 SOC » 将fex (script.bin)直接转化为设备树(dts/dtb)的尝试 » 2020-10-08 12:01:44

SdtElectronics
回复: 0

fex是全志在早期平台上使用的板级配置描述文件,其编译出的bin在boot期间传递给内核。这套机制并不被现在的mainline内核支持,全志后期的平台也开始采用和mainline内核相同的设备树机制来向内核传递板级配置。如果能有简便的方法将fex直接转换为设备树文件,将极大地方便早期平台的mainline工作。linux-sunxi的Wiki页面中提到,近期的全志平台的BSP中仍然有fex的存在,并通过BSP中提供的一个特殊版本的dtc编译器编译到设备树。我尝试使这个dtc编译器能独立于BSP中的内核编译出来,然后用它做了一次fex到设备树的转换。中间有很多失败和patch暂时省略。

项目的GitHub仓库在这里

目前的进展是,已知这个编译器是依赖一种特殊的设备树文件工作的,这些依赖在仓库的./dts/目录下。其中最关键的是.${PACK_CHIP}-soc.dtb.dts.tmp文件,似乎包含了那个芯片上所有外设的设备树字段。现在只从BSP中获取到了两个SoC (sun8iw6-A83T, sun8iw15-R311)对应的这种设备树文件。我手上没有这两个芯片的设备,因此转换出来的设备树是否在主线内核上可用暂时存疑;并且想要利用这个编译器转化其他针对芯片的fex的话,应该要手动编写对应的soc.dtb.dts.tmp文件才行。

这是近几天初步的一些成果,我不太熟悉设备树这一块,手上全志的设备也不多,要是有对这项工作感兴趣的大佬参与进来就好了

#90 Re: 全志 SOC » 淘了一个a20行车记录仪主板,并编译lichee » 2020-10-04 15:57:51

巧了,我前段时间也在折腾这块板子,最后用了点“奇技淫巧”把script.bin导出来了: https://whycan.com/t_5343.html
如果有人需要我可以上传一下转换过后的fex。
另外从fex内容中看不出板子上8脚的fpc母座和空焊的无线模块焊盘连接的GPIO(fex没有gpio_para段)。目前我想到的最简单的方法就是把SoC吹下来然后挨个脚测,或者在gpio_vx里顺序把每个引脚拉高一次然后测电平,但可能是因为之前配置的方式不对或没选对引脚,没能测到电平变化。
这个板子疑似是全志的公版方案,我找到了一篇疑似其软件方案的文档:

https://wenku.baidu.com/view/5dd3cba8195f312b3069a548.html
国庆这段时间又去折腾另一张四块五的Mstar的行车记录仪板子了,那个自带系统是RTOS,前两天编译了一个疑似可用的镜像。之后有时间还要接着折腾这个A20的板子

#92 Re: 技术人生/软件使用技巧/破解经验/技术吐槽/灌水 » script.bin文件头特征以及直接从flash镜像文件中提取的方法 » 2020-09-23 09:52:44

怎么突然找不到编辑主题的按钮了,我就在跟贴里更新吧。上次我发了个在网上流传的主流方法全部失效时从debugfs提取fex字段的贴子,可以当成前情提要。其实同时我还想到另一种方法,就是用CH341之类的编程线读出SPI flash中的内容,然后直接从中读取出script.bin。读取出来的内容是一个头部有垃圾的多分区镜像,没法直接挂载的,所以提取文件又有两种途径,一是定位FAT分区的bootloader的头部然后提取出分区镜像并挂载,然后从中取出文件;二是直接定位script.bin的头部然后一步提取出文件本体。昨晚尝试第二种途径成功,就急急忙忙发贴记录了一下关键步骤。
完成一楼所述过程之后就可以编辑转换后的FEX内容或者直接把FEX再通过sunxi-fexc转换回bin了。我认为这应该是目前提取script.bin的终极方法,因为不论平台被如何精简,不论平台boot是什么过程,只要script.bin确实存在并且可以读出ROM的全部内容,这种方法就一定可行。 当然上述过程还适用于在已知头部和尾部特征的情况下从编程线读出镜像中提取任何文件。

#93 技术人生/软件使用技巧/破解经验/技术吐槽/灌水 » script.bin文件头特征以及直接从flash镜像文件中提取的方法 » 2020-09-22 23:55:44

SdtElectronics
回复: 6

通过16进制编辑器打开其他board的已有script.bin,可见其明显的文件头部特征:
1, 0x00处有1byte的未知标记,这个board的bin和我flash的bin中此标记不同,不明其含义;
2, 0x10处有ascii字符串"product",为fex的第一个section。之后的部分略。

   0     4c 00 00 00 00 00 00 00  01 00 00 00 02 00 00 00  70 72 6f 64 75 63 74 00  00 00 00 00 00 00 00 00  00 00 00 00 00 00   L...............product...............
  26     00 00 00 00 00 00 00 00  00 00 02 00 00 00 fc 02  00 00 74 61 72 67 65 74  00 00 00 00 00 00 00 00  00 00 00 00 00 00   ..................target..............

利用该特征定位到script在读出的镜像文件中的偏移量,此次提取出的镜像中2是0x66000

65FEA     ff ff ff ff ff ff ff ff  ff ff ff ff ff ff ff ff  ff ff ff ff ff ff 55 00  00 00 00 00 00 00 01 00  00 00 02 00 00 00   ......................U...............
66010     70 72 6f 64 75 63 74 00  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  02 00 00 00 56 03   product.............................V.

使用dd命令将script.bin从镜像中剪裁导出:

dd if=flash.bin of=/mnt/r/sc.bin bs=1 skip=417792 count=39016

值得注意的是:
1, dd命令的skip等数值选项接收的都是十进制参数,需将16进制偏移量换算后传入(0x66000=417792)
2, 我没有找到script.bin文件尾的特征,事实证明不将文件末端的无关部分裁去也不影响后续过程中使用sunxi-fexc转换bin为fex,因此命令的count选项完全可以不要。但由于bs设置得很低,如果不限制输出的文件的大小可能会导致整个过程较慢。
使用sunxi-fexc将bin格式转换为fex:

./sunxi-fexc -v -I bin -O fex ./sc.bin sc.fex

(待续)

#94 技术人生/软件使用技巧/破解经验/技术吐槽/灌水 » 一种通过debugfs获取sys_config(FEX)内容的方法 » 2020-09-19 15:28:30

SdtElectronics
回复: 5

论坛的大神大多数热衷于用SoC自己打板子,做正向开发,我个人倒是更喜欢“捡垃圾”,在某宝或者闲鱼上蹲一些拆机或者工厂流出的成品板。好处是这些板子大多很白菜,价格比自己打板制作的成本还低,也省了不少事;坏处是成品板IO引出少,使用不灵活,而且必须逆向出一些配置才好进一步利用。前几天我就买到几张A20的板子,便宜是真的便宜,坑也是真的坑,它的存储只有一个8M的SPI flash,因此rootfs被精简得不成样子,提取script.bin的过程也遇到了很大困难:因为进入FEL后还处在boot0状态下,无法从FEL提取;u-boot的bootdelay是0,无法进入U-boot;bootloader分区不在分区表上没法挂载;很多工具也缺失。最后Linux-sunxi的Wiki上所有的方法都试遍了也没能提取出FEX来。
本着尽量不要放弃的精神,我自己摸索出了一个dump出FEX的部分section的办法:
首先进入/sys/kernel/debug目录:

cd /sys/kernel/debug

ls一下,走运的话debugfs已经被挂载了。如果和我一样不走运,ls出来会是空的。

root@android:/sys/kernel/debug # ls
root@android:/sys/kernel/debug # 

这时候要先挂载debugfs:

mount -t debugfs none /sys/kernel/debug

再cd、ls一次,就能看到debugfs的内容了。

root@android:/sys/kernel/debug # ls
asoc
bdi
binder
gpio
hid
ion
memblock
mmc0
regmap
regulator
shrinker
suspend_stats
sys_config
usb
wakeup_sources

再进入cd sys_config目录:

cd sys_config

下面有一个dump_mainkey文件。echo想要知道的section名称进去,对应的FEX配置就会打印到内核缓冲上。以product section为例:

root@android:/sys/kernel/debug/sys_config # echo product > dump_mainkey        
[ 1560.039481] script_dump_mainkey: dump product
[ 1560.044360] =========================================================
[ 1560.051672]     name:      product
[ 1560.055522]     sub_key:   name           type      value
[ 1560.061579]                version        string    "100"
[ 1560.067666]                machine        string    "evb-v10"
[ 1560.074133] =========================================================

常用的section名称在linux-sunxi的FEX guide Wiki/ 页面上有。这个页面上的名称很可能是不全的,同时一些section在一些平台上也可能没有。利用dump出来的信息编辑你自己的FEX,就可以开始制作适用于你的设备的img了。

页脚

工信部备案:粤ICP备20025096号 Powered by FluxBB

感谢为中文互联网持续输出优质内容的各位老铁们。 QQ: 516333132, 微信(wechat): whycan_cn (哇酷网/挖坑网/填坑网) service@whycan.cn