rt-thread/bsp/nuvoton/libraries/nu_packages/Demo/ccap_demo.c

548 lines
18 KiB
C

/**************************************************************************//**
*
* @copyright (C) 2019 Nuvoton Technology Corp. All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2022-8-16 Wayne First version
*
******************************************************************************/
#include <rtthread.h>
#if defined(BSP_USING_CCAP)
#include "drv_ccap.h"
#include <dfs_posix.h>
#define DBG_ENABLE
#define DBG_LEVEL DBG_LOG
#define DBG_SECTION_NAME "ccap.demo"
#define DBG_COLOR
#include <rtdbg.h>
#define THREAD_PRIORITY 5
#define THREAD_STACK_SIZE 4096
#define THREAD_TIMESLICE 5
#define DEF_CROP_PACKET_RECT
#define DEF_ENABLE_PLANAR_PIPE 0
#define DEF_DURATION 10
#if defined(BSP_USING_CCAP0) && defined(BSP_USING_CCAP1)
#define DEF_GRID_VIEW 1
#elif defined(BSP_USING_CCAP0) || defined(BSP_USING_CCAP1)
#define DEF_GRID_VIEW 0
#endif
typedef struct
{
char *thread_name;
char *devname_ccap;
char *devname_sensor;
char *devname_lcd;
} ccap_grabber_param;
typedef ccap_grabber_param *ccap_grabber_param_t;
typedef struct
{
ccap_config sCcapConfig;
struct rt_device_graphic_info sLcdInfo;
uint32_t u32CurFBPointer;
uint32_t u32FrameEnd;
rt_sem_t semFrameEnd;
} ccap_grabber_context;
typedef ccap_grabber_context *ccap_grabber_context_t;
static void nu_ccap_event_hook(void *pvData, uint32_t u32EvtMask)
{
ccap_grabber_context_t psGrabberContext = (ccap_grabber_context_t)pvData;
if (u32EvtMask & NU_CCAP_FRAME_END)
{
rt_sem_release(psGrabberContext->semFrameEnd);
}
if (u32EvtMask & NU_CCAP_ADDRESS_MATCH)
{
LOG_I("Address matched");
}
if (u32EvtMask & NU_CCAP_MEMORY_ERROR)
{
LOG_E("Access memory error");
}
}
static rt_device_t ccap_sensor_init(ccap_grabber_context_t psGrabberContext, ccap_grabber_param_t psGrabberParam)
{
rt_err_t ret;
ccap_view_info_t psViewInfo;
sensor_mode_info *psSensorModeInfo;
rt_device_t psDevSensor = RT_NULL;
rt_device_t psDevCcap = RT_NULL;
struct rt_device_graphic_info *psLcdInfo = &psGrabberContext->sLcdInfo;
ccap_config_t psCcapConfig = &psGrabberContext->sCcapConfig;
psDevCcap = rt_device_find(psGrabberParam->devname_ccap);
if (psDevCcap == RT_NULL)
{
LOG_E("Can't find %s", psGrabberParam->devname_ccap);
goto exit_ccap_sensor_init;
}
psDevSensor = rt_device_find(psGrabberParam->devname_sensor);
if (psDevSensor == RT_NULL)
{
LOG_E("Can't find %s", psGrabberParam->devname_sensor);
goto exit_ccap_sensor_init;
}
/* Packet pipe for preview */
if (DEF_GRID_VIEW)
{
psCcapConfig->sPipeInfo_Packet.u32Width = psLcdInfo->width / 2;
psCcapConfig->sPipeInfo_Packet.u32Height = psLcdInfo->height / 2;
psCcapConfig->sPipeInfo_Packet.u32PixFmt = (psLcdInfo->pixel_format == RTGRAPHIC_PIXEL_FORMAT_RGB565) ? CCAP_PAR_OUTFMT_RGB565 : 0;
psCcapConfig->u32Stride_Packet = psLcdInfo->width;
if (!rt_strcmp(psGrabberParam->devname_ccap, "ccap1"))
psCcapConfig->sPipeInfo_Packet.pu8FarmAddr = psLcdInfo->framebuffer + (psCcapConfig->sPipeInfo_Packet.u32Width * 2);
else
psCcapConfig->sPipeInfo_Packet.pu8FarmAddr = psLcdInfo->framebuffer;
}
else
{
psCcapConfig->sPipeInfo_Packet.pu8FarmAddr = psLcdInfo->framebuffer;
psCcapConfig->sPipeInfo_Packet.u32Height = psLcdInfo->height;
psCcapConfig->sPipeInfo_Packet.u32Width = psLcdInfo->width;
psCcapConfig->sPipeInfo_Packet.u32PixFmt = (psLcdInfo->pixel_format == RTGRAPHIC_PIXEL_FORMAT_RGB565) ? CCAP_PAR_OUTFMT_RGB565 : 0;
psCcapConfig->u32Stride_Packet = psLcdInfo->width;
}
/* Planar pipe for encoding */
#if DEF_ENABLE_PLANAR_PIPE
psCcapConfig->sPipeInfo_Planar.u32Width = psLcdInfo->width / 2;
psCcapConfig->sPipeInfo_Planar.u32Height = psLcdInfo->height / 2;
psCcapConfig->sPipeInfo_Planar.pu8FarmAddr = rt_malloc_align(psCcapConfig->sPipeInfo_Planar.u32Height * psCcapConfig->sPipeInfo_Planar.u32Width * 2, 32);
psCcapConfig->sPipeInfo_Planar.u32PixFmt = CCAP_PAR_PLNFMT_YUV420; //CCAP_PAR_PLNFMT_YUV422;
psCcapConfig->u32Stride_Planar = psCcapConfig->sPipeInfo_Planar.u32Width;
if (psCcapConfig->sPipeInfo_Planar.pu8FarmAddr == RT_NULL)
{
psCcapConfig->sPipeInfo_Planar.u32Height = 0;
psCcapConfig->sPipeInfo_Planar.u32Width = 0;
psCcapConfig->sPipeInfo_Planar.u32PixFmt = 0;
psCcapConfig->u32Stride_Planar = 0;
}
LOG_I("Planar.FarmAddr@0x%08X", psCcapConfig->sPipeInfo_Planar.pu8FarmAddr);
LOG_I("Planar.FarmWidth: %d", psCcapConfig->sPipeInfo_Planar.u32Width);
LOG_I("Planar.FarmHeight: %d", psCcapConfig->sPipeInfo_Planar.u32Height);
#endif
/* open CCAP */
ret = rt_device_open(psDevCcap, 0);
if (ret != RT_EOK)
{
LOG_E("Can't open %s", psGrabberParam->devname_ccap);
goto exit_ccap_sensor_init;
}
/* Find suit mode for packet pipe */
if (psCcapConfig->sPipeInfo_Packet.pu8FarmAddr != RT_NULL)
{
/* Check view window of packet pipe */
psViewInfo = &psCcapConfig->sPipeInfo_Packet;
if ((rt_device_control(psDevSensor, CCAP_SENSOR_CMD_GET_SUIT_MODE, (void *)&psViewInfo) != RT_EOK)
|| (psViewInfo == RT_NULL))
{
LOG_E("Can't get suit mode for packet.");
goto fail_ccap_init;
}
}
/* Find suit mode for planner pipe */
if (psCcapConfig->sPipeInfo_Planar.pu8FarmAddr != RT_NULL)
{
int recheck = 1;
if (psViewInfo != RT_NULL)
{
if ((psCcapConfig->sPipeInfo_Planar.u32Width <= psViewInfo->u32Width) ||
(psCcapConfig->sPipeInfo_Planar.u32Height <= psViewInfo->u32Height))
recheck = 0;
}
if (recheck)
{
/* Check view window of planner pipe */
psViewInfo = &psCcapConfig->sPipeInfo_Planar;
/* Find suit mode */
if ((rt_device_control(psDevSensor, CCAP_SENSOR_CMD_GET_SUIT_MODE, (void *)&psViewInfo) != RT_EOK)
|| (psViewInfo == RT_NULL))
{
LOG_E("Can't get suit mode for planner.");
goto exit_ccap_sensor_init;
}
}
}
#if defined(DEF_CROP_PACKET_RECT)
/* Set cropping rectangle */
if (psViewInfo->u32Width >= psCcapConfig->sPipeInfo_Packet.u32Width)
{
/* sensor.width >= preview.width */
psCcapConfig->sRectCropping.x = (psViewInfo->u32Width - psCcapConfig->sPipeInfo_Packet.u32Width) / 2;
psCcapConfig->sRectCropping.width = psCcapConfig->sPipeInfo_Packet.u32Width;
}
else
{
/* sensor.width < preview.width */
psCcapConfig->sRectCropping.x = 0;
psCcapConfig->sRectCropping.width = psViewInfo->u32Width;
}
if (psViewInfo->u32Height >= psCcapConfig->sPipeInfo_Packet.u32Height)
{
/* sensor.height >= preview.height */
psCcapConfig->sRectCropping.y = (psViewInfo->u32Height - psCcapConfig->sPipeInfo_Packet.u32Height) / 2;
psCcapConfig->sRectCropping.height = psCcapConfig->sPipeInfo_Packet.u32Height;
}
else
{
/* sensor.height < preview.height */
psCcapConfig->sRectCropping.y = 0;
psCcapConfig->sRectCropping.height = psViewInfo->u32Height;
}
#else
/* Set cropping rectangle */
psCcapConfig->sRectCropping.x = 0;
psCcapConfig->sRectCropping.y = 0;
psCcapConfig->sRectCropping.width = psViewInfo->u32Width;
psCcapConfig->sRectCropping.height = psViewInfo->u32Height;
#endif
/* ISR Hook */
psCcapConfig->pfnEvHndler = nu_ccap_event_hook;
psCcapConfig->pvData = (void *)psGrabberContext;
/* Get Suitable mode. */
psSensorModeInfo = (sensor_mode_info *)psViewInfo;
/* Feed CCAP configuration */
ret = rt_device_control(psDevCcap, CCAP_CMD_CONFIG, (void *)psCcapConfig);
if (ret != RT_EOK)
{
LOG_E("Can't feed configuration %s", psGrabberParam->devname_ccap);
goto fail_ccap_init;
}
{
int i32SenClk = psSensorModeInfo->u32SenClk;
if (DEF_GRID_VIEW && DEF_ENABLE_PLANAR_PIPE)
i32SenClk = 45000000; /* Bandwidth limitation: Slow down sensor clock */
/* speed up pixel clock */
if (rt_device_control(psDevCcap, CCAP_CMD_SET_SENCLK, (void *)&i32SenClk) != RT_EOK)
{
LOG_E("Can't feed setting.");
goto fail_ccap_init;
}
}
/* Initial CCAP sensor */
if (rt_device_open(psDevSensor, 0) != RT_EOK)
{
LOG_E("Can't open sensor.");
goto fail_sensor_init;
}
/* Feed settings to sensor */
if (rt_device_control(psDevSensor, CCAP_SENSOR_CMD_SET_MODE, (void *)psSensorModeInfo) != RT_EOK)
{
LOG_E("Can't feed setting.");
goto fail_sensor_init;
}
ret = rt_device_control(psDevCcap, CCAP_CMD_SET_PIPES, (void *)psViewInfo);
if (ret != RT_EOK)
{
LOG_E("Can't set pipes %s", psGrabberParam->devname_ccap);
goto fail_ccap_init;
}
return psDevCcap;
fail_sensor_init:
if (psDevSensor)
rt_device_close(psDevSensor);
fail_ccap_init:
if (psDevCcap)
rt_device_close(psDevCcap);
exit_ccap_sensor_init:
psDevCcap = psDevSensor = RT_NULL;
return psDevCcap;
}
static void ccap_sensor_fini(rt_device_t psDevCcap, rt_device_t psDevSensor)
{
if (psDevSensor)
rt_device_close(psDevSensor);
if (psDevCcap)
rt_device_close(psDevCcap);
}
#if DEF_ENABLE_PLANAR_PIPE
static int ccap_save_planar_frame(char *name, rt_tick_t timestamp, const void *data, size_t size)
{
int fd;
char szFilename[32];
int wrote_size = 0;
rt_snprintf(szFilename, sizeof(szFilename), "/%s-%08d.yuv", name, timestamp);
fd = open(szFilename, O_WRONLY | O_CREAT);
if (fd < 0)
{
LOG_E("Could not open %s for writing.", szFilename);
goto exit_ccap_save_planar_frame;
}
if ((wrote_size = write(fd, data, size)) != size)
{
LOG_E("Could not write to %s (%d != %d).", szFilename, wrote_size, size);
goto exit_ccap_save_planar_frame;
}
wrote_size = size;
exit_ccap_save_planar_frame:
if (fd >= 0)
close(fd);
return wrote_size;
}
#endif
static void ccap_grabber(void *parameter)
{
rt_err_t ret;
ccap_grabber_param_t psGrabberParam = (ccap_grabber_param_t)parameter;
ccap_grabber_context sGrabberContext;
rt_device_t psDevCcap = RT_NULL;
rt_device_t psDevLcd = RT_NULL;
rt_tick_t last, now;
rt_bool_t bDrawDirect;
rt_memset((void *)&sGrabberContext, 0, sizeof(ccap_grabber_context));
psDevLcd = rt_device_find(psGrabberParam->devname_lcd);
if (psDevLcd == RT_NULL)
{
LOG_E("Can't find %s", psGrabberParam->devname_lcd);
goto exit_ccap_grabber;
}
/* Get LCD Info */
ret = rt_device_control(psDevLcd, RTGRAPHIC_CTRL_GET_INFO, &sGrabberContext.sLcdInfo);
if (ret != RT_EOK)
{
LOG_E("Can't get LCD info %s", psGrabberParam->devname_lcd);
goto exit_ccap_grabber;
}
/* Check panel type */
if (rt_device_control(psDevLcd, RTGRAPHIC_CTRL_PAN_DISPLAY, (void *)sGrabberContext.sLcdInfo.framebuffer) == RT_EOK)
{
/* Sync-type LCD panel, will draw to VRAM directly. */
int pixfmt = RTGRAPHIC_PIXEL_FORMAT_RGB565;
bDrawDirect = RT_TRUE;
rt_device_control(psDevLcd, RTGRAPHIC_CTRL_SET_MODE, (void *)&pixfmt);
}
else
{
/* MPU-type LCD panel, draw to shadow RAM, then flush. */
bDrawDirect = RT_FALSE;
}
ret = rt_device_control(psDevLcd, RTGRAPHIC_CTRL_GET_INFO, &sGrabberContext.sLcdInfo);
if (ret != RT_EOK)
{
LOG_E("Can't get LCD info %s", psGrabberParam->devname_lcd);
goto exit_ccap_grabber;
}
LOG_I("LCD Type: %s-type", bDrawDirect ? "Sync" : "MPU");
LOG_I("LCD Width: %d", sGrabberContext.sLcdInfo.width);
LOG_I("LCD Height: %d", sGrabberContext.sLcdInfo.height);
LOG_I("LCD bpp:%d", sGrabberContext.sLcdInfo.bits_per_pixel);
LOG_I("LCD pixel format:%d", sGrabberContext.sLcdInfo.pixel_format);
LOG_I("LCD frame buffer@0x%08x", sGrabberContext.sLcdInfo.framebuffer);
LOG_I("LCD frame buffer size:%d", sGrabberContext.sLcdInfo.smem_len);
sGrabberContext.semFrameEnd = rt_sem_create(psGrabberParam->devname_ccap, 0, RT_IPC_FLAG_FIFO);
if (sGrabberContext.semFrameEnd == RT_NULL)
{
LOG_E("Can't allocate sem resource %s", psGrabberParam->devname_ccap);
goto exit_ccap_grabber;
}
/* initial ccap & sensor*/
psDevCcap = ccap_sensor_init(&sGrabberContext, psGrabberParam);
if (psDevCcap == RT_NULL)
{
LOG_E("Can't init %s and %s", psGrabberParam->devname_ccap, psGrabberParam->devname_sensor);
goto exit_ccap_grabber;
}
/* Start to capture */
if (rt_device_control(psDevCcap, CCAP_CMD_START_CAPTURE, RT_NULL) != RT_EOK)
{
LOG_E("Can't start %s", psGrabberParam->devname_ccap);
goto exit_ccap_grabber;
}
/* open lcd */
ret = rt_device_open(psDevLcd, 0);
if (ret != RT_EOK)
{
LOG_E("Can't open %s", psGrabberParam->devname_lcd);
goto exit_ccap_grabber;
}
last = now = rt_tick_get();
while (1)
{
if (sGrabberContext.semFrameEnd)
{
rt_sem_take(sGrabberContext.semFrameEnd, RT_WAITING_FOREVER);
}
if (!bDrawDirect)
{
//MPU type
struct rt_device_rect_info sRectInfo;
/* Update fullscreen region. */
sRectInfo.x = 0;
sRectInfo.y = 0;
sRectInfo.height = sGrabberContext.sLcdInfo.height;
sRectInfo.width = sGrabberContext.sLcdInfo.width;
rt_device_control(psDevLcd, RTGRAPHIC_CTRL_RECT_UPDATE, &sRectInfo);
}
else if (!DEF_GRID_VIEW)
{
int i32FBSize = sGrabberContext.sLcdInfo.width * sGrabberContext.sLcdInfo.height * (sGrabberContext.sLcdInfo.bits_per_pixel >> 3);
int i32VRAMPiece = sGrabberContext.sLcdInfo.smem_len / i32FBSize;
ccap_config sCcapConfig = {0};
uint32_t u32BufPtr = (uint32_t)sGrabberContext.sCcapConfig.sPipeInfo_Packet.pu8FarmAddr
+ (sGrabberContext.u32FrameEnd % i32VRAMPiece) * i32FBSize;
/* Pan to valid frame address. */
rt_device_control(psDevLcd, RTGRAPHIC_CTRL_PAN_DISPLAY, (void *)u32BufPtr);
sCcapConfig.sPipeInfo_Packet.pu8FarmAddr = sGrabberContext.sCcapConfig.sPipeInfo_Packet.pu8FarmAddr
+ ((sGrabberContext.u32FrameEnd + 1) % i32VRAMPiece) * i32FBSize ;
#if DEF_ENABLE_PLANAR_PIPE
sCcapConfig.sPipeInfo_Planar.pu8FarmAddr = sGrabberContext.sCcapConfig.sPipeInfo_Planar.pu8FarmAddr;
sCcapConfig.sPipeInfo_Planar.u32Width = sGrabberContext.sCcapConfig.sPipeInfo_Planar.u32Width;
sCcapConfig.sPipeInfo_Planar.u32Height = sGrabberContext.sCcapConfig.sPipeInfo_Planar.u32Height;
sCcapConfig.sPipeInfo_Planar.u32PixFmt = sGrabberContext.sCcapConfig.sPipeInfo_Planar.u32PixFmt;
#endif
rt_device_control(psDevCcap, CCAP_CMD_SET_BASEADDR, &sCcapConfig);
#if DEF_ENABLE_PLANAR_PIPE
{
int OpModeShutter = 1;
/* One-shot mode, trigger next frame */
rt_device_control(psDevCcap, CCAP_CMD_SET_OPMODE, &OpModeShutter);
}
#endif
}
sGrabberContext.u32FrameEnd++;
/* FPS */
now = rt_tick_get();
if ((now - last) >= (DEF_DURATION * 1000))
{
#if DEF_ENABLE_PLANAR_PIPE
{
uint32_t u32Factor = 0;
if (sGrabberContext.sCcapConfig.sPipeInfo_Planar.u32PixFmt == CCAP_PAR_PLNFMT_YUV420)
u32Factor = 3;
else if (sGrabberContext.sCcapConfig.sPipeInfo_Planar.u32PixFmt == CCAP_PAR_PLNFMT_YUV422)
u32Factor = 4;
if (u32Factor > 0)
{
ccap_save_planar_frame(psGrabberParam->thread_name, now, (const void *)sGrabberContext.sCcapConfig.sPipeInfo_Planar.pu8FarmAddr, sGrabberContext.sCcapConfig.sPipeInfo_Planar.u32Width * sGrabberContext.sCcapConfig.sPipeInfo_Planar.u32Height * u32Factor / 2);
}
}
#endif
LOG_I("%s: %d FPS", psGrabberParam->devname_ccap, sGrabberContext.u32FrameEnd / DEF_DURATION);
sGrabberContext.u32FrameEnd = 0;
last = now;
}
}
exit_ccap_grabber:
ccap_sensor_fini(rt_device_find(psGrabberParam->devname_ccap), rt_device_find(psGrabberParam->devname_sensor));
if (psDevLcd != RT_NULL)
rt_device_close(psDevLcd);
return;
}
static void ccap_grabber_create(ccap_grabber_param_t psGrabberParam)
{
rt_thread_t ccap_thread = rt_thread_find(psGrabberParam->thread_name);
if (ccap_thread == RT_NULL)
{
ccap_thread = rt_thread_create(psGrabberParam->thread_name,
ccap_grabber,
psGrabberParam,
THREAD_STACK_SIZE,
THREAD_PRIORITY,
THREAD_TIMESLICE);
if (ccap_thread != RT_NULL)
rt_thread_startup(ccap_thread);
}
}
int ccap_demo(void)
{
#if defined(BSP_USING_CCAP0)
static ccap_grabber_param ccap0_grabber_param = {"grab0", "ccap0", "sensor0", "lcd"};
ccap_grabber_create(&ccap0_grabber_param);
#endif
#if defined(BSP_USING_CCAP1)
static ccap_grabber_param ccap1_grabber_param = {"grab1", "ccap1", "sensor1", "lcd"};
ccap_grabber_create(&ccap1_grabber_param);
#endif
return 0;
}
MSH_CMD_EXPORT(ccap_demo, camera capture demo);
//INIT_ENV_EXPORT(ccap_demo);
#endif