飞思卡尔智能车之摄像头使用篇

飞思卡尔智能车之摄像头使用

   今天来给大家说说摄像头的使用,很显然摄像头对摄像头组的重要性是不言而喻的,因为摄像头是小车提取赛道信息最关键的传感器了,所以只有把摄像头使用好才能让你的小车快速稳定的跑起来。

   摄像头我选择的是数字摄像头,大家可以在某宝上购买。买来之后首先要做的就是给摄像头调焦,可以使用视频采集卡将摄像头与电脑连接,然后在电脑上看摄像头的图像调节摄像头焦距直至图像最清晰为止,摄像头和采集卡的连接很简单只要把摄像头的VTO和GND接口与采集卡的视频线接口连接好就OK,然后把视频卡插在电脑上安装好驱动和相应的软件即可,驱动和软件买采集卡的时候卖家会给。焦距调好后,我们要配置摄像头了,以实现摄像头的功能。配置摄像头也就是对摄像头内部芯片寄存器的配置,寄存器的具体配置要参考你所使用摄像头芯片的datesheet,寄存器的配置就和配置单片机寄存器一样的,不同寄存器的不同数值代表着摄像头的不同功能。不过这个配置是需要和单片机进行通信的,通过单片机给摄像头的寄存器写入数值,通信协议一般是用IIC通信。不懂IIC协议的同学可以去学习下,单片机没有IIC接口的,就得通过IO端口软件模拟出时序来通信了,不过也不难就是按照它协议规定的时序读写数据就行了。通过配置寄存器实现摄像头的数字量输出,也可以配置摄像头的分辨率和对比度,或者曝光率(这个曝光率可能只是调节它的上下限范围,拍摄时摄像头根据环境自动调整,我用的就是)。

下面是我软件模拟IIC的程序,供大家参考

/*-----------------------------------------------------------------------
delay_1ns         : 延时程序
编写日期          :2014-12-15 
最后修改日期      :2014-12-15  
-----------------------------------------------------------------------*/

void BFDly_ms(uint8_t ms)
{
   volatile uint16_t ii,jj;
   if (ms<1) ms=1;
   for(ii=0;ii<ms;ii++)
   //  for(jj=0;jj<1335;jj++);     //16MHz--1ms
     for(jj=0;jj<3400;jj++);    //48MHz--1ms  
     //for(jj=0;jj<5341;jj++);    //64MHz--1ms  
    //  for(jj=0;jj<18200;jj++);     //200MHz--1ms  
} 


/*-----------------------------------------------------------------------
BFdelay_1us         : 延时程序
编写日期          :2014-12-15  
最后修改日期      :2014-12-15   
-----------------------------------------------------------------------*/

void BFdelay_1us(uint8_t us)                 //1us延时函数
  {
   volatile uint8_t i ;

    for(i=0;i<us;i++) 
    {     }

  }



