mipi--I2C调试

发布时间 2023-03-30 15:56:56作者: DMCF

1.DTS配置参考---->
https://wiki.t-firefly.com/Core-3568J/module_camera.html

2.调试参考文档---->Rockchip_Developer_Guide_Linux4.4_Camera_CN

修改kconfg和Makefile---->k356x_linux_release_20211019/kernel/drivers/media/i2c
修改设备树---->/home/salesdmcf/proj/rk356x_linux_release_20211019/kernel/arch/arm64/boot/dts/rockchip/rk3568-firefly-aioj-cam-8ms1m-imx327.dtsi
修改驱动文件---->k356x_linux_release_20211019/kernel/drivers/media/i2c/imx327.c
修改DTS---->rk3568-firefly-aioj.dts

3.upgrade_tool调试步骤

1)sudo upgrade_tool
List of rockusb connected
DevNo=1 Vid=0x2207,Pid=0x330c,LocationID=106 Loader
Found 1 rockusb,Select input DevNo,Rescan press <R>,Quit press <Q>:q

2)拷贝update.img文件到PC用工具这个固件升级
cd /home/salesdmcf/proj/rk356x_linux_release_20211019/rockdev/pack

注意:!!!!必须用RKDevTool_Release_v2.81工具整个固件升级,分区升级有问题的!!
注意:!!!!进入load 模式---->升级固件 【界面】---->升级

注意:!!!!进入load 模式---->高级功能[【界面】---->进入maskrom---->下载boot---->切换存储----> 先切换到SPINOR---->擦除所有---->然后再切换到--->Emmc---->擦除所有---- >升级固件 【界面】---->升级


详细方法---->https://wiki.t-firefly.com/Core-3568J/03-upgrade_firmware_with_flash.html#faq ## 切换升级存储器

3) 软件方式 ----> 进入load 模式,硬件方式请参考《开发环境搭建---firefly》

## reboot loader

4 ubuntu串口调试---->不要用,会死机
sudo apt-get install minicom---->安装串口
ls /dev/ttyUSB* ---->查询串口
sudo minicom -s ---->设置串口
设备串口参数,注意没有硬件流控时,按f 设置为 No,
save setup as dfl ----> 设置保存为默认配置,然后 Exit
sudo minicom---->打开串口
按 Ctrl+A 然后 x 退出 ---->推出串口

按 Ctrl+X---->进入串口

5 设备树在源码中的位置 ---->/sys/firmware/devicetree/base ###目录下面为设备树展开成sysfs的目录和二进制属性文件


##这里确认DTS根文件
$ cat model
AIO-3568J HDMI (Linux) ##model==

$ cat compatible
rockchip,rk3568-firefly-aiojrockchip,rk3568 ##compatible==

cd /sys/firmware/devicetree/base/i2c@fe5d0000

ls sail ##查看是否有IMX327芯片

6. 修改内核配置---->home/salesdmcf/proj/rk356x_linux_release_20211019/kernel/arch/arm64/configs/firefly_linux_defconfig


修改 kernel/buildroot 等配置后编译烧录发现不生效
通过make menuconfig修改配置之后,使用build.sh编译,烧录发现修改没有生效。然后检查 config 发现之前的修改消失了。

这是因为只将修改保存到了临时文件.config,编译时build.sh会用配置文件覆盖掉.config

所以修改后要保存到配置文件:

make ARCH=arm64 savedefconfig
mv defconfig arch/arm64/configs/firefly_linux_defconfig
之后再使用build.sh编译。

7.dts源码分析
/home/salesdmcf/proj/rk356x_linux_release_20211019/kernel/arch/arm64/boot/dts/rockchip


rk3568-firefly-aioj.dts ##入口文件,提供model,compatible确认
---->rk3568-firefly-aioj.dtsi ##regulator
---->rk3568-firefly-port.dtsi ##所有外部接口板,比如UART,I2C,SPI,CAN,ADC,wireless,gmac0的regulator和pinctrl描述
---->rk3568-firefly-core.dtsi ##核心板述,比如pmu_io_domains,usb,sd,nand,hdmi的regulator和pinctrl的描
---->rk3568.dtsi ###这里是接口的具体详细描述,
---->rk3568-dram-default-timing.dtsi ##DDR
---->rockchip-pinconf.dtsi ##pinctrl PIN管脚配置
---->rockchip-pinconf.dtsi ## 驱动能力配置
---->rk3568-linux.dtsi ##这里是传到参数的地方
---->rk3568-firefly-aioj-cam-2ms2m.dtsi ##重点在这里,MIPI驱动单独

 


rk3568-firefly-aioj-ipc-mipi_M101014_BE45_A1.dts
rk3568-firefly-aioj-mipi101_JDM007_BC40.dts
k3568-firefly-roc-pc.dts


