GC0308攝像頭驅動程序使用的是linux v4l2協議,通過i2c信號進行控制。GC0308攝像頭。對上電時序要求非常嚴格,一定要依據datasheet初始化攝像頭。
本驅動使用的3.10內核,所以首先要配置dts,在內核啟動階段支持攝像頭,結合硬件原理圖。首先配置硬件接口屬性:
pinctrl_ipu1_4: ipu1grp-4 { /*++++ GC0308 camera */ fsl,pins = < MX6QDL_PAD_GPIO_3__CCM_CLKO2 0x130b0 /*時鍾*/ MX6QDL_PAD_CSI0_DAT10__GPIO5_IO28 0x80000000 <span style="font-family: Arial, Helvetica, sans-serif;">/*CAM_nRST_CSI0_DAT10*/</span> MX6QDL_PAD_CSI0_DAT11__GPIO5_IO29 0x80000000 /* DAT11 */ <span style="font-family: Arial, Helvetica, sans-serif;">/*CAM_SHDN_CSI0_DAT11*/</span> MX6QDL_PAD_CSI0_DAT12__IPU1_CSI0_DATA12 0x80000000 /*12-19八條數據線*/ MX6QDL_PAD_CSI0_DAT13__IPU1_CSI0_DATA13 0x80000000 MX6QDL_PAD_CSI0_DAT14__IPU1_CSI0_DATA14 0x80000000 MX6QDL_PAD_CSI0_DAT15__IPU1_CSI0_DATA15 0x80000000 MX6QDL_PAD_CSI0_DAT16__IPU1_CSI0_DATA16 0x80000000 MX6QDL_PAD_CSI0_DAT17__IPU1_CSI0_DATA17 0x80000000 MX6QDL_PAD_CSI0_DAT18__IPU1_CSI0_DATA18 0x80000000 MX6QDL_PAD_CSI0_DAT19__IPU1_CSI0_DATA19 0x80000000 MX6QDL_PAD_CSI0_DATA_EN__IPU1_CSI0_DATA_EN 0x80000000 /* 硬件懸空*/ MX6QDL_PAD_CSI0_PIXCLK__IPU1_CSI0_PIXCLK 0x80000000 /*像素時鍾*/ MX6QDL_PAD_CSI0_MCLK__IPU1_CSI0_HSYNC 0x80000000 MX6QDL_PAD_CSI0_VSYNC__IPU1_CSI0_VSYNC 0x80000000 >; };
然后配置攝像頭屬性:
&i2c3{ clock-frequency = <100000>; pinctrl-names = "default"; pinctrl-0 = <&pinctrl_i2c3_1>; status = "okay"; gc0308: gc0308@21{ compatible = "gc0308-capture"; reg = <0x21>; pinctrl-names = "default"; pinctrl-0 = <&pinctrl_ipu1_4>; /* GC0308 camera*/ clocks = <&clks 201>; clock-names = "csi_mclk"; DOVDD-supply = <&vgen4_reg>; /* 1.8v */ AVDD-supply = <&vgen3_reg>; /* 2.8v, on rev C board is VGEN3, on rev B board is VGEN5 */ DVDD-supply = <&vgen2_reg>; /* 1.5v*/ pwn-gpios = <&gpio5 29 1>; /* active low: SD1_DAT0 */ rst-gpios = <&gpio5 28 0>; /* active high: SD1_DAT1 */ csi_id = <0>; mclk = <24000000>; mclk_source = <0>; }; };
部分內核驅動代碼分析:
static const struct i2c_device_id gc0308_id[] = { {"gc0308-capture", 0}, {}, }; MODULE_DEVICE_TABLE(i2c, gc0308_id);
將gc0308_id增加i2c隊列。當在dts中有對應名字的設備聲明時,調用驅動程序的probe函數。
static struct i2c_driver gc0308_i2c_driver = { .driver = { .owner = THIS_MODULE, .name = "gc0308-capture", }, .probe = gc0308_probe, .remove = gc0308_remove, .id_table = gc0308_id, };驅動程序初始化的時候,增加到i2c設備。
static s32 gc0308_write_reg(u8 reg, u8 val) { u8 au8Buf[3] = {0}; au8Buf[0] = reg; au8Buf[1] = val; if (i2c_master_send(gc0308_data.i2c_client, au8Buf, 2) < 0) { pr_err("%s:write reg error:reg=%x,val=%x\n", __func__, reg, val); return -1; } return 0; }
static s32 gc0308_read_reg(u8 reg, u8 *val) { u8 au8RegBuf[2] = {0}; u8 u8RdVal = 0; au8RegBuf[0] = reg; if (1 != i2c_master_send(gc0308_data.i2c_client, au8RegBuf, 1)) { pr_err("%s:write reg error:reg=%x\n", __func__, reg); return -1; } if (1 != i2c_master_recv(gc0308_data.i2c_client, &u8RdVal, 1)) { pr_err("%s:read reg error:reg=%x,val=%x\n", __func__, reg, u8RdVal); return -1; } return u8RdVal; }I2C數據讀寫
static struct v4l2_int_ioctl_desc gc0308_ioctl_desc[] = { {vidioc_int_dev_init_num, (v4l2_int_ioctl_func *)ioctl_dev_init}, {vidioc_int_dev_exit_num, ioctl_dev_exit}, {vidioc_int_s_power_num, (v4l2_int_ioctl_func *)ioctl_s_power}, {vidioc_int_g_ifparm_num, (v4l2_int_ioctl_func *)ioctl_g_ifparm}, {vidioc_int_init_num, (v4l2_int_ioctl_func *)ioctl_init}, {vidioc_int_enum_fmt_cap_num, (v4l2_int_ioctl_func *)ioctl_enum_fmt_cap}, {vidioc_int_g_fmt_cap_num, (v4l2_int_ioctl_func *)ioctl_g_fmt_cap}, {vidioc_int_g_parm_num, (v4l2_int_ioctl_func *)ioctl_g_parm}, {vidioc_int_s_parm_num, (v4l2_int_ioctl_func *)ioctl_s_parm}, {vidioc_int_g_ctrl_num, (v4l2_int_ioctl_func *)ioctl_g_ctrl}, {vidioc_int_s_ctrl_num, (v4l2_int_ioctl_func *)ioctl_s_ctrl}, {vidioc_int_enum_framesizes_num, (v4l2_int_ioctl_func *)ioctl_enum_framesizes}, {vidioc_int_g_chip_ident_num, (v4l2_int_ioctl_func *)ioctl_g_chip_ident}, };一系列ioctl函數的指針數組,供應用程序調用。
static int get_device_id(void) { u8 au8RegBuf[2] = {0}; u8 u8RdVal = 0; au8RegBuf[0] = 0x00; if (1 != i2c_master_send(gc0308_data.i2c_client, au8RegBuf, 1)) { pr_err("%s:write reg error:reg=%x\n", __func__, 0xfb); return -1; } if (1 != i2c_master_recv(gc0308_data.i2c_client, &u8RdVal, 1)) { pr_err("%s:read reg error:reg=%x,val=%x\n", __func__, 0xfb, u8RdVal); return -1; } printk(KERN_INFO "u8RdVal=%x\n\n", u8RdVal); return u8RdVal; }讀取設備ID,詳細讀ID的指令,依據datasheet確定。
當ID成功讀到了。接下來。設置多個攝像頭屬性,通常做成一個數組。此時攝像頭基本能工作了。
當插入驅動模塊以后會在/dev/ 文件夾下產生一個videoX 設備。這時候能夠使用cheese xawtv等程序進行測試。