//--------------------------------------------------------------------------------------------------
// 函数名称: iic_start()
// 函数功能: 启动I2C总线子程序
//--------------------------------------------------------------------------------------------------
void iic_start(void)
{ 	

	  BFSDA = 1;  
    BFdelay_1us(1);      // 延时1us 
   	BFCLK = 1;
    BFdelay_1us(1);      // 延时5us 
    BFSDA = 0;
	  BFdelay_1us(1);  
  	BFCLK = 0;
    BFdelay_1us(2);
}
//--------------------------------------------------------------------------------------------------
// 函数名称: iic_stop()
// 函数功能: 停止I2C总线数据传送子程序
//--------------------------------------------------------------------------------------------------
void iic_stop(void)
{ 
	BFSDA = 0;   	   //时钟保持高,数据线从低到高一次跳变,I2C通信停止
	BFdelay_1us(1);      // 延时1us 
        BFCLK = 1;
	BFdelay_1us(1);
	BFSDA = 1;
	BFdelay_1us(1);
	BFCLK = 0;
        BFdelay_1us(2);
}
//--------------------------------------------------------------------------------------------------
// 函数名称: slave_ACK
// 函数功能: 从机发送应答位子程序
//--------------------------------------------------------------------------------------------------
void slave_ACK(void)
{
	BFSDA = 0; 
  BFdelay_1us(1);      // 延时1us 
	BFCLK = 1;
	BFdelay_1us(1);			
	BFSDA = 1;
  BFdelay_1us(1);      // 延时1us 
	BFCLK = 0;
  BFdelay_1us(2);
}
//--------------------------------------------------------------------------------------------------
// 函数名称: slave_NOACK
// 函数功能: 从机发送非应答位子程序,迫使数据传输过程结束
//--------------------------------------------------------------------------------------------------
void slave_NOACK(void)
{ 
	BFSDA = 1;  
  BFdelay_1us(1);      // 延时1us 
	BFCLK = 1;
	BFdelay_1us(3);
	BFSDA = 0;
  BFdelay_1us(1);      // 延时1us 
	BFCLK = 0;
}
//--------------------------------------------------------------------------------------------------
// 函数名称: check_ACK
// 函数功能: 主机应答位检查子程序,迫使数据传输过程结束
//--------------------------------------------------------------------------------------------------
uint8_t check_ACK(void)
{ 
	uint8_t check ;
        
        BFSDA = 1; 
        BFDDRA = 0 ;
        ATD0DIEN =ATD0DIEN|0x40;
        BFSDA = 1 ;
        
        BFdelay_1us(1);      // 延时1us 
	      BFCLK = 1;
	      check = 0;
	if(BFSDA == 1)    // 若BFSDA=1 表明非应答,置位非应答标志F0
	      check = 1;
        BFdelay_1us(1);      // 延时1us 
       	BFCLK = 0;
        BFDDRA = 1 ;         //
        BFSDA = 0 ;
        return  check ;
}