8.rk3568-firefly-aioj-cam-2ms2m.dtsi分析

sensor2->csi_dphy2->mipi_csi2->vicap->isp_vir1

9.设备树的路径:
cd /sys/firmware/devicetree/base/i2c@fe5d0000 ##I2C4

1127 0 drwxr-xr-x 4 root root 0 Jan 12 10:18 i2c@fdd40000
3221 0 drwxr-xr-x 4 root root 0 Jan 12 10:18 i2c@fe5a0000
3254 0 drwxr-xr-x 2 root root 0 Jan 12 10:18 i2c@fe5b0000
3267 0 drwxr-xr-x 2 root root 0 Jan 12 10:18 i2c@fe5c0000
3280 0 drwxr-xr-x 6 root root 0 Jan 12 10:18 i2c@fe5d0000
3391 0 drwxr-xr-x 5 root root 0 Jan 12 10:18 i2c@fe5e0000

10. DTS中CAMER的几个概念澄清

port ---->视频设备的数据接口
endpoint ---->同一总线的的远程设备,也称节点
remote-endpoint ----> 两个endpoint 节点通过“remote-endpoint”句柄彼此联系

例子:

####rk3568-firefly-port.dtsi####

//////////////////////////////////////////////////////SENSOR配置////////////////////////////////////////
&i2c4 {
status = "okay";
gc8034: gc8034@37 {
compatible = "galaxycore,gc8034";
status = "disabled";
reg = <0x37>;
clocks = <&cru CLK_CIF_OUT>;
clock-names = "xvclk";
pinctrl-names = "default";
pinctrl-0 = <&cif_clk>;
reset-gpios = <&gpio3 RK_PB6 GPIO_ACTIVE_LOW>;
pwdn-gpios = <&gpio4 RK_PB4 GPIO_ACTIVE_LOW>;
rockchip,grf = <&grf>;
rockchip,camera-module-index = <0>;
rockchip,camera-module-facing = "back";
rockchip,camera-module-name = "RK-CMK-8M-2-v1";
rockchip,camera-module-lens-name = "CK8401";
port { ##数据输出口
gc8034_out: endpoint { ## ----data0---->
remote-endpoint = <&mipi_in_ucam1>; ##----link0---->
data-lanes = <1 2 3 4>; ##4个lan 都输出
};
};
};
}

///////////////////////////////////////////////////////cs2_dphy0配置///////////////////////////////////
&csi2_dphy_hw {
status = "okay"; ##必须把这个打开??
};

&csi2_dphy0 {
status = "okay";

ports { ##数据接口,这个数据接口包含一个接受口,一个输出口
#address-cells = <1>;
#size-cells = <0>;
port@0 { ##这个是数据接受口
reg = <0>;
#address-cells = <1>;
#size-cells = <0>;

mipi_in_ucam0: endpoint@1 {
reg = <1>;
remote-endpoint = <&ucam_out0>;
data-lanes = <1 2 3 4>;
};
mipi_in_ucam1: endpoint@2 { ## <----data1----
reg = <2>;
remote-endpoint = <&gc8034_out>; ## <-----link1----
data-lanes = <1 2 3 4>;
};
mipi_in_ucam2: endpoint@3 {
reg = <3>;
remote-endpoint = <&ov5695_out>;
data-lanes = <1 2>;
};
};
port@1 { ##这个是数据输出口
reg = <1>;
#address-cells = <1>;
#size-cells = <0>;

csidphy_out: endpoint@0 { ##----data2---->
reg = <0>;
remote-endpoint = <&isp0_in>; ##----link2---->
};
};
};
};

/////////////////////////////////////ISP里面的配置///////////////////////
&rkisp {
status = "okay";
};

&rkisp_mmu {
status = "okay";
};

&rkisp_vir0 {
status = "okay";

port {
#address-cells = <1>;
#size-cells = <0>;

isp0_in: endpoint@0 { ##<----data3----
reg = <0>;
remote-endpoint = <&csidphy_out>; ##<----link3----
};
};
};

###rk3568.dtsi####

rkisp_vir0: rkisp-vir0 {
compatible = "rockchip,rkisp-vir";
rockchip,hw = <&rkisp>; ##----bing---->
status = "disabled";
};


rkisp: rkisp@fdff0000 {
compatible = "rockchip,rk3568-rkisp";
reg = <0x0 0xfdff0000 0x0 0x10000>;
interrupts = <GIC_SPI 57 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 58 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 60 IRQ_TYPE_LEVEL_HIGH>;
interrupt-names = "mipi_irq", "mi_irq", "isp_irq";
clocks = <&cru ACLK_ISP>, <&cru HCLK_ISP>, <&cru CLK_ISP>;
clock-names = "aclk_isp", "hclk_isp", "clk_isp";
resets = <&cru SRST_ISP>, <&cru SRST_H_ISP>;
reset-names = "isp", "isp-h";
rockchip,grf = <&grf>;
power-domains = <&power RK3568_PD_VI>;
iommus = <&rkisp_mmu>;
rockchip,iq-feature = /bits/ 64 <0x3FBFFFE67FF>;
status = "disabled";
};

