时隔一年,对全国大学生智能车竞赛做段总结(二)

图像处理

先说说这个构成图像的这个数据,也就是逐飞给的头文件里的这188*120的二维数组,它是以什么方式传输的。

头文件中定义的二维数组:

//配置摄像头参数
#define MT9V03X_W               188                 //图像宽度     范围1-188
#define MT9V03X_H               120                 //图像高度    范围1-120

当然,后续我们在图像这块儿处理的还是这188*120个点,只是,如果数据量过大,我们可以选择压缩图片啊什么的,等比例压缩。不过我个人试过,总感觉压缩下来图像或多或少有点失真,当然,可能也是我的方式有问题。我压缩的办法呢,就是整个二维数组,长和宽,间隔一个整数取值。最后压缩成的是一个94*60的数组,但是在1.8寸的TFT彩屏上显示的画面与没压缩之间的图像,我肉眼看上去,我是觉得图像失真的。

数据传输

DMA

什么是DMA呢?

DMA,全称Direct Memory Access,即直接存储器访问。

DMA应用得其实还是比较广泛的,包括2019年英特尔公司推出的DPDK(Data Plane Development Kit数据平面开发套件。它本身推出的目的呢是想在网络通信这方面,提高数据传输速率,提高硬件层的工作效率。

DMA传输将数据从一个地址空间复制到另一个地址空间,提供在外设和存储器之间或者存储器和存储器之间的高速数据传输

图像呢,是由摄像头传感器完成的,而图像呢是通过DMA中断传输给我们的MCU的。

//-------------------------------------------------------------------------------------------------------------------
//  @brief      MT9V03X摄像头DMA完成中断
//  @param      NULL
//  @return     void            
//  @since      v1.0
//  Sample usage:                此函数在isr.c中被dma中断调用
//-------------------------------------------------------------------------------------------------------------------
void mt9v03x_dma(void)
{
    CLEAR_DMA_FLAG(MT9V03X_DMA_CH);

    if(IfxDma_getChannelTransactionRequestLost(&MODULE_DMA, MT9V03X_DMA_CH))
    {//图像有错位
        mt9v03x_finish_flag = 0;
        dma_stop(MT9V03X_DMA_CH);
        IfxDma_clearChannelTransactionRequestLost(&MODULE_DMA, MT9V03X_DMA_CH);
        mt9v03x_dma_init_flag = 1;
    }
    else
    {
        mt9v03x_dma_int_num++;

        if(mt9v03x_dma_int_num >= link_list_num)
        {
            //采集完成
            mt9v03x_dma_int_num = 0;
            mt9v03x_finish_flag = 1;//一副图像从采集开始到采集结束耗时3.8MS左右(50FPS、188*120分辨率)
            dma_stop(MT9V03X_DMA_CH);
        }
    }
}

图像采集与图像显示

这里软件定义了一个图像采集完成标志位,至于一些别的参数什么的,就不大需要管了,都是一些摄像头的参数,人家给你设定好了直接用就行,至于你自己想调点什么曝光度啊,那也行,没调出毛病在TFT或者说是IPS上能显示出图像,那随你想咋搞咋搞。

摄像头的初始化函数,反正也是别人写好的,就直接放这儿了。

void mt9v03x_init(void)
{
    uint8 i;
    camera_type = CAMERA_GRAYSCALE;//设置连接摄像头类型
    camera_buffer_addr = mt9v03x_image[0];

    boolean interrupt_state = disableInterrupts();

    uart_init (MT9V03X_COF_UART, 9600, MT9V03X_COF_UART_TX, MT9V03X_COF_UART_RX);    //初始换串口 配置摄像头
    enableInterrupts();//开启中断

    //等待摄像头上电初始化成功 方式有两种:延时或者通过获取配置的方式 二选一
    //systick_delay_ms(STM0, 1000);//延时方式
    get_config(MT9V03X_COF_UART, GET_CFG);//获取配置的方式

    uart_receive_flag = 0;
    set_config(MT9V03X_COF_UART, MT9V03X_CFG);

    //获取配置便于查看配置是否正确
    get_config(MT9V03X_COF_UART, GET_CFG);

    disableInterrupts();

    //摄像头采集初始化
    //初始化 数据引脚
    for(i=0; i<8; i++)
    {
        gpio_init((PIN_enum)(MT9V03X_DATA_PIN+i), GPI, 0, PULLUP);
    }

    link_list_num = eru_dma_init(MT9V03X_DMA_CH, GET_PORT_IN_ADDR(MT9V03X_DATA_PIN), camera_buffer_addr, MT9V03X_PCLK_PIN, RISING, MT9V03X_W*MT9V03X_H);//如果超频到300M 倒数第二个参数请设置为FALLING

    eru_init(MT9V03X_VSYNC_PIN, FALLING);    //初始化场中断,并设置为下降沿触发中断
    restoreInterrupts(interrupt_state);
}

总之呢,这些东西并不需要我们写,或者说,从gitee.com把那边的开源项目拷贝过来,我们需要自己动手的东西就很少了。英飞凌的TC系列芯片,一般都是多核的,我们选择其中一个任何,在里面的主函数写入

int core0_main(void)
{
    mt9v03x_init();
    while(1)
    {

    }
}

没看错,只是在下面那个死循环体之前写入摄像头初始化函数,这一帧图像就采集完了。简单吧,简单!但这并不意味着你能做好一辆小车。在最开始的字句里我就说过,我们要做的事很少,就是调库,但并不意味着我们什么都不需要做,仅仅只是没脑子地调用人家封装好的库函数就完事了。

显然,只是摄像头初始化并不能得到我们的摄像头就采集到了图像这一不客观事实,我们需要借助东西去观察。可以是TFT/IPS这样的oled显示屏,也可以用上位机。并且,有些与你同台竞技的佬,他们的上位机是自己做的,可谓是小母牛开飞机......

调库嘛,简单,库函数一放就啥也出来了撒。

//-------------------------------------------------------------------------------------------------------------------
//  @brief      总钻风(灰度摄像头)液晶显示函数
//  @param      *p                 图像数组地址
//  @param      width             图像宽度
//  @param      height             图像高度
//  @return     void
//  @since      v1.0
//  Sample usage:               lcd_displayimage032(mt9v03x_csi_image[0], MT9V03X_CSI_W, MT9V03X_CSI_H)//显示灰度摄像头 图像
//  @note       图像的宽度如果超过液晶的宽度,则自动进行缩放显示。这样可以显示全视野
//-------------------------------------------------------------------------------------------------------------------
void lcd_displayimage032(uint8 *p, uint16 width, uint16 height) 
{
    uint32 i,j;
                
    uint16 color = 0;
    uint16 temp = 0;
    
    uint16 coord_x = 0;
    uint16 coord_y = 0;

    
    if(0==TFT_DISPLAY_DIR || 1==TFT_DISPLAY_DIR)//竖屏
    {
        coord_x = height>TFT_X_MAX?TFT_X_MAX:height;
        coord_y = width>TFT_Y_MAX?TFT_Y_MAX:width;

        for(j=0;j<coord_y;j++)
        {
            lcd_set_region(0,j,coord_x-1,j);
            for(i=0;i<coord_x;i++)
            {
                temp = *(p+i*width+j*width/coord_y);//读取像素点
                color=(0x001f&((temp)>>3))<<11;
                color=color|(((0x003f)&((temp)>>2))<<5);
                color=color|(0x001f&((temp)>>3));
                lcd_writedata_16bit(color); 
            }
        }
        
    }
    else//横屏
    {
        coord_x = width>TFT_X_MAX?TFT_X_MAX:width;
        coord_y = height>TFT_Y_MAX?TFT_Y_MAX:height;
        lcd_set_region(0,0,coord_x-1,coord_y-1);

        for(j=0;j<coord_y;j++)
        {
            for(i=0;i<coord_x;i++)
            {
                temp = *(p+j*width+i*width/coord_x);//读取像素点
                color=(0x001f&((temp)>>3))<<11;
                color=color|(((0x003f)&((temp)>>2))<<5);
                color=color|(0x001f&((temp)>>3));
                lcd_writedata_16bit(color); 
            }
        }
    }
}

函数括号里的三项,第一项是图像首地址,也就是整个图像第一个点,举例就是Image[0],第二个是宽,第三个是高。

放在主函数中应该是这样。

int core0_main(void)
{
    mt9v03x_init();
    lcd_init();
    while(1)
    {
        if(图像采集标志位为1)
        {
            标志位清零,图像接着采集
            lcd_displayimage032(图像名称[0],宽,高);
        }
    }
}

这里吧,摄像头得初始化,显示屏得初始化,一个为了采集数据,一个为了显示数据,仅此而已罢了。至于逐飞给的资料包里也提供了一个上位机,不过我觉得那个用起来不好使,要接电脑,还需要usb转ttl接线。

  • 1
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

沈千曦

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值