//--------------------------------------------------------------------------------------------------
// 函数名称: IICSendByte
// 入口参数: ch
// 函数功能: 发送一个字节
//--------------------------------------------------------------------------------------------------
void IICSendByte(uint8_t ch)
{ 
	uint8_t n=8;     // 向BFSDA上发送一位数据字节,共八位
	while(n--)
	{ 
		if((ch&0x80) == 0x80)    // 若要发送的数据最高位为1则发送位1
		{
                  BFSDA = 1;    // 传送位1
                  BFdelay_1us(1);
                  BFCLK = 1;
                  BFdelay_1us(2);
                  BFCLK = 0;  
                  BFdelay_1us(1);
                  BFSDA = 0;
                  BFdelay_1us(1);
                  
		}
		else
		{  
                  BFSDA = 0;    // 否则传送位0
                  BFdelay_1us(1);
                  BFCLK = 1;
                  BFdelay_1us(2);
                  BFCLK = 0;  
                  BFdelay_1us(2);
		}
		ch = ch<<1;    // 数据左移一位
	}
}
//--------------------------------------------------------------------------------------------------
// 函数名称: IICreceiveByte
// 返回接收的数据
// 函数功能: 接收一字节子程序
//--------------------------------------------------------------------------------------------------
uint8_t IICreceiveByte(void)
{
	uint8_t n=8;    // 从BFSDA线上读取一上数据字节,共八位
	uint8_t tdata = 0;
	while(n--)
	{      
                BFDDRA = 0 ;
                ATD0DIEN =ATD0DIEN|0x40;
                BFSDA = 1;
                BFdelay_1us(1);
                BFCLK=0;
                BFdelay_1us(2);
	             	BFCLK = 1;
                BFdelay_1us(2);
		            tdata = tdata<<1;    // 左移一位,或_crol_(temp,1)
		            if(BFSDA == 1)
                  tdata = tdata|0x01;    // 若接收到的位为1,则数据的最后一位置1
		            else 
                  tdata = tdata&0xfe;    // 否则数据的最后一位置0
	              BFCLK=0;
                BFDDRA = 1 ;
	}
	return(tdata);
}
//--------------------------------------------------------------------------------------------------
// 函数名称: writeNbyte
// 入口参数: slave_add从机地址,n要发送的数据个数
// 函数功能: 发送n位数据子程序
//--------------------------------------------------------------------------------------------------
uint8_t writeNbyte(uint8_t slave_add, uint8_t *slave_data,uint16_t n)
{          
	uint8_t send_da,check = 1;
        uint16_t i=0;
        uint8_t *Send_databuff ;
        Send_databuff = slave_data ;
      //  uart_putchar(UART0,slave_data[0]);
      //   uart_putchar(UART0,n>>8);
	iic_start();                // 启动I2C
	IICSendByte(slave_add);     // 发送地址位
	check = check_ACK();                // 检查应答位
  //   SCI0_SendChar1(check);
        if(check == 1)
	{ 
	      return IICEorr;    // 若非应答表明器件错误或已坏,置错误标志位SystemError
	}
	while(n--)
	{ 
          send_da = Send_databuff[i++];	
          IICSendByte(send_da);     
          check= check_ACK();    // 检查应答位
          //uart_putchar(UART0,send_da);
          if (check == 1)
          {
            return IICEorr;    // 若非应答表明器件错误或已坏,置错误标志位SystemError
          }
	}
	iic_stop();         // 全部发完则停止
    
    return IICOK;
}
//--------------------------------------------------------------------------------------------------
// 函数名称: receiveNbyte
// 入口参数: slave_add从机地址,n要接收的数据个数
// 函数功能: 接收n位数据子程序
//--------------------------------------------------------------------------------------------------
uint8_t receiveNbyte(uint8_t slave_add,uint8_t *rece_data, uint16_t n)
{ 
	uint8_t receive_da,check;
        uint16_t i=1;
        uint8_t *rece_data_buff ;
        rece_data_buff = rece_data ;
        
        iic_start();
	IICSendByte(0XDC);
        BFdelay_1us(1);      // 延时1us 
        check =check_ACK();
	if(check == 1)
	{
		return IICEorr ;
	}
        IICSendByte(rece_data_buff[0]);
        BFdelay_1us(1);      // 延时1us 
        check =check_ACK();
	if(check == 1)
	{
		return IICEorr ;
	}
       // uart_putchar(UART0,rece_data_buff[0]);
	iic_start();
	IICSendByte(slave_add);
        BFdelay_1us(1);      // 延时1us 
	check =check_ACK();
	if(check == 1)
	{
		return IICEorr ;
	}
        
	for(;n > 1;n--)
	{ 
		receive_da=IICreceiveByte();
		rece_data_buff[i++]=receive_da;
		slave_ACK();    // 收到一个字节后发送一个应答位
                
         //       uart_putchar(UART0,receive_da);
                
	}
        
       receive_da=IICreceiveByte();
       rece_data_buff[i++]=receive_da;
       slave_NOACK();    // 收到最后一个字节后发送一个非应答位
    //   uart_putchar(UART0,receive_da);
       iic_stop();
       return IICOK;
        
}
             

//--------------------------------------------------------------------------------------------------
// 函数名称: IICWriteGpio_inint
// 入口参数: 
// 函数功能: 
//--------------------------------------------------------------------------------------------------
void IICWriteGpio_inint(void)
{
   DDR1AD0 |= 0X60 ;
   
   PT1AD0_PT1AD06 =1 ; 
   PT1AD0_PT1AD05 =1 ;  
}

   摄像头寄存器的配置搞定的话就可以编写摄像头采集数据的时序程序了,这个需严格根据摄像头的场中断和行中断的时序来编写,否则时序乱了就会导致摄像头采集到消隐区的数据都是无效的数据,我也给大家看过那个时序图,大家得看懂那个时序图,这样编写程序才不会出错。我的采集思路是在摄像头的一帧图像中利用行中断来采集数据,消隐区的时间来处理数据。

采集数据时序程序如下,本人用的单片机是XS128,大家自己研究研究

 

/*********************************************************
/***************  图像采样中断服务子函数  ****************
 ********************************************************/
 
