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