登录
首页 嵌入式系统 嵌入式系统
回帖 发帖
正文

主题:i.MX27视频应用技术详解-TW2835

点击:677 回复:0


简解要介绍mx27中强大的视频处理功能

  Freescale公司出品的mx27芯片,秉承了欧美系芯片产商内部资源丰富,性能稳定可靠的设计原则。其中最为耀眼的功能就是其使用简单却又功能强大的视频处理单元。其内部的视频处理部分共有3个模块,分别

1.CMOS Sensor Interface可以直接接传统的CMOS摄像头接口,也可以接CCIR视频接口,这是负责视频的采集接口部分
2.内部集成了增强型低功耗的多媒体加速器eMMA,可以进行视频的前预处理和后处理以及类似去环块(dering),色彩空间转换,放大和图像的平滑缩放等,这是视频的前后期处理部分.
3.内置硬件编解码模块,支持H.264/MPEG4硬件编解码,全双工不占用CPU资源。

我们先来介绍视频的采集和预处理部分。
视频采集芯片我们采用的是可以支持4路视频输入的TW2835。可以达到D1的视频效果,完全满足基本的视频监控需求
  Techwell公司推出了TW2835四信道视频和音频控制器。建立在TW2834的成功基础之上,TW2835将6个主要程序整合进一个芯片中,其中包括:4个高质量的NTSC/PAL视频解码器、4个音频模拟数字转换器、一个音频复用器,双色显示控制器,双视频译码器以及一个先进的OSD(屏幕菜单式调节方式)。此外,TW2835每个芯片还使用了一个单一的同步动态存储器(SDRAM),可用于可选的BGA包中,还拥有一个被称为TW2836的pin-to-pin(管脚到管脚)姊妹芯片。TW2836除了不包含音频之外,与TW2835具有同样的功能。TW2835利用了Techwell安全监控集成电路产品解决方案的广泛组合,是帮助DVR(数码视频录像机)生产商提高质量与功能、加快上市时间以及减少成本的重要一步。在其众多功能中,TW2835支持详尽的实时D1录制、在重放过程中将信道ID信息添加到视频流媒体中,用于自动解码与显示,同时还包含一个5层的图形覆盖功能,为OSD、单盒、2D阵列箱以及鼠标指示器显示特征/位图。TW2835还包含一个简单的界面,使用多段连接支持多达16个信道系统。此外,TW2835还嵌入了几种特别的监视功能,其中包括:运动监测、放大以及水平与垂直缩放控制。凭借置入旨在减少交叉噪音的反锯齿过滤器和高质量的梳状过滤器,TW2835已经成为一种针对DVR与Quad/Multiplexers的高性能、具有成本效益的解决方案。
  tw2835比较灵活,既可以单路切换采集4路D1视频,也可以将4路视频集成到1路D1视频输出,可以根据你的需求在应用程序上控制。成都莱得科技(www.nidetech.com)提供的驱动程序给应用程序提供了控制接口。并且提供完整的驱动程序源代码,该方案和代码在客户的实际产品中获得了成功的验证。现在我们简要的介绍一下tw2835在mx27开发板中的驱动编写和遇到的一些问题。
 
  一、驱动的架构和实现
  http://blog.gkong.com/uploadfile4/2011101712451630.jpg
  tw2835和mx27的视频流传输是通过mx27的CSI接口,命令控制接口采用I2C
也就是是说驱动分为两个部分
1、 i2c驱动:
负责初始化和配置芯片,例如设置和获取颜色,复位芯片,设置通道属性,检测芯片的存在与否,设置csi接口的时钟等
 
