Posts made by q1215200171
-
LicheeRV 入门开发一帖通
目录
- 板卡系列介绍
- 开箱教程
- 上手点灯
- 外设功能验证
- Debian镜像体验
- BSP SDK 开发指南
- WAFT 开发指南
1. 板卡系列介绍
LicheeRV系列是Lichee系列下的RV子系列,主要为RISC-V内核的Linux SBC产品。
目前有以下几款产品:Nezha CM, HDMI Dock, 86-Panel
购买地址为:https://item.taobao.com/item.htm?id=663345415205
交流QQ群为:488268051板卡相关开发资料已上传到百度云:
链接:https://pan.baidu.com/s/1QJTaDw6kkTM4c_GAlmG0hg
提取码:wbef2. 开箱教程
LicheeRV产品默认使用TF卡启动,不管你购买的哪款产品,请先备好tf卡与读卡器。烧录软件
全志镜像的卡烧录工具下载地址:https://dl.sipeed.com/shareURL/LICHEE/D1/SDK/
下载 PhoenixCard.rar 解压运行其中的主程序即可系统镜像
默认系统镜像已上传到百度盘,会持续更新系统镜像分为 Tina与Debian两种,Tina为专用小linux镜像,Debian为桌面级镜像
板级配置
在上述百度盘的board目录下为板级配置文件,如果底包(系统镜像)的后缀与实际板卡不符,需要再使用此fex覆盖板级配置来正确显示。烧录步骤
打开烧录软件,按顺序点击下图按键进行烧录对应镜像如果烧录的镜像后缀与板子实际型号不符,下载对应的 boot_package_XXX.fex 来覆盖板级配置
覆盖指令为:
sudo dd if=boot_package_XXX.fex of=/dev/sdX bs=1K seek=16400
前面的镜像烧录,建议使用USB3.0的读卡器烧录,此时烧录100MB的Tina镜像约用时半分钟,烧录4GB的Debian镜像,约用时10分钟。
开机启动
将上面烧录好的镜像卡,插入核心板的tf卡槽里,接好系统串口(86 panel板载了USB转串口,可以直接插对应C口),上电启动,可以在串口以115200波特率看到系统启动信息,Tina启动时间约10s,Debian启动时间久些,约2~3分钟。BusyBox v1.27.2 () built-in shell (ash) __ ___ _ __ _ / |/ /__ _(_)_ __ / / (_)__ __ ____ __ / /|_/ / _ `/ /\ \ / / /__/ / _ \/ // /\ \ / /_/ /_/\_,_/_//_\_\ /____/_/_//_/\_,_//_\_\ ---------------------------------------------- Maix Linux (Neptune, 5C1C9C53) ----------------------------------------------
以上是Tina系统进入系统终端的logo打印,出现该字样说明系统启动成功
如果你需要使用ssh登录,则默认Tina的用户名密码为 root,tina
Debian的用户名密码为 sipeed,licheepi如果你只购买了LICheRV D1核心板,需要传输大文件的话,可以使用adb进行文件传输,adb push/pull 即可。
3.点灯教程
当我们成功进入系统后,就可以进行基础的点灯操作啦!
(注:该教程不适用于86-panel,因为对应引脚连接了外设,86panel用户可以拆下核心板来操作实验)核心板的螺丝固定焊盘旁有一颗LED,查看原理图:https://dl.sipeed.com/shareURL/LICHEE/D1/HDK/Lichee_RV/2_Schematic
可知该LED连接的是PC1,换算该IO的数字标号为:2*32+1=65,或者查看IO复用情况表:
cat /sys/kernel/debug/pinctrl/2000000.pinctrl/pinmux-pins ... pin 64 (PC0): device 2008000.ledc function ledc group PC0 pin 65 (PC1): UNCLAIMED pin 66 (PC2): UNCLAIMED pin 67 (PC3): UNCLAIMED pin 68 (PC4): UNCLAIMED pin 69 (PC5): UNCLAIMED pin 70 (PC6): UNCLAIMED pin 71 (PC7): UNCLAIMED
我们先导出该GPIO:
echo 65 > /sys/class/gpio/export cd /sys/class/gpio/export/gpio65
然后再将该IO置为输出状态,即可操作其电平:
echo out>direction echo 1 > value #LED点亮 echo 0 > value #LED熄灭
至此我们就成功在RISC-V 64 D1上点灯啦~
你也可以对 串行RGB LED WS2812 进行花式点灯:
cd /sys/class/leds/ echo 255 > sunxi_led0r/brightness;echo 0 > sunxi_led0g/brightness;echo 0 > sunxi_led0b/brightness; echo 0 > sunxi_led0r/brightness;echo 255 > sunxi_led0g/brightness;echo 0 > sunxi_led0b/brightness; echo 0 > sunxi_led0r/brightness;echo 0 > sunxi_led0g/brightness;echo 255 > sunxi_led0b/brightness;
4. 外设功能验证
4.1 音频功能
录音设备查看
root@MaixLinux:~# arecord -l **** List of CAPTURE Hardware Devices **** card 0: audiocodec [audiocodec], device 0: SUNXI-CODEC 2030000.codec-0 [] Subdevices: 1/1 Subdevice 0: subdevice 0 card 1: snddmic [snddmic], device 0: 2031000.dmic-dmic-hifi dmic-hifi-0 [] Subdevices: 1/1 Subdevice 0: subdevice 0 card 2: sndhdmi [sndhdmi], device 0: 2034000.daudio-audiohdmi-dai 20340a4.hdmiaudio-0 [] Subdevices: 1/1 Subdevice 0: subdevice 0
播放设备查看
root@MaixLinux:~# aplay -l **** List of PLAYBACK Hardware Devices **** card 0: audiocodec [audiocodec], device 0: SUNXI-CODEC 2030000.codec-0 [] Subdevices: 1/1 Subdevice 0: subdevice 0 card 2: sndhdmi [sndhdmi], device 0: 2034000.daudio-audiohdmi-dai 20340a4.hdmiaudio-0 [] Subdevices: 1/1 Subdevice 0: subdevice 0
录放音测试:
arecord -D hw:1,0 -f S16_LE -t wav -d 3 t.wav aplay -D hw:0,0 t.wav
另外可以使用alsamixer 进行音量调整
4.2 USB功能
默认内核支持外挂U盘的驱动,插上U盘后可以使用 fdisk -l 查看到新增的 /dev/sda
如果U盘没有被格式化,可以使用mkfs.vfat指令来格式化U盘,再使用mount指令挂载
默认Tina固件里的 /dev/mmcblk0p8 分区即可使用上述方式格式化后挂载,来提升可用空间4.3 有线网络
LicheeRV-86 Panel 支持百兆网络,使用套餐附送的网线尾线接上网线后,执行以下指令来连接有线网络ifconfig eth0 up udhcpc -ieth0
4.4 无线网络
LicheeRV 底板默认使用XR829或者RTL8723BS wifi模块,可以使用以下指令进行联网操作:先配置热点信息:
vim /etc/wifi/wpa_supplicant.conf network={ ssid="Sipeed_2.4G" psk="XXXX" } 配置完成后重启
,ifconfig wlan0 up; udhcpc -iwlan0 即可连上对应的wifi。
连上网络后,你就可以使用ssh远程登录板卡,或者使用scp来进行文件传输啦~4.5 屏显触摸
LicheeRV系列支持以下显示屏:SPI屏 1.14寸屏(TODO) RGB屏 4.3寸 480x272;5.0寸 800x480; RGB+SPI屏 4.0寸 480x480(st7701s); 4.0寸 720x720(nv3052c) MIPI屏 8.0寸 1280x720(ILI9881C)
Tina下可以通过以下指令测试屏幕显示:
fbviewer xxx.jpg
如果需要调试屏幕驱动,可以使用以下指令查看屏幕驱动信息:cat /sys/class/disp/disp/attr/sys screen 0: de_rate 300000000 hz, ref_fps:60 mgr0: 480x480 fmt[rgb] cs[0x204] range[full] eotf[0x4] bits[8bits] err[0] force_sync[0] unblank direct_show[false] iommu[1] dmabuf: cache[0] cache max[0] umap skip[0] overflow[0] lcd output backlight( 50) fps:59.5 esd level(0) freq(60) pos(0) reset(0) 480x 480 err:0 skip:184 irq:230715 vsync:0 vsync_skip:0 BUF enable ch[1] lyr[0] z[16] prem[N] a[globl 255] fmt[ 0] fb[ 480, 480; 480, 480; 480, 480] crop[ 0, 0, 480, 480] frame[ 0, 0, 480, 480] addr[ffe00000, 0, 0] flags[0x 0] trd[0,0]
屏幕彩条测试:echo 1 > /sys/class/disp/disp/attr/colorbar
如果你购买的是86面板套餐,可以使用 ts_test进行触摸测试
(注意触摸驱动有瑕疵,ts_test测试时松开后,光标会不动,但是终端仍会正常打印信息)4.6 视频播放
最终我们可以尝试在LicheeRV上播放BadApple啦~
Tina镜像中内置了ffmpeg软件包,ffmpeg是强大的多媒体库,可以用于录屏或者播放
录屏指令:ffmpeg -f fbdev -framerate 10 -i /dev/fb0 record.avi
播放指令(分别是扬声器播放音频和hdmi播放音频):ffmpeg -i /mnt/UDISK/badapple_640480_xvid.mp4 -pix_fmt bgra -f fbdev /dev/fb0 -f alsa hw:0,0 ffmpeg -i /mnt/UDISK/badapple_640480_xvid.mp4 -pix_fmt bgra -f fbdev /dev/fb0 -f alsa hw:2,0
这里由于是CPU软解,所以测试最高分辨率约为720x540, 再高会变卡
4.7 麦克风阵列
如果你使用的是dock板,那么还可以外接麦克风阵列版进行声场成像演示:
直接执行debian系统下内置的micarr_0609指令即可
有麦克风阵列相关的二次开发需求,可以联系support@sipeed.com5. Debian镜像体验
对于只接触过桌面级系统的开发者,推荐使用Debian镜像,可在上面的网盘里下载
LicheeRV_Debian_86_480p 为 480p的86盒板卡的debian镜像
LicheeRV_Debian_hdmi 为 dock的hdmi输出的debian镜像
如果是其他板卡或者屏幕,请自行使用对应的fex覆盖板级配置。
烧录完成后,插卡启动,稍等2分钟左右,屏幕上就会显示登录界面输入用户名 sipeed,密码 licheepi,即可进入桌面 (使用USB HOST口外接键鼠输入)
进入桌面后可以进行一些基础操作
接下来让我们尝试在Debian下跑一下Hello World:
另有720P高清屏的效果对比,有米的同学可以考虑入手:
6.BSP SDK 开发指南
为了方便用户自行开发,矽速整理发布了 LicheeRV 的bsp开发docker镜像,大家使用该镜像可以快速开始D1的系统级开发。
在网盘中下载对应的docker文件后,解压到tar文件,
docker import licheerv_d1_compile.tar licheerv_d1_compile:lastest
然后即可run该容器,用户名为nihao,密码为sipeed123
进入容器后的基础编译操作为:cd ~/sdk/tina-d1-open_new/ source build/envsetup.sh lunch //选1 make menuconfig //去掉里面的 alsa-plugin选项,否则编译不过 make -j96 #按实际核数编译 pack # 打包
SDK内置了一些版型的dts,你可以自行选择编辑:
device/config/chips/d1/configs/nezha/board_xxx.dts其他SDK的开发说明,可以参见全志开发平台上下载的相关文档
https://open.allwinnertech.com/
也可以加全志交流QQ群:498263967如果需要自己下载SDK开发,参考全志在线相关网页:https://d1.docs.aw-ol.com/en/
7.WAFT 开发指南
TODO转载自Sipeed社区:https://bbs.sipeed.com/thread/1300
-
Reply: 【单板仅需99】D1哪吒计算条上线!为智能家居提供高性价比的RISC-V算力
淘宝已上线,Sipeed Lichee RV 86 Panel 智能家居 中控开发板 支持Linux WAFT
https://item.taobao.com/item.htm?id=663345415205
你的86 @jordonwu -
【FAQ】Wi-Fi/BT MAC地址定制
问题背景
很多Wi-Fi/BT模组默认出厂是不带MAC地址的,整机厂需要根据需求,烧写特定的MAC地址。MAC地址通路
Linux-4.9后,全志平台模组MAC地址定制流程如下系统启动后,引导程序会加载env中定义的key,并传递给cmdline和内核dts。 如果安全存储中没有mac/wifi_mac/bt_mac这几个key,或者值解析失败,我们将尝试从私有分区加载并解析这些key。在内核空间中,addr_mgt驱动程序读取cmdline或dts中与Mac相关的键,对其进行解析并导出到其他驱动程序以使用。 为了让用户空间可以访问这些地址值,创建了sysfs来保存地址值。
配置
uboot env
请确保env.cfg中有如下配置项存在:
dts
dts配置参考如下
其中,type_addr_xx表示mac地址的来源,值含义如下- 0: 不指定类型
- 1: 使用烧写的mac地址
- 2: 使用chipid生成的mac地址
- 3: 使用sysfs写入的地址
- 其他: 不提供地址
烧写
使用全志烧号工具DragonSN或DragonKey烧写mac/wifi_mac/bt_mac到私有分区或secure storge中。合法的mac地址格式为xx:xx:xx:xx:xx:xx, x 是16禁止值,0-9,a-f。使用
内核空间
Linux-4.9
- 读取Wi-Fi MAC地址
int get_wifi_custom_mac_address(char *addr_str)
- 读取BT MAC地址
int get_bt_custom_mac_address(char *addr_str)
- 读取以太网 MAC地址
int get_eth_custom_mac_address(char *addr_str)
Linux-5.4
int get_custom_mac_address(int fmt, char *name, char *addr)
fmt: 0为str,1为16进制值
name: “wifi”、“bt”、“eth”用户空间
可以通过sysfs文件节点访问对应值,linux-4.9下主要节点如下:root@venus-a1:/sys/class/addr_mgt# ls -l total 0 -rw-r--r-- 1 root root 4096 2019-01-15 17:22 addr_bt -rw-r--r-- 1 root root 4096 2019-01-15 17:22 addr_eth -r--r--r-- 1 root root 4096 2019-01-15 17:22 addr_type -rw-r--r-- 1 root root 4096 2019-01-15 17:22 addr_wifi
linux-5.4下主要节点如下:
console:/ # ls -l /sys/class/addr_mgt/ total 0 -rw-r--r-- 1 bluetooth net_bt_admin 4096 2020-12-22 19:33 addr_bt -rw-r--r-- 1 root root 4096 2020-12-23 13:10 addr_eth -rw-r--r-- 1 root root 4096 2020-12-23 13:10 addr_wifi -r--r--r-- 1 root root 4096 2020-12-23 13:10 summary
-
【FAQ】全志D1芯片 XR829扫卡失败问题排查
【问题背景】
硬件:D1 + Wi-Fi模组(XR829)
软件:melis-v3.0
说明:该FAQ旨在记录【问题简述】
WiFi初始化指令执行后无法扫描到WiFi模组,即扫卡失败【问题分析】
1、首先排查硬件问题,其次再找软件bug
(1)同样的模组在跑其它软件时,可以正常工作;排除模组的硬件问题
(2)定位软件bug,扫卡失败发生WiFi初始化阶段12、解软件bug
WiFi模组引脚图:
(1)WiFi初始化时会对模组进行上电
可能时上电时序不对导致扫卡失败,检查REG_ON引脚时序
REG_ON:执行WiFi初始化指令后,引脚时序应该是10ms高电平-》20ms低电平-》高电平
(2)上电时序没有问题的话,检查sdio驱动问题,因为扫卡是通过是sdio下发cmd5
使用其它sdio设备,看其是否可以正常工作;使用sd卡测试,可以正常工作,说明不是sdio的驱动问题检查指令引脚是否发出cmd5——SDIO CMD:在扫卡阶段检查该引脚是否有cmd5命令输入
cmd5命令格式如下:
检查扫卡频率是否正常——SDIO CLK:在扫卡阶段,该引脚应该是输入4KHZ、50%占空比的方波。
3、在检查这两个大方向后还是无法排查问题
(1)和正常模组对比差异点
最后发现LPO引脚输入24MHZ时钟,而正常模组为高电平;将该引脚修改为高电平,可以正常扫到卡。【解决方法】
LPO引脚正常情况下是输入32.768KHZ的震荡信号作为低功耗时使用,故输入32.768KHZ时钟;测试可以正常扫到卡。
执行wifi初始化指令后,设置LPO输入32KHZ、50%占空比的方波 -
【FAQ】全志D1芯片 如何移植 rtl8821cu wifi 驱动到 Linux-5.4内核?
问题背景
移植 Linux-4.9 或之前的内核版本下的 wifi 驱动到 Linux-5.4 内核版本时会出现编译和运行错误,
该 FAQ 主要用于帮助开发人员解决驱动移植出现的问题。问题分析
移植到 Linux-5.4 内核版本时出现如下编译错误:WARNING: module 8821cu uses symbol kernel_read from namespace VFS_internal_I_am_really_a_filesystem_and_am_NOT_a_driver, but does not import it.
出现该 WANRING 的原因是内核版本升级后文件系统存在差异,驱动调用了 kernel_read()和 kernel_write() 函数,需要进行 import VFS_internal_I_am_really_a_filesystem_and_am_NOT_a_driver 处理。
移植到 Linux-5.4 内核版本时运行的错误:
出现该问题的原因是 Linux-5.3 及以后版本 cfg80211.h 里结构体 wiphy_vendor_command 新增了变量 policy 和 maxattr。解决方案
出现编译错误时解决方案如下:
diff --git a/drivers/net/wireless/rtl8821cu/os_dep/linux/os_intfs.c b/drivers/net/wireless/rtl8821cu/os_dep/linux/os_intfs.c index c4b515763ac8..466a3b27f521 100644 --- a/drivers/net/wireless/rtl8821cu/os_dep/linux/os_intfs.c +++ b/drivers/net/wireless/rtl8821cu/os_dep/linux/os_intfs.c @@ -28,6 +28,8 @@ MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Realtek Wireless Lan Driver"); MODULE_AUTHOR("Realtek Semiconductor Corp."); MODULE_VERSION(DRIVERVERSION); +MODULE_IMPORT_NS(VFS_internal_I_am_really_a_filesystem_and_am_NOT_a_driver);
出现运行错误时解决方案为遍历驱动里所有使用 wiphy_vendor_command 结构体并修改如下:
diff --git a/drivers/net/wireless/rtl8821cu/os_dep/linux/rtw_cfgvendor.c b/drivers/net/wireless/rtl8821cu/os_dep/linux/rtw_cfgvendor.c index 6bac27d37766..09e24dcc9480 100644 --- a/drivers/net/wireless/rtl8821cu/os_dep/linux/rtw_cfgvendor.c +++ b/drivers/net/wireless/rtl8821cu/os_dep/linux/rtw_cfgvendor.c @@ -1756,6 +1756,11 @@ static const struct wiphy_vendor_command rtw_vendor_cmds[] = { }, .flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV, .doit = rtw_cfgvendor_gscan_get_capabilities +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 3, 0)) + , + .policy = VENDOR_CMD_RAW_DATA, + .maxattr = 1 +#endif },
-
【FAQ】全志D1芯片 Tina 如何查看通过 procd init 脚本启动的应用输出到 stdout/stderr 的打印信息?
问题描述
当我们使用 procd init 脚本让某个应用程序实现开机自启时,会发现应用程序中原本通过 printf/fprintf 等输出到 stdout/stderr 的打印信息都无法从串口或 adb shell 中看到了。
这些打印默认是输出到什么地方?我们可以如何看到这些打印?
原因
一般情况下,当用户在终端中执行命令来运行某个应用程序时,stdin/stdout/stderr 就确定下来是在当前终端,因此应用程序的打印信息自然能从当前终端中显示出来。
而如果该应用程序是通过 procd init 脚本进行开机自启,它会被认为是一个守护进程(daemon)。守护进程是随系统自启的,它们有可能在用户登录终端之前就已经开始运行了,也无法得知用户是从哪个终端登录,因此也就无法将打印信息输出到用户所在的终端。
解决方法
一般来说,要获取守护进程的打印,需要通过 syslog 之类记录系统整体日志的方法。procd init 脚本也提供了方法将应用程序的打印重定向到 syslog 中。
下面是一个简单的 procd init 脚本例子,它会启动应用程序 /usr/bin/foobar,但我们默认没法看到 foobar 输出到 stdout/stderr 的打印:
#!/bin/sh /etc/rc.common START=50 USE_PROCD=1 start_service() { procd_open_instance procd_set_param command /usr/bin/foobar procd_close_instance }
通过增加“procd_set_param stdout 1”和“procd_set_param stderr 1”两个参数,可将其输出到 stdout/stderr 的内容重定向到 syslog:
#!/bin/sh /etc/rc.common START=50 USE_PROCD=1 start_service() { procd_open_instance procd_set_param command /usr/bin/foobar procd_set_param stdout 1 # 将其 stdout 的内容重定向到 syslog procd_set_param stderr 1 # 将其 stderr 的内容重定向到 syslog procd_close_instance }
如此设置后,就可以从 syslog 中看到 foobar 应用程序输出的打印。
-
【FAQ】全志D1芯片 如何解决Gstreamer:fb UI旋转(直接修改内核参数)后,sunxifbsink显示异常问题?
1、问题背景
客户在使用D1做项目开发时,通过直接修改内核参数的方法来旋转fb以达到旋转UI的目的(如下图所示),但此修改会导致sunxifbsink中获取到的视频层信息也随之改变,影响显示效果。2、解决方法
需要重置视频层的分辨率为屏幕物理分辨率,改动如下图所示: -
【FAQ】全志D1芯片 如何对D1主频进行调节?
【问题背景】
在D1项目开发的过程中,有时候需要调节CPU主频,以对一些场景、功耗或性能进行测试,故对主频的调节方法进行介绍,方便大家后面调主频【适用场景】
硬件:D1芯片平台 软件:Tina系统【基本操作】
1.在烧了Tina固件的D1开发板上可以看到调频相关节点:
2.在系统启动log中,可以看到默认频率,一般是1G
3.查看内核文件发现SDK提供了RV的调频代码,看来可以将调频模块加载起来
4.打开内核相关配置:make ARCH=riscv menuconfig,选择:CPU Power Management ---> CPU Frequency scaling ---> ARCH RISCV Allwinner nvmem based SUN50I CPUFreq driver
如图:
5.查找调频相关的dts中频率表的配置,只有1008M,可以进行超频修改,比如改到1.3G
6.重新编译打包烧写,可以跑到1.3G
-
【FAQ】全志D1芯片 如何解决Gstreamer播放1080视频显示异常问题(重影)?
1、问题描述
Gstreamer升级后测试发现,在播放1080P视频时,画面会出现重影现象。2、问题分析
通过抓取图像数据以及对log分析,怀疑与VE的数据对齐有关,经验证确实如此。在解码的时候,VE会做16位对齐,所以1080P解码出来的数据除了需要将frame buffer的宽高设置给显示外,还需要做crop处理。而造成重影现象的原因就是在做crop处理前未将frame buffer的真实宽高设置给显示。3、解决方法
在omx层第二次设置输出状态时,图像宽高为对齐后的宽高,sunxifbsink做crop处理;见附件。将附件中的0008 patch放在tina/package/multimedia/gst1-omx/patches目录下,然后用附件中的gst-plugins-aw-1.8.2.tar.x替换dl目录下的同名文件。
gst-plugins-aw-1.8.2.tar.xz
0008-Solve-the-ghosting-problem-of-sunxifbsink-display-du.patch -
【FAQ】全志D1芯片 如何在 Linux Device Tree 中配置预留内存?
前言
有时我们需要在 Linux 内核中预留一部分内存空间用作特殊用途(给安全模块使用,给其它处理器使用,或是给特定的驱动程序使用等),在 Device Tree 中有提供两种方法对预留内存进行配置:memreserve 和 reserved-memory。
memreserve
memreserve 的使用方法比较简单,如下所示,会将从地址 0x40000000 开始共 1MB 的内存空间预留出来:
/memreserve/ 0x40000000 0x00100000;
使用 memreserve 预留出来的内存一般无法再被 Linux 系统使用(当然,也可以通过特殊方法让代码固定访问该地址,但这种并非标准用法,在此不展开描述)。
reserved-memory
reserved-memory 框架提供了更多样的使用方法,并且与内核的 DMA API 和 CMA 框架紧密联系。
推荐先阅读一下内核自带的文档 Documentation/devicetree/bindings/reserved-memory/reserved-memory.txt,里面有其详细的语法说明和注意事项(例如 reserved-memory 节点中的 #address-cells 和 #size-cells 的值需要与根节点的保持一致)。
下面对几种常见的使用方法进行举例说明:
通过 memremap/ioremap 来使用
在 Device Tree 配置如下,然后通过“memory-region”参数可将该预留内存分配给特定的设备驱动使用:reserved-memory { #address-cells = <2>; #size-cells = <2>; ranges; foobar_reserved: foobar@70000000 { no-map; reg = <0x0 0x70000000 0x0 0x10000000>; }; }; foobar_driver: foobar_driver@0 { memory-region = <&foobar_reserved>; };
在设备驱动程序中,可解析 Device Tree 节点获得预留内存的物理地址和大小,然后通过 memremap/ioremap 映射这片内存空间来使用:
/* Get reserved memory region from Device-tree */ np = of_parse_phandle(dev->of_node, "memory-region", 0); if (!np) { dev_err(dev, "No %s specified\n", "memory-region"); goto error1; } rc = of_address_to_resource(np, 0, &r); if (rc) { dev_err(dev, "No memory address assigned to the region\n"); goto error1; } lp->paddr = r.start; lp->vaddr = memremap(r.start, resource_size(&r), MEMREMAP_WB); dev_info(dev, "Allocated reserved memory, vaddr: 0x%0llX, paddr: 0x%0llX\n", (u64)lp->vaddr, lp->paddr);
通过 DMA API 来使用
设置“shared-dma-pool”属性后,可让设备驱动通过 DMA API 来使用预留内存:
reserved-memory { #address-cells = <2>; #size-cells = <2>; ranges; foobar_reserved: foobar@70000000 { compatible = "shared-dma-pool"; no-map; reg = <0x0 0x70000000 0x0 0x10000000>; }; }; foobar_driver: foobar_driver@0 { memory-region = <&foobar_reserved>; };
设备驱动程序中可类似常规地使用 DMA API,它申请的内存不是来源于默认的 CMA 内存池,而是来源于该预留内存:
/* Initialize reserved memory resources */ rc = of_reserved_mem_device_init(dev); if(rc) { dev_err(dev, "Could not get reserved memory\n"); goto error1; } /* Allocate memory */ dma_set_coherent_mask(dev, 0xFFFFFFFF); lp->vaddr = dma_alloc_coherent(dev, ALLOC_SIZE, &lp->paddr, GFP_KERNEL); dev_info(dev, "Allocated coherent memory, vaddr: 0x%0llX, paddr: 0x%0llX\n", (u64)lp->vaddr, lp->paddr);
给 CMA 预留内存
有时我们不需要将预留内存分配给特定的设备驱动,而只是想给默认 CMA 内存池分配一片固定的内存区域,这时我们可配置上“reusable”和“linux,cma-default”:
reserved-memory { #address-cells = <2>; #size-cells = <2>; ranges; linux,cma { compatible = "shared-dma-pool"; reusable; reg = <0x0 0x70000000 0x0 0x10000000>; linux,cma-default; }; };
由此可见,不同于 memreserve,通过 reserved-memory 预留的内存有可能进入系统 CMA,这需要满足以下几个条件:
- compatible 需要为“shared-dma-pool”
- 没有定义“no-map”属性
- 定义了“reusable”属性
-
【FAQ】全志D1芯片 如何解决Audiocodec使用S24_LE格式进行录音,软件分析波形异常的问题?
问题背景
硬件:R329
软件:Tina
内核:Linux-4.9问题描述
使用Audiocodec进行录音,格式S24_LE,录制的.wav波形在某些软件中异常
arecord -D hw:audiocodec -f S24_LE -r 16000 -c 2 -d10 /tmp/test3_S24_LE.wav
需要放大很多倍才能看到声音波形问题分析
1.R329的Audiocodec用于录音的ADC只支持16bit和20bit的采样精度。采样后的数字信号会存放到RX_FIFO中,RX_FIFO的大小为256*20-bit,其他平台可以在User Manual确认支持的采样精度,从而判断是否会有这个问题产生
2.RX_DATA是一个32位的寄存器,保存的是从RX_FIFO获取的一个channel的样本数据,当使用arecord进行录音时,RX_DATA中的值会经DMA搬至内存,最后保存到.wav中
其中RX_DATA有四种模式去获取RX_FIFO的数据,S24_LE和S32_LE均采用20-bit mode0
当设置了20bit采样精度时,对应的两种模式如下图所示:
3.先说明一下S24_LE和S32_LE这两种格式的区别
S24_LE指有符号整型,范围是-2^23 ~ ((2^23) - 1),有效数据在低24位
S32_LE指有符号整型,范围是-2^31 ~ ((2^31) - 1),有效数据在高24位LSB MSB 1st byte 2nd byte 3rd byte 4th byte alignment S32_LE: 00000000 xxxxxxxx xxxxxxxx xxxxxxxx 32 bits S24_LE: xxxxxxxx xxxxxxxx xxxxxxxx 00000000 32 bits S24_3LE: xxxxxxxx xxxxxxxx xxxxxxxx 24 bits
4.在驱动程序中,S24_LE和S32_LE虽然都支持,但他们两者都是使用20-bit的mode0,这导致这两种格式保存到文件中的数据排布是一致的,但生成的wav头信息中的采样位数则不一样,从下图可以看出两者的差异
S32_LE的wav文件信息:
若软件以S32_LE进行解析,以上红框的数据变为0x0f80f0,依然可以保留全部有效数据
S24_LE的wav文件信息:
若软件以S24_LE进行解析,以上红框的数据变为0x55f000,便会丢失一部分数据
解决方案
总结原因就是audiocodec的采样精度只支持16和20bit,因此PCM格式中S24_LE虽然也支持,但硬件的特性使驱动并不能做到很好的适配,若软件以标准S24_LE格式进行分析,则会丢失高位的有效数据,这取决于软件如何对数据进行分析,解决方法有以下三种
- 使用audiocodec时,使用-f S32_LE,修改wav头信息中的采样位数位32,这对大部分软件都有效
arecord -D hw:-f S32_LE -r 16000 -c 2 -d10 /tmp/test32.wav
- 如果必须使用S24_LE格式进行录音,可以选择其他支持24bit采样的音频接口,如I2S等
- 假如必须使用audiocodec声卡,S24_LE格式进行录音,可以自行调整RX_DATA寄存器的模式,结合RX_DATA寄存器中实际的有效数据分布,自己开发软件进行数据分析
如果有分析和处理音频数据的需求,可以参考以上思路,结合RX_DATA寄存器去调整
-
【FAQ】全志D1芯片 mp4(Xvid)视频文件播放花屏问题
1、问题背景
客户使用F133进行一体广告机项目开发过程中,测试到附件中的片源《少女时代OhMVFullHD1080(播放花屏).mp4》播放时会出现花屏现象。但在之前的C800相同项目中该片源测试正常。2、问题描述
出错第一帧标号为17(标号从0开始),如下图所示:抓取该帧花屏图像,现象如下:
花掉的图像帧数据如下:
3、问题分析
(1)关掉cache,花屏现象仍旧,排除漏刷cache影响;
(2)64位系统(tina/melis)播放均花屏,32位系统正常;
(3)寄存器对比,未发现异常;
(4)在FbmRequestBuffer中将请求到的buffer清零,图像仍会花掉,但是花屏现象如下:
推测在视频播放的过程中,该部分未有数据写出,仍然保留该buffer中上一帧的数据。(未修改代码前出错第一帧下半部分之所以是黑色的,是因为该视频前面的12帧均为黑色图像帧,所以该buffer中残留有上一帧的数据)
最后,通过添加打印发现,正常情况(R528平台),在未解码完一帧时,会通过检查同步标记函数而进入下一个packet的解码 ,但是现在异常(F133/D1)情况下跑到了else里面,导致一帧图像没有解码完,就解下一帧了。 正好前面抓图的现象也是第一帧出错的图像,下半部分是前面图像的数据残余。
经分析,该问题的根本原因是在64位编译器中,i>>32 都等于i;而在32位编译器中,i>>32 都等于0。所以此发现也正好解释了之前的测试结果 “64位系统(tina/melis)播放均花屏,32位系统正常”。
4、解决办法
对出现右移32位的情况做判断,即return (rbit-n)<0?0:((rbit-n)>=32?0:(ld->bit_a & (0xFFFFFFFF >> (ld->bitcnt))) >> (rbit-n));对应的库文件见附件。
library.7z -
【FAQ】全志XR806芯片FAQ汇总(你不知道的和你想知道的的这里都有)
01、【FAQ】全志XR806芯片 系统异常重启如何解决?
02、【FAQ】全志XR806芯片 如何更换打印log口?
03、【FAQ】全志XR806芯片 为什么开机时候串口以及部分gpio会有电平跳变?
04、【FAQ】全志XR806芯片 mac地址要如何存放以及如何获取?
05、【FAQ】全志XR806芯片 固件烧录完成后,程序不是预期烧录程序或者甚至无法启动如何解决?
06、【FAQ】全志XR806芯片 如何使用timer命令行命令?
07、【FAQ】全志XR806芯片 如何修改中断优先级?
08、【FAQ】全志XR806芯片 如何设置AP模式默认IP地址、子网掩码、网关?
09、【FAQ】全志XR806芯片 低功耗蓝牙BLE断开连接错误码和分析?
10、【FAQ】全志XR806芯片 select引发崩溃如何解决?
11、【FAQ】全志XR806芯片 如何打开 LwIP 调试信息?
12、【FAQ】全志XR806芯片 执行扫描动作时,偶尔会扫描不到目标的AP如何解决?
13、【FAQ】全志XR806芯片 如何解决编译错误undefined reference to?
14、【FAQ】全志XR806芯片 standby模式下gpio的电平状态是如何变化?
15、【FAQ】全志XR806芯片 如何清除扫描列表缓存?
16、【FAQ】全志XR806芯片 RTC时钟不能跑、有时候时间不准是什么原因?
17、【FAQ】全志XR806芯片 如何使用watchpoint功能?
18、【FAQ】全志XR806芯片getsockopt、setsockopt失败如何解决?
19、【FAQ】全志XR806芯片 串口修改波特率后与目标波特率不匹配如何解决?
20、【FAQ】全志XR806芯片 Xradio Skylark 中的无线网络回调事件含义
21、【FAQ】全志XR806芯片 如何使用phoenixMC软件把应用固件与etf固件合并在一起?
22、【FAQ】全志XR806芯片 RF参数是如何保存的?
23、【FAQ】全志XR806芯片 汇编代码调试技巧
24、【FAQ】全志XR806芯片 如何解决第三方静态库函数符号重复?
25、【FAQ】全志XR806芯片 如何创建自定义状态回调函数?
26、【FAQ】全志XR806芯片 如何获取ap的rssi值?
27、【FAQ】全志XR806芯片 XR806如何添加本地音频到flash?
28、【FAQ】全志XR806芯片 XR MCU的外设驱动为什么修改无效?
29、【FAQ】全志XR系列 XRMCU如何播放xip中的音频?
30、【FAQ】全志XR系列 XRMCU如何修改录音编码器的输入数据?
31、【FAQ】全志XR系列 如何调试wifi频偏问题?
32、【FAQ】全志XR系列 如何统计XRMCU的内存使用情况
33、【FAQ】全志XR系列 设置音频结构体HttpStreamBufferConfig成员有什么意义?
34、【FAQ】全志全系列芯片 APST平台无法下载或者更新工具
35、【FAQ】全志XR806 Freertos+XRMCU+ADC采样频率偏低
36、【FAQ】全志XR806 Freertos + XRMCU +ADC采样数值不准
37、【FAQ】全志系列芯片如何把flash擦成空片?
38、持续更新.... -
【FAQ】全志XR806芯片 XR MCU的外设驱动为什么修改无效?
1.问题背景
为了节省更多的flash空间,在xr872/xr808/xr806芯片上把大部分的驱动都已经做了rom化处理,即出厂的时候已经把外设驱动都集成到rom当中,因此默认sdk中驱动的代码是不能修改的。而客户有时候调试需要在驱动层添加一些打印信息或者修改外设驱动的配置,此时则需要舍弃rom化的驱动,自己在源代码上添加相应的驱动了。2.问题描述
XR MCU的外设驱动为什么修改无效。3.解决办法
- 先舍弃rom化驱动的代码:在lib\xradio_v2目录下的rom_symbol.ld文件,删除所要修改的模块的驱动相关的函数。注,xr806的rom_symbol.ld文件在lib\xradio_v3文件夹。
- 在src\driver\chip这个目录相应的外设模组上的源代码文件填上所需的函数,一般情况下把src\rom\rom_bin\src\driver\chip相关模组的函数移植过去即可,因为该目录下的驱动只提供客户查看,并没有编译到sdk镜像里面,真正编译到镜像的是src\driver\chip里面的函数。
-
【FAQ】全志XR806芯片 XR806如何添加本地音频到flash?
问题背景
XR806的文件管理系统是littlefs或spifs,不像fatfs可以直接进行文件传输,有客户放映不清楚如何通过文件管理系统调用音频文件。问题描述
XR806SDK中的audio_demo找不到本地mp3等音频文件。问题分析
- 因为XR806的文件管理系统是littlefs或者spifs,所以需要在PC本地把文件打包成littlefs文件系统格式,否则无法识别。打包工具是SDK下的tools/fs_img_tools/mklittlefs。
- 上一步打包好的文件,如果通过“section”的方式打包进img镜像会有64byte的偏移,所以需要用“raw_bin”的方式进行打包。
解决方法
打包音频文件
新建一个文件夹,如data(名称随意),并把目标音频文件存放仅该目录下,值得注意的是因为audio_demo中默认播放的的是music文件夹下的音频文件,所以音频文件也必须放在music文件夹下。. ├── data │ └── music │ └── 1.mp3 └── mklittlefs
打包该文件夹使用如下命令
./mklittlefs -c data/ -d 0 -b 4096 -p 256 -s 524288 lfs.bin
-c后接目标路径。
-d后接debug等级,默认为0,不用修改。
-b后接block的大小,littlefs默认为4096,一般情况下不用修改。
-p后接page大小,默认为256,不用修改。
-s后接littlefs镜像大小,和在make menuconfig中的配置必须一致。
lfs.bin是生成的镜像文件名。名称随意,但一般是.bin后缀。make menuconfig配置
进入图形化界面配置,并选中filesystem support后选项配置如下。推荐勾选上flash filesystem image pack support,编译代码后会自动把lfs.bin打包到镜像,否则只能在phoenixMC的调试界面中擦除flash地址1572864(0x18000)后的内容,并手动把lfs.bin写进flash。其中步骤1所说的镜像大小524288就是由2048*1024-1572864而来。--- filesystem support [*] flash filesystem image pack support FileSystem Type Select (LittleFS) ---> (1572864) little filesystem start address (4096) little filesystem block size (128) little filesystem block count
修改工程cfg文件配置
把前面打包好的lfs.bin复制到project/demo/audio_demo/image/xr806目录下,并修改目录下的image.cfg。{ "magic" : "AWIH", "version" : "0.5", "image" : {"max_size": "1532K"}, "section" :[ {"id": "0xa5ff5a00", "bin" :"boot_40M.bin", "cert": "null", "flash_offs": "0K", "sram_offs": "0x00230000", "ep": "0x00230101", "attr":"0x1"}, {"id": "0xa5fe5a01", "bin" :"app.bin", "cert": "null", "flash_offs": "71K", "sram_offs": "0x00201000", "ep": "0x00201101", "attr":"0x1"}, {"id": "0xa5fd5a02", "bin" :"app_xip.bin", "cert": "null", "flash_offs": "104K", "sram_offs": "0xffffffff", "ep": "0xffffffff", "attr":"0x2"}, {"id": "0xa5fa5a05", "bin" :"wlan_bl.bin", "cert": "null", "flash_offs": "1075K", "sram_offs": "0xffffffff", "ep": "0xffffffff", "attr":"0x1"}, {"id": "0xa5f95a06", "bin" :"wlan_fw.bin", "cert": "null", "flash_offs": "1078K", "sram_offs": "0xffffffff", "ep": "0xffffffff", "attr":"0x1"}, {"id": "0xa5f85a07", "bin" :"sys_sdd_40M.bin", "cert": "null", "flash_offs": "1103K", "sram_offs": "0xffffffff", "ep": "0xffffffff", "attr":"0x1"}, {} ], "raw_bin" :[ {"bin" :"lfs.bin", "flash_offs": "1536K"}, {} ] }
编译完成后编译烧录即可。
-
【FAQ】全志XR806芯片 如何获取ap的rssi值?
1.问题背景
在网络设备上,很多开发者要把当前的网络质量反馈到用户上,以便用户了解当前网络环境,当网速慢或联网失败时做出及时的网络环境调整。2.问题描述
如何获取目标ap的信号强度。3.解决办法
(1)当连接到ap时获取当前ap的信号强度值,通过以下方式来获取rssi和dbm的值:ret = wlan_ext_request(g_wlan_netif, WLAN_EXT_CMD_GET_SIGNAL, (int)(&signal)); printf("signal = %d! noise = %d!\r\n", (signal.noise + (signal.rssi/2)), signal.noise);
(2)获取附近ap的rssi值和dbm值,可以通过扫描结果来获取,其中获取ap信息结构体wlan_sta_ap里面的rssi成员代表rssi值,level代表dbm值。
wlan_ssid_t ssid; uint8_t bssid[6]; uint8_t channel; uint16_t beacon_int; int freq; int rssi; /* unit is 0.5db */ int level; /* signal level, unit is dbm */ int wpa_flags; int wpa_cipher; int wpa_key_mgmt; int wpa2_cipher; int wpa2_key_mgmt; } wlan_sta_ap_t;
注意:获取的信号强度信息时需要进行一次扫描动作才能刷新缓存结果,所以获取信息之前需要调用wlan_ap_scan_once()函数扫描,并且等待扫描成功后,信号强度信息才会刷新。
-
【FAQ】全志XR806芯片 如何创建自定义状态回调函数?
问题背景
XR_MCU的SDK中,audio,wlan,Fs等模块被引用时,已经根据状态,设置好了回调函数,但是有客户不清楚如何利用SDK自身资源,创建自己应用模块的状态回调函数。问题描述
如何创建自定义状态回调函数。问题分析
状态回调函数依赖于SDK的framework框架,且已经为用户预留了自定义回调函数的框架,audio,bt,fs,net的状态回调框架都是完全公开的,参照编写即可。解决方法
1 添加自定义状态回调类型
在project\common\framework\sys_ctrl\sys_ctrl.h文件中,修改ctrl_msg_type结构体,在CTRL_MSG_TYPE_USER后添加自定义类型,如CTRL_MSG_TYPE_TEST。typedef enum ctrl_msg_type{ CTRL_MSG_TYPE_SYSTEM = 0, CTRL_MSG_TYPE_NETWORK, CTRL_MSG_TYPE_VKEY, CTRL_MSG_TYPE_VOLUME, CTRL_MSG_TYPE_SDCARD, CTRL_MSG_TYPE_FS, CTRL_MSG_TYPE_AUDIO, CTRL_MSG_TYPE_HANDLER, /* message defined by user starts from CTRL_MSG_TYPE_USER */ CTRL_MSG_TYPE_USER = 0x100, CTRL_MSG_TYPE_TEST, //自主添加的回调函数 } ctrl_msg_type;
2 创建回调函数
在.h文件中添加状态类型enum test_status { TEST_MSG_STATE_FIRSR, //状态1 TEST_MSG_STATE_SECOND, //状态2 TEST_MSG_STATE_UNDEFINE, };
在.c文件中添加实体函数
/* 处理状态变化 */ static void test_ctrl_msg_process(uint32_t event, uint32_t data, void *arg) { switch (EVENT_SUBTYPE(event)) { case TEST_MSG_STATE_FIRSR: test_act_first(data); break; case TEST_MSG_STATE_SECOND: test_act_second(data); break; default: break; } }
/* 创建回调函数 */ int test_init(void) { observer_base *base = sys_callback_observer_create(CTRL_MSG_TYPE_TEST, //监控event类型 0xFFFF, //监控类型可分类,可参考fs_ctrl.h test_ctrl_msg_process, //回调函数 NULL); if (base == NULL) { printf("create fail\n"); return -1; } /* 挂载到观察链表 */ if (sys_ctrl_attach(base) != 0) { printf("attach fail\n"); return -1; } }
3 发送状态
当线程中状态发生变化时,通过project\common\framework\sys_ctrl\sys_ctrl.h中定义的API,会调用步骤2中设置好的回调函数。
常规用法/* 实体 */ int sys_event_send(uint16_t type, uint16_t subtype, uint32_t data, uint32_t wait_ms); /* 例,其中wait_ms为队列等待超时时间,队列深度默认为6 */ sys_event_send(CTRL_MSG_TYPE_TEST,TEST_MSG_STATE_FIRSR,0,0);
特殊用法,发送完成后自动释放数据
/* 实体 */ int sys_event_send_with_free(uint16_t type, uint16_t subtype, void *data, uint32_t wait_ms); /* 例,其中data是希望传输到回调函数的数据,使用该API能在传输完成后释放内存 */ struct STtest *testdata = malloc(sizeof(*testdata)); memset(testdata,0,sizeof(*testdata); testdata = dataget(testdata) //数据赋值 sys_event_send_with_free(CTRL_MSG_TYPE_TEST,TEST_MSG_STATE_FIRSR,testdata ,0);//发送完成后会自动释放数据
特殊用法,自定义销毁方式
/* 实体 */ int sys_event_send_with_destruct(uint16_t type, uint16_t subtype, void *data, void (*destruct)(event_msg *), uint32_t wait_ms)
该API和sys_event_send_with_free差异点为,sys_event_send_with_free执行完回调后,会固定执行free(data),但是sys_event_send_with_destruct在执行完回调后,继续执行destruct函数,destruct函数由用户自主编写,可以选择销毁data,也可以选择特殊处理。
-
【FAQ】全志XR806芯片 如何解决第三方静态库函数符号重复?
1.问题背景
联合开发中,由于软件保密,合作方仅提供.a静态库。但可能出现合作方提供的.a静态库和自身的.a静态库冲突,导致镜像合成失败,且可能因为合作方的各种缘故不方便修改静态库。2.问题描述
程序编译过程中没有出错,但是在ld链接过程中提示错误:multiple definition of。3.问题分析
1 对于在程序中可以修改的重复定义,直接修改程序即可。
2 multiple definition of的函数都在保密的.a库文件中,重复函数众多,且无法轻易更改程序。由于.a静态文件是由.o可执行文件打包而来,可以先对.a静态库解包,删除掉重复定义的.o可执行文件后重新打包即可。4.解决方法
1 拆包.a静态库。ar -x libtarget.a
2 删除多余的.o文件。
3 重新打包静态库。ar crv libtarget.a *.o
4 重新编译。
-
【FAQ】全志XR806芯片 汇编代码调试技巧
1.问题背景
问题平台:XR806 + RTOS2.问题描述
XR806(M33内核)适配新的RTOS时,沿用M4F的启动代码后出现了系统奔溃,但是expection显示的PC地址和LR地址都被修改,无法准确判断哪条语句导致的错误。3.问题分析
- 通过log定位到是启动代码出现了问题。
- arm汇编中,可利用b .进入死循环。
- 在汇编中可以利用以下代码打印log:
//.c文件中插入 void AsmPrint(void) { printf("var = %#x\n",PrintMagic); } #汇编代码中插入以下代码查看R0的值 LDR R8,=PrintMagic str R0,[R8] LDR R8,=AsmPrint bx R8
- 最后发现汇编代码正常,但在运行第一个任务时系统奔溃。原因为M33内核新增了PSPlimit功能,任务栈超过了设定值时会直接触发usage Fault。同时RTOS在系统启动前修改了PC地址和LR地址,导致exception中无法正确显示PC地址和LR地址。
4.解决方法
使用__set_PSPLIM可以设置PSPlimit地址,在不确定PSP限制时,可以__set_PSPLIM(0)取消这个功能,MSP也是相同道理。 -
【FAQ】全志XR806芯片 RF参数是如何保存的?
1.问题背景
客户在量产中,由于各个产品一致性不太一样,很多时候需要在出厂时对每个产品进行RF参数的校准才能确保RF性能达到最佳。那么校准后,相关的参数保存在什么地方?2.问题描述
XR系列MCU的RF参数是如何保存的?3.解决办法
目前出厂校准的RF参数主要包括有频偏和发射功率。XR系列MCU获取这两个参数的主要途径有两个:镜像打包时的sdd文件和efuse。而获取来源是遵循以下原则:
1、MCU启动后,会读取efsue上RF参数区域的数据,如果是合法数据,则采用efuse上的参数,而sdd文件的参数会无效。
2、MCU启动后,如果efsue上RF参数区域的数据不是合法数据,则采用sdd文件上的参数。如果在软件代码上写入频偏,代码流程上参考project\example\efpg这个示例写入efuse。 -
【FAQ】全志XR806芯片 如何使用phoenixMC软件把应用固件与etf固件合并在一起?
1.问题背景
在客户量产中,出厂的板子rf参数不太可能做到完全一致,所以需要etf固件来校准rf参数。但是如果烧录完etf固件又再一遍烧录应用固件生产起来就比较麻烦。把两个固件合并在一起的话可以减少生产工序,提高生产效率。2.问题描述
如何使用phoenixMC软件把应用固件与etf固件合并在一起?3.解决办法
3.1使用方法:
1、打开phoenixMC.exe,点击设置按钮。2、点击合并固件,并依次选择应用固件和etf固件。
3、选择完成后,固件会在etf固件的目录下生产新的combineImage.cimg文件,此文件即是两者合并的文件。
3.2注意事项:
- 如果应用固件支持ota升级,合并文件一定要先选择应用固件后再选择etf固件,因为etf固件不支持ota功能。
- 合并后固件flash layout为:app1->应用固件,app2->etf固件。
- 固件进行一次ota升级后,etf固件将会被升级后的应用固件覆盖,即app2的区域装载固件不是etf固件,而是新的etf固件。
-
【FAQ】全志XR806芯片 Xradio Skylark 中的无线网络回调事件含义
1. 问题背景
部分客户在使用 WLAN 时,不清楚各回调事件的含义,不利于上层应用逻辑开发。2. 问题描述
无线网络各个事件的代表的含义是什么?3. 问题分析
首先看一下无线网卡处于 Station 模式下连接 AP 的过程:- Station 发起扫描,扫描附近的 AP
- Station 选中指定的 AP,发起认证(Authentication)、关联(Association)
- (可选) 如果是 WPA/WPA2 加密方式,则 AP 发起四次握手协商密钥
- Station 发起 DHCP 获取 IP 地址
到这里网络通路已经可以正常运行,上层应用数据包可以发出去了。
Station 模式断开连接过程:
- Station 向 AP 发送断开连接请求 (Disassociation)
以上是正常断开连接过程。除此之外的异常断开连接还有 Station 掉电、重启、发送 DHCP 释放报文等。
4. 解决办法
使用 sys_callback_observer_create 创建类型为 CTRL_MSG_TYPE_NETWORK 的事件观察器后,当网络状态发生变化或底层完成相应的任务后,会触发对应的事件回调。创建方法如下:```
observer_base *ob = sys_callback_observer_create(
CTRL_MSG_TYPE_NETWORK,
NET_CTRL_MSG_ALL,
net_ctrl_msg_process,
NULL
);CTRL_MSG_TYPE_NETWORK 类型的事件目前有以下几种:
NET_CTRL_MSG_WLAN_CONNECTED /* 连接成功事件。代表 Station 关联或四次握手完成后(如果 WPA/WPA2 加密,则是四次握手完成后) /
NET_CTRL_MSG_WLAN_DISCONNECTED / 断开连接事件。代表 Station 请求断开连接成功后 /
NET_CTRL_MSG_WLAN_SCAN_SUCCESS / 扫描完成事件。代表 Station 完成一次扫描 /
NET_CTRL_MSG_WLAN_SCAN_FAILED / 扫描失败事件。代表 Station 扫描操作失败 /
NET_CTRL_MSG_WLAN_4WAY_HANDSHAKE_FAILED / 四次握手失败事件。代表 WPA/WPA2 四次握手过程失败 /
NET_CTRL_MSG_WLAN_CONNECT_FAILED / 连接失败时间。代表 Station 在认证/关联阶段失败 /
NET_CTRL_MSG_CONNECTION_LOSS / 连接丢失事件。代表 Station 当前连接丢失,可能是 AP 修改了配置重启、掉电之类的,导致 Station 无法扫描到 /
NET_CTRL_MSG_NETWORK_UP / 网络启动完成事件。代表已经获取到 IP 地址(DHCP 完成),网卡已经可以正常工作,通过协议栈收发网络中的数据包 /
NET_CTRL_MSG_NETWORK_DOWN / 网络关闭事件。代表已经发送 DHCP 释放报文,而且网卡已经关闭 /
NET_CTRL_MSG_NETWORK_IPV6_STATE / IPv6 地址状态事件。代表 IPv6 地址状态发生了变化 */
NET_CTRL_MSG_ALL = ALL_SUBTYPE
}; -
【FAQ】全志XR806芯片 串口修改波特率后与目标波特率不匹配如何解决?
1、问题背景
有客户反应,XR系列MCU在修改完串口波特率后,打印输出的是乱码,通过仪器抓波形发现输出的波特率与设置不一致。2. 问题描述
串口修改波特率后与目标波特率不匹配。3. 问题分析
XR系列MCU的波特率理论计算是baund=apb_freq/(16*div),其中apb_freq是APB总线时钟频率,div是分频系数,div计算结果采用去尾法会产生误差。板级文件board_config.h 中BOARD_APBS_CLK_SRC以及BOARD_APBS_CLK_FACTOR可配置APB时钟总线频率。实际应用中,串口误码率建议小于5%,从计算公式得知波特率会出现不是百分百的匹配的情况,但是波特率在允许的误差范围内是可以正常使用的。4.解决方法
通过调整BOARD_APBS_CLK_SRC以及BOARD_APBS_CLK_FACTOR、或者cpu主频来匹配相应的波特率,但是要注意同一个分频值可能出现不能同时兼容所有波特率的情况。下图是主频和分频计算出波特率的示例:
-
【FAQ】全志XR806芯片 getsockopt、setsockopt失败如何解决?
1. 问题背景
调用 setsockopt 设置 socket 属性失败,或者 getsockopt 获取 socket 属性失败。2. 问题描述
调用 setsockopt、getsockopt 时返回 -1,且 errno 为 ENOPROTOOPT(92)。3. 问题分析
LwIP 当前版本有部分 socket 属性的设置/获取是暂时没有支持的,操作这些属性会返回 errno = ENOPROTOOPT 的错误码。4. 解决办法
以 SDK 中 lwip-2.0.3 为例。可以通过打开文件 include/net/lwip-2.0.3/lwip/sockets.h 查看目前版本 LwIP 支持哪些 socket 属性的设置/获取。#define SO_DEBUG 0x0001 /* Unimplemented: turn on debugging info recording */ #define SO_ACCEPTCONN 0x0002 /* socket has had listen() */ #define SO_DONTROUTE 0x0010 /* Unimplemented: just use interface addresses */ #define SO_USELOOPBACK 0x0040 /* Unimplemented: bypass hardware when possible */ #define SO_LINGER 0x0080 /* linger on close if data present */ #define SO_DONTLINGER ((int)(~SO_LINGER)) #define SO_OOBINLINE 0x0100 /* Unimplemented: leave received OOB data in line */ #define SO_REUSEPORT 0x0200 /* Unimplemented: allow local address & port reuse */ #define SO_SNDBUF 0x1001 /* Unimplemented: send buffer size */ #define SO_RCVBUF 0x1002 /* receive buffer size */ #define SO_SNDLOWAT 0x1003 /* Unimplemented: send low-water mark */ #define SO_RCVLOWAT 0x1004 /* Unimplemented: receive low-water mark */ #define SO_SNDTIMEO 0x1005 /* send timeout */ #define SO_RCVTIMEO 0x1006 /* receive timeout */ #define SO_ERROR 0x1007 /* get error status and clear */ #define SO_TYPE 0x1008 /* get socket type */ #define SO_CONTIMEO 0x1009 /* Unimplemented: connect timeout */ #define SO_NO_CHECK 0x100a /* don't create UDP checksum */
如上所示,若该属性后的注释有 Unimplemented 标识,则未支持。
-
【FAQ】全志XR806芯片 如何使用watchpoint功能?
1.问题背景
watchpoint,一般用来观察某个变量/内存地址的状态(也可以是表达式),如可以监控该变量/内存值是否被程序读/写情况。2.问题描述
在程序运行异常的时候,可以借助watchpoint来进行辅助调试。3.问题分析
在XR MCU SDK中,支持了watchpoint功能,通过使能指定宏以及调用指定函数来使用该功能。4.解决办法
(1)使能watchpoint的宏开关:export __CONFIG_WATCHPOINT:=y
(2)调用watchpoint_add函数添加观察点
以下是代码使用示例:#include <debug/watchpoint.h> static int watchpoint_test_value; static struct watchpoint wp; static enum cmd_status cmd_watchpoint_value_init(char *cmd) { int ret; watchpoint_test_value = 1; wp.address = (unsigned int)&watchpoint_test_value; wp.length = sizeof(watchpoint_test_value); wp.rw = DWT_WRITE; ret = watchpoint_add(&wp); if (ret) { printf("watchpoint_add fail.\n"); } return CMD_STATUS_OK; } static enum cmd_status cmd_watchpoint_value_change(char *cmd) { watchpoint_test_value = 0; return CMD_STATUS_OK; }
-
【FAQ】全志XR806芯片 RTC时钟不能跑、有时候时间不准是什么原因?
1、问题背景
有客户反应,XR系列MCU在跑rtc模块时,读取时间一直是同一个值,或者出现时间与现实时间相比不准确。2. 问题描述
RTC时钟不能跑、有时候时间不准是什么原因。3.解决方法
(1)、先确定RTC使用的时钟源是内部震荡还是外部晶振。并且通过修改板级文件board_config.h中BOARD_LOSC_EXTERNAL这个宏定义来匹配相关配置(0是使用内部震荡作为震荡源,1是使用外部晶振),如果配置是外部晶振,但是却没有外接外部32.768khz晶振的话,RTC是跑不起来的。
#define BOARD_LOSC_EXTERNAL 0 /* 0: inter 32k, 1: external 32k */(2)、使用内部震荡的话,由于是rc震荡产生时钟,所以受到温度漂移影响很大,所以客户如果需要精度高的计时,建议使用外部晶振来作为震荡源。
-
【FAQ】全志XR806芯片 如何清除扫描列表缓存?
问题背景
XR MCU平台发起扫描后,会对扫描结果进行缓存一段时间。问题描述
Wi-Fi设备发起扫描,获取扫描结果。当关闭掉目标路由器后,再次进行扫描获取的扫描结果会依旧包含已关闭的路由器SSID。问题分析
由于实现机制,会对扫描结果进行一定时间缓存,导致已关闭的路由,仍然能扫描到。只要下次发起扫描时,对缓存进行清除,就可以解决该问题。解决办法
调用一下函数即可清除缓存:int wlan_sta_bss_flush(int age);
如移除30s内未更新的AP节点为例:
wlan_sta_bss_flush(30);
-
【FAQ】全志XR806芯片 standby模式下gpio的电平状态是如何变化?
1.问题背景
客户在做低功耗设计时经常会使用GPIO外接一些硬件外设,这些外设在芯片的管脚在休眠时候如果跟GPIO有压差就会产生漏电流。我们如何保证休眠时的GPIO的电平状态呢。2.问题描述
客户提问:XR808/XR809/XR871/XR872和XR806在standby模式下芯片的gpio电平状态如何保持的。3.解决办法
XR808/XR809/XR871/XR872在芯片进入standby模式后,如果属于wakeup IO的管脚电平状态处于高阻抗,要保持电平的话,需要调用HAL_Wakeup_SetIOHold才可以把wakeup io固定在某种电平状态。但是不属于wakeup IO的管脚进入休眠是不受控制的,仍然为高阻态。而XR806所有的GPIO都可以通过hold功能来保持原来的电平。
在硬件设计的时候,如果要休眠保持电平就选wakupio做普通io用。具体哪些管脚属于wakeup IO请在相关芯片的PIN_Multiplexing文档查询。 -
【FAQ】全志XR806芯片 如何解决编译错误undefined reference to?
1.问题背景
把SDK默认的Freertos切换为其他RTOS后,部分用户反馈,工程中已经定义某个函数,但是在编译最后依旧会报错undefined reference to(找不到某个函数)。2.问题描述
工程编译过程中没有报错,但是在最后连接过程时会提示错误undefined reference to"",找不到特定函数。3.问题分析
编译过程中没有报错,在最后连接时才报错,说明编译时引用了某个头文件,指明该函数需要用其他库实现。4.解决方法
1)首先使用nm命令检查该函数所在的.a静态库,确认该静态库中是否已经包含了该函数,如果没有该函数,则检查该函数的编译是不是需要使能某些宏。2)如果已经确认了静态库中已经包含了该函数,说明在gcc链接过程中被忽略了,则可以尝试使用以下三种方法。
-
把该静态库的顺序提前,如first.a中调用了second.a中的某个函数,但是编译时却报错找不到second.a的某个函数,尝试调换两个静态库的顺序。
-
使用-Wl,–start-group和-Wl,–end-group修饰该静态库,代表该静态库不会只检索一次,而是多次来回检索,防止遗漏。缺点如果修饰的静态库太多,会导致编译速度变慢。
-
使用-Wl,–whole-archive和-Wl,–no-whole-archive修饰静态库,代表该静态库的所有函数都不会被忽略,强制链接。缺点是如果该静态库中存在多余的函数,会浪费flash。
-
-
【FAQ】全志XR806芯片 执行扫描动作时,偶尔会扫描不到目标的AP如何解决?
1、问题背景
有客户反应,XR系列MCU在连接进行扫描附近AP时,扫描不出所需要的AP,但第二次或者第三次就能扫描出来了。2. 问题描述
当mcu执行扫描动作时,扫描不出所需要的ap时,可以通过哪些方法来改善这种情况。3. 问题分析
-
增加扫描结果的缓存
如果附近网络环境复杂,AP数量太多时,扫描达到缓存上限值就会忽略掉一些ap数。此时可以通过增加扫描结果的缓存来让目标AP显示出来。具体方法可通过net sta scan result_num指令来查询当前AP的上限值(默认是20个),设置上限则通过net sta bss max count 和net sta scan result 指令来实现(num代表实际所需的AP数量)。函数实现在cmd_wlan.c里面查询相关指令接口。 -
增加信道停留时间
同一个信道的信道停留时间也有限,如果附近的AP都挤在同一个的信道,扫描这个信道的时间超时了也会忽略掉一些AP的信息。所以有时候也需要通过增加信道停留时间的方式来改善扫不出目标AP的情况。具体方法可通过以下代码来实现:
param.num_probes = num_probes; param.probe_delay = probe_delay; param.min_dwell = min_dwell; param.max_dwell = max_dwell; ret = wlan_ext_request(g_wlan_netif, WLAN_EXT_CMD_SET_SCAN_PARAM, (int)(¶m)); 其中几个参数的典型值是:n=2 d=100 min=100 max=125 n
-
-
【FAQ】全志XR806芯片 如何打开 LwIP 调试信息?
1. 问题背景
出现网络问题时,常常需要打开 LwIP 内部打印信息调试、查看协议栈运行状态,以获取更多的有效信息。2. 问题描述
如何打开 LwIP 调试信息?3. 问题分析
LwIP 内部调试信息有两类:
(1) LWIP_DEBUG,可以显示协议栈内部各层运行流程,收发通路信息等。
(2) LWIP_STATS,可以显示协议栈内部各种资源使用情况,包括内存、队列、信号量等。4. 解决办法
(1) 打开 LWIP_DEBUG 的方式
文件 include/net/lwip-x.x.x/arch/cc.h 中使能 DEBUG:/* Debug on/off */ #define LWIP_DEBUG
文件 include/net/lwip-2.0.3/lwipopts.h 中
设置调试等级,使能全局调试信息:
#define LWIP_DBG_MIN_LEVEL LWIP_DBG_LEVEL_ALL #define LWIP_DBG_TYPES_ON LWIP_DBG_ON
根据需要打开相应类别的调试信息,如 socket 相关调试信息:
#define SOCKETS_DEBUG LWIP_DBG_ON
(2) 打开 LWIP_STATS 的方式
文件 include/net/lwip-2.0.3/lwipopts.h 中使能 LWIP_STATS:
#define LWIP_STATS 1 #define LWIP_STATS_DISPLAY 1
根据需要打开相应类别的统计信息(默认已打开),如协议栈堆内存使用信息:
#define MEM_STATS ((MEM_LIBC_MALLOC == 0) && (MEM_USE_POOLS == 0))
最后自行调用相应统计函数,就可以显示当前协议栈统计信息:
LINK_STATS_DISPLAY(); // 链路层统计信息 ETHARP_STATS_DISPLAY(); // ARP层统计信息 IPFRAG_STATS_DISPLAY(); // IP层分片统计信息 IP_STATS_DISPLAY(); // IP层统计信息 IGMP_STATS_DISPLAY(); // IGMP协议统计信息 ICMP_STATS_DISPLAY(); // ICMP协议统计信息 UDP_STATS_DISPLAY(); // UDP层统计信息 TCP_STATS_DISPLAY(); // TCP层统计信息 MEM_STATS_DISPLAY(); // 堆内存使用统计信息 MEMP_STATS_DISPLAY(i); // 内存池i的使用统计信息 SYS_STATS_DISPLAY(); // 系统统计信息,包括队列(mbox)、信号量、锁 stats_display(); // 打印上面所有信息
-
【FAQ】全志XR806芯片 select引发崩溃如何解决?
1. 问题背景
lwip-1.4.1 版本使用 select 函数后引发崩溃。
内部报错:sock != NULL at line 1296 in src/api/sockets.c2. 问题描述
在多线程同时使用 lwip-1.4.1 版本的 select 函数,可能产生崩溃问题。3. 问题分析
lwip-1.4.1 中 lwip_select 函数获取 sock 结构时,未对空指针进行处理,从而引发崩溃。多线程操作同个 socket 的场景下易复现该问题。
该 bug 在 lwip 后续版本中进行了修复。4. 解决办法
方法 (1): 使用 lwip-2.0.3
因为 lwip-2.0.3 中已修复该 bug,切换使用即可。
xradio_skylark_sdk 中切换使用 lwip-2.0.3 的方法:在本地工程的 gcc/localconfig.mk 内部导出 __CONFIG_LWIP_V1 为 n。如下所示:export __CONFIG_LWIP_V1 := n
方法 (2): 合入 lwip 修复成果至 lwip-1.4.1
lwip 开源代码获取方式:git clone https://git.savannah.gnu.org/git/lwip.git
该 bug 在提交 5ceaed291f2c1320d36f9501fadd51923fa1c556 中修复,查看修改的代码:
git show 5ceaed291f2c1320d36f9501fadd51923fa1c556
修改内容如下所示:
commit 5ceaed291f2c1320d36f9501fadd51923fa1c556 Author: sg <goldsimon@gmx.de> Date: Sat Jan 17 21:02:58 2015 +0100 fixed bug #43361 select() crashes with stale FDs diff --git a/CHANGELOG b/CHANGELOG index 2c9aebbb..3bf40441 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -152,6 +152,9 @@ HISTORY ++ Bugfixes: + 2015-01-17: Simon Goldschmidt + * sockets.c: fixed bug #43361 select() crashes with stale FDs + 2015-01-17: Simon Goldschmidt * sockets.c/.h, memp_std.h: fixed bug #40788 "lwip_setsockopt_internal() crashes" by rewriting set/getsockopt functions to combine checks with the actual code diff --git a/src/api/sockets.c b/src/api/sockets.c index 3369c6d1..4109cee4 100644 --- a/src/api/sockets.c +++ b/src/api/sockets.c @@ -1209,6 +1209,7 @@ lwip_select(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset, u32_t msectimeout; struct lwip_select_cb select_cb; int i; + int maxfdp2; SYS_ARCH_DECL_PROTECT(lev); LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_select(%d, %p, %p, %p, tvsec=%"S32_F" tvusec=%"S32_F")\n", @@ -1266,47 +1267,69 @@ lwip_select(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset, SYS_ARCH_UNPROTECT(lev); /* Increase select_waiting for each socket we are interested in */ - for(i = 0; i < maxfdp1; i++) { + maxfdp2 = maxfdp1; + for (i = 0; i < maxfdp1; i++) { if ((readset && FD_ISSET(i, readset)) || (writeset && FD_ISSET(i, writeset)) || (exceptset && FD_ISSET(i, exceptset))) { - struct lwip_sock *sock = tryget_socket(i); - LWIP_ASSERT("sock != NULL", sock != NULL); + struct lwip_sock *sock; SYS_ARCH_PROTECT(lev); - sock->select_waiting++; - LWIP_ASSERT("sock->select_waiting overflow", sock->select_waiting > 0); + sock = tryget_socket(i); + if (sock != NULL) { + sock->select_waiting++; + LWIP_ASSERT("sock->select_waiting > 0", sock->select_waiting > 0); + } else { + /* Not a valid socket */ + nready = -1; + maxfdp2 = i; + SYS_ARCH_UNPROTECT(lev); + break; + } SYS_ARCH_UNPROTECT(lev); } } - /* Call lwip_selscan again: there could have been events between - the last scan (without us on the list) and putting us on the list! */ - nready = lwip_selscan(maxfdp1, readset, writeset, exceptset, &lreadset, &lwriteset, &lexceptset); - if (!nready) { - /* Still none ready, just wait to be woken */ - if (timeout == 0) { - /* Wait forever */ - msectimeout = 0; - } else { - msectimeout = ((timeout->tv_sec * 1000) + ((timeout->tv_usec + 500)/1000)); - if (msectimeout == 0) { - /* Wait 1ms at least (0 means wait forever) */ - msectimeout = 1; + if (nready >= 0) { + /* Call lwip_selscan again: there could have been events between + the last scan (without us on the list) and putting us on the list! */ + nready = lwip_selscan(maxfdp1, readset, writeset, exceptset, &lreadset, &lwriteset, &lexceptset); + if (!nready) { + /* Still none ready, just wait to be woken */ + if (timeout == 0) { + /* Wait forever */ + msectimeout = 0; + } else { + msectimeout = ((timeout->tv_sec * 1000) + ((timeout->tv_usec + 500)/1000)); + if (msectimeout == 0) { + /* Wait 1ms at least (0 means wait forever) */ + msectimeout = 1; + } } - } - waitres = sys_arch_sem_wait(SELECT_SEM_PTR(select_cb.sem), msectimeout); + waitres = sys_arch_sem_wait(SELECT_SEM_PTR(select_cb.sem), msectimeout); + } } - /* Increase select_waiting for each socket we are interested in */ - for(i = 0; i < maxfdp1; i++) { + + /* Decrease select_waiting for each socket we are interested in */ + for (i = 0; i < maxfdp2; i++) { if ((readset && FD_ISSET(i, readset)) || (writeset && FD_ISSET(i, writeset)) || (exceptset && FD_ISSET(i, exceptset))) { - struct lwip_sock *sock = tryget_socket(i); - LWIP_ASSERT("sock != NULL", sock != NULL); + struct lwip_sock *sock; SYS_ARCH_PROTECT(lev); - LWIP_ASSERT("sock->select_waiting > 0", sock->select_waiting > 0); - sock->select_waiting--; + sock = tryget_socket(i); + if (sock != NULL) { + /* @todo: what if this is a new socket (reallocated?) in this case, + select_waiting-- would be wrong (a global 'sockalloc' counter, + stored per socket could help) */ + LWIP_ASSERT("sock->select_waiting > 0", sock->select_waiting > 0); + if (sock->select_waiting > 0) { + sock->select_waiting--; + } + } else { + /* Not a valid socket */ + nready = -1; + } SYS_ARCH_UNPROTECT(lev); } } @@ -1330,6 +1353,12 @@ lwip_select(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset, sys_sem_free(&select_cb.sem); #endif /* LWIP_NETCONN_SEM_PER_THREAD */ + if (nready < 0) { + /* This happens when a socket got closed while waiting */ + set_errno(EBADF); + return -1; + } + if (waitres == SYS_ARCH_TIMEOUT) { /* Timeout */ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_select: timeout expired\n"));
-
【FAQ】全志XR806芯片 低功耗蓝牙BLE断开连接错误码和分析?
1、问题背景
硬件:R系列芯片,XR806
软件:Tina2、问题简述
说明:该FAQ旨在列出和分析低功耗蓝牙BLE常见的断开连接原因,方便排查连接过程、连接之后和绑定过程中的断开连接问题。3、问题分析和解决办法
蓝牙低功耗BLE连接时和连接后和绑定过程中可能会有不同原因造成蓝牙断开,下面是比较常见的几种断开错误码和原因分析。
-
【FAQ】全志XR806芯片 如何设置AP模式默认IP地址、子网掩码、网关?
1. 问题背景
特殊需求想要修改 AP 模式使用的 IP 地址、子网掩码、网关。2. 问题描述
xradio_skylark_sdk 中如何设置 AP 模式默认 IP 地址、子网掩码、网关?3. 问题分析
目前 SDK 中使用都是使用静态的方法去配置 AP 模式的 IP 地址、掩码、网关以及 DHCP 的地址池范围。若要修改,则需要到指定文件中修改。注意:若修改了 AP 的 IP 地址,需要确保 DHCP 分配的地址池与 AP 的 IP 地址是同一子网段下。4. 解决办法
(1) 修改 AP 模式的 IP 地址的方法
在 project/common/framework/sysinfo.c 的 int sysinfo_default(void) 函数内部进行修改:/* netif AP */ IP4_ADDR(&g_sysinfo.netif_ap_param.ip_addr, 192, 168, 51, 1); IP4_ADDR(&g_sysinfo.netif_ap_param.net_mask, 255, 255, 255, 0); IP4_ADDR(&g_sysinfo.netif_ap_param.gateway, 192, 168, 51, 1);
(2) 修改 DHCP 地址池范围的方法
在 src/net/udhcp-0.9.8/dhcpd_cfg.h 进行修改:#define DHCPD_ADDR_START "192.168.51.100" #define DHCPD_ADDR_END "192.168.51.104" #define DHCPD_INTERFACE "en1" #define DHCPD_OPTION "" #define DHCPD_OPT "" #define DHCPD_REMAIN "yes" #define DHCPD_MAX_LEASES "5"
-
【FAQ】全志XR806芯片 如何修改中断优先级?
1.问题背景
XR809/XR871/XR808/XR872上如何修改中断的优先级。2.问题描述
很多模块都会使用到中断,但中断的优先级在模块的初始化或者在使能中断时就设定了,那么如何修改中断的优先级呢?3.解决办法
3.1 中断是如何设定优先级的
中断的优先级是在下面这个函数设定的,每个带中断的模块都会调用到这个函数
HAL_NVIC_ConfigExtIRQ(GPADC_IRQn, GPADC_IRQHandler, NVIC_PERIPH_PRIO_DEFAULT);(第二个参数代表的是优先级)
这个HAL_NVIC_ConfigExtIRQ会调用到HAL_NVIC_SetPriority函数
HAL_NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority)函数的2个参数分别是中断类型以及中断优先级。
最终是HAL_NVIC_SetPriority进行了中断优先级的设定的。3.2如何设定中断优先级
(1).本来修改模块的优先级通过修改这个模块的HAL_NVIC_ConfigExtIRQ函数即可,但大部分模块的初始化函数都已经rom化了,直接修改rom化的初始化函数是无法生效的。(2).所以首先我们需要找到模块中哪里调用了HAL_NVIC_ConfigExtIRQ函数,然后在应用层代码中添加HAL_NVIC_SetPriority函数重新设定优先级
例如:
GPIO模块在HAL_GPIO_EnableIRQ模块中调用到了HAL_NVIC_ConfigExtIRQ函数,那么需在上层应用代码中再次调用HAL_NVIC_SetPriority
函数来进行重新设定中断的优先级HAL_GPIO_EnableIRQ ---> 这个接口里面的代码已经rom化,不能进行优先级设定 HAL_NVIC_SetPriority ---> 重新设定优先级,覆盖HAL_GPIO_EnableIRQ函数里设定的默认值
ADC模块在HAL_ADC_Init模块中调用到了HAL_NVIC_ConfigExtIRQ函数,那么需在上层应用代码中再次调用HAL_NVIC_SetPriority
函数来进行重新设定中断的优先级HAL_ADC_Init ---> 这个接口里面的代码已经rom化,不能进行优先级设定 HAL_NVIC_SetPriority ---> 重新设定优先级,覆盖HAL_ADC_Init函数里设定的默认值
(3).需要注意HAL_NVIC_SetPriority函数的2个参数
void HAL_NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority) IRQn_Type IRQn ---> 中断类型,在应用层需要根据具体的模块进行填写,例如GPIO需要填GPIOA_IRQn/GPIOB_IRQn,ADC需要填GPADC_IRQn uint32_t priority ---> 优先级,根据具体所需进行填写,数字越小优先级越低
-
【FAQ】全志XR806芯片 如何使用timer命令行命令?
1.问题背景
XR809/XR871/XR808/XR872上timer如何使用2.问题描述
timer模块在芯片中只是一个小模块,在发布文档中没有专门的文档进行说明,只有代码用例,部分客户看代码后会对timer里的一些参数云里雾里的3.解决办法
下面会从几个方面帮助客户去了解以及测试timer模块。3.1如何在固件中添加测试timer测试用的命令行命令:
(1).project/XXX/command.c文件中添加timer命令如下:
--- a/platforms/xr806/xr806-os/project/tuya/command.c +++ b/platforms/xr806/xr806-os/project/tuya/command.c @@ -181,6 +181,7 @@ static const struct cmd_data g_main_cmds[] = { + { "timer", cmd_timer_exec }, { "sysinfo",cmd_sysinfo_exec, CMD_DESC("system information command") }, { "help", cmd_main_help_exec, CMD_DESC(CMD_HELP_DESC) },
(2).添加完成后编译烧录固件在命令行中输入help命令可以看到timer命令被编译进固件中了:
[*] net : network command [*] rf : radio frequency command [*] drv : driver command [*] echo : echo command [*] mem : memory command [*] heap : heap use information command [*] thread : thread information command [*] upgrade : upgrade command [*] reboot : reboot command [*] ota : over the airtechnology upgrade commands [*] etf : etf command [*] pm : power management command [*] efpg : efpg command [*] flash : flash control command [*] lmac : low mac command [*] timer [*] sysinfo : system information command [*] ble [*] help : print this message and quit
(3)timer命令的源码在project/common/cmd/cmd_timer.c,用户可以对该源码进行修改测试,也可以参考该文件的timer接口进行应用开发。
3.2timer命令如何使用
(1)timer命令行命令支持下面几个子命令:
timer config i=X m=X s=X d=X p=X --> 配置timer的参数,参数后面说明 timer deconfig i=X --> 取消当前timer的配置参数 timer start i=X --> 启动对应的timer(支持2个timer 0/1) timer stop i=X --> 停止对应的timer timer pause i=X --> 暂停对应的timer timer continue i=X --> 继续对应的timer Timer value i=X --> 获取对应timer的值(该值是剩余多少定时时间,需计算)
(2)timer命令行参数说明:
参数i:0/1 --> 控制的是哪个timer,806支持2个timer,0/1 参数m:repeat/once --> 定时模式,repeat:循环定时模式,once:一次定时模式 参数s:LF/HF --> 时钟源,LF:低频32k晶振,HF:高频晶振,该晶振为外部所接晶振,例如如果接的是40M,HF就是40M,接的是26M,HF就是26M 参数d:1/2/4/8/16/32/64/128 --> 分频系数 参数p: --> 定时period值,该值需要用户根据时钟源,分频系数,定时时间进行计算: 需要用到的计算公式如下: interval = period / ( clk-src / clk-div ) period = interval * ( clk-src / clk-div )
(3)公式参数说明:
参数interval:需要定时的值(例如5s,0.5s) 参数clk-src:时钟源(例如40M,26M) 参数clk-div:分频系数(分频系数的选择影响精度和定时时间长短范围) 参数period:根据定时值,时钟源,分频系数计算出来的值,该值会写到timer寄存器中,timer模块 就是根据这个值进行定时的。
例子:例如需要定时5s,板子外接时钟源是26M,分频系数选4,那么根据公式可以计算出period的值应该需要填32,500,000
4.timer命令实操说明说明
timer config i=1 m=once s=HF d=4 p=32500000 -- 1 <ACK> 200 OK timer start i=2 -- 2 <ACK> 200 OK timer value i=1 -- 3 <ACK> 200 value=19462685 timer value i=1 -- 4 <ACK> 200 value=4617179 timer value i=1 -- 5 <ACK> 200 value=0
(1).配置timer 1,模式设定once模式,时钟源选HF(26M),分频选4,因为想定时5s,因此算出p值需要填写32500000 (2).启动定时器1 (3).第一次获取period值19462685,根据公式interval = period / ( clk-src / clk-div ) 可以算出剩余时间interval = 19462685 / (26000000 / 4) = 2.994s (4).同上可以算出第二次剩余时间 interval = 4617179 / (26000000/4) = 0.71s (5).第3次获取值时因为已经超过5s,所以获取到的值为0
想设定其他分频和时间可根据公式以及需要设定的分频时间算出period值填到对应的函数接口即可。
其他命令相对简单,因此不再进行实操显示
注意:
如果遇到timer测试定时时间与现实实际实际不符,可以从下面一些方向进行排查:
(1).计算晶振与实际晶振部分(例如:计算时使用26M,但实际板子接的时40M)
(2).重复计算一下period值,period值填写错误也会导致定时时间不准确附件timer_pparam.xlsx:timer_param.xlsx
附件是26M参数的时间,分频,period值的参考表,其他频率的晶振也可以参考该表算出period值。 -
【FAQ】全志XR806芯片 固件烧录完成后,程序不是预期烧录程序或者甚至无法启动如何解决?
1.问题背景
872平台上烧录成功后,程序运行没有改变,还是原来的程序,有时候甚至烧录成功后系统无法启动。2.问题描述
烧录固件完成后,提示烧录成功,断上电后,程序跑的还是原来的程序,没有烧录成功,同时还出现概率性提示烧录成功后无法启动现象。3.问题分析
程序跑的还是原来的,同时还出现烧录成功后无法启动。如果img没有问题,那么大概率就是程序根本没有烧录到flash中。所以在烧录flash前,勾选上"写入后进行校验"。勾选上写入校验后,发现,烧录异常,无法进行写入。
4.解决办法
根本原因还是在于固件没有写入 -
【FAQ】全志XR806芯片 mac地址要如何存放以及如何获取?
1.问题背景
XR809/XR871/XR808/XR872上的mac地址改如何存放,还有获取的方式是怎样。2.问题描述
客户使用中XR809/XR871/XR808/XR872中,可能不了解mac地址的存放以及获取方式。3.解决办法
3.1 MAC地址存放在什么地方的
根据prj_config.h里面的#define PRJCONF_MAC_ADDR_SOURCE 来决定
1、SYSINFO_MAC_ADDR_FLASH是sysinfo里面的一个缓冲区。
2、SYSINFO_MAC_ADDR_CHIPID是根据chipid来随机生成,有小概率重复。
3、SYSINFO_MAC_ADDR_EFUSE写进efuse里面的mac地址。
注:MAC地址的第一个字节必须为偶数,可参考mac地址规则3.2 MAC地址怎么获取
如mac地址存放在flash,使用控制台指令:sysinfo get mac
如mac地址存放在efuse,使用控制台指令:efpg get mac -
【FAQ】全志XR806芯片 为什么开机时候串口以及部分gpio会有电平跳变?
1.问题背景
客户在测试时,发现开机的一瞬间串口以及部分gpio会有电平跳变,这个电平跳变的来源是哪里的,是否能消除掉的。2.问题描述
客户设备在开机的时候PB2上的LED会闪烁一下,使用示波器抓有一段5毫秒左右的脉冲。3.解决办法
由于XR系列MCU最开始运行的是固化在ROM里面的brom代码,会执行下列操作:
1、PB2、PB3管脚会反初始化后再初始化成输入功能GPIO,用于检测是否进入烧录模式,导致PB2、PB3管脚会有跳变。
2、UART0会初始化,用于与烧录工具通信,导致UART0的TX、RX管脚有跳变。
3、如果该型号的MCU的flash是外挂的话,那么操作flash相关的管脚也会有跳变,包括hold管脚与wp管脚。
从跳变原因来看,这几个管脚是来自brom的操作,已经固化到芯片上,所以是不能消除的,所以硬件设计的时候需要注意。 -
【FAQ】全志XR806芯片 如何更换打印log口?
1.问题背景
XR809/XR871/XR808/XR872/XR806 上如何把log口uart0更换为uart1或uart2。2.问题描述
我司SDK发布时镜像log默认由uart0输出,有些客户的产品需要把log从非uart0口输出(例如从uart1或uart2输出)。3.解决办法
3.1 修改board_config.c文件,配置对应uart口的ping脚
例如:(下面的代码只做事例讲解,实际代码不同平台可能会不一样,请根据原理图和数据手册进行修改):
(1).添加对应uart的脚static const GPIO_PinMuxParam g_pinmux_uart2[] = { { GPIO_PORT_B, GPIO_PIN_14, { GPIOB_P14_F3_UART2_TX, GPIO_DRIVING_LEVEL_1, GPIO_PULL_UP } }, /* TX */ { GPIO_PORT_B, GPIO_PIN_15, { GPIOB_P15_F3_UART2_RX, GPIO_DRIVING_LEVEL_1, GPIO_PULL_UP } }, /* RX */
(2).确保board_config.c中board_get_pinmux_info函数已有对应的uart口的初始化代码(有些版本可能没有uart2的初始化代码,需要自行添加)
case HAL_DEV_MAJOR_UART: if (minor == UART0_ID) { info[0].pinmux = g_pinmux_uart0; info[0].count = HAL_ARRAY_SIZE(g_pinmux_uart0); } else if (minor == UART1_ID) { info[0].pinmux = g_pinmux_uart1; info[0].count = HAL_ARRAY_SIZE(g_pinmux_uart1); } else if (minor == UART2_ID) { info[0].pinmux = g_pinmux_uart2; info[0].count = HAL_ARRAY_SIZE(g_pinmux_uart2); } else { ret = HAL_INVALID; } break;
3.2 修改board_config.h文件配置log从那个uart输出
例如:把log从uart0修改为从uart2输出#define BOARD_MAIN_UART_ID UART0_ID --改为--> #define BOARD_MAIN_UART_ID UART2_ID
注意点:
不同芯片不同方案会有不同的board_config.c和board_config.h文件,确保修改的文件是你所需要的方案 -
【FAQ】全志XR806芯片 系统异常重启如何解决?
1. 问题背景
产品在做稳定性测试,发现设备异常重启2. 问题描述
用户应用在做长运压测,发现设备长运2~3天后会异常重启,log中未发现有代码奔溃现象。3. 问题分析
系统重启,需要判断重启原因,系统中的重启原因有如下:typedef enum { SYS_POWERON = 0, //硬件上电启动 SYS_WATCHDOG_CHIP_RST = 1, //看门狗复位重启,包含整个系统 SYS_WATCHDOG_CPU_RST = 2, //看门狗复位重启,仅CPU SYS_REBOOT = 6, //执行reboot命令重启 SYS_CPU_RST = 7, //cpu异常复位启动 SYS_NVIC_RST = 8, //中断异常重启 }SystemStartupState;
在系统阶段就可以插桩代码,判断重启原因,下面是示例代码:
const char* startup_state_str[] = {"powerOn", "wdgSocRst", "wdgCpuRst", "sleep", "standby", "hibernation", "reboot", "cpuRst","nvicRst"}; printf("startup state: %s\n", startup_state_str[SysGetStartupState()]);
通过插桩代码,就可以明确重启原因,导致看门狗喂狗不及时而重启。
4. 解决办法
(1)插桩代码,判断重启原因。
(2)如果是看门狗重启,大部分情况下看门狗重启都是由于代码执行异常卡死,解决该问题,就先把看门狗关掉,接上JTAG等调试工具,程序执行异常,就会block住(不会重启),就可以利用jtag判断代码执行位置,进而找出异常点。 -
【FAQ】全志R329芯片FAQ汇总(你不知道的和你想知道的的这里都有)
01、【FAQ】全志R329 Tina中如何使用adb/串口密码登录?
02、【FAQ】全志R329 Tina中使用如何使用perf分析CPU使用率?
03、【FAQ】全志R329以太网模块初始化失败如何解决?
04、【FAQ】全志R329以太网网络不通或丢包严重怎么解决?
05、【FAQ】全志R329以太网常用调试命令和排查手段
06、【FAQ】全志R329以太网使能报No phy found或Initialize hardware error怎么解决
07、【FAQ】全志R系列Tina系统Wi-Fi联网失败排查思路
08、【FAQ】全志R系列Tina-Linux4.9部分平台唤醒源配置
09、【FAQ】全志R系列休眠唤醒使用和框架介绍
10、【FAQ】全志R329 Tina下如何使用硬件Watchdog?
11、【FAQ】全志R系列启动频率的修改方法
12、【FAQ】全志R329如何查看和修改sdio的频率?
13、【FAQ】全志R329如何进行coredump的配置与调试?
14、【FAQ】全志R系列如何进行Tina根文件系统定制?
15、【FAQ】全志R系列Tina内核配置不生效如何解决?
16、【FAQ】全志R329如何在DragonSN烧写mac地址?
18、【FAQ】全志R系列如何解决wpa_supplicant服务启动问题?
19、【FAQ】全志R329如何进行WiFi驱动收发帧打印控制?
20、【FAQ】全志R329如何解决Tina删除内核根目录.config后一直编译失败的问题?
21、【FAQ】全志R329如何于Tina下支持多用户?
22、【FAQ】全志R系列Wi-Fi唤醒问题排查思路
23、【FAQ】全志R329如何解决Tina双系统安全固件启动失败问题?
24、【FAQ】全志R329如何通过wpa_cli手动调试wifi station?
25、【FAQ】全志R329如何实现Tina secureboot 打包分离模式支持?
26、【FAQ】全志R329如何解决在线mp3流媒体seek异常?
27、【FAQ】全志R系列wpa_supplicant和wpa_cli简介
28、【FAQ】全志R系列Tina 如何使用 NFS?
29、【FAQ】全志R329如何在平台修改uboot阶段CPU频率?
30、【FAQ】全志R329Tina下如何判断是安全固件?
31、【FAQ】全志R329如何将I2C时钟频率变成800kHz?
32、【FAQ】全志R系列如何在Tina下使用bootchartd来分析rootfs启动时间?
33、【FAQ】全志R329如何通过uboot修改设备树属性?
34、【FAQ】全志R329如何解决RTL驱动hostap启动失败的问题?
35、【FAQ】全志R329如何解决无法将wlan0加到br-lan这个网桥上面的问题?
36、【FAQ】全志R329如何解决蓝牙设备断开慢的问题?
37、【FAQ】全志R329如何利用wpa_cli设置国家代码?
38、【FAQ】全志R329Tina网络adb的使用
39、【FAQ】全志R329如何解决在Audiocodec使用S24_LE格式进行录音时产生的软件分析波形异常问题?
40、【FAQ】全志R系列如何解决gpio-keys驱动在系统启动上报两次input事件?
41、【FAQ】全志R329以太网常用调试说明
42、【FAQ】全志R329如何在Linux Device Tree中配置预留内存?
43、【FAQ】全志R329如何正确查看XRADIO相关版本信息?
44、【FAQ】全志R329如何解决调用Bluez mainloop_add_fd函数失败的问题?
45、【FAQ】全志R329蓝牙设备类型分类与过滤方法
46、【FAQ】全志R329一些正常的I2C报错
47、【FAQ】全志R329如何关闭HT40?
48、【FAQ】全志R329 XRADIO如何查看发射功率和频偏?
49、【FAQ】全志R329如何在WiFi Softap模式添加自定义Vendor IE?
50、【FAQ】全志R329如何解决蓝牙播放无声(snd_pcm_open error: Out of memory)?
51、【FAQ】全志R329 多台设备的adb device id相同,无法指定设备,如何指定设备的adb device id?
52、【FAQ】全志R329如何解决Tina中package下新增软件包目录很深导致检测不到问题
53、【FAQ】全志R329如何使用DMIC的高通滤波寄存器滤掉低频噪声?
54、【FAQ】全志R329如何解决Tina双系统uboot校验rootfs失败的问题?
55、【FAQ】全志R329如何在Tina安全应用程序调试?
56、【FAQ】全志R329在dtbo中如何配置gpio?
57、【FAQ】全志R329 LRADC电压设置、识别精度、识别误差、应用等相关知识
58、【FAQ】全志R329如何设置蓝牙自动重连时间或关闭自动重连?
59、【FAQ】全志R系列 Tina 如何查看通过 procd init 脚本启动的应用输出到 stdout/stderr 的打印信息?
60、【FAQ】全志R329如何在Tina在不烧写secure bit下开发OPTEE安全应用?
61、【FAQ】全志R329 MiniGUI如何获取和设置BITMAP像素点?
62、【FAQ】全志R329如何判断Tina安全启动boot分区证书与boot分区数据是匹配与否?
63、【FAQ】全志R329Tina安全启动校验linux/rootfs失败直接重启如何解决?
64、【FAQ】全志R329Tina下几种安全存储实现有何区别
65、【FAQ】全志R329 Tina下为何有些optee的demo只有NA源码没有TA源码?
66、【FAQ】全志R329Tina中TA下有哪些加解密接口?
67、【FAQ】全志R329Tina下无法选中libcedarx如何解决?
68、【FAQ】全志R系列在Tina下如何确认方案的optee版本信息
69、【FAQ】全志R系列在Tina下如何设置optee-secure-storage默认保存路径
70、【FAQ】全志R329如何使用audiocodec如何减少snd_pcm_open的耗时
71、【FAQ】全志R329如何进入monitor模式
72、【FAQ】全志R329如何进行Tina Linux蓝牙低功耗默认连接参数修改
73、【FAQ】全志R329在Tina如何在蓝牙已连接情况下拒绝其他耳机回连
74、【FAQ】全志全系列芯片 APST平台无法下载或者更新工具
75、【FAQ】全志 F系列/R系列/V系列 RTOS平台cache操作接口介绍
76、【FAQ】全志系列芯片如何把flash擦成空片?
77、【FAQ】持续更新中...... -
R329语音识别视频教程,从编译到部署,完全可用
R329语音识别 视频教程, 从编译到部署,完全可用
R329语音识别之前发布了一个简单版本, 今天终于出了一个详细的版本, 可以自行在 R329上实现编译
获取到语音识别的结果用于其他的DIY项目
手把手教会如何编译, 以及在哪修改源码更改功能视频地址: https://www.bilibili.com/video/BV1Rq4y1B7WH?spm_id_from=333.999.0.0
项目地址: https://github.com/7758258abc/r329_speed
下载Maix-Speech-master的地址: https://github.com/sipeed/Maix-Speech -
RISC-V SoC + AI | 在全志 D1「哪吒」开发板上,跑个 ncnn 神经网络推理框架的 demo
引言
D1 是全志科技首款基于 RISC-V 指令集的 SoC,主核是来自阿里平头哥的 64 位的 玄铁 C906。「哪吒」开发板 是全志在线基于全志科技 D1 芯片定制的 AIoT 开发板,是目前还比较罕见的使用 RISC-V SoC 且可运行 GNU/Linux 操作系统的可量产开发板。
ncnn 是腾讯优图实验室推出的一个为移动端极致优化的高性能神经网络前向计算框架,是目前同样也比较罕见的为 RISC-V 架构做过适配和优化的神经网络框架。
本文 是一份教程,步骤骑着步骤 (step by step) 地展示了如何在一块全新的全志 D1「哪吒」开发板上,跑个 ncnn 神经网络推理框架的 demo。
本文的完成参考了以下材料,非常感谢 nihui、BedRock 等开发者们在互联网上的分享!
[1] D1 哪吒 - 在线文档
[2] nihui: 在全志d1开发板上玩ncnn
[3] 腾讯优图ncnn新版本适配国产CPU,全志D1加持最高速度提升70倍!目录
- 引言
- 必要的材料
- 上电!
- 重刷固件
- 安装并配置交叉编译工具链
- 编译 ncnn,并准备 demo 程序
- 使用 ADB 传输文件
- 运行 demo
1、必要的材料
- 「哪吒」开发板
- Type-C USB 线
- USB 转 TTL 转换器
- 安装有 GNU/Linux 或较新版本的 Windows 10 / 11 的可联网电脑
(本文以 Windows 10 的电脑为例来展示)
2、上电
取出哪吒和 USB 转 TTL 转换器,先把转换器按下图所示接上 DEBUG 端口,再将转换器插入电脑的 USB 口。如果电脑没有识别到串口,可能是因为没有安装转换器的驱动程序,可以在 这里 下载。
在使用开发板自带的固件时,DEBUG 端口会在开发板上电后通过 115200 波特率的 UART 串口来提供一个 Shell,我们可以使用 PuTTY、MobaXterm 或者其它类似的终端模拟器访问这个串口来在 Shell 中操作开发板。(如果使用 PuTTY,可以参考下图来设置)
启动终端模拟器后,暂时只能看到个黑框,因为开发板还没上电。
找出 Type-C USB 线,将开发板的 OTG 接口与电脑相连即可上电(虽然连 POWER 也可以,但是后边一直需要使用这个 OTG 接口,所以推荐连接它)。上电后可发现,开发板上的灯亮了,而且终端模拟器中开始一条条地冒出各种提示了!大概 10 ~ 20 秒后,根据一行按下回车的提示,在终端模拟器中按下回车,即可进入如下图所示的界面,然后就可以执行各种 GNU/Linux 命令了。
3、重刷固件
新开封的开发板里边自带的固件是基于 Open v1.0 版本的 D1 Tina SDK 编译的,在运行 ncnn 程序时会发生非法指令错误 (参考 这里),根据全志在线开发者论坛中网友 BedRock 的评论,我们可将固件更新为基于 Open v1.01 版本的 SDK 编译的固件再进行后续的步骤。
如果想自己编译固件或者修改固件,可以参考 D1 文档中的 “Tina SDK版本” 部分 下载 SDK。当然,如果只想先跑个 ncnn 玩玩看的话,我们可以直接在 “固件下载” 选择一款全志原厂为我们准备好的固件,比如我可以选择 D1哪吒HDMI测试固件20210804(开机HDMI就有小企鹅启动logo) (名字有点长) 为例。这个固件估计是为展示 HDMI 输出功能而设计的,我们给开发板用 HDMI 线接上个屏幕就能看到个小企鹅,但是因为它是基于 v1.01 SDK 编译的,所以我们刚好也能用它来跑 ncnn 的 demo。
下载好 .img 固件文件后,参考 D1 文档中 “编译与烧写” 的 “烧写” 部分 ,下载 全志USB驱动,使用管理员权限运行 install.bat 安装驱动,再安装烧写软件进行操作,即可将固件烧写进开发板。比如,在 Windows 电脑中我们要下载 PhoenixSuit 软件来烧写固件。要注意的是,在烧写软件中要选择 “全盘擦除升级”,如下图所示,否则无法成功烧写。
烧写成功后,重新上电即可。
4、安装并配置交叉编译工具链
注意:第 4 节和第 5 节的操作需要在 GNU/Linux 操作系统下进行。如果你的电脑装有 GNU/Linux 操作系统、或者可使用虚拟机,可直接在 GNU/Linux 操作系统中进行操作。如果你的电脑装的是较新版本的 Windows 10 / 11,也可以上网搜索相关教程安装并配置个 WSL (Windows Subsystem Linux),然后在 WSL 中进行操作。
在平头哥芯片开放社区的 “资源下载” 页面,我们可以在 “工具链-900系列” 中找到 V2.0.1 版本的 riscv64-linux-x86_64-20210512.tar.gz,下载它到电脑里,放到 <自己想放的路径>,解压它
tar -xf riscv64-linux-x86_64-20210512.tar.gz
解压完成后,使用 vim 或者其它文本编辑器,打开 ~/.bashrc,在结尾添加工具链的路径至环境变量,即下列内容
# My PATH export RISCV_ROOT_PATH=/<自己想放的路径>/riscv64-linux-x86_64-20210512
保存并关闭编辑器,然后使用 source 命令刷新 Shell 环境:
source ~/.bashrc
就安装并配置好交叉编译工具链了。
5、编译 ncnn,并准备 demo 程序
在 GitHub 上下载 2021 年 7 月 20 日 Release 的 ncnn 的源代码(Source code)到电脑里,比如 ncnn-20210720.tar.gz,解压,然后进入 ncnn-20210720 目录,准备并开始编译
mkdir build-c906 cd build-c906 cmake -DCMAKE_TOOLCHAIN_FILE=../toolchains/c906.toolchain.cmake -DCMAKE_BUILD_TYPE=relwithdebinfo -DNCNN_OPENMP=OFF -DNCNN_THREADS=OFF -DNCNN_RUNTIME_CPU=OFF -DNCNN_RVV=ON -DNCNN_SIMPLEOCV=ON -DNCNN_BUILD_EXAMPLES=ON .. make -j32
编译大概需要几分钟,完成后,可在 build-c906 目录中找到编译好的所有文件。
我们可先尝试两个 demo,分别为:
① 使用 benchncnn 做基准测试
② 使用 NanoDet 模型对一张自己的图片做目标检测(NanoDet 是个可在移动端超快运行的超轻量目标检测模型)为了方便后续将文件传输进开发板,我们可先在电脑上创建 benchncnn_demo 和 nanodet_demo 两个文件夹,将 ① 和 ② 所需的文件分别都放进去准备好。
对于 ①,我们把 ncnn 目录中的 build-c906/benchmark/benchncnn 和 benchmark/*.param 复制进去即完成准备。
对于 ②,我们需要把 ncnn 目录中的 build-c906/examples/nanodet 复制进去,在 ncnn-assets中下载 nanodet_m.bin 和 nanodet_m.param 两个文件放进去,再放入一张你想检测的图片即可。
即,两个目录的结构分别是类似这样的:
benchncnn_demo/ ├ benchncnn ├ alexnet.param ├ blazeface.param └ <一大堆其它模型的 param> nanodet_demo/ ├ nanodet ├ nanodet_m.bin ├ nanodet_m.param └ <你想检测的图片>
6. 使用 ADB 传输文件
ADB 是 Android Debug Bridge,原本为 Android 设计,全志给 Tina Linux 也做了适配,所以我们可以使用 ADB 来调试 Tina Linux 的设备。参考 D1 文档中的 “研发生产工具” 部分 可以下载,下载完成后,将其路径加入环境变量即可使用。
我们在跑这两个 demo 时,可能暂时只需要用到以下几个命令,如果需要使用其它功能也可上网搜索相关教程。
检查设备与电脑的连接情况,可以使用
adb devices
adb push 可以把电脑上的文件或文件夹传给开发板,使用语法如下
adb push <电脑上的文件路径> <开发板上的目标路径>
adb pull 可以把开发板上的文件或文件夹传给电脑,使用语法如下
adb pull <开发板上的文件路径> <电脑上的目标路径>
要注意的是,开发板上的路径都需要填写绝对路径。
7、运行 demo
把第 5 节所示的类似 benchncnn_demo 和 nanodet_demo 的文件夹通过 adb push 命令传进开发板,在开发板上进入 benchncnn_demo 目录,再输入
./benchncnn 4 1 0 -1 0
可开始做基准测试。如果成功运行即可慢慢看到类似下图这样的提示,展示了当前平台运行 ncnn 使用各种模型推理一帧所需的毫秒耗时。
(如果不想等测试慢慢跑完,也可以按 Ctrl + C 停止运行)
在开发板上进入 nanodet_demo 目录,再输入
./nanodet <你想检测的图片>
即可使用 NanoDet 对这张图片目标检测。运行完成后,可在命令行中看到文字版的检测结果,也可以使用 adb pull 把生成的 image.png 图片文件传给电脑再打开查看,上边会画好边界框、类别和置信度。比如这是它检测出的南京市长 江大桥上边的车车车车。
到了这里,我们就成功在一块运行 GNU/Linux 系统的 RISC-V 开发板上跑了个神经网络框架的 demo,如果想进行后续的实验或研发,可以了解有关 D1 哪吒 和 ncnn 的更多内容。
B站视频讲解:https://www.bilibili.com/video/BV1wQ4y1B7VU
本文来源:https://verimake.com/topics/275 -
【366期开放夜】自制一款低成本的RISC-V 64单板机(基于全志D1s)
本周开放夜嘉宾Young将为我们带来一个最低成本的RISC-V单板机分享。有哪些特征?
门槛高么?
可以拿来做些什么?
我也可以设计、制作一个吗?
我们的嘉宾将为您解答。
嘉宾介绍
Young
Young,电子科技大学通信工程专业大四学生。
既是从前端到操作系统均有涉猎的软件开发者,也是一名硬件hacker。自接触到嵌入式Linux以来深深被这个领域吸引,从ARM、MIPS到RISC-V架构的平台均有接触过。
RISC-V是什么?
RISC-V是一个基于精简指令集(RISC)原则的开源指令集架构(ISA),V表示为第五代。第五代的问世受到了大量创客们的广泛关注,但它的硬件却不易获得。
自制的RISC-V是什么样子的?
芯片
全志科技D1s是系统级封装(SIP)采用了阿里平头官方C906,具有单RISC-V 64内核@1.008G和64MB DDR2。
单板机介绍
在一个紧凑的56*56毫米的2层板上,引出全部IO,包括模拟外围设备
配有标准接口,包括USB、micro SD、LCD、Line-in和耳机
为自制优化布局,适合在加热台上焊接
拿来运行Linux妥妥的!
我们可以获得的体验感与现在的发行版别无二致,甚至可以安装任何我们想要的东西。
本次开放夜我们将采用线上分享
也欢迎大家来到线下一起交流讨论!
博云路111号 B1
期待的小伙伴们准备好加入我们!
活动时间:12月2日 19:30 - 21:00
我们不见不散!
线上观看直播入口
-
【小白自制Linux开发板】Debian文件系统制作,以及WIFI配置、交换分区配置
该片文章将完整记录一个Debian的最小文件系统的生成,以及自定义配置WIFI组件、网络组件和交换分区配置
本文章参考:
https://whycan.com/t_4236.html
http://www.leux.cn/doc/debootstrap.html(该网站在备案中,可能暂时无法查看了)
帖子整理完成。1. 制作Debian系统
构建debian文件系统,作为记录,最小rootfs在180MB左右。1.1 配置构建环境
安装构建文件系统的工具,一个是用来chroot,一个是用来构建文件系统sudo apt install qemu-user-static -y sudo apt install debootstrap -y mkdir rootfs
构建文件系统之前,你要知道你想要构建哪个版本的文件系统,
我从 https://www.debian.org/mirror/list.zh-cn.html 这里,
选择了我访问速度快的源,并且该源有armel。armhf (支持硬件浮点)
armel (软浮点)我看华为源挺快的,就用这个了mirrors.huaweicloud.com
然后就是debian的版本,我尝试一下最新的,bustersudo debootstrap --foreign --verbose --arch=armel buster rootfs http://mirrors.huaweicloud.com/debian/
构建完成之后,需要chroot进去修改密码等配置
cd rootfs sudo mount --bind /dev dev/ sudo mount --bind /sys sys/ sudo mount --bind /proc proc/ sudo mount --bind /dev/pts dev/pts/ cd .. sudo cp /usr/bin/qemu-arm-static rootfs/usr/bin/ sudo chmod +x rootfs/usr/bin/qemu-arm-static sudo LC_ALL=C LANGUAGE=C LANG=C chroot rootfs /debootstrap/debootstrap --second-stage --verbose sudo LC_ALL=C LANGUAGE=C LANG=C chroot rootfs
最后一条命令chroot完成,
此时可以先apt-get等给你的文件系统安装你需要的软件包为了提高下载速度,我们首先修改源
vi /etc/apt/sources.list
改为(注意要换其他源用http方式可以访问的,要不然还得处理https的内容):
deb http://mirrors.huaweicloud.com/debian buster main
改完以后执行:
apt-get update
使源生效
安装网络相关的库
apt-get install wpasupplicant #安装WIFI配置相关的组件 apt-get install net-tools #安装网络基础组件、如使用ifconfig等 apt-get install udhcpc #当wifi连接成功后,需要用这个组件去获取IP地址
其他组件
apt-get install wireless-tools apt install sudo vim openssh-server htop apt install pciutils usbutils acpi
1.2 配置账号
修改root登录密码的方式如下passwd root
添加用户
groupadd <用户组> useradd -m -g <用户组> -s /bin/bash <用户名> passwd <用户名>
1.3 新增账号sudo配置
对于出现<用户名> is not in the sudoers file. This incident will be reported.
切换到超级用户:
$ su
打开/etc/sudoers文件:
# vi /etc/sudoers
修改文件内容
保存退出
修改主机名,否则将会以当前编译的系统的主机名进行设置(如:笔者为Ubuntu,相当的尴尬)
HOSTNAME=<你的主机名> echo $HOSTNAME > /etc/hostname echo $HOSTNAME > /proc/sys/kernel/hostname sed -i '/localhost/s/$/\t'"$HOSTNAME"'/g' /etc/hosts
1.4 配置时区
修改系统默认时区cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
1.5 配置SSH
如果要使用SSH服务,并且允许SSH客户端以root方式登录需要进行一下设置。vi rootfs/etc/ssh/sshd_config
添加
PermitRootLogin yes
1.6 rootfs打包
当所有的内容制作完成,就可以清理缓存,打包之后就可以替换你的文件系统了apt-cache clean #删除安装包 exit #退出chroot rm rootfs/usr/bin/qemu-arm-static
卸载刚在挂载的文件夹。
cd rootfs sudo umount dev/pts/ sudo umount dev/ sudo umount sys/ sudo umount proc/ sudo umount dev/pts/
打包文件。
# cd rootfs #进到文件系统目录,如果已经在该文件夹下,可以忽略 tar cvf ../rootfs.tar . #要注意那个. 代表当前目录
生成的rootfs.tar任意解压到文件系统即可
2. 使用wpa_supplicant连接wifi
创建配置文件
vi /etc/wpa_supplicant.conf
输入内容:
network={ ssid="我的热点" psk="我的密码" }
然后执行命令
wpa_supplicant -B -d -i wlan0 -c /etc/wpa_supplicant.conf
示例如下
root@dika-pc:~# wpa_supplicant -B -i wlan0 -c /etc/wpa_supplicant.conf Successfully initialized wpa_supplicant rfkill: Cannot open RFKILL control device root@dika-pc:~# [ 444.817608] wlan0: authenticate with e4:67:1e:02:33:c0 [ 444.843208] wlan0: send auth to e4:67:1e:02:33:c0 (try 1/3) [ 444.900867] wlan0: authenticated [ 444.935561] wlan0: associate with e4:67:1e:02:33:c0 (try 1/3) [ 444.986874] wlan0: RX AssocResp from e4:67:1e:02:33:c0 (capab=0x411 status=0 aid=4) [ 445.033483] wlan0: associated [ 446.047125] IPv6: ADDRCONF(NETDEV_CHANGE): wlan0: link becomes ready ^C
执行dhcp命名,获取IP地址
root@dika-pc:~# udhcpc -i wlan0 udhcpc: started, v1.30.1 udhcpc: sending discover udhcpc: sending select for 172.16.1.106 udhcpc: lease of 172.16.1.106 obtained, lease time 43200 root@dika-pc:~# ping www.dika.ren PING www.dika.ren (149.129.80.46) 56(84) bytes of data. 64 bytes from 149.129.80.46: icmp_seq=3 ttl=45 time=217 ms 64 bytes from 149.129.80.46: icmp_seq=4 ttl=45 time=417 ms 64 bytes from 149.129.80.46: icmp_seq=5 ttl=45 time=102 ms 64 bytes from 149.129.80.46: icmp_seq=6 ttl=45 time=108 ms ^C --- www.dika.ren ping statistics --- 6 packets transmitted, 4 received, 33.3333% packet loss, time 346ms rtt min/avg/max/mdev = 102.009/211.020/417.367/127.586 ms
3. 增加swap分区
在使用一些软件的过程中,会遇到系统崩溃,尤其是使用 apt-get install 的时候,所以需要加入swap分区,可以简单理解为虚拟内存。
使用
free
查看当前swap大小,
使用如下命令创建你想要添加swap分区的大小dd if=/dev/zero of=/swap1 bs=1M count=512 #改成你要设置的SWAP大小,512就是512MB
设置swap分区文件
mkswap /swap1
激活swap分区
swapon /swap1
此时使用free命令就能看到创建好的swap分区,但这只是临时性的, 重启会失效,需要配置一下,下次开机时候要开swap
vi /etc/fstab
操作,在最后一行添加
/swap1 swap swap defaults 0 0
展示
开始进入Debian使用我们自定义的pi账户登录
使用命令查看Swap分区
以上内容来自 WhyCan Forum哇酷开发者社区
小白自制Linux开发板(F1C200s)整理系列,持续更新中
作者:twzy -
Reply: 【资料释放】D1哪吒计算条资源汇总(持续更新)
@jordonwu 编译和烧写的内容都已经在D1的在线文档里公布了 https://d1.docs.aw-ol.com/study/study_4compile/
这部分帖子的内容是从sipeed那边搬运过来的 -
Reply: 【资料释放】D1哪吒计算条资源汇总(持续更新)
昨天晚上本来准备睡觉发现D1的核心板居然上架了,立刻上车了一个,那么问题来了,我是想买来点手里的MIPI带鱼屏,看了下资料,官方的核心板很明显是考虑到内嵌设计的,本体只有用户指示灯和SPI屏的接口,倒是底下放了两个M2的金手指来引出IO,MIPI的差分对也在金手指上,想用的话就得自己画个底板了,两个M2金手指的相对间距在外围尺寸图里看不出来,找了找有3D模型,我是在模型里测的,最后糊了个空板,周围有几个朋友一起买的,他们拿去之后转了下格式后丢给我一起备份了,尺寸我看了下应该是没什么问题了,就在这一起放出来吧。
注意:压缩包里一共有三个版本,kicad的是5.99,阿狸狗是17.2,用某d的那个朋友说无所谓啥版本都能兼容。。
空板如图。一边的座子标了那几个电源脚,画板的时候记得别放反了~
PS:铜柱我用的是个小尺寸的,如果自己有更合适的话可以换掉;我打算用是4.2mm高度的B-KEY座子,算了下核心板下面除去背面的器件还有大概1.5mm空隙,可以塞小芯片和比较薄的wifi模块。
文件放在下面了,有需要的小伙伴可以自取~
-
Reply: 【资料释放】D1哪吒计算条资源汇总(持续更新)
哪吒计算条(LicheeRV-Nezha Compute Module)位号图
Lichee_RV_7001(TOP-Designator Drawing).pdf
Lichee_RV_7001(Bottom-Designator Drawing).pdf -
Reply: 【资料释放】D1哪吒计算条资源汇总(持续更新)
哪吒计算条(LicheeRV-Nezha Compute Module)规格书
中文版:Sipeed Lichee RV 规格书 V1.1.pdf
英文版:Sipeed Lichee RV Datasheet V1.1.pdf -
Reply: 【单板仅需99】D1哪吒计算条上线!为智能家居提供高性价比的RISC-V算力
@jordonwu 官方Tina Linux编译烧写文档见:https://d1.docs.aw-ol.com/study/study_4compile/
板子由合作伙伴Sipeed推出,更多相关参数可到Sipeed社区了解 -
Reply: 「免费申请」基于安谋科技STAR-C1的全志XR806 IoT开发板试用活动
PS.如果已有XR806开发板,也可以参与发文,极术社区同样有好礼相赠
开发过程中,遇到任何问题,欢迎访问全志在线开发者社区发帖咨询及加入XR806活动交流群,有任何问题也可以直接在微信群里咨询。
-
【小白自制Linux开发板】 二. u-boot移植
上一篇: 【小白自制Linux开发板】 一. 瞎抄原理图与乱画PCB中我们做了一个小型而没用的开发板,用的是Licheepi Nano的镜像,那从本篇开始我们开始自己构建它的灵魂吧。
我们都知道,PC在启动的时候,首先是进入BIOS,再根据BIOS中配置信息引导后续的启动操作系统,比如配置Windows启动。
而对于嵌入式linux中,并没有BIOS,这时候就需要一种类似引导程序来处理。于是就有了BootLoader。
BootLoader是一段小程序,可以把它想象成PC机linux上的GRUB/LILO引导程序,可以直接从flash或TF卡中运行,来装载内核。它可以初始化硬件设备,从而将系统的软硬件环境带到一个合适的状态,以便为最终调用操作系统做好准备。
1. 嵌入式开发板的启动过程
一个嵌入式系统从软件角度来看分为三个层次:
-
引导加载程序
包括固化在芯片中的boot程序(可选)和BootLoader两大部分,对于固化的boot程序。主要是芯片通过外围电路连接的实际情况选择读入程序的位置,比如:通过TF卡或是SPI以及其他方式启动,至于优先顺序这就要具体看芯片的数据手册,个人没做过具体测试。 -
linux内核
特定于嵌入式平台的定制内核 -
文件系统
包括了系统命令和应用程序
BootLoader启动过程可分为单阶段和多阶段(stage1、stage2),其中stage1完成初始化硬件,如CPU寄存器、内存控制器,为stage2准备内存空间。一般stage1是可以直接在nor flash中运行的,并将stage2复制到内存RAM中,设置堆栈,然后跳转到stage2(从这也可以看出stage2是在RAM中运行的,与stage1不同)
Boot Parameters 顾名思义,就是配置了要启动内核的参数,包含要加载系统内核相关文件的位置,要加载到内存中的位置,定位到文件系统的位置,相关输入输出的呈现等一系列参数。
kernel 在存放在bootloader之后,对于SoC来说,代码都需要在RAM中运行,这里与MCU不一样的地方就是引入了MMU(内存管理单元)。对于MCU而言,由于其执行速度低,因此运行代码都在ROM中直接运行,而对于Flash而言,其读取速度远不及RAM的速度,因此对于运行速度非常快的SoC而言,所有的代码都需要在RAM中运行。但是这里有一个问题,RAM掉电数据将会丢失,故代码保存不可能放在RAM中,当前所有的嵌入式设备而言,代码保存都是放在ROM中,因此在SoC中运行代码需要将代码搬运到RAM中然后再执行。
Root Filesystem 由于其执行过程需要对ROM进行读写操作,因此可以不用搬运到RAM中,但是实际过程中内核启动后会产生一个虚拟的文件系统,该文件系统是挂在根文件系统的关键所在,这里不详细讲解。整体来说,大致的过程为,嵌入式设备上电后将执行bootloader,对硬件进行硬件和堆栈初始化,然后搬运内核到RAM中并启动内核,紧接着挂载根文件系统。
2. 环境配置与参考项目
系统:Ubuntu 16
编辑器:VSCode
参考项目:Lichee-Pi Nano
地址:https://wiki.sipeed.com/soft/Lichee/zh/Nano-Doc-Backup/index.html
Lichee-Pi Nano
需要注意的是一定要选择Nano版本,因为我们开发板使用的主控芯片和Nano的主控是一致的,所以后续我们要编译U-boot,内核都可以参考(bai piao)这里面的配置。
主控芯片:F1c100s/F1c200s,100s内置32MB DDR1内存,200s内置64MB DDR1内存,200s贵一点,他们都是QFN88封装。
ARM926ejs内核,主频默认408MHz,据了解做产品出货的一般在600M左右。
带有100M的SPI接口2个,SDIO接口1个,USB OTG接口,还有CSI摄像头接口,LCD RGB显示屏接口,音频接口,I2C I2S UART PWM等等。
还有就是他们不支持硬件浮点,所以浮点运算使用软浮点方式。
F1c100s/F1c200s芯片功能3.交叉编译器
我们通过PC版的Linux自带的gcc编译的程序只能在当前系统架构下的cpu架构(x86)下运行,如果我们想要编写的程序在嵌入式Linux下运行,那么就需要用到对应的编译器。
我们做的开发板主控芯片F1C200S,内核为ARM9,其架构使用的是ARMv5架构,所以我们也要选用对应的编译器,同样,这样的编译器很多,这里我们使用最常用的arm-linux-gnueabi- ,因为交叉编译器F1C200S必须高于6.0版本,这里我们使用7.2版本
下载完成后解压文件:
tar -vxjf gcc-linaro-7.2.1-2017.11-x86_64_arm-linux-gnueabi.tar.xz
然后在/usr/local目录下新建arm-linux-gcc目录
sudo mkdir /usr/local/arm-linux-gcc
进入解压目录下:
cd gcc-linaro-7.2.1-2017.11-x86_64_arm-linux-gnueabi/
将该目录下的所有文件复制到新建的目录下
sudo cp -rd * /usr/local/arm-linux-gcc/
最后需要添加该工具链的环境变量使其可以在任何目录下执行,打开/etc/profile文件
sudo nano /etc/profile
在文件末尾添加以下内容
PATH=$PATH:/usr/local/arm-linux-gcc/bin
添加完毕,使路径生效
source /etc/profile
接下来在终端输入:
arm-linux-
然后连按两次Tab键,如图在表示成功:
如果没有出现,则进行下面操作,安装必要的动态链接库
sudo apt-get install lib32ncurses5 lib32z1
至此,我们完成了编译工具的配置。
4. 编译U-boot
当Arm开发板上电以后第一个要加载到内存并运行的程序就是BootLoader,BootLoader的同类型程序很多,如U-boot、X-boot、Rt-Thread,这里我们依然选中最常用的U-boot作为目标(因为其他的我也不会呀),
最新版本的uboot几乎包含当前主流的SoC芯片,前面提到本开发板使用的芯片和licheePI nano相同,大部分硬件也是兼容的,为了快速移植该部分,这里采用licheePI nano的u-boot来进行移植。在终端输入如下命令克隆u-boot:
git clone https://github.com/Lichee-Pi/u-boot.git -b nano-v2018.01
克隆完毕文件会保存在当前目录下,进入该目录,
cd u-boot
在该文件夹下有很多分支,我们可以查看所有分支,使用如下命令:
git branch -a
现在我们使用的是nano开发板,所以将当前分支切换到nano分支,命令如下:
git checkout nano-v2018.01u-boot
切换到Nano分支
默认的没有指定交叉工具链和架构,因此在编译之前需要指定交叉工具链和芯片架构,u-boot的交叉编译器在u-boot 的根目录下中的Makefile文件中定义了。打开文件找到CROSS_COMPILE变量,修改为如下:
ARCH=arm CROSS_COMPILE=arm-linux-gnueabi-
配制交叉编译环境
这样我们就能使用我们指定的编译器来编译u-boot了。
在u-boot项目的config目录下存在对多种板子的配置描述文件,由于每个板子的外设不同,因此编译之前必须要对u-boot进行配置。然而配置是一件比较繁琐的事情,特别是像u-boot这种比较复杂的项目而言,初学者几乎无法完成。幸运的是对于大部分开发板而言,config目录下有其配置好的默认配置文件。进入config目录中,然后执行ls查看当前所有的配置文件
cd config ls
查看配制文件
找到licheepi_nano_defconfig 和 licheepi_nano_spiflash_defconfig,前者表示为TF卡启动,后者表示从SPI 设备启动,因为我们做的小板只有从TF卡启动,所以我们需要使用 licheepi_nano_defconfig 。
现在回到上级目录,然后执行
make licheepi_nano_defconfig cd .. make make licheepi_nano_defconfig
这样我们把licheepi_nano_defconfig 作为默认配置项。
接下来我们就可以用图形界面进行配置了,执行
make menuconfig
此时出现图形配置选项,如下图所示
u-boot Menuconfig配制,注意红框中的配置,我们后续要用到。
至此我们的u-boot环境配置就完成了,但是我们还有个问题要解决:如何让u-boot引导系统
我们在PC端安装Windows系统的时候往往需要选择启动顺序,比如需要优先通过光驱或u盘启动等。
同样在u-boot中也需要这样的配置,当然u-boot比PC配置稍微复杂一丢丢。我们前面提到Linux嵌入式系统结构分布中有个Boot Parameters 部分,这部分就是做引导配置的,那怎么配置呢,总体来说可以分为两部分:
-
bootcmd,主要用于描述控制Linux内核文件以及其他描述文件加载到内存中位置以及启动Linux内核系统等
-
bootargs,用于配制文件系统、串口信息等。
bootcmd
在最开始提到过,内核一般不在flash中运行,这样就需要将内核搬运到内存中,这个过程需要u-boot来完成。对于mmc (TF卡)而言,在u-boot有专门的命令load mmc,该命令可以将mmc中的代码从flash搬运到指定的地址处。
当u-boot中环境变量bootdelay计数到0时,此时uboot就会开始执行bootcmd中的命令。
bootdelay这个环境变量是一个计数器,当u-boot主体运行完毕后,此时bootdelay该变量的值将会开始递减,递减时间为1s,当递减到0时,此时u-boot将会跳转到bootcmd处开始执行bootcmd命令,(你可以简单理解为PC启动后有一两秒时间等待,你可以可以通过F8或Enter键打断进入Bios设置的过程,这个等待时间就由u-boot中的bootdelay来控制)。
下面我们需要记住这句指令,这就是我们当前制作的开发板需要用到的bootcmd全部内容
load mmc 0:1 0x80008000 zImage;load mmc 0:1 0x80c08000 suniv-f1c100s-licheepi-nano.dtb;bootz 0x80008000 - 0x80c08000;
如果你需要详细了解这句话那就接着往下看,如果不需要则可以跳到 下面的u-boot参数配置环节
对于上面命令,我们根据分号拆分为3部分:
1> load mmc 0:1 0x80008000 zImage; 2> load mmc 0:1 0x80c08000 suniv-f1c100s-licheepi-nano.dtb; 3> bootz 0x80008000 - 0x80c08000;
其中两个 load mmc 命令、一个bootz 命令。
先看第一条:
load mmc 0:1 0x80008000 zImage
load mmc有三个参数:第一个参数是mmc(TF卡)分区,第二个参数是内存中目标地址,第三个参数是源文件。
即上面的命令意思是将mmc的0:1 分区中的zImage复制到内存中的0x80008000地址处。这里的zimage就是Linux内核,后续会提到该文件编译,0:1这个可以这样理解0表示TF卡(TF卡属于mmc存储器的一种),1这表示TF卡的第一个分区(boot分区)后面会提到。
而对于内存位置 0x80008000 地址位置,将其理解为默认值就行了。这样完成了zImage的加载。
下面分析第二条命令:
load mmc 0:1 0x80c08000 suniv-f1c100s-licheepi-nano.dtb
有了上面的加载zImage的说明,可以很轻松的理解上面的命令意思是将mmc的0:1分区中的suniv-f1c100s-licheepi-nano.dtb文件加载到内存中的0x80c08000地址处。对于suniv-f1c100s-licheepi-nano.dtb 这个文件,叫做设备树文件,简单来说就是当前开发板上面所有外设备描述文件,这部分将会在后续内核编译部分进行详细说明。
对于第三条命令:
bootz 0x80008000 - 0x80c08000
的意思是告诉内核镜像的起始地址为0x80008000,加载的设备树地址为0x80c08000。这里是告诉cpu从这里开始启动Linux, bootz命令的格式是:bootz空格0x80008000空格-空格0x80c08000,注意-左右有空格。
除了bootz 命令外,有些系统里面还可能存在一个叫做bootm命令,这是是对没有使用设备树内核的镜像启动命令,早期版本的内核没有引入设备树,因此对于早期的内核一般使用的是bootm,其命令格式为bootm内核地址,比如bootm x0x30008000,意思是从0x30008000开始启动内核,启动内核的过程其实是将pc指针指向该地址,这样处理器就会从该地址处运行代码。
这里我们就完成了bootcmd的说明,接下来我们看另外一个参数。
bootargs
bootargs也是u-boot环境变量中一个非常重要的变量,上面已经讲解了内核的启动可以通过bootcmd来完成,那接下来内核启动完毕后必须挂在根文件系统(rootfs)。但是内核并不知道根文件系统的具体位置,我们必须要告诉根文件的位置后内核才能将其挂载,这时就需要有bootargs变量。该变量的作用是告诉内核根文件系统的位置和属性以及必要的配置,
console=ttyS0,115200 panic=5 rootwait root=/dev/mmcblk0p2 earlyprintk rw
同上面分析的方法一样,我们依然将这部分命令拆成几部分来说明。这里需要说明的是,这部分配置信息是由u-boot 直接按照参数字符串方式提供给Linux内核,然后由Linux内核进行执行的,这也说明里为什么格式与bootcmd配置方式不一致。**console=ttyS0,115200 **表示终端为ttyS0即串口0,波特率为115200;
panic=5 字面意思是恐慌,即linux内核恐慌,其实就是linux不知道怎么执行了,此时内核就需要做一些相关的处理,这里的5表示超时时间,当Linux卡住5秒后仍未成功就会执行Linux恐慌异常的一些操作。
rootwait 该参数是告诉内核挂在文件系统之前需要先加载相关驱动,这样做的目的是防止因mmc驱动还未加载就开始挂载驱动而导致文件系统挂载失败,所以一般bootargs中都要加上这个参数。
root=/dev/mmcblk0p2 表示根文件系统的位置在mmc的0:2分区处,/dev是设备文件夹,内核在加载mmc中的时候就会在根文件系统中生成mmcblk0p2设备文件,这个设备文件其实就是mmc的0:2分区(这里对应TF卡的第二个分区:rootfs),这样内核对文件系统的读写操作方式本质上就是读写/dev/mmcblk0p2该设备文件。
earlyprintk 参数是指在内核加载的过程中打印输出信息,这样内核在加载的时候终端就会输出相应的启动信息。rw表示文件系统的操作属性,此处rw表示可读可写。
5.u-boot参数配置
make menuconfig
选中 Enable boot arguments 按空格选中,下面会显示:() Boot arguments
然后选中Boot arguments ,按回车,进入配置窗口,接下来上面解释过的bootargs 参数信息:
console=ttyS0,115200 panic=5 rootwait root=/dev/mmcblk0p2 earlyprintk rw
配置bootargs信息
然后按Tab键选中<OK>,保存并进入主菜单。
同理配置:Enable a default value for bootcmd 按空格选中,下面会显示:() bootcmd value 配置项,
选中bootcmd value 进入配置界面,输入bootcmd命令:
load mmc 0:1 0x80008000 zImage;load mmc 0:1 0x80c08000 suniv-f1c100s-licheepi-nano.dtb;bootz 0x80008000 - 0x80c08000;
配置bootcmd参数
按Tab键选中<OK>,保存并进入主菜单。
6.u-boot编译与烧录
先保存图形配置界面后推出界面,在终端执行make -j4即可对整个u-boot进行编译。
编译u-boot
make -j4后面的-j4表示4个核心进行编译,若电脑的处理器是2核心,请使用make -j2进行编译。
编译完成后会在当前目录生成u-boot-sunxi-with-spl.bin烧录文件。
根目录下找到 u-boot-sunxi-with-spl.bin 文件
该文件就是我们最终要烧录的二进制文件。
在当前目录下会有一个隐藏的文件.config,该文件是u-boot编译后根据各个选项产生的配置文件,这个配置文件记录了所有配置选项的宏开关,编译的时候是根据最终的.config文件来进行编译的,当然编译前是需要有脚本解析.config文件然后进行相应的编译。
烧录到TF卡
只要将u-boot-sunxi-with-spl.bin烧录到tf卡的8k偏移处地址就可以了,烧录步骤如下:使用dd命令进行块搬移:
sudo dd if=u-boot-sunxi-with-spl.bin of=/dev/sdb bs=1024 seek=8
该命令中:
- if 文件名:输入文件名,缺省为标准输入。即指定源文件。< if=input file >
- of 文件名:输出文件名,缺省为标准输出。即指定目的文件。< of=output file >
- bs bytes:同时设置读入/输出的块大小为bytes个字节。
- seek blocks:从输出文件开头跳过blocks个块后再开始复制。
这里的输出文件(of)为主机电脑的/dev/sdb文件,也就是TF卡,这里也体现了Linux一切皆文件的思想。
/dev/sdb 这个可以用gparted 软件查看,该软件可以直接用命令安装即可:
sudo apt-get install gparted
此时在Ubuntu下面可以看到如下软件:
安装好GParted软件
打开软件
GParted
在右上角可以看到两个硬盘,/dev/sda 为本地硬盘,/dev/sdb 是我们将要写数据的TF(当然这只是墨云自己的配置使然,具体情况请根据实际情况而定),因此这里的of=/dev/sdb 烧录到8k偏移地址处是指绝对地址,这个绝对地址指的是TF卡的物理地址。这8K的值是由F1C200S 中固化的启动代码决定的,所以照抄即可。
烧写u-boot
然后我们正常退出TF卡,然后插入我们自制的开发板,通过USB线连接电脑,
连接开发板
打开电脑中的命令行工具,我这里使用Xshell,
打开Xshell,新建连接:
配置名称 ,协议选择Serial
配置串口
通过下拉选中com端口,波特率为115200,其他默认即可,点击确定,然后双击主界面左侧会话管理中的刚建立的会话,此时进入连接状态。
因为在你插入USB通电的时候开发板就已经启动了,所以当你打开串口连接的时候可能未必会看到信息,所以按一下重启键,就可以看到如下的输出信息了,这就是我们的u-boot,执行到u-bbot计数完成后会产生错误,那是因为我们还没有进行系统内核的移植,所以默认就会进入u-boot命令模式。
启动信息
输入pri命令打印环境变量的所有值,可以找到已经配置的bootcmd 和bootargs
pri命令结果
至此完成了u-boot移植的全部内容,对于u-boot的移植方法,在后续移植Linux内核和文件系统时都会用到,都是大同小异的,所以有了本篇的说明,之后操作将会非常简单。
而关于u-boot的内容事实上非常的复杂繁琐,有兴趣的可以自行去了解到,毕竟作为一个小白的我初衷只是先让小板先跑起来
参考资料
Lite200 (lishanwen) -- https://lishanwen.cn/index.php/2021/07/03/lite200/全志F1C200S F1C100S 介绍 ( 迪卡魏曼依奇君 ) https://blog.csdn.net/tunqimai9331/article/details/95938903
荔枝派Nano 全流程指南 (矽速科技) https://wiki.sipeed.com/soft/Lichee/zh/Nano-Doc-Backup/index.html
以上内容来自 WhyCan Forum哇酷开发者社区
小白自制Linux开发板(F1C200s)整理系列,持续更新中
作者:twzy -
-
【FAQ】全志R329Tina下无法选中libcedarx如何解决?
问题背景
Tina SDK用户反馈,对于特定方案(v833),在make menuconfig时,可以搜索到PACKAGE_libcedarx,但无法选择。
如上图所示,可以搜索到PACKAGE_libcedarx,但是没有显示其目录路径,同时也没有显示是否选中。
问题分析
在Tina中, 使用 make 编译 tina 下的任何目标,都会通过 build/scan.mk 生成必要的临时文件,scan.mk 会扫描tina package、target等目录下的文件信息,并将对应的扫描结果保存在 tmp 目录,这是tina下所有目标生成的前提。
对于本例来说,PACKAGE_libcedarx对应目录package/allwinner/tina_multimedia,经过扫描之后,会生成临时文件tmp/info/.packageinfo-allwinner_tina_multimedia,此时,该文件的内容如下:
$ cat tmp/info/.packageinfo-allwinner_tina_multimedia Source-Makefile: package/allwinner/tina_multimedia/Makefile
现在,我们打开一个menuconfig可以选中的pacakge,比如tplayerdemo,其对应的临时文件tmp/info/.packageinfo-allwinner_tina_multimedia_demo_tplayerdemo的内容如下:
$ cat tmp/info/.packageinfo-allwinner_tina_multimedia_demo_tplayerdemo Source-Makefile: package/allwinner/tina_multimedia_demo/tplayerdemo/Makefile Package: tplayerdemo Submenu: tina_multimedia_demo Version: 1-1 Depends: +libc +SSP_SUPPORT:libssp +USE_GLIBC:librt +USE_GLIBC:libpthread @TPLAYER libcedarx libstdcpp Conflicts: Menu-Depends: Provides: Build-Depends: libcedarx Section: utils Category: Allwinner Title: use tplayer interface in tina_multimedia Maintainer: Source: Type: ipkg Description: CedarX2.8 tplayerdemo @@
对比两者,可发现libcedarx的临时文件只有一行,缺少很多信息,如Package、Submenu、Depends等,而Package、Submenu、Depends信息都是对应package下的Makefile定义。
对package/allwinner/tina_multimedia/Makefile进行检查,发现上述定义都被下列条件语句所限制。
feq ($(TARGET_BOARD_PLATFORM),$(filter $(TARGET_BOARD_PLATFORM),r16 r58 r40 r18 r6 c200s g102 r11 r7 r30 r311 r333 r331 r7s t7 r332 v306 dolphin h3 h6 mr133 r328s2 r328s3 mr813 r329 r818 a33i r528 r528rv d1 f133 t113))
显然,这里表示的意思是,只有这些方案,才会定义libcedarx所需的目标。
问题解决
对于本例(v833)来说,补丁如下。
diff --git a/allwinner/tina_multimedia/Makefile b/allwinner/tina_multimedia/Makefile index 8f3f263af..28cc01eb2 100755 --- a/allwinner/tina_multimedia/Makefile +++ b/allwinner/tina_multimedia/Makefile @@ -435,7 +435,7 @@ ifeq ($(CONFIG_ONLY_DISABLE_AUDIO),y) CONF_ONLY_DISABLE_AUDIO = -DONLY_DISABLE_AUDIO endif -ifeq ($(TARGET_BOARD_PLATFORM),$(filter $(TARGET_BOARD_PLATFORM),r16 r58 r40 r18 r6 c200s g102 r11 r7 r30 r311 r333 r331 r7s t7 r332 v306 dolphin h3 h6 mr133 r328s2 r328s3 mr813 r329 r818 a33i r528 r528rv d1 f133 t113)) +ifeq ($(TARGET_BOARD_PLATFORM),$(filter $(TARGET_BOARD_PLATFORM),r16 r58 r40 r18 r6 c200s g102 r11 r7 r30 r311 r333 r331 r7s t7 r332 v306 dolphin h3 h6 mr133 r328s2 r328s3 mr813 r329 r818 a33i r528 r528rv d1 f133 t113 v833)) define Package/$(PKG_NAME)/config source "$(SOURCE)/Config.in" @@ -765,6 +765,6 @@ endef endif -ifeq ($(TARGET_BOARD_PLATFORM),$(filter $(TARGET_BOARD_PLATFORM),r16 r58 r40 r18 r6 c200s g102 r11 r7 r30 r311 r333 r331 r7s t7 r332 v306 dolphin h3 h6 mr133 r328s2 r328s3 mr813 r329 r818 a33i r528 r528rv d1 f133 t113)) +ifeq ($(TARGET_BOARD_PLATFORM),$(filter $(TARGET_BOARD_PLATFORM),r16 r58 r40 r18 r6 c200s g102 r11 r7 r30 r311 r333 r331 r7s t7 r332 v306 dolphin h3 h6 mr133 r328s2 r328s3 mr813 r329 r818 a33i r528 r528rv d1 f133 t113 v833)) $(eval $(call BuildPackage,$(PKG_NAME))) endif
-
【FAQ】全志R329Tina中TA下有哪些加解密接口?
问题背景
客户希望在安全OS端实现如下功能:
- RSA私钥的生成与存储;
- 公钥的提取(指定私钥,提取对应的公钥)
- 指定信息的签名;对非安全OS传入的信息进行签名后,返回签名结果;
- 信息的加密、解密;对传入的信息进行加密、解密等操作,并返回结果;
想咨询一下当前Tina optee是否支持openssl库,如果不支持openssl库,如何实现上述功能。
问题分析
首先optee不支持openssl库,但是有其他替代方案。
GlobalPlatform API
OPTEE实现了《GPD_TEE_Internal_Core_API_Specification》这一套API,提供了对称加密、非对称加密、签名、提取密钥等操作。
mbedtls库
mbedtls是为嵌入式设备而开发的一个TLS协议的轻量级实现,虽然是为嵌入式设备而开发,但它也能被用于其他各种平台,因此也常常被用作OpenSSL的一个轻量级替代。Tina SDK中optee-3.7.0中已经支持。
解决办法
GlobalPlatform API
密钥生成API:
TEE_GenerateKey密钥提取API:
TEE_GetObjectBufferAttribute签名验证API:
TEE_AsymmetricSignDigest
TEE_AsymmetricVerifyDigest加解密API:
TEE_CipherInit
TEE_CipherUpdate
TEE_CipherDoFinal
TEE_AsymmetricEncrypt
TEE_AsymmetricDecrypt可参考Linaro Security Working Group提供的例子,https://github.com/linaro-swg/optee_examples
mbedtls库
mbedtls算是比较常用的库,可参考官方API文档:https://tls.mbed.org/api/本文提供了一个rsa例子,如附件0003-optee-add-optee-mbedtls-rsa-demo.patch
0003-optee-add-optee-mbedtls-rsa-demo.patch -
【FAQ】全志R329 Tina下为何有些optee的demo只有NA源码没有TA源码?
问题描述
客户发现Tina SDK下有些optee demo 没有TA,提出问题:
为什么有的sample只有 NA的源码,比如 optee-keybox 和 optee-getdmkey,对应 TA的程序在哪里?
问题分析
高版本optee(对Tina,optee-3.7.0支持)新增了PTA(Pseudo Trusted Application)的支持。
PTA作用与TA类似,但是PTA不是一个TA,是一个接口,可以被TA与NA调用。
PTA直接在optee os中源码实现,直接编译到optee.bin中,运行也与optee_os是同一个特权等级。
简单来说,就是把原本在安全世界应用层实现的功能放到了操作系统层。
解决办法
一般来说TA是由客户自行开发,但是全志基于optee实现一些特有功能,这部分以PTA的形式集成到optee.bin中,暂未开源。
目前仅有optee-keybox与optee-getdmkey两个包是这样。
客户如需开发与keybox与dm-crypt相关的NA,可以参考上述两个包,直接调用相关命令,与正常的NA开发没有什么不同,TA则不用开发。
-
【FAQ】全志R329Tina下几种安全存储实现有何区别
问题描述
客户在《TinaLinux_安全开发指南》上看到有多种安全储存实现,想详细了解他们的区别,进行选择。
问题解答
Tina上目前支持三种安全存储,分别是keybox、optee secure storage与dm-crypt。
keybox
keybox是全志实现的一种安全存储方法,keybox使用flash区域是特意预留的,该区域未映射到逻辑扇区,通常的数据操作无法访问,具有量产不擦除特性。
keybox上保存的数据都是通过optee中进行加解密的。
注:
- 只有使用nand与emmc介质的方案才支持keybox,nor介质的方案不支持。
- keybox最多支持31个key烧写,每个key最大支持3KB数据。设计之初主要目的是存放密钥、证书等小数据对象。
- 可以通过DragonSN与控制台命令方式,烧写数据到keybox上。
optee secure storage
OPTEE Secure Storage是optee提供的根据GP TEE Internal API规范实现的一种安全存储技术。它将数据发送到Secure OS进行加密,然后保存到Linux端的文件系统(/data/tee)或者RPMB中。
注:
- 保存的数据大小没有限制,最大可以为optee os的堆大小。optee官方提到设计的目的主要是用于存放密钥、hash值以及计数器等小数据对象。
- 客户需要开发相关的NA与TA来使用optee secure storage功能,全志有提供一个参考demo,位于package/security/optee-secure-storage目录。
dm-crypt
dm-crypt 是Linux内核提供的一种基于加密API框架和设备映射器(device mapper)子系统的安全存储技术。dm-crypt是对整个文件系统进行加密,在初始化好后,可以直接读写该文件系统,内核自动对数据进行加解密。
注:
- dm-crypt的密钥需要保存在安全域,在初始化时,从安全域读取,配置给Linux内核。
- 从flash上看,该分区数据是经过加密的,但是Linux端可以直接明文访问。
- 由于是一个文件系统,所以这种方式可以保存大数据对象,用户数据分区常采用这种安全机制。
-
【FAQ】全志R329Tina安全启动校验linux/rootfs失败直接重启如何解决?
问题描述
客户发现在安全启动时,如果uboot中校验linux、rootfs失败,会进入uboot控制台,不符合他们的安全要求。
他们希望校验失败重启或停止运行。
问题分析
默认情况下,Tina安全启动中:
- 如果brom校验toc0(sboot)失败,会跳fel烧写;
- 如果sboot校验toc1(bl31/optee/uboot等)失败,会跳fel烧写;
- 如果uboot校验linux、rootfs失败,会进入uboot控制台。
可以根据不同客户的安全需求进行定制。
解决办法
参考下面补丁修改tina/lichee/brandy-2.0/u-boot-2018/cmd/bootm.c文件
diff --git a/cmd/bootm.c b/cmd/bootm.c index 0a267b4251..887c1f0a79 100644 --- a/cmd/bootm.c +++ b/cmd/bootm.c @@ -248,13 +248,17 @@ int do_bootm(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) full = 0; } if (sunxi_verify_partion(&verify_pattern, rootfs_name, "rootfs", full) != 0) { - return -1; + gd->debug_mode = 8; + printf("sunxi verify rootfs fail, reboot\n"); + reset_cpu(0); } #endif /*CONFIG_SUNXI_PART_VERIFY*/ if (sunxi_verify_os(os_load_addr, env_get("boot_from_partion")) != 0) { - return -1; + gd->debug_mode = 8; + printf("sunxi verify linux kernel fail, reboot\n"); + reset_cpu(0); } } #endif /*CONFIG_SUNXI_SECURE_BOOT*/
-
【FAQ】全志R329如何判断Tina安全启动boot分区证书与boot分区数据是匹配与否?
问题描述
客户自己移植Tina打包流程,反馈使用安全启动后,boot分区校验失败,导致启动不了,关键log如下:
[06.319]partinfo: name boot1, start 0xe00, size 0xb400 [06.895]kernel len:10350592, part len:23592960 **++hash compare is not correct++** **>>>>>>>hash of file<<<<<<<<<<** 5bed1d5c: 98590b3b 3b1116da 174ab29e 30a13e78 ;.Y....;..J.x>.0 5bed1d6c: 60efaf5c 567b8961 38d000dd a2da3783 \..`a.{V...8.7.. **>>>>>>>hash in certif<<<<<<<<<<** 5c3e2218: dd7f2b61 2343e66f cb2f648c 03c76a71 a+..o.C#.d/.qj.. 5c2e2228: 6a2ee78b f03c1694 43da3873 8474b958 ...j..<.s8.CX.t. bootm - boot application image from memory Usage: bootm [addr [arg ...]]
问题分析
log中hash in file是对boot分区payload部分计算sha256的值;hash in certify表示证书中的记录的sha256值,两个值必须相等。
本例中,log提示这两个值不等。可能的原因有很多:有可能是分区数据本身就有问题,也有可能计算sha256时内存中boot.img数据被修改等等。
但是,第一步要澄清分区中数据是否存在问题,即判断boot分区中证书与boot.img是否是匹配的。
本例中客户自己移植打包过程,容易造成上述问题。
解决办法
Tina安全启动中,boot分区包含boot.img与boot.img的证书。
在打包过程中,先将out/<platform>/boot.img复制到out/<platform>/image/boot.fex
然后通过dragonsecboot -toc1 dragon_toc.cfg xxx命令对out/<platform>/boot.fex进行签名,生成证书out/<platform>/image/toc1/cert/boot.der
dragonsecboot -toc1 dragon_toc.cfg ${ROOT_DIR}/keys ${CNF_BASE_FILE} ${ROOT_DIR}/image/version_base.mk
最后通过sigbootimg 命令将证书boot.der附在boot.fex文件后。此时boot.fex中包含了boot.img与其证书。
sigbootimg --image boot.fex --cert toc1/cert/boot.der --output boot_sig.fex
boot.fex与证书boot.der要匹配,如果不匹配就会导致校验不过。
可以进行如下验证:
① 执行sha256sum out/<platform>/boot.img,得到boot.img的sha256值。
② 将①运算的sha256值与out/<platform>/image/toc1/cnf/boot.cnf中的usr_cert块进行对比。看是否一样,如果不一样,则表明boot.img与证书不一致,打包流程出现问题,检查下是否有严格按照上述打包流程走。 -
【FAQ】全志R329 MiniGUI如何获取和设置BITMAP像素点?
问题描述
在MiniGUI中使用FillBoxWithBitmap加载一张图片后,需要获取到每一个像素点并重新设置像素点,实时更新内存中图像数据,做一些特效,比如旋转
问题分析
通过FillBoxWithBitmap加载图片后,返回的是BITMAP结构体,里面包含指向图像内存的指针bmBits,因此访问和修改该指针里的内容即可修改
解决方案
需要一个结构体来存RGBA数据
typedef struct tagRGBQUAD { BYTE rgbBlue; BYTE rgbGreen; BYTE rgbRed; BYTE rgbReserved; } RGBQUAD;
下面是获取像素的方法
static void getPixel(PBITMAP src) { RGBQUAD srcdib[src->bmWidth * src->bmHeight]; int x, y, point = 0; Uint8 *srcrow; Uint32 pixel; /* 循环获取像素点 */ for (y = 0; y < src->bmHeight; y++) { for (x = 0; x < src->bmWidth; x++) { /* 得到像素点的地址 */ srcrow = (Uint8 *) src->bmBits + y * src->bmPitch + x * src->bmBytesPerPixel; pixel = *((Uint32 *) (srcrow)); /* 这是MiniGUI中根据Pixel转成RGBA的函数 */ Pixel2RGBA(HDC_SCREEN, pixel, &srcdib[point].rgbRed, &srcdib[point].rgbGreen, &srcdib[point].rgbBlue, &srcdib[point].rgbReserved); /* 打印看看对不对 */ printf("%d %d %d %d\n", srcdib[point].rgbReserved, srcdib[point].rgbRed, srcdib[point].rgbGreen, srcdib[point].rgbBlue); /* 记录点的位置 */ point++; } } }
下面是设置像素的方法
static void setPixel(PBITMAP src, PBITMAP dstbmp) { /* 这里根据源图片重新构造一个PBITMAP对象 */ dstbmp->bmType = src->bmType; dstbmp->bmBitsPerPixel = src->bmBitsPerPixel; dstbmp->bmBytesPerPixel = src->bmBytesPerPixel; dstbmp->bmAlpha = src->bmAlpha; dstbmp->bmColorKey = src->bmColorKey; #ifdef _FOR_MONOBITMAP dstbmp->bmColorRep = src->bmColorRep; #endif dstbmp->bmAlphaMask = src->bmAlphaMask; dstbmp->bmAlphaPitch = src->bmAlphaPitch; dstbmp->bmWidth = src->bmWidth; dstbmp->bmHeight = src->bmHeight; dstbmp->bmPitch = src->bmPitch; dstbmp->bmBits = malloc(src->bmWidth * src->bmHeight * src->bmBytesPerPixel); /* dstdib存的是自己需要的每个像素点的值,根据需要自己赋值 */ RGBQUAD dstdib[src->bmWidth * src->bmHeight]; int x, y, point = 0; Uint32 pixel; /* 设置每一个像素点的值 */ for (y = 0; y < src->bmHeight; y++) { for (x = 0; x < src->bmWidth; x++) { pixel = RGBA2Pixel(HDC_SCREEN, dstdib[point].rgbRed, dstdib[point].rgbGreen, dstdib[point].rgbBlue, dstdib[point].rgbReserved); /* 打印看看像素是否正常 */ printf("%d %d %d %d\n", dstdib[point].rgbReserved, dstdib[point].rgbRed, dstdib[point].rgbGreen, dstdib[point].rgbBlue); printf("pixel=%x\n", pixel); /* MiniGUI根据pixel设置像素点的函数 */ SetPixelInBitmap(dstbmp, x, y, pixel); /* 记录点的位置 */ point++; } } }
-
【FAQ】全志R329如何在Tina在不烧写secure bit下开发OPTEE安全应用?
问题描述
正常情况下,需要在安全环境下开发安全相关功能。但是一旦开启安全功能就烧写掉efuse中的secure bit,以后芯片就只能启动安全固件。
客户处于功能开发阶段,如果把很多芯片烧写成安全,会造成一定的浪费,客户希望在不开启安全的情况下,也能正常的开发OPTEE的TA/NA。
问题分析
处理之前,我们简单了解下TrustZone与Secure bit之间的关系。
TrustZone的作用范围是CPU(处理器、gic、cache、总线等)。Secure bit是一个开关,开启后,TZMA/SMC/SPC/EFUSE/CE等模块,其作用范围是外设。
正常使用时,需要TrustZone与Secure bit一起协同工作,才能保障整个系统的安全。
这里需要说明的是,在Secure bit没有烧写时,TrustZone依旧是起作用的,CPU依旧有安全态与非安全态,但是外设、存储等是没有安全属性的,所以CPU在非安全态也可以访问所有的外设、内存。
回到客户问题,OPTEE的安全应用TA与非安全应用NA交互是基于TrustZone硬件基础,所以是可以在Secure bit没有烧写时进行OPTEE TA/NA的开发。
解决办法
arm方案
对于arm32方案来说,非安全固件中默认就包含了OPTEE,可以直接参考《TinaLinux_安全开发指南》进行开发。
aarch64方案
对于aarch64方案来说,非安全固件中默认没有包含OPTEE,在tina/device/config/chips/<chip>目录加入如下补丁后,非安全固件中就会包含OPTEE。
diff --git a/configs/default/boot_package.cfg b/configs/default/boot_package.cfg index 5b38539..2152e6e 100644 --- a/configs/default/boot_package.cfg +++ b/configs/default/boot_package.cfg @@ -2,6 +2,7 @@ ;item=Item_TOC_name, Item_filename, item=u-boot, u-boot.fex item=monitor, monitor.fex +item=optee, optee.fex ;item=logo, bootlogo.bmp.lzma ;item=shutdowncharge, bempty.bmp.lzma ;item=androidcharge, battery_charge.bmp.lzma
打包脚本修改
在tina/scripts加入如下补丁,其作用是在非安全情况下也对OPTEE中的公钥进行更新。如不更新,运行TA时对TA的校验将会失败,导致TA运行不了。
diff --git a/pack_img.sh b/pack_img.sh index f8b11fa..78ced4f 100755 --- a/pack_img.sh +++ b/pack_img.sh @@ -1749,19 +1749,6 @@ function do_signature() fi fi - # update optee pubkey - if [ -f ${ROOT_DIR}/staging_dir/target/usr/dev_kit/arm-plat-${PACK_CHIP}/export-ta_arm32/keys/default_ta.pem ]; then - ${PACK_TOPDIR}/scripts/update_optee_pubkey.py \ - --in_file optee.fex --out_file optee-new.fex \ - --key ${ROOT_DIR}/staging_dir/target/usr/dev_kit/arm-plat-${PACK_CHIP}/export-ta_arm32/keys/default_ta.pem - if [ $? -eq 0 ]; then - mv optee-new.fex optee.fex - else - pack_error "update optee pubkey error!" - exit 1 - fi - fi - if [ -f ${LONGAN_COMMON_DIR}/sign_config/cnf_base.cnf ] ; then CNF_BASE_FILE=${LONGAN_COMMON_DIR}/sign_config/cnf_base.cnf else @@ -1907,6 +1894,18 @@ function do_pack_tina() fi fi + if [ -f ${ROOT_DIR}/staging_dir/target/usr/dev_kit/arm-plat-${PACK_CHIP}/export-ta_arm32/keys/default_ta.pem ]; then + ${PACK_TOPDIR}/scripts/update_optee_pubkey.py \ + --in_file optee.fex --out_file optee-new.fex \ + --key ${ROOT_DIR}/staging_dir/target/usr/dev_kit/arm-plat-${PACK_CHIP}/export-ta_arm32/keys/default_ta.pem + if [ $? -eq 0 ]; then + mv optee-new.fex optee.fex + else + pack_error "update optee pubkey error!" + exit 1 + fi + fi + if [ "x${PACK_SIG}" = "xsecure" ] ; then echo "secure" do_signature
注意事项
按照上述办法可以进行在不烧写seucre bit情况下开发OPTEE的安全应用,但是由于外设、内存等资源的安全属性没有进行设置,因此务必在烧写secure bit的芯片上进行功能验证与测试。
-
【FAQ】全志R系列 Tina 如何查看通过 procd init 脚本启动的应用输出到 stdout/stderr 的打印信息?
问题描述
当我们使用 procd init 脚本让某个应用程序实现开机自启时,会发现应用程序中原本通过 printf/fprintf 等输出到 stdout/stderr 的打印信息都无法从串口或 adb shell 中看到了。
这些打印默认是输出到什么地方?我们可以如何看到这些打印?
原因
一般情况下,当用户在终端中执行命令来运行某个应用程序时,stdin/stdout/stderr 就确定下来是在当前终端,因此应用程序的打印信息自然能从当前终端中显示出来。
而如果该应用程序是通过 procd init 脚本进行开机自启,它会被认为是一个守护进程(daemon)。守护进程是随系统自启的,它们有可能在用户登录终端之前就已经开始运行了,也无法得知用户是从哪个终端登录,因此也就无法将打印信息输出到用户所在的终端。
解决方法
一般来说,要获取守护进程的打印,需要通过 syslog 之类记录系统整体日志的方法。procd init 脚本也提供了方法将应用程序的打印重定向到 syslog 中。
下面是一个简单的 procd init 脚本例子,它会启动应用程序 /usr/bin/foobar,但我们默认没法看到 foobar 输出到 stdout/stderr 的打印:
#!/bin/sh /etc/rc.common START=50 USE_PROCD=1 start_service() { procd_open_instance procd_set_param command /usr/bin/foobar procd_close_instance }
通过增加“procd_set_param stdout 1”和“procd_set_param stderr 1”两个参数,可将其输出到 stdout/stderr 的内容重定向到 syslog:
#!/bin/sh /etc/rc.common START=50 USE_PROCD=1 start_service() { procd_open_instance procd_set_param command /usr/bin/foobar procd_set_param stdout 1 # 将其 stdout 的内容重定向到 syslog procd_set_param stderr 1 # 将其 stderr 的内容重定向到 syslog procd_close_instance }
如此设置后,就可以从 syslog 中看到 foobar 应用程序输出的打印。
-
【FAQ】全志R329如何设置蓝牙自动重连时间或关闭自动重连?
问题背景
通常情况下,蓝牙设备因距离远或信号不好会发生断开连接,但环境恢复后蓝牙设备会自动重新连接。
问题描述
部分客户想设置这个自动重新连接的时间,或者因某种原因想直接关闭自动重连。
问题分析
(1)蓝牙自动重连的实现。
在bluez里面,bluez/plugins/policy.c文件,负责解析配置文件和实现蓝牙重连的策略。
bluez/src/main.conf是蓝牙可配置文件,[Policy]部分就是重连配置。用户也可以在etc/bluetooth/目录下找到main.conf文件修改配置。
(2)配置参数
ReconnectUUIDs:设置重新连接的services
ReconnectAttempts:设置重新连接的次数
ReconnectIntervals:设置连接间隔,和连接次数对应
AutoEnable:发现adapters时自动使能它解决办法
在main.conf修改配置参数,如关闭自动重连就将连接次数设置为0即可。
不适用main.conf配置参数,直接在policy.c文件里修改默认配置。 -
【FAQ】全志R329 LRADC电压设置、识别精度、识别误差、应用等相关知识
问题背景
不少朋友在使用LRADC可能会有以下几种疑问:
- LRADC允许输入电压是多少?
- 支持检测最大/小的电压是多少?
- LRADC最小能检测多大的电压变化?
- LRADC的误差有多少?
- LRADC应该如何设计按键的分压值?最多能接入多少个按键。
本文主要讲述以上几个点。
设备环境
- 软件系统:Tina系统
- 硬件芯片:带LRADC的soc
LRADC详解
1、LRADC正常工作需要输入的电压是1.8V。但是,最大不能超过1.8V。
2、LRADC最大能检测的电压值是1.35V。所以即使输入的是1.8V,LRADC读出来的还是1.35V。最小能检测的电压是0V,即直接下拉接地。
(LRADC最大能检测的电压值每款芯片可能不一样,有的芯片是1.2V,详情查看user manual)3、LRADC的分辨率为6bit,即64个数。当最大检测电压为1.35V时,每一档电压为1350/64=21.1mV,即为LRADC最小能检测电压变化范围。
4、分辨率为6bit,精度是为4bit,理论误差为±84mV。但是经过硬件中的误差纠正后,精度可为5bit,即理论误差为±42mV(这个是理论误差,实际误差要比理论的要小,经过测试最大为±30mV左右的误差)。
5、由于这个误差的存在,在将LRADC用作按键的时候需要注意,尽量把两个按键之间的电压间隔设计大一点,这里推荐最小间隔为**“120mV”**,并且不超过1.2V。(因为当电压超过1.266V后,可认为1.266V~1.35V之间的电压是不准确的。)因此,最大可以设计120mV/240mV/…1200mV等10个档位。
不过,一般在公版上都是设置5个按键档位,硬件设计可参考下图:
image.png
LRADC拓展
LRADC精度比较低,一般不推荐用其作为普通ADC使用的。那么,除了作为按键使用的话,还可以用作什么用途呢?
这里还推荐一种使用方式,许多客户可能需要在同个板子上使用多种方案,不同方案只有可能有些模块是不一样的,但是固件又只能出一种。这时候,就可以使用LRADC达到区分方案,从而加载不同dts适配各个方案的目的。
首先,可以在硬件中,不同的方案设置不同电阻上/下拉连接到LRADC,使得不同方案输入到LRADC的电压值不一样。比如说,方案A直接10k电阻上拉到1.8V,方案B通过10k电阻下拉到0V。那么,只需要在uboot里调用lradc读电压接口,再稍做判断即可知道当前硬件是属于什么方案,然后再加载不同dts。
lradc读电压接口代码,在uboot里的路径为:u-boot-2018/board/sunxi/sunxi_lradc_vol.cimage.png
首先是调用lradc_data_init()对LRADC进行一个初始化,然后再调用lradc_read_data(u32 reg_base)读取实际的电压值,详情使用方法可以参考下方的sunxi_read_lradc_vol(void)函数。
需要注意的是:
LRADC默认的采样频率要500Hz,即2ms采样一样,而电压data寄存器需要采样8次才能更新一次电压,所以一共需要至少16ms才能准确读取真实的电压。
因此,在调用LRADC初始化后至少30ms后,才能调用用lradc_read_data去读取电压。 -
【FAQ】全志R329在dtbo中如何配置gpio?
问题背景
客户同一款产品,一供模拟功放和二供模拟功放使用不同的PIN脚来使能PA,客户想在dtbo中根据硬件版本类型配置不同的pin脚。但是在dtbo中根据dts的方法配置的时候会报syntax error。如当时按以下方法配置成PM8的情况,会提示syntax error
&codec_ex { gpio-spk = <&r_pio PM 8 1 1 1 1>; };
问题分析
在dts中,如果要使用gpio.h中定义的pin脚,那么可以直接用#include <dt-bindings/gpio/gpio.h>这句话把gpio.h头文件包含之后就可以使用里面定义的宏了。但dtbo的语法不支持这种include的方式,也不支持直接在dtbo中定义宏的方式,因此要配置PM8这个pin时,不能写PM这个符号,只能写PM实际定义的数字。所有pin定义的数字定义在lichee/linux-4.9/include/dt-bindings/gpio/gpio.h,如下所示:
/* sunxi gpio arg */ #define PA 0 #define PB 1 #define PC 2 #define PD 3 #define PE 4 #define PF 5 #define PG 6 #define PH 7 #define PI 8 #define PJ 9 #define PK 10 #define PL 11 #define PM 12 #define PN 13 #define PO 14 #define PP 15 #define default 0xffffffff
解决方法
在device/config/chips/r329/configs/xxx/dtbo/xxx.dts中根据实际要配置的pin脚加入以下代码,如配置使能脚为PM8,则根据lichee/linux-4.9/include/dt-bindings/gpio/gpio.h中的宏定义把PM改成12,改动如下:
&codec_ex { gpio-spk = <&r_pio 12 8 1 1 1 1>; };
-
【FAQ】全志R329如何在Tina安全应用程序调试?
问题描述
客户将Tina上的安全应用移植到自己的SDK,编译完成后,执行时出现如下问题:
printf(“NA:open session\n”);
teecErr = TEEC_OpenSession(&ctx, &teecSession, &ta_UUID,
TEEC_LOGIN_PUBLIC, NULL, NULL, NULL);在打印了NA:open session后,卡在了TEEC_OpenSession后面就没走了。
问题分析
TEEC_OpenSession函数作用是建立一个指定NA(非安全程序)与指定TA(可信程序)之间的通信通道。
在此处出错,表明NA与TA建立不了联系,可能原因有多种。
首先要对NA与TA通信过程要有整体了解,才确认在哪个节点出现问题。
解决办法
了解NA与TA通信流程。如下图所示:
整个通信设计5个模块: NA <-> optee_client <-> linux驱动(driver/tee/optee)<-> optee(闭源)-> TA。
在上述每个过程中都可能出现问题。下一步,检查每个模块是否工作正常。
optee_client。 optee_client提供NA使用的libteec库,以及tee-supplicant后台应用程序。
确保rootfs中有包含该库与应用程序,同时tee-supplicant有在后台运行。linux驱动。 检查内核配置有没有开CONFIG_TEE与CONFIG_OPTEE。启动后,检查是否存在tee的设备节点/dev/tee0与/dev/teepriv0。
由于optee为闭源,可以先从NA查到linux驱动,比如TEEC_OpenSession这个最终会调用到内核driver/tee/tee_core.c中的tee_ioctl_open_session函数。检查上述流程是否存在问题。
TA。TA也是客户自己开发。NA与TA交互接口如下图所示。比如本例NA调用TEEC_OpenSession,会与TA中的TA_CreateEntryPoint与TA_OpenSessionEntryPoint接口交互,可以在这两个接口中加入打印,看是否有到达TA。
如果最终确认是optee中的问题,可以找AW内部安全负责人进行处理。
-
【FAQ】全志R329如何解决Tina双系统uboot校验rootfs失败的问题?
问题描述
内核双备份系统安全固件启动失败,有如下log:
Hit any key to stop autoboot: 0
[06.393]partinfo: name boot1, start 0xe00, size 0x7800
[06.989]begin to verify rootfs
get part: rootfs info failed
bootm - boot application image from memory
Usage:
bootm [addr [arg …]]boot application image stored in memory
问题分析
从log中看,提示没有找到名字为rootfs的分区信息。
此问题原因:由于Tina默认当前是但系统,uboot校验rootfs时直接指定的rootfs的分区名“rootfs”。但是客户是双系统,rootfs有两个分区,分区名为rootfs1与rootfs2,而这里直接指定rootfs,会造成找不到对应的分区信息。
解决办法
不直接指定,从evn环境变量中获取当前要启动的rootfs分区。在tina/lichee/brandy-2.0/u-boot-2018/加入如下补丁:
diff --git a/cmd/bootm.c b/cmd/bootm.c index f4071e026e..0a267b4251 100644 --- a/cmd/bootm.c +++ b/cmd/bootm.c @@ -231,6 +231,7 @@ int do_bootm(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) struct sunxi_image_verify_pattern_st verify_pattern = { 0x1000, 0x100000, -1 }; + char *rootfs_name = env_get("root_partition"); char *s = env_get("rootfs_per_MB"); if (strcmp(s, "full") == 0) { full = 1; @@ -246,7 +247,7 @@ int do_bootm(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) full = 0; } - if (sunxi_verify_partion(&verify_pattern, "rootfs", "rootfs", full) != 0) { + if (sunxi_verify_partion(&verify_pattern, rootfs_name, "rootfs", full) != 0) { return -1; }
-
【FAQ】全志R329如何使用DMIC的高通滤波寄存器滤掉低频噪声?
开发环境
硬件:R328
软件:Tina
内核: Linux-4.9问题描述
客户使用DMIC录音,想通过配置高通滤波寄存器,滤掉800hz的噪声
与高通滤波相关的寄存器:
HPF_EN_CTR: 0x38
HPF_COEF_REG: 0x3c
HPF_GAIN_REG: 0x40解决方案
只需要更改HPF_COEF_REG中的值即可,假如效果没有达到预期,还请找出噪声来源,从源头上解决。
coef值与截止频率的关系是Fc=fs*(1-coef)/2π,其中fs是采样频率。
coef的值是3.24格式,也就是有效位是27bit,3个整数位,24个小数位,默认值为0x00FFAA45,换算成10进制就是0.99869,当采样频率是48k时,默认设置的截止频率Fc=48k *(1-0.99869)/2π = 10Hz。
下面演示如何通过配置HPF_COEF_REG寄存器滤掉100hz的正弦波。
使用audiocodec播放100hz正弦波。
HPF_COEF_REG使用默认值,DMIC使用采样频率16k录音,保存文件为100hz_default.wav。
截止频率为100Hz的话,经计算得出coef值为0.96075,因此HPF_COEF_REG修改为0x00f5f1db,DMIC使用采样频率16k录音,保存文件为100hz_setHPF.wav。
两者的音频波形如下,可见100hz_setHPF的波形中,录回正弦波的db值比100hz_default更小。
因为驱动中并没有对该寄存器进行操作,所以目前只有硬改寄存器的方式进行配置,之后会考虑在dts中添加截止频率的配置项。
-
【FAQ】全志R329如何解决Tina中package下新增软件包目录很深导致检测不到问题
问题描述
在Tina/package下新增软件包配置,如果新增软件包的目录太深,将会导致tina检测不到。
比如tina/package/dir1/dir2/dir3/dir4/Makefile可以检测到,但是tina/package/dir1/dir2/dir3/dir4/dir5/Makefile将检测不到。
问题分析
Tina构建时,将根据一定的规则扫描各个目录下的Makefile文件,这些规则定义在tina/build目录下。tina/build目录下有一个scan.mk文件,打开后可以看到有一个$(FILELIST)的编译目标,语句比较简单。
主要操作是删除一个临时文件,然后在(SCAN_DIR)下查找目录深度为1到(SCAN_DEPTH)的Makefile文件,找到之后进行一些处理,并写入到(FILELIST),我们可以打开FILELIST文件看看,里面记录了package下目录深度为1到(SCAN_DEPTH)的包目录。
FILELIST:=$(TMP_DIR)/info/.files-$(SCAN_TARGET)-$(SCAN_COOKIE) $(FILELIST): $(OVERRIDELIST) rm -f $(TMP_DIR)/info/.files-$(SCAN_TARGET)-* $(call FIND_L, $(SCAN_DIR)) $(SCAN_EXTRA) -mindepth 1 $(if $(SCAN_DEPTH),-maxdepth $(SCAN_DEPTH)) -name Makefile | xargs grep -aHE 'call $(GREP_STRING)' | sed -e 's#^$(SCAN_DIR)/##' -e 's#/Makefile:.*##' | uniq | awk -v of=$(OVERRIDELIST) -f build/scan.awk > $@
所以,差不多知晓其中$(SCAN_DEPTH)就是扫描的深度,在tina/build下查找改SCAN_DEPTH关键字,发现在tina/build/toplevel.mk下有相关设置。
prepare-tmpinfo: FORCE @+$(MAKE) -r -s out/host/.prereq-build $(PREP_MK) mkdir -p tmp/info $(_SINGLE)$(NO_TRACE_MAKE) -j1 -r -s -f build/scan.mk SCAN_TARGET="packageinfo" SCAN_DIR="package" SCAN_NAME="package" SCAN_DEPS="$(TOPDIR)/build/package*.mk $(TOPDIR)/overlay/*/*.mk" SCAN_DEPTH=5 SCAN_EXTRA="" $(_SINGLE)$(NO_TRACE_MAKE) -j1 -r -s -f build/scan.mk SCAN_TARGET="targetinfo" SCAN_DIR="target/allwinner" SCAN_NAME="target" SCAN_DEPS="*.mk $(TOPDIR)/build/kernel*.mk $(TOPDIR)/build/target.mk" SCAN_DEPTH=2 SCAN_EXTRA="" SCAN_MAKEOPTS="TARGET_BUILD=1"
可以看到上述prepare-tmpinfo目标中的部分内容中,会对两个目录进行扫描,一个是tina/package目录,最大扫描深度为5; 一个是tina/target/allwinner目录,最大扫描深度为2。
到现在,问题点已经找到。
解决办法
将toplevel.mk中package目录扫描深度SCAN_DEPTH改大就可以了。
这里也需要注意一点,SCAN_DEPTH改大之后,扫描内容变多,会导致编译前的准备工作变慢,因此最好还是不要将软件包放那么深。
-
【FAQ】全志R329 多台设备的adb device id相同,无法指定设备,如何指定设备的adb device id?
问题背景
系统平台:R329 + Tina
问题描述
多块R329开发板连接电脑后的adb device id相同,均为一串0,同时连接时无法通过adb -s [device id] shell指定设备连接,需要给每个设备指定不同的device id。
问题分析
一串0的长度与chip id的长度相似,猜测默认是将chip id 作为device id,但是这几块开发板没有烧码导致device id相同;
cat /sys/class/sunxi_info/sys_info查看sunxi_serial确实是一串0。
解决方法
Tina_Linux_USB_开发指南.pdf中有配置USB序列号的介绍,其中是在uboot将chip id作为androidboot.serialno,由adbd脚本读取该值,写入节点/sys/kernel/config/usb_gadget/g1/strings/0x409/serialnumber
读取serialnumber的代码如下:
get_serialnumber() { str=`cat /proc/cmdline |tr ' ' '\n' | grep 'androidboot.serialno' | awk -F "=" '{print $2}'` [ -z $str ] || SERIALNUMBER=$str }
这里我直接修改脚本而不去修改dts:
修改/etc/rc.d/K99adbd中的get_serialnumber函数为:
get_serialnumber(){ SERIALNUMBER="12345678" }
其中的12345678即为指定的device id。
也可以读取将指定文件内容作为serialnumber,文件不存在时随机生成并保存为文件:
ADB_SERIAL_CONF="/etc/adb_serial.conf" get_serialnumber() { if [ ! -f $ADB_SERIAL_CONF ];then rand_var=$(head -200 /dev/urandom | cksum | cut -f1 -d " ") if [ -n "$rand_var" ];then SERIALNUMBER=$(printf %010d $rand_var) echo $SERIALNUMBER > $ADB_SERIAL_CONF fi else SERIALNUMBER=$(cat $ADB_SERIAL_CONF) fi }
-
【FAQ】全志R329如何解决蓝牙播放无声(snd_pcm_open error: Out of memory)?
问题背景
客户使用蓝牙a2dp source功能,连接蓝牙耳机播放音乐高概率出现播放无声的现象。
但在客户使用相同btmanager版本的其他机型上并没有遇到这个问题。问题描述
在出现播放无声问题时,btmanager会一直出现如下的打印:BTMG[aw_pcm_open:400]: --->Couldn't open PCM:bluealsa:DEV=28:37:13:3B:DA:78 BTMG[_a2dp_src_pcm_write:150]: a2dp source open pcm error: Out of memory;
从log来看,播放时没有成功打开蓝牙虚拟声卡,所以播放没有声音是必然的。其中打开蓝牙声卡是使用标准函数snd_pcm_open,
而错误信息是 Out of memory。显然从日志来看,此问题与系统内存有关系。客户机器是512M内存。问题分析
既然与内存有关系,便从内存方向去分析此问题:
1)查看当前内存信息
cat /proc/meminfo
但是看内存信息,在声卡一直打不开的时候,MemFree也有 10M左右,所以就觉得有点奇怪了,为什么内存这么多,还是申请不到内存打开声卡呢?
难道是脏内存太多了?2)尝试线程的栈调大
- 把btmanager里面相关的线程栈调高至20K,还是无效。
- 修改C库把默认线程的栈修改为1M,依然无效。
3)清理缓存
是不是脏内存太多,内存不连续?
使用echo 3 > /proc/sys/vm/drop_caches 清理一下,发现就可以正常申请了。
但是让内存的同事看了下连续内存还是足够的,所以这还不是根本原因。4)跟踪代码
通过在alsa-lib 里面增加打印,最终发现是在snd_user_file函数里面调用wordexp(file, &we, WRDE_NOCMD);
返回了WRDE_NOSPACE,而打开的file /usr/share/alsa/alsa.conf也只有10K左右。再继续跟踪C库里面的wordexp函数,发现是在fork的时候有问题。
接着又做了个实验,同时在出问题的时候,我们在非打开声卡的地方调用fork函数,也是会申请不到内存。
所以本质上是fork函数导致的,打开声卡的过程刚好触发了这个问题。解决办法
对于fork函数,有如下的行为:
fork函数会创建带有独立虚拟地址空间的新进程,内核会把当前进程的虚拟内存中数据结构复制一份给新进程。
看了下客户应用的出问题时的内存情况:
异常时,应用的VmPeak和VmSize偏高,这个说明此时应用申请的虚拟内存是非常高了。
此时如果有打开声卡,触发fork的操作,子进程也需要申请那么大的虚拟内存。
问题就在此,突然申请那么大的虚拟内存,被内核限制了。解决方案:
1、客户出问题的方案,由于跑特殊的算法导致使用了比较大的虚拟内存。
可以通过优化应用内存解决。2、取消内核的限制
在系统启动脚本加上 echo > 1 /proc/sys/vm/overcommit_memory 这个命令操作。
内核就不会限制应用申请比较大的虚拟内存了。 -
【FAQ】全志R329如何修改bluez缓存保存路径
问题背景
系统改为只读之后,蓝牙的扫描/配对等信息无法保存。
问题描述
客户的系统在开发前期所有的分区都是可读可写的,此时蓝牙功能还是正常的。
到了项目后期,可能会把系统改为只读了,只有data分区可写。
这时会发现本来已经配对连接过的蓝牙设备,在开关蓝牙之后就丢失了。问题分析
bluez默认的缓存路径为:/etc/lib/bluetooth/
由于修改了分区权限,会导致相关信息无法写入。解决办法
修改缓存路径到可写的分区,修改Makefile即可。
路径:package/utils/bluez/Makefile
假如data分区可写,就迁移到data:
把 --localstatedir=/etc 修改为 --localstatedir=/data,重新编译bluez。
修改后保存路径变为:/data/lib/bluetooth/ -
【FAQ】全志R329如何在WiFi Softap模式添加自定义Vendor IE?
问题背景
对于某些应用场景,需要在Probe Response帧和Beacon帧中添加指定的Vendor IE信息,以满足特定需求
问题分析
对于802.11管理帧,IE是可变的,因此可以在其Payload末尾添加IE信息,IE由ID、Length(有效载荷长度,单位为字节)和Payload构成
实现方法
在在hostapd.conf配置文件中增加vendor_elements选项,格式为 id + len + payload十六进制形式,举例如下:
vendor_elements=23020600 //id=0x23=35, length=0x02=2, payload=0600
测试验证
通过抓包确定IE是否添加成功 -
【FAQ】全志R329 XRADIO如何查看发射功率和频偏?
问题背景
硬件:R328+ Wi-Fi模组(XRADIO)
软件:Tina3.0及以上
说明:该FAQ旨在记录XRADIO驱动常见的调试Tips。与具体驱动相关,具有一般性。问题简述
客户在做吞吐性能优化过程中问到:如何查看和修改xr829的发射功率和频偏?
问题分析
发射功率和频偏都是性能优化过程中比较重要的参数,不支持驱动直接修改。
都是在firmware文件中设置的,都是经过大量测试才定的。所以一般不建议客户自己修改,以避免引起性能问题。解决方法
可以通过sdd工具查看,如下:
借用sdd工具导入firmware(package/firmware/linux-firmware/xr829/sdd_xr829_40M.bin)文件即可。
注意:区分不同晶振的sdd文件不一样。注:sdd工具获取
可以从一号通获取:https://one.allwinnertech.com/#/devtool?menuID=37 -
【FAQ】全志R329如何关闭HT40?
问题背景
硬件:R328+ Wi-Fi模组(XRADIO)
软件:Tina3.0及以上
说明:该FAQ旨在记录XRADIO驱动常见的调试Tips。与具体驱动相关,具有一般性。问题简述
客户在做吞吐性能优化过程中问到:xr829的HT40如何关闭?
问题分析
1.XR829如何关闭HT40?
HT40 怎么关闭,如果在Makefile里面加入 ccflags-y += -DSUPPORT_HT40去掉,wifi无法连接热点.
直接在Makefile中注释#ccflags-y += -DSUPPORT_HT40- 直接编译不过,发现就是一个头文件的函数:*wsm_get_80211_frame(),没有其他任何地方用,只在/wlan/wsm.h中定义了。
- 强行注释掉函数:*wsm_get_80211_frame()后,编译通过,系统起来无法扫描,也无法联网。
解决方法
正确做法:
在wlan/main.c中:
#ifndef SUPPORT_NON_HT40_CHIP - | IEEE80211_HT_CAP_SUP_WIDTH_20_40 + //| IEEE80211_HT_CAP_SUP_WIDTH_20_40 | IEEE80211_HT_CAP_SGI_20 - | IEEE80211_HT_CAP_SGI_40 + //| IEEE80211_HT_CAP_SGI_40 #endif'''
-
【FAQ】全志R329一些正常的I2C报错
问题背景
在使用I2C通信的时候,经常会出现一些I2C通信失败的报错,但是呢,有一些报错是正常的,并不会影响到系统或者模块正常工作的。下面就讲述一些常见的I2C正常报错。
问题分析
1、当在dts中配置了一些i2c设备,并且在kernel_menuconfig中也选上对应的驱动,但是实际硬件中并没有连接真实的硬件,这就会导致在系统启动时,会有不少的I2C报错打印出来。
这里以一款触摸屏为例:系统启动时触摸屏驱动会多次地使用I2C向触摸屏模块进行通信,但是由于实际硬件没有连接触摸屏,通信是一定会失败的,因此就会打印一大堆通信失败的字眼。这些其实都是正常的,并不会影响到系统正常加载。
解决办法:
如果不想要看到这么多错误的打印的话,可以尝试以下几种办法:
(1)可以修改系统启动时的打印等级,通过命令“cconfigs”跳转到方案的配置目录,然后找到文件env*.cfg(这里*指的是内核版本,若是4.9内核,则配置文件名为env-4.9.cfg,在4.9以前版本的内核中都是这样命名的。只有在5.4内核中,是直接命名为env.cfg)。然后修改参数loglevel,将其值改为0,那么就可以减少很多内核打印了。
(2)在触摸屏代码中,在probe函数中初始化一些资源后,尽快地调用I2C去读取触摸屏的一些ID号或者是版本号,若读取失败后,就立即退出驱动加载。没必要继续进行其他设置相关的I2C通信。
2、使用i2c-detect工具检索某个I2C总线哪些地址存在设备时,出现很多错误通信的log信息,可能会令人误以为该I2C总线是有问题的。如下图所示:
上图命令i2cdetect -y 2的作用是:检索I2C2总线上所有的I2C设备。这里的打印其实都是正常的,检索是通过cpu主动向全部I2C地址读写数据来检测的。可能硬件上一根I2C总线就连接了一个I2C设备,只会占用一个I2C设备地址,那么其他地址都是为空的。自然而然地,与这些空的设备地址通信肯定是失败的,因此也一定会有错误的打印打出来。
解决办法:
(1)减少系统内核打印等级,进入内核打印等级修改节点“printk”的目录:
cd /proc/sys/kernel
ls
cat printk
echo 2 > printk
一般内核打印等级为4,当输入到printk的值小于4时,即可把大于等于4的打印等级全部关闭。(2)使用adb去调试:
内核打印是通过串口打印出来的,若通过usb连接开发板,通过adb调试进入系统调试界面,adb是不能接收串口打印的,因此自然地看不到内核错误打印。 -
【FAQ】全志R329蓝牙设备类型分类与过滤方法
问题背景
在蓝牙扫描时,有的客户想对扫描到的蓝牙设备类型进行分类,并过滤出想要的设备。
问题描述
蓝牙设备类型是怎么分类的?
用C代码怎么实现蓝牙设备分类的函数?问题分析
蓝牙设备类型也可以称为cod(class of device)。
Cod的结构如上图,一共有3个byte,其中一共分为4个部分。
主要分为:
(1)Service class(服务类型)
描述此设备有什么功能?Networking?Audio?Telephony?(2)Major device class(设备的主类型)
这个是我们解析需要关注的重要部分,它是用来说描述蓝牙设备的一个大分类。
比如:Computer、Phone 、Audio/Video、Peripheral等等。
如果只是想了解到这个蓝牙设备的大致类型,解析到Major device class就可以了。(3)Minor device class(设备的子类型)
对应于每种Major类型下面的子类,从这里更能清楚了解到这个设备。
比如 Audio/Video这个主类型,它还可以分为Wearable Headset Device、Hands-free Device、Headphones、Loudspeaker。对于COD,蓝牙官方网站有更详细的介绍:
https://www.bluetooth.com/specifications/assigned-numbers/baseband/
解决办法
那么用代码应该怎么解析呢?
在Tina 平台上扫描到设备时是带有class信息的。比如bt_test扫描的结果如下:
[bt]#scan 1 [bt]#63.778892: [bt_test_discovery_status_cb:80]: bt start scanning. 63.778957: [btmg_gap_bluez_callback:1204]: Discovery started 63.951689: [bt_test_dev_add_cb:104]: address:7C:2A:DB:F9:67:B7,name:Redmi 9,class:5898764,icon:phone,address type:public,rssi:-60 64.552549: [bt_test_dev_add_cb:104]: address:E0:DC:FF:E9:34:1E,name:flyBT,class:5898764,icon:phone,address type:public,rssi:-56 66.288756: [bt_test_dev_add_cb:104]: address:90:F0:52:69:2F:86,name:houxiaoni,class:5898764,icon:phone,address type:public,rssi:-76 66.303184: [bt_test_dev_add_cb:104]: address:00:19:86:00:03:C9,name:PCJIJIANJUN2,class:260,icon:computer,address type:public,rssi:-78
其实icon信息也能看出设备的主类型,这是bluez协议栈处理好的。
我们直接对class的值按照cod的规则进行解析即可,参考代码:只解析主要设备类
const char *get_device_class_info(uint32_t device_class) { switch ((device_class & 0x1f00) >> 8) { case 0x01: return "computer"; case 0x02: return "phone"; case 0x03: return "network-wireless"; case 0x04: return "audio"; case 0x05: return "input-mouse"; case 0x06: return "printer"; break; } return NULL; }
解析到次要设备类
const char *get_device_class_info(uint32_t device_class) { switch ((device_class & 0x1f00) >> 8) { case 0x01: return "computer"; case 0x02: switch ((device_class & 0xfc) >> 2) { case 0x01: case 0x02: case 0x03: case 0x05: return "phone"; case 0x04: return "modem"; } break; case 0x03: return "network-wireless"; case 0x04: switch ((device_class & 0xfc) >> 2) { case 0x01: case 0x02: return "audio-Headset"; /* Headset */ case 0x06: return "audio-Headphone"; /* Headphone */ case 0x0b: /* VCR */ case 0x0c: /* Video Camera */ case 0x0d: /* Camcorder */ return "camera-video"; default: return "audio"; /* Other audio device */ } break; case 0x05: switch ((device_class & 0xc0) >> 6) { case 0x00: switch ((device_class & 0x1e) >> 2) { case 0x01: case 0x02: return "input-gaming"; } break; case 0x01: return "input-keyboard"; case 0x02: switch ((device_class & 0x1e) >> 2) { case 0x05: return "input-tablet"; default: return "input-mouse"; } } break; case 0x06: if (device_class & 0x80) return "printer"; if (device_class & 0x20) return "camera-photo"; break; } return NULL; }
代码中没有对所有子设备进行解析,如果以上没有涉及到,请参考蓝牙COD进行解析。
-
【FAQ】全志R329如何解决调用Bluez mainloop_add_fd函数失败的问题?
问题背景
Tina系统使用的蓝牙协议栈为开源bluez协议栈,在上层应用开发的时候会常常使用mainloop_add_fd函数,尤其是btmanager中也有很多模块是调用了该函数。
问题描述
在使用Tina btmanager ble模块时,启动异常,提示打印"Failed to add listen socket(11),error(-38)",详细如下:
1607334876.975885: [gatt_run:355]: ++ 1607334876.976066: [l2cap_le_att_listen:263]: ++ 1607334876.976208: [l2cap_le_att_listen:296]: Started listening on ATT channel 1607334876.976311: [gatt_run:371]: sockfds 12 13 1607334876.976820: [gatt_set_bta_mainloop_id:27]: tid: 3047021836 1607334876.977276: [gatt_run:373]: tid 3047021836 1607334876.977541: [gatt_set_bta_cmd_fd:39]: gatt_commfds 12 13 1607334876.977884: [gatt_bta_register_handler:44]: ++ 1607334876.979351: [gatt_run:383]: Failed to add listen socket(11),error(-38)
问题分析
"[gatt_run:383]: Failed to add listen socket(11),error(-38)"该行错误打印,提示的源代码如下:```
ret = mainloop_add_fd(fd, EPOLLIN, server_listen_cb, server, NULL);
if (ret < 0) {
BTMG_ERROR("Failed to add listen socket(%d),error(%d)\n",fd,ret);
close(fd);
netcfg_state = 0;
return NULL;
}从上可以判断是调用了mainloop_add_fd函数异常,该函数是链接的开源bluez协议栈中的库,于是搜索了bluez中关于mainloop_add_fd的实现,发现实现该函数有有三处地方,分别是 (1)src/shared/mainloop-ell.c:编入到src/.libs/libshared-ell.a库中。
int mainloop_add_fd(int fd, uint32_t events, mainloop_event_func callback,
void *user_data, mainloop_destroy_func destroy)
{
return -ENOSYS;
}(2)src/shared/mainloop-glib.c:编入到src/.libs/libshared-glib.a库中。
int mainloop_add_fd(int fd, uint32_t events, mainloop_event_func callback,
void *user_data, mainloop_destroy_func destroy)
{
return -ENOSYS;
}(3) src/shared/mainloop.c:编入到src/.libs/libshared-mainloop.a库中。
int mainloop_add_fd(int fd, uint32_t events, mainloop_event_func callback,
void *user_data, mainloop_destroy_func destroy)
{
struct mainloop_data *data;
struct epoll_event ev;
int err;
if (fd < 0 || fd > MAX_MAINLOOP_ENTRIES - 1 || !callback)
return -EINVAL;
data = malloc(sizeof(*data));
if (!data)
return -ENOMEM;
memset(data, 0, sizeof(*data));
data->fd = fd;
data->events = events;
data->callback = callback;
data->destroy = destroy;
data->user_data = user_data;
memset(&ev, 0, sizeof(ev));
ev.events = events;
ev.data.ptr = data;
err = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, data->fd, &ev);
if (err < 0) {
free(data);
return err;
}
mainloop_list[fd] = data;
return 0;
}而 Failed to add listen socket(11),error(-38)提示-38正好是"-ENOSYS", 在errno.h中有定义
#define ENOSYS 38 /* Function not implemented */
所以排查到是链接的函数不对,应用程序中同时链接了libshared-glib.a和libshared-mainloop.a,最后取用的是libshared-glib.a,应该使用的是libshared-mainloop.a。 **解决办法** 在使用mainloop_add_fd函数的时候,要尤其注意,要链接使用正确的库。使用libshared-mainloop.a而不是libshared-glib.a和libshared-ell.a。