11.I2C的几个重要结构体---->kernel/include/linux/i2c.h

struct i2c_driver {
unsigned int class;

/* Standard driver model interfaces */
int (*probe)(struct i2c_client *, const struct i2c_device_id *);
int (*remove)(struct i2c_client *);

/* New driver model interface to aid the seamless removal of the
* current probe()'s, more commonly unused than used second parameter.
*/
int (*probe_new)(struct i2c_client *);

/* driver model interfaces that don't relate to enumeration */
void (*shutdown)(struct i2c_client *);

/* Alert callback, for example for the SMBus alert protocol.
* The format and meaning of the data value depends on the protocol.
* For the SMBus alert protocol, there is a single bit of data passed
* as the alert response's low bit ("event flag").
* For the SMBus Host Notify protocol, the data corresponds to the
* 16-bit payload data reported by the slave device acting as master.
*/
void (*alert)(struct i2c_client *, enum i2c_alert_protocol protocol,
unsigned int data);

/* a ioctl like command that can be used to perform specific functions
* with the device.
*/
int (*command)(struct i2c_client *client, unsigned int cmd, void *arg);

struct device_driver driver;
const struct i2c_device_id *id_table;

/* Device detection callback for automatic device creation */
int (*detect)(struct i2c_client *, struct i2c_board_info *);
const unsigned short *address_list;
struct list_head clients;

bool disable_i2c_core_irq_mapping;
};


struct i2c_client {
unsigned short flags; /* div., see below */
unsigned short addr; /* chip address - NOTE: 7bit */
/* addresses are stored in the */
/* _LOWER_ 7 bits */
char name[I2C_NAME_SIZE];
struct i2c_adapter *adapter; /* the adapter we sit on */
struct device dev; /* the device structure */
int init_irq; /* irq set at initialization */
int irq; /* irq issued by device */
struct list_head detected;
#if IS_ENABLED(CONFIG_I2C_SLAVE)
i2c_slave_cb_t slave_cb; /* callback for slave mode */
#endif
};

struct i2c_algorithm {
/* If an adapter algorithm can't do I2C-level access, set master_xfer
to NULL. If an adapter algorithm can do SMBus access, set
smbus_xfer. If set to NULL, the SMBus protocol is simulated
using common I2C messages */
/* master_xfer should return the number of messages successfully
processed, or a negative value on error */
int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs,
int num);
int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr,
unsigned short flags, char read_write,
u8 command, int size, union i2c_smbus_data *data);

/* To determine what the adapter supports */
u32 (*functionality) (struct i2c_adapter *);

#if IS_ENABLED(CONFIG_I2C_SLAVE)
int (*reg_slave)(struct i2c_client *client);
int (*unreg_slave)(struct i2c_client *client);
#endif
};


struct i2c_adapter {
struct module *owner;
unsigned int class; /* classes to allow probing for */
const struct i2c_algorithm *algo; /* the algorithm to access the bus */
void *algo_data;

/* data fields that are valid for all devices */
const struct i2c_lock_operations *lock_ops;
struct rt_mutex bus_lock;
struct rt_mutex mux_lock;

int timeout; /* in jiffies */
int retries;
struct device dev; /* the adapter device */

int nr;
char name[48];
struct completion dev_released;

struct mutex userspace_clients_lock;
struct list_head userspace_clients;

struct i2c_bus_recovery_info *bus_recovery_info;
const struct i2c_adapter_quirks *quirks;

struct irq_domain *host_notify_domain;
};


struct i2c_msg {

__u16 addr; /* 设备地址*/
__u16 flags; /* 标志 */
__u16 len; /* 消息长度*/
__u8 *buf; /* 消息数据*/
};
总结:
i2c_driver【对应一套驱动方法】 ---->attach_adapter()会探测---->i2c_client【物理设备】 ---->i2c_adapter【物理上的一个适配器】 ---->i2c_algorithm【对应一套通信方法】---->i2c_msg 【I2C消息】

这i2c_driver、i2c_client、i2c_adapter和i2c_algorithm这4个数据结构的作用及关系
1) i2c_adapter与i2c_algorithmi ####i2c_adapter---->i2c_algorithm---->master_xfer()----i2c_msg 收发数据