//驱动初始化
  static __init int tw2835_init(void)
  {
          u8 err;

          gpio_tw2835_active();

          err = i2c_add_driver(&tw2835_i2c_driver);
          return err;
  }

  //首先要对硬件引脚功能进行初始化,
  void gpio_tw2835_active(void)
{

//设置CSI接口的引脚功能
       gpio_request_mux(MX27_PIN_CSI_D0, GPIO_MUX_PRIMARY);
       gpio_request_mux(MX27_PIN_CSI_D1, GPIO_MUX_PRIMARY);
       gpio_request_mux(MX27_PIN_CSI_D2, GPIO_MUX_PRIMARY);
       gpio_request_mux(MX27_PIN_CSI_D3, GPIO_MUX_PRIMARY);
       gpio_request_mux(MX27_PIN_CSI_D4, GPIO_MUX_PRIMARY);
       gpio_request_mux(MX27_PIN_CSI_MCLK, GPIO_MUX_PRIMARY);
       gpio_request_mux(MX27_PIN_CSI_PIXCLK, GPIO_MUX_PRIMARY);
       gpio_request_mux(MX27_PIN_CSI_D5, GPIO_MUX_PRIMARY);
       gpio_request_mux(MX27_PIN_CSI_D6, GPIO_MUX_PRIMARY);
       gpio_request_mux(MX27_PIN_CSI_D7, GPIO_MUX_PRIMARY);
       gpio_request_mux(MX27_PIN_CSI_VSYNC, GPIO_MUX_PRIMARY);
       gpio_request_mux(MX27_PIN_CSI_HSYNC, GPIO_MUX_PRIMARY);


       /* Power down control */
       mdelay(10);
       gpio_request_mux(MX27_PIN_USBH1_RXDM, GPIO_MUX_GPIO);
       mxc_set_gpio_direction(MX27_PIN_USBH1_RXDM, 0);
       mxc_set_gpio_dataout(MX27_PIN_USBH1_RXDM, 1);

       /* Reset芯片*/
       mdelay(10);
       gpio_request_mux(MX27_PIN_CSPI1_RDY, GPIO_MUX_GPIO);
       mxc_set_gpio_direction(MX27_PIN_CSPI1_RDY, 0);
       mxc_set_gpio_dataout(MX27_PIN_CSPI1_RDY, 0);
       mdelay(10);
       mxc_set_gpio_dataout(MX27_PIN_CSPI1_RDY, 1);
       mdelay(10);

}

然后,进行调用i2c驱动接口,i2c_add_driver,加入新的驱动。由框架自动调用attach函数
在该函数中,设定tw2835的主时钟,然后调用i2c_probe进行芯片的地址检测
static int tw2835_attach(struct i2c_adapter *adap)
{
       uint32_t mclk = 27000000;
       struct clk *clk;
       int err;

       clk = clk_get(NULL, "csi_clk");
       clk_enable(clk);
       set_mclk_rate(&mclk);

       err = i2c_probe(adap, &addr_data, &tw2835_detect_client);
       clk_disable(clk);
       clk_put(clk);

       return err;
}
在扫描到芯片的地址和预置的地址一致的时候,调用tw2835_detect_client
该函数

 
 
#define TW2835_I2C_ADDRESS     0x42 //tw2835的i2c地址
#define TW2835_DEVICE_ID 0x05 //芯片内部的id,从寄存器中读出

1.调用i2c_attach_client将adapter设置到tw2835_i2c_client中
2.将tw2835_i2c_client加入到i2c设备列表中去
3.分配一个摄像头接口数据,设置主时钟
static int tw2835_detect_client(struct i2c_adapter *adapter, int address,
                                    int kind)
{
       tw2835_i2c_client.adapter = adapter;
       if (i2c_attach_client(&tw2835_i2c_client)) {
                 tw2835_i2c_client.adapter = NULL;
                 printk(KERN_ERR "tw2835_detect_client: i2c_attach_client failed\n");
                 return -1;
       }

       interface_param = (sensor_interface *)
          kmalloc(sizeof(sensor_interface), GFP_KERNEL);
       if (!interface_param) {
                 printk(KERN_ERR "tw2835_detect_client: kmalloc failed \n");
                 return -1;
       }
       interface_param->mclk = 27000000;

       printk(KERN_INFO "TW2835 Detected\n");

       return 0;
}


struct camera_sensor camera_sensor_if = {
       .set_color = tw2835_set_color,
       .get_color = tw2835_get_color,
       .set_ae_mode = tw2835_set_ae_mode,
       .get_ae_mode = tw2835_get_ae_mode,
       .set_ae = tw2835_set_ae,
       .set_awb = tw2835_set_awb,
       .flicker_control = tw2835_flicker_control,
       .get_control_params = tw2835_get_control_params,
       .config = tw2835_config,
       .reset = tw2835_reset,
       .get_status = tw2835_get_status,
       .set_channel = tw2835_set_channel,
};
EXPORT_SYMBOL(camera_sensor_if);
导出camera_sensor_if,在v4l2采集驱动中被使用,用来实现该芯片的控制接口,因此如果有多个视频采集芯片驱动,那么同时只能有一个驱动导出该结构体。否则会
发生冲突。