#pragma CODE_SEG __NEAR_SEG NON_BANKED
void interrupt 24 PTJ6_inter(void) 
{
        if(HREF_max%4==0)
       {  
          Data[HREF_counter][0] = PORTA; _asm(nop);
          Data[HREF_counter][1] = PORTA; 
          Data[HREF_counter][2] = PORTA; _asm(nop);
          Data[HREF_counter][3] = PORTA; 
          Data[HREF_counter][4] = PORTA; _asm(nop);
          Data[HREF_counter][5] = PORTA;
          Data[HREF_counter][6] = PORTA;
          Data[HREF_counter][7] = PORTA;  
          Data[HREF_counter][8] = PORTA; 
          Data[HREF_counter][9] = PORTA; 
          Data[HREF_counter][10] = PORTA;
          Data[HREF_counter][11] = PORTA; 
          Data[HREF_counter][12] = PORTA;
          Data[HREF_counter][13] = PORTA;
          Data[HREF_counter][14] = PORTA;
          Data[HREF_counter][15] = PORTA;
          Data[HREF_counter][16] = PORTA;
          Data[HREF_counter][17] = PORTA;
          Data[HREF_counter][18] = PORTA;
          Data[HREF_counter][19] = PORTA;
          Data[HREF_counter][20] = PORTA;
          Data[HREF_counter][21] = PORTA;
          Data[HREF_counter][22] = PORTA;
          Data[HREF_counter][23] = PORTA;
          Data[HREF_counter][24] = PORTA;
          Data[HREF_counter][25] = PORTA;
          Data[HREF_counter][26] = PORTA;
          Data[HREF_counter][27] = PORTA;
          Data[HREF_counter][28] = PORTA;
          Data[HREF_counter][29] = PORTA;
          Data[HREF_counter][30] = PORTA;
          Data[HREF_counter][31] = PORTA; 
          Data[HREF_counter][32] = PORTA; 
          Data[HREF_counter][33] = PORTA;
          Data[HREF_counter][34] = PORTA; 
          Data[HREF_counter][35] = PORTA; 
          Data[HREF_counter][36] = PORTA;
          Data[HREF_counter][37] = PORTA;
          Data[HREF_counter][38] = PORTA;
          Data[HREF_counter][39] = PORTA;
          Data[HREF_counter][40] = PORTA;
          Data[HREF_counter][41] = PORTA;
          Data[HREF_counter][42] = PORTA;
          Data[HREF_counter][43] = PORTA; 
          Data[HREF_counter][44] = PORTA;
          Data[HREF_counter][45] = PORTA;
          Data[HREF_counter][46] = PORTA;
          Data[HREF_counter][47] = PORTA;
          Data[HREF_counter][48] = PORTA;
          Data[HREF_counter][49] = PORTA;
          Data[HREF_counter][50] = PORTA;
          Data[HREF_counter][51] = PORTA; 
          Data[HREF_counter][52] = PORTA;
          Data[HREF_counter][53] = PORTA; 
          Data[HREF_counter][54] = PORTA;
          Data[HREF_counter][55] = PORTA; 
          Data[HREF_counter][56] = PORTA;
          Data[HREF_counter][57] = PORTA; 
          Data[HREF_counter][58] = PORTA;
          Data[HREF_counter][59] = PORTA; 
          Data[HREF_counter][60] = PORTA;
          Data[HREF_counter][61] = PORTA; 
          Data[HREF_counter][62] = PORTA;
          Data[HREF_counter][63] = PORTA; 
          Data[HREF_counter][64] = PORTA;
          Data[HREF_counter][65] = PORTA;
          Data[HREF_counter][66] = PORTA;
          Data[HREF_counter][67] = PORTA; 
          Data[HREF_counter][68] = PORTA;
          Data[HREF_counter][69] = PORTA; 
          Data[HREF_counter][70] = PORTA;
          Data[HREF_counter][71] = PORTA; 
          Data[HREF_counter][72] = PORTA; 
          Data[HREF_counter][73] = PORTA;  
          Data[HREF_counter][74] = PORTA;
          Data[HREF_counter][75] = PORTA;  
          Data[HREF_counter][76] = PORTA;
          Data[HREF_counter][77] = PORTA;  
          Data[HREF_counter][78] = PORTA;
          Data[HREF_counter][79] = PORTA;   
          Data[HREF_counter][80] = PORTA;
          Data[HREF_counter][81] = PORTA;
          Data[HREF_counter][82] = PORTA;
          Data[HREF_counter][83] = PORTA;
          Data[HREF_counter][84] = PORTA;
          Data[HREF_counter][85] = PORTA;
          Data[HREF_counter][86] = PORTA;
          Data[HREF_counter][87] = PORTA;
          Data[HREF_counter][88] = PORTA;
          Data[HREF_counter][89] = PORTA;
          Data[HREF_counter][90] = PORTA;
          Data[HREF_counter][91] = PORTA;
          Data[HREF_counter][92] = PORTA;
          Data[HREF_counter][93] = PORTA;
          Data[HREF_counter][94] = PORTA; 
          Data[HREF_counter][95] = PORTA;
          Data[HREF_counter][96] = PORTA;
          Data[HREF_counter][97] = PORTA;
          Data[HREF_counter][98] = PORTA;
          Data[HREF_counter][99] = PORTA;
          Data[HREF_counter][100] = PORTA;
          Data[HREF_counter][101] = PORTA;
          Data[HREF_counter][102] = PORTA;
          Data[HREF_counter][103] = PORTA;
          Data[HREF_counter][104] = PORTA;
          Data[HREF_counter][105] = PORTA;
          Data[HREF_counter][106] = PORTA;
          Data[HREF_counter][107] = PORTA;
          Data[HREF_counter][108] = PORTA;
          Data[HREF_counter][109] = PORTA;
          Data[HREF_counter][110] = PORTA; 
          Data[HREF_counter][111] = PORTA;
          Data[HREF_counter][112] = PORTA; 
          Data[HREF_counter][113] = PORTA;
          Data[HREF_counter][114] = PORTA; 
          Data[HREF_counter][115] = PORTA;
          Data[HREF_counter][116] = PORTA;
          Data[HREF_counter][117] = PORTA;
          Data[HREF_counter][118] = PORTA; 
          Data[HREF_counter][119] = PORTA;
          PIFJ = PIFJ|0x40;     //清除行中断标志 
          HREF_counter++;
          HREF_max++;
          
      }
      
         
      else
      {
          
          PIFJ = PIFJ|0x40;     //清除行中断标志  
          HREF_max++; 
        
      }
         if(HREF_counter==Line_max)
       {
          PIEJ_PIEJ6=0 ;  //禁止行中断(PJ6)
          HREF_counter=0;
          HREF_max=0;
          ccd_data_deal_end_flags=1;
       }
    
}

