1569|0

504

帖子

4

TA的资源

纯净的硅(高级)

楼主
 

【ST多款开发板返场测评】STM32F767 Nucleo-144 实现基于USB-UVC的摄像头(显示) [复制链接]

6a9a010dfb1e5fd88dc8b1336b5b648d

 

前言

      前面我们实现了UVC设备的枚举与打开关闭。现在我们来添加数据流实现UVC的显示。

先来实现NV12格式,stm32f7还支持硬件jpeg编解码,后面有时间再实现jpeg格式。

 

过程

  • 修改配置描述符支持NV12格式

usbd_video.h开头中定义#define USBD_UVC_FORMAT_UNCOMPRESSED

  • usbd_video.h中

默认GUID为NV12

#define UVC_GUID_YUY2                                 0x32595559U
#define UVC_GUID_NV12                                 0x3231564EU

#ifndef UVC_UNCOMPRESSED_GUID
#define UVC_UNCOMPRESSED_GUID                         UVC_GUID_NV12
#endif /* UVC_UNCOMPRESSED_GUID */

 

  • 修改图片大小
/* These defines shall be updated in the usbd_conf.h file */
#ifndef UVC_WIDTH
#define UVC_WIDTH                                     240U
#endif /* UVC_WIDTH */

#ifndef UVC_HEIGHT
#define UVC_HEIGHT                                    240U

 

可以看到格式和大小都变过来了

 

 

  • 使能sof中断

usbd_conf.c的USBD_LL_Init

  hpcd.Init.Sof_enable = 1;

因为需要在USBD_VIDEO_SOF回调中进行一次发送,触发发送完中断,然后在后面中断中进行图片数据发送,形成发送流。

  hpcd.Init.dma_enable = 1;

  • 添加测试数据源头

生成RGB格式的NV12数据

uint8_t s_framebuffer[H_SIZE*V_SIZE*3/2];

//uint8_t s_framebuffer_out[H_SIZE*V_SIZE];



uint32_t color_table[] =

{

    0xFF0000,  /*  */

    0x00FF00,  /*  */

    0x0000FF,  /*  */

};

        

/*

 Y = 0.298R + 0.612G + 0.117B;   [13,235]

 U = -0.168R - 0.330G + 0.498B + 128; [16,239]

 V = 0.449R - 0.435G - 0.083B + 128; [16,239]

*/

static void update_buffer(void)

{

    uint8_t* p = (uint8_t*)s_framebuffer;

    static unsigned int index = 0;

    int y = 0;

    int u = 0;

    int v = 0;

    uint8_t r = 0;

    uint8_t g = 0;

    uint8_t b = 0;

    if(index >= sizeof(color_table)/sizeof(color_table[0]))

    {

        index = 0;

    }

    r = (color_table[index] >> 16) & 0xFF;

    g = (color_table[index] >> 8) & 0xFF;

    b = (color_table[index] >> 0) & 0xFF;

    index++;

    y = (0.298*r + 0.612*g + 0.117*b);

    u = (-0.168*r - 0.330*g + 0.498*b + 128);

    v = (0.449*r - 0.435*g - 0.083*b + 128);

    if(y>235)

    {

        y=235;

    }

    if(y<16)

    {

        y=16;

    }

    if(u>239)

    {

        u=239;

    }

    if(u<16)

    {

        u=16;

    }

    if(v>239)

    {

        v=239;

    }

    if(v<16)

    {

        v=16;

    }

    for(int i=0;i<H_SIZE*V_SIZE;i++)

    {

        *p++ = y;

    }

    for(int i=0;i<H_SIZE*V_SIZE/4;i++)

    {

        *p++ = u;

        *p++ = v;

    }

}

 

解决BUG

  • usbd_video_if_template.c中

  static uint8_t packet_index = 0U; 索引值在UVC_PACKET_SIZE比较小,tImagesSizes[x]比较大时会溢出,该为static uint32_t packet_index = 0U;

 

  • VIDEO_Itf_Data中是在中断中处理的,此时进行Delay会阻塞中断处理,并且也必须systick优先级大于USB中断优先级才能systick中断嵌套tick继续走行。我们直接注释掉即可。这里只显示一个图片,不控制图片间的间隔。

//USBD_Delay(USBD_VIDEO_IMAGE_LAPS);

 

 

优化-解决更新图片导致花屏的问题

修改为发送完一帧才能更新framebuffer,否则正在发送时修改会花屏。  

USBD_VIDEO_SOF中注释掉发送,在主循环中触发发送流

 

/**
  * [url=home.php?mod=space&uid=159083]@brief[/url] USBD_VIDEO_SOF
  *         handle SOF event
  * @param  pdev: device instance
  * @retval status
  */
