您的位置:首页 > 其它

Freescale实纪——关于线性CCD TSL1401CL与曝光的一些心得

2014-08-04 00:30 260 查看
        Freescale安徽赛结束,感触颇多。
        这次就线性CCD,特别是曝光的问题想做些总结。
        Freescale光电组指定的传感器是线性CCD——TSL1401CL。
        下面是官方说明中对此线性CCD的简单描述:
        


        所以,TSL1401CL的核心是128个光电二极管组成的感光阵列,阵列后面有一排积分电容,光电二极管在光能量冲击下产生光电流,构成有源积分电路,那么积分电容就是用来存储光能转化后的电荷。积分电容存储的电荷越多,说明前方对应的那个感光二极管采集的光强越大。反映在像素点上就是,像素灰度低。光强接近饱和,像素点灰度趋近于全白,则呈白电平。
        TSL1401CL的功能框图与引脚配置如下:
        


        


         我个人认为, 对于做车来说,不需要懂太多关于线性CCD的硬性知识,因为太底层的架构与原理也比较复杂,对小车所要涉及的图像处理也没有太紧密的联系。但是,对于线性CCD的工作基本原理,特别是时序,要有清晰的概念。
         在代码编写与CCD运用中,我的实际经验告诉我,积分时间与曝光时间最最重要。
         先就时序谈谈我自己的见解。现附上官方文档的时序图:
       


       


         对于线性CCD的时序操作,我觉得基本要知道的首先是TSL1401CL的关键引脚(见上表):除了必备的VCC,GND以外,还有与单片机IO口连接的CLK和SI脚,与单片机AD采集联通的AO脚。首先,对于CCD来讲,前18个时钟周期是像素复位时间,不进行积分与曝光。而且,第一个逻辑时钟SI必须出现在下一个时钟信号CLK上升沿之前。从时序图可清晰的看出CCD的操作过程,SI信号相当于一个标志,当它变为高电平后,我们就可以在每个CLK信号高电平到来后进行数据的AD采样。对于这样的整个时钟的时序操作,包括对CLK和SI的操作,就可以写成曝光函数。具体如下(代码具体参照了蓝宙的资料):
         //======曝光函数======//
         void StartIntegration(void) 
         {
                unsigned char i;
 
                TSL1401_SI = 1;         /* SI  = 1 */
                SamplingDelay();           /*合理延时100ns*/
                TSL1401_CLK = 1;        /* CLK = 1 */
                SamplingDelay();
                TSL1401_SI = 0;         /* SI  = 0 */
                SamplingDelay();
                TSL1401_CLK = 0;        /* CLK = 0 */
                for(i=0; i<127; i++) 
                {
                       SamplingDelay();
                       SamplingDelay();
                       TSL1401_CLK = 1;    /* CLK = 1 */
                       SamplingDelay();
                       SamplingDelay();
                       TSL1401_CLK = 0;    /* CLK = 0 */
               }
        }
        这仅仅是对CCD做的时序操作,通过对与CLK于SI脚连接的端口做电平变换,使TSL1401CL进入正常的工作状态。完了以后我们才能对CCD上AO口采集到的数据进行AD采样。采样函数如下:
void ImageCapture(unsigned char * ImageData,unsigned char * ImageData2) 
{
    unsigned char i;
    unsigned int  temp_int;
    unsigned int  temp2_int;
 
    TSL1401_SI = 1;         //SI  = 1 
    SamplingDelay();
    TSL1401_CLK = 1;        // CLK = 1 
    SamplingDelay();
    TSL1401_SI = 0;         // SI  = 0 
    SamplingDelay();
 
    //Delay 20us for sample the first pixel
    
    for(i = 0; i < 20; i++)    //更改25,让CCD的图像看上去比较平滑。
    {                
        Cpu_Delay1us();                      //把该值改大或者改小达到自己满意的结果。
    }
      
    temp_int = AD_Measure12(0);    //读取AD0口采集到的数据。
   
    *ImageData++ =(byte)(temp_int>>4);         
    
    TSL1401_CLK = 0;        // CLK = 0 
 
    for(i=0; i<127; i++) 
    {
        SamplingDelay();
        SamplingDelay();
        TSL1401_CLK = 1;    //CLK = 1 
        SamplingDelay();
        SamplingDelay();
        
        temp_int = AD_Measure12(0);
        
        *ImageData++ =(byte)(temp_int>>4);      
        TSL1401_CLK = 0;    // CLK = 0 
    }
    SamplingDelay();
    SamplingDelay();
    TSL1401_CLK = 1;        // CLK = 1  
    SamplingDelay();
    SamplingDelay();
    TSL1401_CLK = 0;        // CLK = 0
}
         从上述代码中可看出我们的代码完全按照时序进行,只是在先前的曝光函数中的适当位置插入了AD采样的语句。其中:
    for(i = 0; i < 20; i++)    //更改25,让CCD的图像看上去比较平滑。
    {                
        Cpu_Delay1us();                      //把该值改大或者改小达到自己满意的结果。
    } 
        这段代码做了一个合理延时,大约20us,当然i值可调,延时时间可调。针对其作用,蓝宙的说法是,在于控制AD采集的时间点,让采集数据更稳定,出来的图像更平滑。我也尝试过删除这个延时,在我的上位机上看到的图像采集也没有受到什么影响,也很稳定。小车跑出来的效果也正常,所以我个人认为这个延时或许必要性没那么强,只是相当于一个补充与完善,大家可以自己试试看。
        解决了时序与采集,需要将ImageCapture()函数写在StartIntegration()函数后。
        下面一个关键问题就是对积分时间与曝光时间的掌控。何时进行曝光,曝光的周期设置是整个图像处理部分的先头和关键。积分其实是对像素进行放电的时间。其中,积分时间的计算公式是:
        tint(min)=(128-18)*clock period+20us
        其中,128是线性CCD一次采集的像素数;18是所需的逻辑设置时钟,也就是之前提到的复位时间;20us是像素转移时间,顾名思义是将积分电容储存的电荷经过运算电路转移到AO数据输出的时间。
        其实我感觉此公式大概理解一下就行,更加清晰的体现了一下工作原理。公式本身对于我们的代码与对CCD的操作提供不了什么太直接地帮助。
        个人的经验总结是,光线越强,积分时间就越长,我们进行曝光的周期就该越短。这样采集的周期相应缩短,采到的数据更加稳定。
        所以自然引出曝光的另一个重要内容:选择自适应曝光或是固定曝光。自适应曝光的写法蓝宙电子的附送资料很详细,大家可以参考。但本人觉得自适应更适合初学者,比较好上手。但是自适应的做法本身很浪费,也没必要。它适合光线很不均匀的环境,但实际上一般的实验室与实际比赛场地不存在这样的问题,而且对于自适应算法,二值化后在小车过急弯CCD可能看到全黑图像时,二值化的阈值不好处理,容易误判,我后面会提到。所以,我认为固定曝光才是真正的出路。自适应的特点是曝光周期可变,所以他能适应多种复杂的光线;而固定曝光的优点就是曝光周期固定,在光线正常情况下采集更加稳定,图像抖动减小且更加清晰,更有利于黑线的提取与二值化。
       当然,平常在实验室,光线均匀,我用10ms的曝光周期正好。但是实际到了省赛的大体育馆,那种光线强到和太阳直射几乎无差,建议大家将曝光缩短到5ms,而且务必加上偏振片(旋到合适角度),这样出来的图像才会明暗差距清晰,凹槽明显。
        至于固定曝光与采集的代码位置,大家可以用PIT定时器中断,将CCD基础处理部分写在中断里,设置标志位什么的,以下是本人写的中断(10ms固定曝光),包含了速度采集于二值化等内容,不吝赐教:
 #pragma CODE_SEG __NEAR_SEG NON_BANKED
__interrupt void PITCh0IntISR(void) 
{
    static unsigned char TimerCnt10ms = 0;
    static unsigned char TimerCnt2s = 0;
    static unsigned char u=0;
    static unsigned char b=0;
    
    PITTF_PTF0 = 1;
    TimerCnt10ms++;
    TimerCnt2s++;
    
    if(TimerCnt10ms >= 50)
     { 
        TimerCnt10ms = 0;
        TimerFlag10ms = 1;
 
        LED1 = ~LED1;
        speed_back = PACNT;//返回速度值
        PACNT = 0;
        ImageCapture(Pixel,Pixel2);//CCD采样
                                                                             
        //SendImageData(Pixel); 
        Get_Dyn_Th();                    //获得动态阈值
        Bi_conversion();                 //二值化处理
        Filter_Pixel_Two();              //数据滤波(均值)     
        //SendImageData(Pixel); 
        
        Get_Flag();
        Get_Flag2();
        CCD1_mid_point();
        CCD2_mid_point();
     } else if(TimerCnt10ms==10)  
       {
            StartIntegration();   //开始曝光     
       }
}
#pragma CODE_SEG DEFAULT
        解决了曝光与采集,这时相应的写出SCI串口发送程序,就可在上位机上观察到线性CCD采集到的原始图像了。关于图像的进一步处理,包括二值化与动态阈值我会在后面的文章谈到。
        谢谢大家!
 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息