#pragma CODE_SEG DEFAULT
<strong><span style="font-family:FangSong_GB2312;font-size:18px;color:#663300;background-color: rgb(102, 255, 255);">主函数里通过场信号的高低电平变化来判断一场是否结束</span></strong> 
void main()          <pre class="objc" name="code">     {      while(1)
           {    
                while(VREF);
                while(!VREF);
                PIFJ_PIFJ6=1;    //清行中断标志
                PIEJ_PIEJ6=1;    //PJ6允许中断,即行中断允许,进行视频数据采集  
                while(!ccd_data_deal_end_flags);  //等待视频数据采集结束
                ccd_data_deal_end_flags=0;
                Led_flag=1;
            }
     }
<strong><span style="font-family:FangSong_GB2312;font-size:18px;color:#663300;background-color: rgb(102, 255, 255);">如果按照正确的时序采集数据的话基本上是没问题了,大家可以通过调试软件查看采集到的数据,观察是否为有效数据,一般黑色的灰度值小于100,白色灰度值为200左右,这样的数据就是正常的,有个别数据异常那可能是噪点,程序上就要通过滤波来滤除这些噪点,滤波有很多方法以后会给大家介绍。以上采集数据的方法仅是我个人方法,大家也可以自己根据时序想出更好的方法来,当然肯定有更好的方法的,大家可以在网上找找资料,比如可以开辟出双数组,一个用来采集数据用,另一个用来处理数据用,这样效率更高。</span></strong>

                
  • 18
    点赞
  • 217
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值