AI-Talk开发板之Camera

发布于:2024-11-27 ⋅ 阅读:(79) ⋅ 点赞:(0)

一、说明

 AI-Talk开发板配备了一只DVP摄像头,型号GC0328,像素30万。

1、接口

2、与处理器的信号连接

使用i2c1接口。 

 二、工程

1、测试代码

使用duomotai_ap sdk中的例程进行测试。

2、设备树

设备树文件路径:duomotai_ap/.sdk/csk/boards/arm/csk6011a_box_lite/csk6011a_box_lite.dts

在i2c1节点下添加gc0328的配置

gc0328: gc0328@21 {
	compatible = "galaxyc,gc0328";
	status = "okay";
	reg = <0x21>;
	reset-gpios = <&exgpioa 1 1>;
    pwdn-gpios = <&exgpioc 7 0>;
};
3、驱动

驱动文件路径:duomotai_ap/.sdk/csk/drivers/video/gc0328.c。

内核加载驱动后,首先执行gc0328_init()函数,启动函数会设置pwdn_gpios和reset_gpios的状态,代码如下:

static int gc0328_init(const struct device *dev)
{
	LOG_INF("Galaxyc GC0328 init");
	struct gc0328_data *drv_data = dev->data;
	int ret = 0;

	if (drv_data->pwdn_gpios.port) {
		gpio_pin_configure_dt(&drv_data->pwdn_gpios, GPIO_OUTPUT_ACTIVE);
		gpio_pin_set_dt(&drv_data->pwdn_gpios, 0);
	}

	if (drv_data->reset_gpios.port) {
		gpio_pin_configure_dt(&drv_data->reset_gpios, GPIO_OUTPUT_ACTIVE);
		gpio_pin_set_dt(&drv_data->reset_gpios, 0);
		k_sleep(K_MSEC(3));
		gpio_pin_set_dt(&drv_data->reset_gpios, 1);
		k_sleep(K_MSEC(1));
	}

	// ret = gc0328_reg_init(dev);
	return ret;
}

如果pwdn_gpios和reset_gpios使用的是exmcu的管脚,那可能不会成功,根据启动日志可以看出,内核会先加载gc0328的驱动,再加载exmcu的驱动,那么加载gc0328的驱动时,exmcu还无法操作。

由于内核加载gc0328的驱动时,csk6011a还未输出mclk,所以没有在gc0328_init()中执行gc0328_reg_init(dev)函数,而是应用启动之后调用gc0328_set_ctrl()函数初始化sensor。

static int gc0328_set_ctrl(const struct device *dev, unsigned int cid, void *value)
{
	LOG_DBG("gc0328 set ctrl %d", cid);
	int ret = 0;

	switch (cid) {
	case VIDEO_CID_HFLIP:
		ret = gc0328_set_horizontal_mirror(dev, *(int *)value);
		break;
	case VIDEO_CID_VFLIP:
		ret = gc0328_set_vertical_flip(dev, *(int *)value);
		break;
	/*
		该sensor只有在mclk有输入时钟的情况下,i2c才能正常通信,所以并不能在内核设备初始化流程中进行sensor初始化。
		目前是通过set_ctrl来提供sensor初始化的api.
	*/
	case VIDEO_CID_INIT:
		k_sleep(K_MSEC(10));
		ret = gc0328_reg_init(dev);
		break;
	default:
		return -ENOTSUP;
	}

	return ret;
}

三、调试