static uint8_t  USBD_VIDEO_SOF(USBD_HandleTypeDef *pdev)
{
  USBD_VIDEO_HandleTypeDef *hVIDEO = (USBD_VIDEO_HandleTypeDef *) pdev->pClassDataCmsit[pdev->classId];
  uint8_t payload[2] = {0x02U, 0x00U};

  /* Check if the Streaming has already been started by SetInterface AltSetting 1 */
  if (hVIDEO->uvc_state == UVC_PLAY_STATUS_READY)
  {
    /* Transmit the first packet indicating that Streaming is starting */
    //(void)USBD_LL_Transmit(pdev, VIDEOinEpAdd, (uint8_t *)payload, 2U);

    /* Enable Streaming state */
    hVIDEO->uvc_state = UVC_PLAY_STATUS_STREAMING;
		hVIDEO->sending = 0;
  }

  /* Exit with no error code */
  return (uint8_t)USBD_OK;
}

主函数中触发发送

 

  

 while (1)
  {
		uart_debug_send(16);
		shell_exec_shellcmd();
		if((((USBD_VIDEO_HandleTypeDef *)(USBD_Device.pClassDataCmsit[0]))->uvc_state) == UVC_PLAY_STATUS_STREAMING)
    {
			if((((USBD_VIDEO_HandleTypeDef *)(USBD_Device.pClassDataCmsit[0]))->sending) == 0)
      {
				 ((USBD_VIDEO_HandleTypeDef *)(USBD_Device.pClassDataCmsit[0]))->sending = 1;
					framebuffer_update();  
				  (void)USBD_LL_Transmit(&USBD_Device, 0x81, (uint8_t *)payload, 2U);
			}
		}

发送完一帧停止等待主函数中重新启动

发送完一帧不再发送

static uint8_t  USBD_VIDEO_DataIn(USBD_HandleTypeDef *pdev, uint8_t epnum)
{
  USBD_VIDEO_HandleTypeDef *hVIDEO = (USBD_VIDEO_HandleTypeDef *) pdev->pClassDataCmsit[pdev->classId];
  static uint8_t  packet[UVC_PACKET_SIZE + (UVC_HEADER_PACKET_CNT * 2U)] = {0x00U};
  static uint8_t *Pcktdata = packet;
  static uint16_t PcktIdx = 0U;
  static uint16_t PcktSze = UVC_PACKET_SIZE;
  static uint8_t  payload_header[2] = {0x02U, 0x00U};
  uint8_t i = 0U;
  uint32_t RemainData, DataOffset = 0U;

  /* Check if the Streaming has already been started */
  if (hVIDEO->uvc_state == UVC_PLAY_STATUS_STREAMING)
  {
    /* Get the current packet buffer, index and size from the application layer */
    ((USBD_VIDEO_ItfTypeDef *)pdev->pUserData[pdev->classId])->Data(&Pcktdata, &PcktSze, &PcktIdx);

    /* Check if end of current image has been reached */
    if (PcktSze > 2U)
    {
      /* Check if this is the first packet in current image */
      if (PcktIdx == 0U)
      {
        /* Set the packet start index */
        payload_header[1] ^= 0x01U;
      }

      RemainData = PcktSze;

      /* fill the Transmit buffer */
      while (RemainData > 0U)
      {
        packet[((DataOffset + 0U) * i)] = payload_header[0];
        packet[((DataOffset + 0U) * i) + 1U] = payload_header[1];

        if (RemainData > pdev->ep_in[VIDEOinEpAdd & 0xFU].maxpacket)
        {
          DataOffset = pdev->ep_in[VIDEOinEpAdd & 0xFU].maxpacket;
          (void)USBD_memcpy((packet + ((DataOffset + 0U) * i) + 2U),
                            Pcktdata + ((DataOffset - 2U) * i), (DataOffset - 2U));

          RemainData -= DataOffset;
          i++;
        }
        else
        {
          (void)USBD_memcpy((packet + ((DataOffset + 0U) * i) + 2U),
                            Pcktdata + ((DataOffset - 2U) * i), (RemainData - 2U));

          RemainData = 0U;
        }
      }
			/* Transmit the packet on Endpoint */
			(void)USBD_LL_Transmit(pdev, (uint8_t)(epnum | 0x80U),
														 (uint8_t *)&packet, (uint32_t)PcktSze);
		   do_debug(DEBUG_TAG_SETUP,DEBUG_LEVEL_INFO,"%d %d\r\n",PcktSze,PcktIdx);
    }
    else
    {
      /* Add the packet header */
      packet[0] = payload_header[0];
      packet[1] = payload_header[1];
		  do_debug(DEBUG_TAG_SETUP,DEBUG_LEVEL_INFO,"done\r\n");
    }
  }

  /* Exit with no error code */
  return (uint8_t) USBD_OK;
}

 

 

测试

详见视频

 

总结

以上实现了UVC摄像头显示, 就可以玩转GUI了,后面移植GUI比如LVGL等就可以进行有意思的开发了。后一篇先玩个有意思的,移植NES模拟器,玩转NES游戏。

此帖出自stm32/stm8论坛
点赞 关注
 

回复
举报
您需要登录后才可以回帖 登录 | 注册

随便看看
查找数据手册?

EEWorld Datasheet 技术支持

相关文章 更多>>
关闭
站长推荐上一条 1/8 下一条

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

站点相关: 国产芯 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网

北京市海淀区中关村大街18号B座15层1530室 电话:(010)82350740 邮编:100190

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2025 EEWORLD.com.cn, Inc. All rights reserved
快速回复 返回顶部 返回列表