=嵌入式开发中的一句话备忘 - 嵌入式linux(非安卓)
>uboot
//uboot编译时,help帮助为了详细,需要把单板配置头文件(比如include/configs/imx8qm_mek_android_auto.h)的这个配置 不要 #undef:
#undef CONFIG_SYS_LONGHELP
嵌入式linux
//cmdline的配置 如
console=hvc0 vmalloc=400M DFTC_IVI_CONFIG=01_0x2000000000000000 DFTC_IC_CONFIG=01_0x2000000000000000
可以通过__setup 来获取,类似M57项目的如下:
__setup("DFTC_IVI_CONFIG=", guest_get_ivi_config);
//从对方IP地址接收文件
tftp -gr 文件名 对方IP地址
//向对方IP地址发送文件
tftp -pr 文件名 对方IP地址
//常用添加打印:
printk("nisy_%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
查询内核打印的配置:
cat /proc/sys/kernel/printk
关闭打印:
echo 0 > /proc/sys/kernel/printk
打开所有打印:
echo 8 > /proc/sys/kernel/printk
//查看运行中Linux的内核配置选项
zcat /proc/config.gz
需要开启内核配置项CONFIG_IKCONFIG才会生成这个文件
//强制关闭串口打印:
echo 0 > /proc/sys/kernel/printk
//DTC除了可以编译.dts文件以外,其实也可以“反汇编”.dtb文件为.dts文件,其指令格式为:
dtc -I dtb -O dts -o xxx.dts arch/arm/boot/dts/xxx.dtb
用这种方式把树莓派的设备树“还原”出来。
//Linux kernal的文档使用rst结构化文本编写,阅读kernal\msm-4.1.4\README文档可知,可以通过
make htmldocs
生成可读的html,不过也可以直接在
https://www.kernel.org/doc/html/latest/index.html#
就能搜索。
//打印编译fail的某个.c文件的完整编译命令,内核原始makefile有v=1来开启,也可以在直接篡改一下:
原始:
# Use 'make V=1' to see the full commands
ifeq ("$(origin V)", "command line")
KBUILD_VERBOSE = $(V)
endif
ifndef KBUILD_VERBOSE
KBUILD_VERBOSE = 0
endif
修改后:
# Use 'make V=1' to see the full commands
ifeq ("$(origin V)", "command line")
KBUILD_VERBOSE = 1
endif
ifndef KBUILD_VERBOSE
KBUILD_VERBOSE = 1
endif
// 对于某些定位问题的时候需要时间戳,可以使用 ts命令 在每一行行首增加了时间戳。下面给出另一个例子:
$ ls -l | ts
Aug 21 13:34:25 total 120
Aug 21 13:34:25 drwxr-xr-x 2 sk users 12288 Aug 20 20:05 Desktop
Aug 21 13:34:25 drwxr-xr-x 2 sk users 4096 Aug 10 18:44 Documents
Aug 21 13:34:25 drwxr-xr-x 24 sk users 12288 Aug 21 13:06 Downloads
[...]
--- 来自文章 (https://linux.cn/article-9962-1.html 《一套有用的 Unix 实用程序 》)
类似地,可以用sudo execsnoop |ts
来记录函数调用的时间戳。
//
Q:linux下有多套gcc版本,如何手工切换需要的版本?
A:使用 update-alternatives 选择某个版本的gcc:
往系统添加一个 arm-linux-guneabi-gcc-5 的链接配置并设置优先级。
$ sudo update-alternatives --install /usr/bin/arm-linux-gnueabi-gcc arm-linux-gnueabi-gcc /usr/bin/arm-linux-gnueabi-gcc-5 5
接下来往系统添加一个 arm-linux-guneabi-gcc-7 的链接配置并设置优先级。
$ sudo update-alternatives --install /usr/bin/arm-linux-gnueabi-gcc arm-linux-gnueabi-gcc /usr/bin/arm-linux-gnueabi-gcc-7 7
使用 update-alternatives 命令来选择一个配置。
$ sudo update-alternatives --config arm-linux-gnueabi-gcc
然后可以看到arm-linux-gnueabi-gcc 指向的是/etc/alternatives/ 的一个自己选择的版本号的gcc
ls -al /usr/bin/arm-linux-gnueabi-gcc
lrwxrwxrwx 1 root root 39 7月 13 2020 /usr/bin/arm-linux-gnueabi-gcc -> /etc/alternatives/arm-linux-gnueabi-gcc-5
//i2ctools软件中的工具 i2ctransfer 和 i2cdump 都可以读芯片的寄存器,命令稍不同,如下:
注意i2cdetect 扫描到只是说明设备树中有这个设备,实际可能这个芯片根本没有贴片.
# i2cdetect -y -a 9
0 1 2 3 4 5 6 7 8 9 a b c d e f
00: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- 2c -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- 41 42 43 44 45 -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- UU -- -- -- -- --
70: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
可以看到bus=9下面有6a,41~45,2c这几个i2c设备.
i2cdump -f -y 9 0x6a
No size specified (using byte-data access)
0 1 2 3 4 5 6 7 8 9 a b c d e f 0123456789abcdef
00: ef 00 00 01 00 00 90 51 2d d4 ff e4 91 f3 50 0b ?..?..?Q-?.???P?
10: 00 00 f3 0f e4 9b 00 01 00 00 60 00 f4 ff 40 02 ..????.?..`.?.@?
20: 55 00 0f 00 00 00 00 bf 00 00 00 00 00 00 00 00 U.?....?........
30: fa 45 99 99 b6 00 00 00 00 00 00 2e 24 54 c8 22 ?E???.......$T?"
40: 5a 10 00 a0 00 00 55 ff ef 0f ff 00 00 00 05 00 Z?.?..U.??....?.
50: 00 00 05 00 00 00 05 00 00 00 05 90 51 2d 00 00 ..?...?...??Q-..
60: 45 ff 1f 00 00 12 96 00 50 b0 00 00 00 00 00 00 E.?..??.P?......
70: f0 3f 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ??..............
80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
90: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
mek_8q_raite:/ #
mek_8q_raite:/ #
mek_8q_raite:/ # i2ctransfer -f -y 9 w1@0x6a 0x0 r2
0xef 0x00
mek_8q_raite:/ #
mek_8q_raite:/ # i2ctransfer -f -y 9 w1@0x6a 0x2 r2
0x00 0x01
mek_8q_raite:/ # i2ctransfer -f -y 9 w1@0x6a 0x4 r2
0x00 0x00
mek_8q_raite:/ # i2ctransfer -f -y 9 w1@0x6a 0x6 r2
0x90 0x51
mek_8q_raite:/ # i2ctransfer -f -y 9 w1@0x6a 0x8 r2
0x2d 0xd4
mek_8q_raite:/ #
mek_8q_raite:/ #
mek_8q_raite:/ # i2ctransfer -f -y 9 w1@0x6a 0x0 r16
0xef 0x00 0x00 0x01 0x00 0x00 0x90 0x51 0x2d 0xd4 0xff 0xe4 0x91 0xf3 0x50 0x0b
1|mek_8q_raite:/ # i2cget -f -y 9 0x6a 0 //读寄存器0#
0xef
mek_8q_raite:/ # i2cget -f -y 9 0x6a 1 //读寄存器1#
0x00
mek_8q_raite:/ # i2cset -f -y 9 0x6a 1 4 //写寄存器1#
mek_8q_raite:/ # i2cget -f -y 9 0x6a 1 //读寄存器1#
0x04
注意:i2cdump/i2cget 读寄存器只能输入1byte地址,返回1到多字节结果;如果需要输入地址为2byte,则只能使用 i2ctransfer ,比如读max96722的ID寄存器:
console:/ # i2ctransfer -f -y 2 w2@0x29 0x00 0x0d r1
(输入地址2byte=0x000d,返回值1byte)
0xa1
//正确返回,地址为0x000d
console:/ # i2cget -f -y 2 0x29 0x0d
0x00
//错误返回,地址当做0x0d
//从i2c 4号总线0x38设备的0x3a01寄存器开始读16个字节的数据,w2:表示寄存器0x3a01的长度为2个字节
i2ctransfer -y -f 4 w2@0x38 0x3a 0x01 r16
向i2c 4号总线0x38设备的0x3a01寄存器写0x10,w3:表示寄存器0x3a01和写入值0x10的长度为3字节
i2ctransfer -y -f 4 w3@0x38 0x3a 0x01 0x10
//截屏: 截取fb1界面到sdcard,文件名为fb1.png
screencap -d /dev/graphics/fb1 sdcard/fb1.png
//使用文件系统节点来操作时,需要注意有可能设备树的修改导致节点路径变更了,比如:
原始:
/sys/devices/platform/58226000.i2c/i2c-9/9-006a/max9286_reset
删除了i2c 一个bus后,变成
/sys/devices/platform/58226000.i2c/i2c-8/8-006a/max9286_reset
可能造成上层找不到文件节点,导致功能异常.
上层使用节点,最好使用find 先搜到节点路径,用搜索结果再去操作.
//python循环打印max96722(bus2,addr=0x29)的寄存器的值:
import os
import time
a=0
os.system('adb root')
os.system('adb remount')
s_hex=""
while a < 255:
b='adb shell i2ctransfer -f -y 2 w2@0x29 0x00 '
b += s_hex + hex(a)
b +=' r1'
os.system(b)
a+=1
//dos的命令每隔5秒周期性操作adb shell "factory_test -s fan_switch on" 的参考脚本:
@echo off
adb root
for /l %%i in (1,0,1) do adb shell "factory_test -s fan_switch on" &&choice /T 1 /C ync /CS /D y /n
pause
//打印结构体的内容,使用 print_hex_dump(),类似:
print_hex_dump(KERN_NOTICE,"" ,DUMP_PREFIX_OFFSET ,16,1,sd,sizeof(struct v4l2_subdev),true);
//CPU异常占用分析:
- 使用
top -H -p pid号
确认当前进程所有线程CPU占用 - 使用
ps -AT -O psr | grep cpu
占用异常pid 确认线程状态以及所运行的cpu
//如果下电/上电正常,但是reboot不正常,请检查芯片的上电流程的下电部分,是否时间太少.
skill
打印函数执行时间
举例:
int vb2_ioctl_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p)
{
struct video_device *vdev = video_devdata(file);
int val = 0;
#ifdef FEATURE_DONGFENG_DEBUG
u64 start_time, end_time ;
u64 diff = 0;
start_time = ktime_get_real_ns();
udelay(10); //延时10us,因为2次调用ktime_get_real_ns()有可能因为调度或者中断,导致后一次的值反而小,所以这里人为在第一次取时间戳后加一个短延时
#endif
if (vb2_queue_is_busy(vdev, file))
{
return -EBUSY;
}
val = vb2_dqbuf(vdev->queue, p, file->f_flags & O_NONBLOCK);
#ifdef FEATURE_DONGFENG_DEBUG
{
end_time = ktime_get_real_ns();
diff = end_time - start_time;
diff =(u32) diff /(u32) 1000UL;
}
if(diff > 30000)
{
printk("df_dqbuf:%c-%llu us\n",vdev->name[8] , diff);
}
#endif
return val;
}
visual studio code 中过滤内核代码的无关部分
比如 kernel-4.14.code-workspace
文件:
{
"folders": [
{
"path": "."
}
],
"settings": {
"search.exclude": {
"arch/alpha" :true,
"arch/m68k" :true,
"arch/arc" :true,
"arch/arm" :true,
"arch/blackfin" :true,
"arch/c6x" :true,
"arch/cris" :true,
"arch/frv" :true,
"arch/h8300" :true,
"arch/hexagon" :true,
"arch/ia64" :true,
"arch/m32r" :true,
"arch/metag" :true,
"arch/microblaze" :true,
"arch/mn10300" :true,
"arch/nios2" :true,
"arch/mips" :true,
"arch/openrisc" :true,
"arch/parisc" :true,
"arch/powerpc" :true,
"arch/s390" :true,
"arch/score" :true,
"arch/sh" :true,
"arch/sparc" :true,
"arch/tile" :true,
"arch/um" :true,
"arch/unicore32" :true,
"arch/x86" :true,
"arch/xtensa" :true,
"arch/arm64/boot/dts/actions" :true,
"arch/arm64/boot/dts/allwinner" :true,
"arch/arm64/boot/dts/amd" :true,
"arch/arm64/boot/dts/apm" :true,
"arch/arm64/boot/dts/broadcom" :true,
"arch/arm64/boot/dts/exynos" :true,
"arch/arm64/boot/dts/hisilicon" :true,
"arch/arm64/boot/dts/qcom" :true,
"arch/arm64/boot/dts/renesas" :true,
"arch/arm64/boot/dts/socionext" :true,
"arch/arm64/boot/dts/xilinx" :true,
"arch/arm64/boot/dts/al" :true,
"arch/arm64/boot/dts/altera" :true,
"arch/arm64/boot/dts/amlogic" :true,
"arch/arm64/boot/dts/arm" :true,
"arch/arm64/boot/dts/cavium" :true,
"arch/arm64/boot/dts/freescale" :true,
"arch/arm64/boot/dts/lg" :true,
"arch/arm64/boot/dts/marvell" :true,
"arch/arm64/boot/dts/nvidia" :true,
"arch/arm64/boot/dts/realtek" :true,
"arch/arm64/boot/dts/rockchip" :true,
},
"files.exclude": {
"arch/alpha" :true,
"arch/m68k" :true,
"arch/arc" :true,
"arch/arm" :true,
"arch/blackfin" :true,
"arch/c6x" :true,
"arch/cris" :true,
"arch/frv" :true,
"arch/h8300" :true,
"arch/hexagon" :true,
"arch/ia64" :true,
"arch/m32r" :true,
"arch/metag" :true,
"arch/microblaze" :true,
"arch/mn10300" :true,
"arch/nios2" :true,
"arch/mips" :true,
"arch/openrisc" :true,
"arch/parisc" :true,
"arch/powerpc" :true,
"arch/s390" :true,
"arch/score" :true,
"arch/sh" :true,
"arch/sparc" :true,
"arch/tile" :true,
"arch/um" :true,
"arch/unicore32" :true,
"arch/x86" :true,
"arch/xtensa" :true,
"arch/arm64/boot/dts/actions" :true,
"arch/arm64/boot/dts/allwinner" :true,
"arch/arm64/boot/dts/amd" :true,
"arch/arm64/boot/dts/apm" :true,
"arch/arm64/boot/dts/broadcom" :true,
"arch/arm64/boot/dts/exynos" :true,
"arch/arm64/boot/dts/hisilicon" :true,
"arch/arm64/boot/dts/qcom" :true,
"arch/arm64/boot/dts/renesas" :true,
"arch/arm64/boot/dts/socionext" :true,
"arch/arm64/boot/dts/xilinx" :true,
"arch/arm64/boot/dts/al" :true,
"arch/arm64/boot/dts/altera" :true,
"arch/arm64/boot/dts/amlogic" :true,
"arch/arm64/boot/dts/arm" :true,
"arch/arm64/boot/dts/cavium" :true,
"arch/arm64/boot/dts/freescale" :true,
"arch/arm64/boot/dts/lg" :true,
}
}
}
//在Linux系统内部,可以通过查看配置文档来获取内核编译的开关信息。这些配置文档通常位于/boot/config-$(uname -r)或/proc/config.gz。
- cat /boot/config-$(uname -r)命令可以查看当前正在运行的内核配置。
- zcat /proc/config.gz命令可以查看压缩的内核配置文档。请注意,在内核编译时增减相应的选项才会生成该文档,因此如果系统中没有该文档,可能无法获取内核编译的开关信息。