1 /**************************************************************************//**
2 *
3 * @copyright (C) 2019 Nuvoton Technology Corp. All rights reserved.
4 *
5 * SPDX-License-Identifier: Apache-2.0
6 *
7 * Change Logs:
8 * Date            Author       Notes
9 * 2022-8-16       Wayne        First version
10 *
11 ******************************************************************************/
12 
13 #include <rtthread.h>
14 
15 #if defined(BSP_USING_CCAP)
16 
17 #include "drv_ccap.h"
18 #include <dfs_posix.h>
19 
20 #define DBG_ENABLE
21 #define DBG_LEVEL DBG_LOG
22 #define DBG_SECTION_NAME  "ccap.demo"
23 #define DBG_COLOR
24 #include <rtdbg.h>
25 
26 #define THREAD_PRIORITY   5
27 #define THREAD_STACK_SIZE 4096
28 #define THREAD_TIMESLICE  5
29 
30 #define DEF_CROP_PACKET_RECT
31 #define DEF_ENABLE_PLANAR_PIPE  0
32 
33 #define DEF_DURATION         10
34 #if defined(BSP_USING_CCAP0) && defined(BSP_USING_CCAP1)
35     #define DEF_GRID_VIEW        1
36 #elif defined(BSP_USING_CCAP0) || defined(BSP_USING_CCAP1)
37     #define DEF_GRID_VIEW        0
38 #endif
39 
40 typedef struct
41 {
42     char *thread_name;
43     char *devname_ccap;
44     char *devname_sensor;
45     char *devname_lcd;
46 } ccap_grabber_param;
47 typedef ccap_grabber_param *ccap_grabber_param_t;
48 
49 typedef struct
50 {
51     ccap_config    sCcapConfig;
52     struct rt_device_graphic_info sLcdInfo;
53     uint32_t       u32CurFBPointer;
54     uint32_t       u32FrameEnd;
55     rt_sem_t       semFrameEnd;
56 } ccap_grabber_context;
57 typedef ccap_grabber_context *ccap_grabber_context_t;
58 
nu_ccap_event_hook(void * pvData,uint32_t u32EvtMask)59 static void nu_ccap_event_hook(void *pvData, uint32_t u32EvtMask)
60 {
61     ccap_grabber_context_t psGrabberContext = (ccap_grabber_context_t)pvData;
62 
63     if (u32EvtMask & NU_CCAP_FRAME_END)
64     {
65         rt_sem_release(psGrabberContext->semFrameEnd);
66     }
67 
68     if (u32EvtMask & NU_CCAP_ADDRESS_MATCH)
69     {
70         LOG_I("Address matched");
71     }
72 
73     if (u32EvtMask & NU_CCAP_MEMORY_ERROR)
74     {
75         LOG_E("Access memory error");
76     }
77 }
78 
ccap_sensor_init(ccap_grabber_context_t psGrabberContext,ccap_grabber_param_t psGrabberParam)79 static rt_device_t ccap_sensor_init(ccap_grabber_context_t psGrabberContext, ccap_grabber_param_t psGrabberParam)
80 {
81     rt_err_t ret;
82     ccap_view_info_t psViewInfo;
83     sensor_mode_info *psSensorModeInfo;
84     rt_device_t psDevSensor = RT_NULL;
85     rt_device_t psDevCcap = RT_NULL;
86     struct rt_device_graphic_info *psLcdInfo = &psGrabberContext->sLcdInfo;
87     ccap_config_t    psCcapConfig = &psGrabberContext->sCcapConfig;
88 
89     psDevCcap = rt_device_find(psGrabberParam->devname_ccap);
90     if (psDevCcap == RT_NULL)
91     {
92         LOG_E("Can't find %s", psGrabberParam->devname_ccap);
93         goto exit_ccap_sensor_init;
94     }
95 
96     psDevSensor = rt_device_find(psGrabberParam->devname_sensor);
97     if (psDevSensor == RT_NULL)
98     {
99         LOG_E("Can't find %s", psGrabberParam->devname_sensor);
100         goto exit_ccap_sensor_init;
101     }
102 
103     /* Packet pipe for preview */
104     if (DEF_GRID_VIEW)
105     {
106         psCcapConfig->sPipeInfo_Packet.u32Width    = psLcdInfo->width / 2;
107         psCcapConfig->sPipeInfo_Packet.u32Height   = psLcdInfo->height / 2;
108         psCcapConfig->sPipeInfo_Packet.u32PixFmt   = (psLcdInfo->pixel_format == RTGRAPHIC_PIXEL_FORMAT_RGB565) ? CCAP_PAR_OUTFMT_RGB565 : 0;
109         psCcapConfig->u32Stride_Packet             = psLcdInfo->width;
110         if (!rt_strcmp(psGrabberParam->devname_ccap, "ccap1"))
111             psCcapConfig->sPipeInfo_Packet.pu8FarmAddr = psLcdInfo->framebuffer + (psCcapConfig->sPipeInfo_Packet.u32Width * 2);
112         else
113             psCcapConfig->sPipeInfo_Packet.pu8FarmAddr = psLcdInfo->framebuffer;
114     }
115     else
116     {
117         psCcapConfig->sPipeInfo_Packet.pu8FarmAddr = psLcdInfo->framebuffer;
118         psCcapConfig->sPipeInfo_Packet.u32Height   = psLcdInfo->height;
119         psCcapConfig->sPipeInfo_Packet.u32Width    = psLcdInfo->width;
120         psCcapConfig->sPipeInfo_Packet.u32PixFmt   = (psLcdInfo->pixel_format == RTGRAPHIC_PIXEL_FORMAT_RGB565) ? CCAP_PAR_OUTFMT_RGB565 : 0;
121         psCcapConfig->u32Stride_Packet             = psLcdInfo->width;
122     }
123 
124     /* Planar pipe for encoding */
125 #if DEF_ENABLE_PLANAR_PIPE
126     psCcapConfig->sPipeInfo_Planar.u32Width    = psLcdInfo->width / 2;
127     psCcapConfig->sPipeInfo_Planar.u32Height   = psLcdInfo->height / 2;
128     psCcapConfig->sPipeInfo_Planar.pu8FarmAddr = rt_malloc_align(psCcapConfig->sPipeInfo_Planar.u32Height * psCcapConfig->sPipeInfo_Planar.u32Width * 2, 32);
129     psCcapConfig->sPipeInfo_Planar.u32PixFmt   = CCAP_PAR_PLNFMT_YUV420; //CCAP_PAR_PLNFMT_YUV422;
130     psCcapConfig->u32Stride_Planar             = psCcapConfig->sPipeInfo_Planar.u32Width;
131 
132     if (psCcapConfig->sPipeInfo_Planar.pu8FarmAddr == RT_NULL)
133     {
134         psCcapConfig->sPipeInfo_Planar.u32Height = 0;
135         psCcapConfig->sPipeInfo_Planar.u32Width  = 0;
136         psCcapConfig->sPipeInfo_Planar.u32PixFmt = 0;
137         psCcapConfig->u32Stride_Planar           = 0;
138     }
139 
140     LOG_I("Planar.FarmAddr@0x%08X", psCcapConfig->sPipeInfo_Planar.pu8FarmAddr);
141     LOG_I("Planar.FarmWidth: %d", psCcapConfig->sPipeInfo_Planar.u32Width);
142     LOG_I("Planar.FarmHeight: %d", psCcapConfig->sPipeInfo_Planar.u32Height);
143 #endif
144 
145     /* open CCAP */
146     ret = rt_device_open(psDevCcap, 0);
147     if (ret != RT_EOK)
148     {
149         LOG_E("Can't open %s", psGrabberParam->devname_ccap);
150         goto exit_ccap_sensor_init;
151     }
152 
153     /* Find suit mode for packet pipe */
154     if (psCcapConfig->sPipeInfo_Packet.pu8FarmAddr != RT_NULL)
155     {
156         /* Check view window of packet pipe */
157         psViewInfo = &psCcapConfig->sPipeInfo_Packet;
158 
159         if ((rt_device_control(psDevSensor, CCAP_SENSOR_CMD_GET_SUIT_MODE, (void *)&psViewInfo) != RT_EOK)
160                 || (psViewInfo == RT_NULL))
161         {
162             LOG_E("Can't get suit mode for packet.");
163             goto fail_ccap_init;
164         }
165     }
166 
167     /* Find suit mode for planner pipe */
168     if (psCcapConfig->sPipeInfo_Planar.pu8FarmAddr != RT_NULL)
169     {
170         int recheck = 1;
171 
172         if (psViewInfo != RT_NULL)
173         {
174             if ((psCcapConfig->sPipeInfo_Planar.u32Width <= psViewInfo->u32Width) ||
175                     (psCcapConfig->sPipeInfo_Planar.u32Height <= psViewInfo->u32Height))
176                 recheck = 0;
177         }
178 
179         if (recheck)
180         {
181             /* Check view window of planner pipe */
182             psViewInfo = &psCcapConfig->sPipeInfo_Planar;
183 
184             /* Find suit mode */
185             if ((rt_device_control(psDevSensor, CCAP_SENSOR_CMD_GET_SUIT_MODE, (void *)&psViewInfo) != RT_EOK)
186                     || (psViewInfo == RT_NULL))
187             {
188                 LOG_E("Can't get suit mode for planner.");
189                 goto exit_ccap_sensor_init;
190             }
191         }
192     }
193 
194 #if defined(DEF_CROP_PACKET_RECT)
195     /* Set cropping rectangle */
196     if (psViewInfo->u32Width >= psCcapConfig->sPipeInfo_Packet.u32Width)
197     {
198         /* sensor.width >= preview.width */
199         psCcapConfig->sRectCropping.x = (psViewInfo->u32Width - psCcapConfig->sPipeInfo_Packet.u32Width) / 2;
200         psCcapConfig->sRectCropping.width  = psCcapConfig->sPipeInfo_Packet.u32Width;
201     }
202     else
203     {
204         /* sensor.width < preview.width */
205         psCcapConfig->sRectCropping.x = 0;
206         psCcapConfig->sRectCropping.width  = psViewInfo->u32Width;
207     }
208 
209     if (psViewInfo->u32Height >= psCcapConfig->sPipeInfo_Packet.u32Height)
210     {
211         /* sensor.height >= preview.height */
212         psCcapConfig->sRectCropping.y = (psViewInfo->u32Height - psCcapConfig->sPipeInfo_Packet.u32Height) / 2;
213         psCcapConfig->sRectCropping.height = psCcapConfig->sPipeInfo_Packet.u32Height;
214     }
215     else
216     {
217         /* sensor.height < preview.height */
218         psCcapConfig->sRectCropping.y = 0;
219         psCcapConfig->sRectCropping.height = psViewInfo->u32Height;
220     }
221 #else
222     /* Set cropping rectangle */
223     psCcapConfig->sRectCropping.x      = 0;
224     psCcapConfig->sRectCropping.y      = 0;
225     psCcapConfig->sRectCropping.width  = psViewInfo->u32Width;
226     psCcapConfig->sRectCropping.height = psViewInfo->u32Height;
227 #endif
228 
229     /* ISR Hook */
230     psCcapConfig->pfnEvHndler = nu_ccap_event_hook;
231     psCcapConfig->pvData      = (void *)psGrabberContext;
232 
233     /* Get Suitable mode. */
234     psSensorModeInfo = (sensor_mode_info *)psViewInfo;
235 
236     /* Feed CCAP configuration */
237     ret = rt_device_control(psDevCcap, CCAP_CMD_CONFIG, (void *)psCcapConfig);
238     if (ret != RT_EOK)
239     {
240         LOG_E("Can't feed configuration %s", psGrabberParam->devname_ccap);
241         goto fail_ccap_init;
242     }
243 
244     {
245         int i32SenClk = psSensorModeInfo->u32SenClk;
246         if (DEF_GRID_VIEW && DEF_ENABLE_PLANAR_PIPE)
247             i32SenClk = 45000000; /* Bandwidth limitation: Slow down sensor clock */
248 
249         /* speed up pixel clock */
250         if (rt_device_control(psDevCcap, CCAP_CMD_SET_SENCLK, (void *)&i32SenClk) != RT_EOK)
251         {
252             LOG_E("Can't feed setting.");
253             goto fail_ccap_init;
254         }
255     }
256 
257     /* Initial CCAP sensor */
258     if (rt_device_open(psDevSensor, 0) != RT_EOK)
259     {
260         LOG_E("Can't open sensor.");
261         goto fail_sensor_init;
262     }
263 
264     /* Feed settings to sensor */
265     if (rt_device_control(psDevSensor, CCAP_SENSOR_CMD_SET_MODE, (void *)psSensorModeInfo) != RT_EOK)
266     {
267         LOG_E("Can't feed setting.");
268         goto fail_sensor_init;
269     }
270 
271     ret = rt_device_control(psDevCcap, CCAP_CMD_SET_PIPES, (void *)psViewInfo);
272     if (ret != RT_EOK)
273     {
274         LOG_E("Can't set pipes %s", psGrabberParam->devname_ccap);
275         goto fail_ccap_init;
276     }
277 
278     return psDevCcap;
279 
280 fail_sensor_init:
281 
282     if (psDevSensor)
283         rt_device_close(psDevSensor);
284 
285 fail_ccap_init:
286 
287     if (psDevCcap)
288         rt_device_close(psDevCcap);
289 
290 exit_ccap_sensor_init:
291 
292     psDevCcap = psDevSensor = RT_NULL;
293 
294     return psDevCcap;
295 }
296 
ccap_sensor_fini(rt_device_t psDevCcap,rt_device_t psDevSensor)297 static void ccap_sensor_fini(rt_device_t psDevCcap, rt_device_t psDevSensor)
298 {
299     if (psDevSensor)
300         rt_device_close(psDevSensor);
301 
302     if (psDevCcap)
303         rt_device_close(psDevCcap);
304 }
305 
306 #if DEF_ENABLE_PLANAR_PIPE
ccap_save_planar_frame(char * name,rt_tick_t timestamp,const void * data,size_t size)307 static int ccap_save_planar_frame(char *name, rt_tick_t timestamp, const void *data, size_t size)
308 {
309     int fd;
310     char szFilename[32];
311     int wrote_size = 0;
312 
313     rt_snprintf(szFilename, sizeof(szFilename), "/%s-%08d.yuv", name, timestamp);
314     fd = open(szFilename, O_WRONLY | O_CREAT);
315     if (fd < 0)
316     {
317         LOG_E("Could not open %s for writing.", szFilename);
318         goto exit_ccap_save_planar_frame;
319     }
320 
321     if ((wrote_size = write(fd, data, size)) != size)
322     {
323         LOG_E("Could not write to %s (%d != %d).", szFilename, wrote_size, size);
324         goto exit_ccap_save_planar_frame;
325     }
326 
327     wrote_size = size;
328 
329 exit_ccap_save_planar_frame:
330 
331     if (fd >= 0)
332         close(fd);
333 
334     return wrote_size;
335 }
336 #endif
337 
ccap_grabber(void * parameter)338 static void ccap_grabber(void *parameter)
339 {
340     rt_err_t ret;
341     ccap_grabber_param_t psGrabberParam = (ccap_grabber_param_t)parameter;
342     ccap_grabber_context sGrabberContext;
343 
344     rt_device_t psDevCcap = RT_NULL;
345     rt_device_t psDevLcd = RT_NULL;
346 
347     rt_tick_t last, now;
348     rt_bool_t bDrawDirect;
349 
350     rt_memset((void *)&sGrabberContext, 0, sizeof(ccap_grabber_context));
351 
352     psDevLcd = rt_device_find(psGrabberParam->devname_lcd);
353     if (psDevLcd == RT_NULL)
354     {
355         LOG_E("Can't find %s", psGrabberParam->devname_lcd);
356         goto exit_ccap_grabber;
357     }
358 
359     /* Get LCD Info */
360     ret = rt_device_control(psDevLcd, RTGRAPHIC_CTRL_GET_INFO, &sGrabberContext.sLcdInfo);
361     if (ret != RT_EOK)
362     {
363         LOG_E("Can't get LCD info %s", psGrabberParam->devname_lcd);
364         goto exit_ccap_grabber;
365     }
366 
367     /* Check panel type */
368     if (rt_device_control(psDevLcd, RTGRAPHIC_CTRL_PAN_DISPLAY, (void *)sGrabberContext.sLcdInfo.framebuffer) == RT_EOK)
369     {
370         /* Sync-type LCD panel, will draw to VRAM directly. */
371         int pixfmt = RTGRAPHIC_PIXEL_FORMAT_RGB565;
372         bDrawDirect = RT_TRUE;
373         rt_device_control(psDevLcd, RTGRAPHIC_CTRL_SET_MODE, (void *)&pixfmt);
374     }
375     else
376     {
377         /* MPU-type LCD panel, draw to shadow RAM, then flush. */
378         bDrawDirect = RT_FALSE;
379     }
380 
381     ret = rt_device_control(psDevLcd, RTGRAPHIC_CTRL_GET_INFO, &sGrabberContext.sLcdInfo);
382     if (ret != RT_EOK)
383     {
384         LOG_E("Can't get LCD info %s", psGrabberParam->devname_lcd);
385         goto exit_ccap_grabber;
386     }
387 
388     LOG_I("LCD Type: %s-type",   bDrawDirect ? "Sync" : "MPU");
389     LOG_I("LCD Width: %d",   sGrabberContext.sLcdInfo.width);
390     LOG_I("LCD Height: %d",  sGrabberContext.sLcdInfo.height);
391     LOG_I("LCD bpp:%d",   sGrabberContext.sLcdInfo.bits_per_pixel);
392     LOG_I("LCD pixel format:%d",   sGrabberContext.sLcdInfo.pixel_format);
393     LOG_I("LCD frame buffer@0x%08x",   sGrabberContext.sLcdInfo.framebuffer);
394     LOG_I("LCD frame buffer size:%d",   sGrabberContext.sLcdInfo.smem_len);
395 
396     sGrabberContext.semFrameEnd = rt_sem_create(psGrabberParam->devname_ccap, 0, RT_IPC_FLAG_FIFO);
397     if (sGrabberContext.semFrameEnd == RT_NULL)
398     {
399         LOG_E("Can't allocate sem resource %s", psGrabberParam->devname_ccap);
400         goto exit_ccap_grabber;
401     }
402 
403     /* initial ccap & sensor*/
404     psDevCcap = ccap_sensor_init(&sGrabberContext, psGrabberParam);
405     if (psDevCcap == RT_NULL)
406     {
407         LOG_E("Can't init %s and %s", psGrabberParam->devname_ccap, psGrabberParam->devname_sensor);
408         goto exit_ccap_grabber;
409     }
410 
411     /* Start to capture */
412     if (rt_device_control(psDevCcap, CCAP_CMD_START_CAPTURE, RT_NULL) != RT_EOK)
413     {
414         LOG_E("Can't start %s", psGrabberParam->devname_ccap);
415         goto exit_ccap_grabber;
416     }
417 
418     /* open lcd */
419     ret = rt_device_open(psDevLcd, 0);
420     if (ret != RT_EOK)
421     {
422         LOG_E("Can't open %s", psGrabberParam->devname_lcd);
423         goto exit_ccap_grabber;
424     }
425 
426     last = now = rt_tick_get();
427     while (1)
428     {
429         if (sGrabberContext.semFrameEnd)
430         {
431             rt_sem_take(sGrabberContext.semFrameEnd, RT_WAITING_FOREVER);
432         }
433 
434         if (!bDrawDirect)
435         {
436             //MPU type
437             struct rt_device_rect_info sRectInfo;
438 
439             /* Update fullscreen region. */
440             sRectInfo.x = 0;
441             sRectInfo.y = 0;
442             sRectInfo.height = sGrabberContext.sLcdInfo.height;
443             sRectInfo.width = sGrabberContext.sLcdInfo.width;
444 
445             rt_device_control(psDevLcd, RTGRAPHIC_CTRL_RECT_UPDATE, &sRectInfo);
446         }
447         else if (!DEF_GRID_VIEW)
448         {
449             int i32FBSize = sGrabberContext.sLcdInfo.width * sGrabberContext.sLcdInfo.height * (sGrabberContext.sLcdInfo.bits_per_pixel >> 3);
450             int i32VRAMPiece = sGrabberContext.sLcdInfo.smem_len / i32FBSize;
451             ccap_config sCcapConfig = {0};
452 
453             uint32_t u32BufPtr = (uint32_t)sGrabberContext.sCcapConfig.sPipeInfo_Packet.pu8FarmAddr
454                                  + (sGrabberContext.u32FrameEnd % i32VRAMPiece) * i32FBSize;
455 
456             /* Pan to valid frame address. */
457             rt_device_control(psDevLcd, RTGRAPHIC_CTRL_PAN_DISPLAY, (void *)u32BufPtr);
458 
459             sCcapConfig.sPipeInfo_Packet.pu8FarmAddr = sGrabberContext.sCcapConfig.sPipeInfo_Packet.pu8FarmAddr
460                     + ((sGrabberContext.u32FrameEnd + 1) % i32VRAMPiece) * i32FBSize ;
461 
462 #if DEF_ENABLE_PLANAR_PIPE
463             sCcapConfig.sPipeInfo_Planar.pu8FarmAddr = sGrabberContext.sCcapConfig.sPipeInfo_Planar.pu8FarmAddr;
464             sCcapConfig.sPipeInfo_Planar.u32Width = sGrabberContext.sCcapConfig.sPipeInfo_Planar.u32Width;
465             sCcapConfig.sPipeInfo_Planar.u32Height = sGrabberContext.sCcapConfig.sPipeInfo_Planar.u32Height;
466             sCcapConfig.sPipeInfo_Planar.u32PixFmt = sGrabberContext.sCcapConfig.sPipeInfo_Planar.u32PixFmt;
467 #endif
468 
469             rt_device_control(psDevCcap, CCAP_CMD_SET_BASEADDR, &sCcapConfig);
470 
471 #if DEF_ENABLE_PLANAR_PIPE
472             {
473                 int OpModeShutter = 1;
474                 /* One-shot mode, trigger next frame */
475                 rt_device_control(psDevCcap, CCAP_CMD_SET_OPMODE, &OpModeShutter);
476             }
477 #endif
478         }
479 
480         sGrabberContext.u32FrameEnd++;
481 
482         /* FPS */
483         now = rt_tick_get();
484         if ((now - last) >= (DEF_DURATION * 1000))
485         {
486 #if DEF_ENABLE_PLANAR_PIPE
487             {
488                 uint32_t u32Factor = 0;
489                 if (sGrabberContext.sCcapConfig.sPipeInfo_Planar.u32PixFmt == CCAP_PAR_PLNFMT_YUV420)
490                     u32Factor = 3;
491                 else if (sGrabberContext.sCcapConfig.sPipeInfo_Planar.u32PixFmt == CCAP_PAR_PLNFMT_YUV422)
492                     u32Factor = 4;
493 
494                 if (u32Factor > 0)
495                 {
496                     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);
497                 }
498             }
499 #endif
500             LOG_I("%s: %d FPS", psGrabberParam->devname_ccap, sGrabberContext.u32FrameEnd / DEF_DURATION);
501             sGrabberContext.u32FrameEnd = 0;
502             last = now;
503         }
504     }
505 
506 exit_ccap_grabber:
507 
508     ccap_sensor_fini(rt_device_find(psGrabberParam->devname_ccap), rt_device_find(psGrabberParam->devname_sensor));
509 
510     if (psDevLcd != RT_NULL)
511         rt_device_close(psDevLcd);
512 
513     return;
514 }
515 
ccap_grabber_create(ccap_grabber_param_t psGrabberParam)516 static void ccap_grabber_create(ccap_grabber_param_t psGrabberParam)
517 {
518     rt_thread_t ccap_thread = rt_thread_find(psGrabberParam->thread_name);
519     if (ccap_thread == RT_NULL)
520     {
521         ccap_thread = rt_thread_create(psGrabberParam->thread_name,
522                                        ccap_grabber,
523                                        psGrabberParam,
524                                        THREAD_STACK_SIZE,
525                                        THREAD_PRIORITY,
526                                        THREAD_TIMESLICE);
527 
528         if (ccap_thread != RT_NULL)
529             rt_thread_startup(ccap_thread);
530     }
531 }
532 
ccap_demo(void)533 int ccap_demo(void)
534 {
535 #if defined(BSP_USING_CCAP0)
536     static ccap_grabber_param ccap0_grabber_param = {"grab0", "ccap0", "sensor0", "lcd"};
537     ccap_grabber_create(&ccap0_grabber_param);
538 #endif
539 #if defined(BSP_USING_CCAP1)
540     static ccap_grabber_param ccap1_grabber_param = {"grab1", "ccap1", "sensor1", "lcd"};
541     ccap_grabber_create(&ccap1_grabber_param);
542 #endif
543     return 0;
544 }
545 MSH_CMD_EXPORT(ccap_demo, camera capture demo);
546 //INIT_ENV_EXPORT(ccap_demo);
547 #endif
548