# dtof驱动移植
最近在玩一款dToF传感器[TMF8821](https://ams-osram.com/products/sensor-solutions/direct-time-of-flight-sensors-dtof/ams-tmf8821-configurable-4x4-multi-zone-time-of-flight-sensor),接口是I2C,现在将这款传感器的驱动移植到RV1106G3下面。
### 内核模块
rv1106使用的是`5.10.160`内核版本。TMF8821提供linux官方驱动[TMF882x_Driver_Linux_v3.56.zip](https://ams-osram.com/o/download-server/document-download/download/29942077) 内核模块。
> GitHub上面有其他公司修改后的版本:https://github.com/brainlab-vied/tmf8820_21_28_driver_linux
修改`Makefile`,设置好`ARCH`和交叉编译器路径
```makefile
KDIR:=/home/bruce/Documents/luckfox-pico/sysdrv/source/kernel
PWD?=$(shell pwd)
MAKE := make
ARCH := arm
CROSS_COMPILE := /home/bruce/Documents/luckfox-pico/tools/linux/toolchain/arm-rockchip830-linux-uclibcgnueabihf/bin/arm-rockchip830-linux-uclibcgnueabihf-
KBUILD_OUTPUT := $(abspath $(dir $(lastword $(KDIR))))/objs_kernel
ifneq ($(KERNELRELEASE),)
#kbuild part of Makefile
include Kbuild
else
#normal Makefile
all:
$(MAKE) ARCH=$(ARCH) CROSS_COMPILE=$(CROSS_COMPILE) -C $(KDIR) M=$(PWD) modules
modules:
$(MAKE) ARCH=$(ARCH) CROSS_COMPILE=$(CROSS_COMPILE) -C $(KDIR) M=$(PWD) $@
sign:
$(SIGN_SCRIPT) sha512 $(LINUX_SRC)/signing_key.priv $(LINUX_SRC)/signing_key.x509 $(DEVICE_NAME).ko
clean:
$(MAKE) -C $(LINUX_SRC) M=$$PWD clean
endif
```
直接编译有错误
```sh
make CONFIG_SENSORS_TMF882X=m
make ARCH=arm CROSS_COMPILE=/home/bruce/Documents/luckfox-pico/tools/linux/toolchain/arm-rockchip830-linux-uclibcgnueabihf/bin/arm-rockchip830-linux-uclibcgnueabihf- -C /home/bruce/Documents/luckfox-pico/sysdrv/source/kernel M=/home/bruce/Documents/ams_tmf8820_linux_driver_src_v3.56 modules
make[1]: Entering directory '/home/bruce/Documents/luckfox-pico/sysdrv/source/kernel'
CC [M]/home/bruce/Documents/ams_tmf8820_linux_driver_src_v3.56/tmf882x_driver.o
In file included from /home/bruce/Documents/ams_tmf8820_linux_driver_src_v3.56/tmf882x_host_interface.h:36,
from /home/bruce/Documents/ams_tmf8820_linux_driver_src_v3.56/tmf882x_mode_app.h:39,
from /home/bruce/Documents/ams_tmf8820_linux_driver_src_v3.56/tmf882x_interface.h:38,
from /home/bruce/Documents/ams_tmf8820_linux_driver_src_v3.56/tmf882x_driver.c:57:
/home/bruce/Documents/ams_tmf8820_linux_driver_src_v3.56/tmf882x_shim_linux_kernel.h: In function 'tof_get_timespec':
/home/bruce/Documents/ams_tmf8820_linux_driver_src_v3.56/tmf882x_shim_linux_kernel.h:89:5: error: implicit declaration of function 'getnstimeofday'; did you mean 'getname_flags'? [-Werror=implicit-function-declaration]
getnstimeofday(ts);
^~~~~~~~~~~~~~
getname_flags
In file included from /home/bruce/Documents/ams_tmf8820_linux_driver_src_v3.56/tmf882x_interface.h:38,
from /home/bruce/Documents/ams_tmf8820_linux_driver_src_v3.56/tmf882x_driver.c:57:
/home/bruce/Documents/ams_tmf8820_linux_driver_src_v3.56/tmf882x_mode_app.h: At top level:
/home/bruce/Documents/ams_tmf8820_linux_driver_src_v3.56/tmf882x_mode_app.h:228:25: error: field 'timestamp' has incomplete type
struct timespec timestamp;
^~~~~~~~~
/home/bruce/Documents/ams_tmf8820_linux_driver_src_v3.56/tmf882x_driver.c: In function 'tof_ram_patch_callback':
/home/bruce/Documents/ams_tmf8820_linux_driver_src_v3.56/tmf882x_driver.c:2250:12: error: variable 'start_ts' has initializer but incomplete type
struct timespec start_ts = {0}, end_ts = {0};
^~~~~~~~
/home/bruce/Documents/ams_tmf8820_linux_driver_src_v3.56/tmf882x_driver.c:2250:33: error: excess elements in struct initializer [-Werror]
struct timespec start_ts = {0}, end_ts = {0};
^
/home/bruce/Documents/ams_tmf8820_linux_driver_src_v3.56/tmf882x_driver.c:2250:33: note: (near initialization for 'start_ts')
/home/bruce/Documents/ams_tmf8820_linux_driver_src_v3.56/tmf882x_driver.c:2250:21: error: storage size of 'start_ts' isn't known
struct timespec start_ts = {0}, end_ts = {0};
^~~~~~~~
/home/bruce/Documents/ams_tmf8820_linux_driver_src_v3.56/tmf882x_driver.c:2250:12: error: variable 'end_ts' has initializer but incomplete type
struct timespec start_ts = {0}, end_ts = {0};
^~~~~~~~
/home/bruce/Documents/ams_tmf8820_linux_driver_src_v3.56/tmf882x_driver.c:2250:47: error: excess elements in struct initializer [-Werror]
struct timespec start_ts = {0}, end_ts = {0};
^
/home/bruce/Documents/ams_tmf8820_linux_driver_src_v3.56/tmf882x_driver.c:2250:47: note: (near initialization for 'end_ts')
/home/bruce/Documents/ams_tmf8820_linux_driver_src_v3.56/tmf882x_driver.c:2250:37: error: storage size of 'end_ts' isn't known
struct timespec start_ts = {0}, end_ts = {0};
^~~~~~
/home/bruce/Documents/ams_tmf8820_linux_driver_src_v3.56/tmf882x_driver.c:2276:17: error: implicit declaration of function 'timespec_sub'; did you mean 'timespec64_sub'? [-Werror=implicit-function-declaration]
fwdl_time = timespec_sub(end_ts, start_ts).tv_nsec / 1000000;
^~~~~~~~~~~~
timespec64_sub
/home/bruce/Documents/ams_tmf8820_linux_driver_src_v3.56/tmf882x_driver.c:2250:37: error: unused variable 'end_ts' [-Werror=unused-variable]
struct timespec start_ts = {0}, end_ts = {0};
^~~~~~
/home/bruce/Documents/ams_tmf8820_linux_driver_src_v3.56/tmf882x_driver.c:2250:21: error: unused variable 'start_ts' [-Werror=unused-variable]
struct timespec start_ts = {0}, end_ts = {0};
^~~~~~~~
cc1: all warnings being treated as errors
make[2]: *** [scripts/Makefile.build:273: /home/bruce/Documents/ams_tmf8820_linux_driver_src_v3.56/tmf882x_driver.o] Error 1
make[1]: *** [Makefile:1935: /home/bruce/Documents/ams_tmf8820_linux_driver_src_v3.56] Error 2
make[1]: Leaving directory '/home/bruce/Documents/luckfox-pico/sysdrv/source/kernel'
make: *** [Makefile:16: all] Error 2
```
编译失败的主要原因是 Linux 内核 API 的兼容性问题。具体来说,驱动代码中使用了较旧的内核 API(如 `getnstimeofday` 和 `timespec`),而这些 API 在新版本的内核中已经被弃用或修改。
需要修改源代码
```c
struct timespec ts;
getnstimeofday(&ts);// 旧代码
```
替换成
```c
struct timespec64 ts;
ktime_get_real_ts64(&ts);// 新代码
```
代码中使用了 `struct timespec`,需要将其替换为 `struct timespec64`
修改 `tmf882x_shim_linux_kernel.h`:
```c
#include
// 添加头文件
static inline void tof_get_timespec(struct timespec64 *ts) {
ktime_get_real_ts64(ts);// 替换 getnstimeofday
}
```
相关变量都修改好,再次编译成功。可以看到在当前目录下成功生成了`tmf882x.ko`内核模块。
### 修改设备树
根据开发板接线图,准备使用`i2c3`。
修改`sysdrv/source/kernel/arch/arm/boot/dts/rv1106g-luckfox-pico-max.dts`,使能`i2c3`,添加节点`tmf8821`,添加gpio控制引脚。GPIO1_C6接ENABLE引脚,高电平有效。GPIO1_C7接irq引脚,下降沿有效。
```
/* I2C3_M1 */
&i2c3 {
status = "okay";
pinctrl-names = "default";
pinctrl-0 = <&i2c3m1_xfer>;
clock-frequency = <100000>;
tmf8821: tmf8821@41 {
compatible = "ams,tmf882x";
status = "okay";
reg = <0x41>;
enable-gpios = <&gpio1 RK_PC6 GPIO_ACTIVE_HIGH>; // GPIO1_C6 enable
irq-gpios = <&gpio1 RK_PC7 GPIO_ACTIVE_HIGH>; // GPIO1_C7 irq
interrupt-parent = <&gpio1>;
interrupts = ;
};
};
```
编译重新烧录开发板。
### 加载firmware
tmf8821传感器上电后需要加载固件才能进入app运行模式,将官方提供的hex文件放到开发板`/lib/firmware/tmf882x_firmware.bin`。注意:这里必须将官方提供的`.hex`文件重命名为`tmf882x_firmware.bin`,不要转换hex文件。我刚开始用工具将hex文件转换成`bin`文件加载一直无法启动。
### 加载运行
将上面编译好的`tmf882x.ko`,官方提供的设备固件`tmf882x_firmware.bin`下载到设备中
运行
```bash
insmod tmf882x.ko
```
成功启动设备
### 总结
通过在RV1106上集成tmf8821,了解了linux下i2c和gpio中断驱动开发的一些基本知识,并且对RV1106软件构架有了一些认识。