i2c_adapter 对应于物理上的一个适配器,而i2c_algorithm对应一套通信方法。一个I2C适配器需要i2c_algorithm中提供的通信函数来控制适配 器上产生特定的访问周期。
缺少i2c_algorithm的i2c_adapter什么也做不了,因此i2c_adapter中包含其使用的 i2c_algorithm的指针。
i2c_algorithm中的关键函数master_xfer()用于产生I2C访问周期需要的信号,以i2c_msg(即I2C消息)为单位。

 

2) i2c_driver与i2c_client ####i2c_driver---->attach_adapter()---->i2c_client---->i2c_adapter---->client_register

i2c_driver对应一套驱动方法,是纯粹的用于辅助作用的数据结构,它不对应于任何的物理实体。i2c_client对应于真实的物理设备,每个I2C设备都需要一个i2c_client来描述。
i2c_client一般被包含在i2c字符设备的私有信息结构体中。
i2c_driver 与i2c_client发生关联的时刻在i2c_driver的attach_adapter()函数被运行时。attach_adapter()会探测物理设备,当确定一个client存在时,
把该client使用的i2c_client数据结构的adapter指针指向对应的i2c_adapter, driver指针指向该i2c_driver,并会调用i2c_adapter的client_register()函数。
相反的过程发生在 i2c_driver 的detach_client()函数被调用的时候


实例:

struct i2c_driver ##调用i2c_add_driver注册驱动
static struct i2c_driver imx327_i2c_driver = {
.driver = {
.name = IMX327_NAME,
.pm = &imx327_pm_ops,
.of_match_table = of_match_ptr(imx327_of_match),
},
.probe = &imx327_probe,
.remove = &imx327_remove,
.id_table = imx327_match_id,
};


static int __init sensor_mod_init(void)
{
return i2c_add_driver(&imx327_i2c_driver);
}

 

struct imx327 {
struct i2c_client *client;
struct clk *xvclk;
.......
}

static int imx327_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct device *dev = &client->dev;
struct device_node *node = dev->of_node;
struct imx327 *imx327;
struct v4l2_subdev *sd;
....

imx327 = devm_kzalloc(dev, sizeof(*imx327), GFP_KERNEL);
....
imx327->client = client; ##绑定实例为imx327
....
sd = &imx327->subdev;
v4l2_i2c_subdev_init(sd, client, &imx327_subdev_ops); ##注册v4l2_i2c实例
ret = imx327_initialize_controls(imx327);
....
}

3) i2c_adpater与i2c_client
i2c_adpater 与i2c_client的关系与I2C硬件体系中适配器和设备的关系一致,即i2c_client依附于i2c_adpater。由于一个适配器上可以连接多个I2C设备,所以一个i2c_adpater也可以被多个i2c_client依附,i2c_adpater中包括依附于它的i2c_client 的链表


12. i2c-tools ---->https://www.kernel.org/pub/software/utils/i2c-tools/
#apt-get update
#apt-get install gcc
#


13.sensor I2C地址---->注意:::!!!设备地址使用前面7位,如 XC7160b: XC7160b@1b 设置地址为0x1b == 0011011+【bit0】---->实际输出0x36==0011011+【R/W】;

14. media-ctl命令使用--->找到video设备

# media-ctl -p -d /dev/media0 ---->显示拓扑结构

# media-ctl -d /dev/media0 -e "rkisp1_selfpath" ---->查找那个VIDEO使用selfpath
/dev/video2

# media-ctl -d /dev/media0 -e "rkisp1_mainpath" ---->查找那个VIDEO使用mainpath
/dev/video1


15.v4l2-ctl 命令使用---->使用v4l2-ctl抓图

#mkdir /data/
#v4l2-ctl --verbose -d /dev/video0 --set-fmt-video=width=1920,height=1080,pixelformat='NV12' --stream-mmap=4 --set-selection=target=crop,flags=0,top=0,left=0,width=1920,height=1080 --stream-to=/data/out.yuv


#https://ffmpeg.org/download.html---->下载
#tar -jxvf ffmpeg-snapshot.tar.bz2 ---->解压
#./configure --prefix=/usr/local/ffmpeg --enable-shared --enable-libx264 --enable-libx265 --enable-gpl --enable-libfdk-aac --enable-nonfree
#make
#sudo make install ---->编译安装ffmpeg
#sudo apt install yasm libsdl2-dev libx264-dev libx265-dev libfdk-aac-dev ---->安装库
#PATH="/usr/local/ffmpeg/bin:$PATH" ---->编译环境
配置连接库
在 /etc/ld.so.conf 中添加 "/usr/local/ffmpeg/lib"
执行
#sudo ldconfig

把out.yuv文件拷贝出来通过ubuntu去查看

#ffplay -f rawvideo -video_size 1920x1080 -pix_fmt nv12 out.yuv


16.逻辑分析仪的使用
【start】---->右边的小三角形---->设置时间,电压范围,通道
【Analyzers 】---->添加I2C