static void tw2835_interface(sensor_interface * param, u32 width, u32 height)
{
       param->Vsync_pol = 0x0;
       param->clk_mode = 0x0;         //gated
       param->pixclk_pol = 0x0;
       param->data_width = 0x1;
       param->data_pol = 0x0;
       param->ext_vsync = 0x1;
       param->Vsync_pol = 0x0;
       param->Hsync_pol = 0x0;
       param->width = width - 1;
       param->height = height - 1;
       param->pixel_fmt = IPU_PIX_FMT_UYVY;
       param->mclk = 27000000;
       /* setup cropping */
       g_cam->crop_bounds.left = 0;
       g_cam->crop_bounds.width = width;
       g_cam->crop_bounds.top = 0;
       g_cam->crop_bounds.height = height;
       g_cam->crop_defrect = g_cam->crop_bounds;
       if ((g_cam->crop_current.width > g_cam->crop_bounds.width)
             || (g_cam->crop_current.height > g_cam->crop_bounds.height))
                 g_cam->crop_current = g_cam->crop_bounds;
       g_cam->streamparm.parm.capture.capturemode = 0;

       g_cam->standard.index = 0;
       g_cam->standard.id = (height == 576) ? V4L2_STD_PAL : V4L2_STD_NTSC;
       g_cam->standard.frameperiod.denominator = (height == 576) ? 50 : 60;
       g_cam->standard.frameperiod.numerator = 1;
       g_cam->standard.framelines = height;
}

在mxc_v4l2_capture.c[v4l2驱动实现]中定义了cam_data *g_cam;我们在tw2835.c[i2c驱动]中来填充它,
设置我们需要采集的视频的制式,场数,crop的高度和宽度等。


typedef struct _cam_data {
       struct video_device *video_dev;

       /* semaphore guard against SMP multithreading */
       struct semaphore busy_lock;

       int open_count;

       /* params lock for this camera */
       struct semaphore param_lock;

       /* Encorder */
       struct list_head ready_q;
       struct list_head done_q;
       struct list_head working_q;
       int ping_pong_csi;
       spinlock_t int_lock;
       struct mxc_v4l_frame frame[FRAME_NUM];
       int skip_frame;
  int overflow;
       wait_queue_head_t enc_queue;
       int enc_counter;
       dma_addr_t rot_enc_bufs[2];
       void *rot_enc_bufs_vaddr[2];
       int rot_enc_buf_size[2];
       enum v4l2_buf_type type;

       /* still image capture */
       wait_queue_head_t still_queue;
       int still_counter;
       dma_addr_t still_buf;
       void *still_buf_vaddr;

       /* overlay */
       struct v4l2_window win;
       struct v4l2_framebuffer v4l2_fb;
       dma_addr_t vf_bufs[2];
       void *vf_bufs_vaddr[2];
       int vf_bufs_size[2];
       dma_addr_t rot_vf_bufs[2];
       void *rot_vf_bufs_vaddr[2];
       int rot_vf_buf_size[2];
       bool overlay_active;
       int output;
       struct fb_info *overlay_fb;

       /* v4l2 format */
       struct v4l2_format v2f;
       int rotation;
       struct v4l2_mxc_offset offset;

       /* V4l2 control bit */
       int bright;
       int hue;
       int contrast;
       int saturation;
       int red;
       int green;
       int blue;
       int ae_mode;
       int ae_enable;
       int awb_enable;
       int flicker_ctrl;

       /* standart */
       struct v4l2_streamparm streamparm;
       struct v4l2_standard standard;

       /* crop */
       struct v4l2_rect crop_bounds;
       struct v4l2_rect crop_defrect;
       struct v4l2_rect crop_current;

       int (*enc_update_eba) (dma_addr_t eba, int *bufferNum);
       int (*enc_enable) (void *private);
       int (*enc_disable) (void *private);
       void (*enc_callback) (u32 mask, void *dev);
       int (*vf_start_adc) (void *private);
       int (*vf_stop_adc) (void *private);
       int (*vf_start_sdc) (void *private);
       int (*vf_stop_sdc) (void *private);
       int (*csi_start) (void *private);
       int (*csi_stop) (void *private);

       /* misc status flag */
       bool overlay_on;
       bool capture_on;
       int overlay_pid;
       int capture_pid;
       bool low_power;
       wait_queue_head_t power_queue;

       /* camera sensor interface */
       struct camera_sensor *cam_sensor;
} cam_data;

2、 v4l2采集驱动以及显示驱动[overlay输出]
负责控制csi接口采集视频,并实现标准的的v4l2接口,给应用程序调用。这是中应用层访问视频的标志接口。

  二、常见问题。
 
  1.视频出现串扰现象
      最后查出是使用了不合格的tvs管
  2.视频上出现了有规则的横向水波纹
      将TW2835的1.8V DC-DC供电改为1.8V LDO供电消除开关噪声,无水波纹出现
  3.运行时1,2路的图像下部和3,4路蓝屏图像一起闪动
      将TW2835的输出范围由1~254调整为16~235
  4.运行一段时间后视频死掉
      软件调整码流匹配
文章版权属于成都莱得科技有限责任公司所有,转载请注明出处。
网址:www.nidetech.com,联系电话:18080873876,技术交流QQ:1460879610

11-10-17 12:45

工控新闻

更多新闻资讯