1、启动日志
[00:00:00.000,000] [<inf> gc0328: Galaxyc GC0328 init

[00:00:00.050,000] [1;31m<err> i2c_csk: Wait I2C complete time out.[0m
[00:00:00.100,000] [1;31m<err> i2c_csk: Wait I2C complete time out.[0m
[00:00:00.150,000] [1;31m<err> i2c_csk: Wait I2C complete time out.[0m
[00:00:00.200,000] [1;31m<err> i2c_csk: Wait I2C complete time out.[0m
[00:00:00.251,000] [1;31m<err> i2c_csk: Wait I2C complete time out.[0m
[00:00:00.301,000] [1;31m<err> i2c_csk: Wait I2C complete time out.[0m
[00:00:00.351,000] [1;31m<err> i2c_csk: Wait I2C complete time out.[0m
[00:00:00.401,000] [1;31m<err> i2c_csk: Wait I2C complete time out.[0m
[00:00:00.454,000] [1;31m<err> i2c_csk: Wait I2C complete time out.[0m
[00:00:00.504,000] [1;31m<err> i2c_csk: Wait I2C complete time out.[0m
[00:00:00.505,000] [0m<inf> csk6_spi: SPI REG ADDR:0x45500000[0m
[00:00:00.506,000] [0m<inf> csk6_spi: SPI REG ADDR:0x45400000[0m
[00:00:00.506,000] [0m<inf> csk6_exmcu_i2c: exmcu addr:0x6C[0m
[00:00:00.546,000] [0m<inf> csk6_exmcu_i2c: exmcu info, chip type:ch32v003, ver:1.3
[00:00:00.546,000] [0m<inf> pwm_csk_ch32v003: pwm csk ch32v003 init, freq:48000000, pres:480
[00:00:00.546,000] [0m<inf> display_st7789v: st7789v_init
[00:00:00.567,000] [0m<dbg> display_st7789v: st7789v_reset_display: Resetting display[0m

[00:00:00.726,000] [0m<inf> display_st7789v: st7789v_init done
m<inf> csk6_dvp: sensor dev gc0328@21
[00:00:00.736,000] [0m<inf> gc0328: Galaxyc GC0328 init successful, pid 0x9D
2、分析日志

内核启动后会先初始化gc0328,但不成功,有两种原因。

一是此时由于csk6011a还未产生mclk,导致gc0328没有启动,所以i2c通信失败。

二是由于reset和pwdn是接在exmcu上的,此时还没加载exmcu的驱动,无法将reset和pwdn设置到正确的状态,gc0328可能处于复位或关机状态。

四、修改驱动

1、修改gc0328_init()函数

如果pwdn_gpios和reset_gpios使用的是exmcu的引脚,这里设置状态也无效,所以屏蔽函数里面所有的代码:

static int gc0328_init(const struct device *dev)
{
	LOG_INF("Galaxyc GC0328 init null");
/* 
	struct gc0328_data *drv_data = dev->data;
	int ret = 0;

	if (drv_data->pwdn_gpios.port) {
		gpio_pin_configure_dt(&drv_data->pwdn_gpios, GPIO_OUTPUT_ACTIVE);
		gpio_pin_set_dt(&drv_data->pwdn_gpios, 0);
	}

	if (drv_data->reset_gpios.port) {
		gpio_pin_configure_dt(&drv_data->reset_gpios, GPIO_OUTPUT_ACTIVE);
		gpio_pin_set_dt(&drv_data->reset_gpios, 0);
		k_sleep(K_MSEC(3));
		gpio_pin_set_dt(&drv_data->reset_gpios, 1);
		k_sleep(K_MSEC(1));
	}
*/
	// ret = gc0328_reg_init(dev);
	//return ret;
 
  return 0;
}
2、修改gc0328_set_ctrl()函数

在代码“case VIDEO_CID_INIT:”之后设置pwdn_gpios和reset_gpios状态的代码,修改之后代码如下:

	case VIDEO_CID_INIT:
    struct gc0328_data *drv_data = dev->data;
  	if (drv_data->pwdn_gpios.port) {
  		gpio_pin_configure_dt(&drv_data->pwdn_gpios, GPIO_OUTPUT_ACTIVE);
  		gpio_pin_set_dt(&drv_data->pwdn_gpios, 0);
  	}
  
  	if (drv_data->reset_gpios.port) {
  		gpio_pin_configure_dt(&drv_data->reset_gpios, GPIO_OUTPUT_ACTIVE);
  		gpio_pin_set_dt(&drv_data->reset_gpios, 0);
  		k_sleep(K_MSEC(3));
  		gpio_pin_set_dt(&drv_data->reset_gpios, 1);
  		k_sleep(K_MSEC(1));
  	}
   
		k_sleep(K_MSEC(10));
		ret = gc0328_reg_init(dev);
		break;

如果pwdn_gpios和reset_gpios使用的是exmcu的引脚,这样才能将pwdn_gpios和reset_gpios设置到正确的状态。