1 /*---------------------------------------------------------------------------*
2   Project:  TwlSDK - demos.TWL - camera - camera-1
3   File:     main.c
4 
5   Copyright 2007-2008 Nintendo. All rights reserved.
6 
7   These coded instructions, statements, and computer programs contain
8   proprietary information of Nintendo of America Inc. and/or Nintendo
9   Company Ltd., and are protected by Federal copyright law. They may
10   not be disclosed to third parties or copied or duplicated in any form,
11   in whole or in part, without the prior written consent of Nintendo.
12 
13   $Date:: 2009-06-15#$
14   $Rev: 10750 $
15   $Author: kitase_hirotake $
16  *---------------------------------------------------------------------------*/
17 
18 #include <twl.h>
19 #include <twl/camera.h>
20 #include "DEMOBitmap.h"
21 
22 #define NDMA_NO      1           // The NDMA number (0 to 3) to use
23 #define WIDTH       256         // Image width
24 #define HEIGHT      192         // Image height
25 
26 #define LINES_AT_ONCE   CAMERA_GET_MAX_LINES(WIDTH)     // Number of lines transferred in one cycle
27 #define BYTES_PER_LINE  CAMERA_GET_LINE_BYTES(WIDTH)    // Number of bytes in one line's transfer
28 
29 static void VBlankIntr(void);
30 static BOOL CameraInit(void);
31 static void CameraIntrVsync(CAMERAResult result);
32 static void CameraIntrError(CAMERAResult result);
33 static void CameraIntrReboot(CAMERAResult result);
34 static void CameraDmaRecvIntr(void* arg);
35 
36 static CAMERASelect current;
37 static CAMERAFlip flipIn, flipOut;
38 static BOOL flipFlag = FALSE;
39 static BOOL switchFlag = FALSE;
40 static u32 stabilizedCount;
41 
42 static int wp;  // Buffer while capturing data from camera
43 static int rp;  // Buffer most recently copied to VRAM
44 static BOOL wp_pending; // Data capture was cancelled (recapture to same buffer)
45 static u16 buffer[2][WIDTH*HEIGHT] ATTRIBUTE_ALIGN(32);
PendingCapture(void)46 static void PendingCapture(void)
47 {
48     wp_pending = TRUE;
49 }
50 
51 // Character display
PutString(char * format,...)52 static void PutString( char *format, ... )
53 {
54     u16             *dest = G2_GetBG1ScrPtr();
55     char            temp[32+1];
56     int             i;
57     va_list         va;
58 
59     va_start(va, format);
60     (void)OS_VSNPrintf(temp, sizeof(temp), format, va);
61     va_end(va);
62 
63     for (i = 0; i < 32 && temp[i]; i++)
64     {
65         dest[i] = (u16)((u8)temp[i] | (1 << 12));
66     }
67 }
68 
69 /*---------------------------------------------------------------------------*
70   Name:         TwlMain
71 
72   Description:  Main
73 
74   Arguments:    None.
75 
76   Returns:      None.
77  *---------------------------------------------------------------------------*/
TwlMain()78 void TwlMain()
79 {
80     CAMERAResult result;
81 
82     // Initialization
83     OS_Init();
84     OS_InitThread();
85     GX_Init();
86     OS_InitTick();
87     OS_InitAlarm();
88 
89     // DMA is not used in GX (the old DMA conflicts with camera DMA)
90     (void)GX_SetDefaultDMA(GX_DMA_NOT_USE);
91 
92     // Clear VRAM
93     GX_SetBankForLCDC(GX_VRAM_LCDC_A);
94     GX_SetBankForLCDC(GX_VRAM_LCDC_B);
95     MI_CpuClearFast((void*)HW_LCDC_VRAM_A, 128 * 1024);
96     MI_CpuClearFast((void*)HW_LCDC_VRAM_B, 128 * 1024);
97 
98     // Direct bitmap display mode and text display
99     GX_SetBankForBG(GX_VRAM_BG_256_AB);         // Allocate VRAM-A, B banks to BG
100     GX_SetGraphicsMode(GX_DISPMODE_GRAPHICS, GX_BGMODE_4, GX_BG0_AS_2D);
101     GX_SetVisiblePlane(GX_PLANEMASK_BG1 | GX_PLANEMASK_BG3);
102 
103     G2_SetBG1Control(GX_BG_SCRSIZE_TEXT_256x256, GX_BG_COLORMODE_16,
104                      GX_BG_SCRBASE_0x0000, GX_BG_CHARBASE_0x04000, GX_BG_EXTPLTT_01);
105     G2_SetBG1Priority(1);
106     G2_BG1Mosaic(FALSE);
107 
108     G2_SetBG3ControlDCBmp(GX_BG_SCRSIZE_DCBMP_256x256, GX_BG_AREAOVER_XLU, GX_BG_BMPSCRBASE_0x20000);
109     G2_SetBG3Priority(3);
110     G2_BG3Mosaic(FALSE);
111 
112     // Load text
113     {
114         static const GXRgb pal[16] = { GX_RGB(0, 0, 0), GX_RGB(31, 31, 31), };
115         GX_LoadBG1Char(DEMOAsciiChr, 0x00000, sizeof(DEMOAsciiChr));
116         GX_LoadBGPltt(pal, 0x0000, sizeof(pal));
117     }
118     wp = 0;
119     rp = 1;
120     wp_pending = TRUE;
121     stabilizedCount = 0;
122 
123     // V-Blank interrupt settings
124     OS_SetIrqFunction(OS_IE_V_BLANK, VBlankIntr);
125     (void)OS_EnableIrqMask(OS_IE_V_BLANK);
126     (void)OS_EnableIrq();
127     (void)GX_VBlankIntr(TRUE);
128     (void)OS_EnableInterrupts();
129 
130     OS_WaitVBlankIntr();
131     GX_DispOn();
132 
133     // Initialize camera
134     current = CAMERA_SELECT_IN;
135     flipIn  = CAMERA_FLIP_HORIZONTAL;
136     flipOut = CAMERA_FLIP_NONE;
137     flipFlag = FALSE;
138 
139     (void)CameraInit();
140 
141     CAMERA_SetOutputFormat(CAMERA_OUTPUT_RGB);
142     CAMERA_SetTransferLines(CAMERA_GET_MAX_LINES(WIDTH));
143 
144     // Configure DMA interrupt
145     (void)OS_EnableIrqMask(OS_IE_NDMA1);
146 
147     // Camera VSYNC interrupt callback
148     CAMERA_SetVsyncCallback(CameraIntrVsync);
149 
150     // Camera error interrupt callback
151     CAMERA_SetBufferErrorCallback(CameraIntrError);
152 
153     // Camera restart completion callback
154     CAMERA_SetRebootCallback(CameraIntrReboot);
155 
156     // Prepare the DMA to use for the capture
157     MI_StopNDma(NDMA_NO);
158     CAMERA_DmaRecvAsync(NDMA_NO, buffer[wp], CAMERA_GetBytesAtOnce(WIDTH), CAMERA_GET_FRAME_BYTES(WIDTH, HEIGHT), CameraDmaRecvIntr, NULL);
159 
160     // Camera start
161     CAMERA_ClearBuffer();
162     CAMERA_StartCapture();
163     OS_TPrintf("Camera is shooting a movie...\n");
164 
165     while (1)
166     {
167         u16 pad;
168         u16 trg;
169         static u16 old = 0xffff;    // Ignore the trigger by first data
170         static int camera_end_flag = FALSE;
171         static int camera_stop_capture_flag = FALSE;
172         static int camera_notactivate_flag = FALSE;
173 
174         OS_WaitVBlankIntr();
175 
176         pad = PAD_Read();
177         trg = (u16)(pad & ~old);
178         old = pad;
179 
180         if (camera_end_flag == FALSE)
181         {
182             if (trg & PAD_BUTTON_Y)
183             {
184                 flipFlag = TRUE;
185             }
186             if (trg & PAD_BUTTON_X)
187             {
188                 switchFlag = TRUE;
189             }
190             if (camera_stop_capture_flag == FALSE)
191             {
192                 if (trg & PAD_BUTTON_B)
193                 {
194                     static BOOL standby = FALSE;
195                     standby ^= 1;
196                     if (standby)
197                     {
198                         // Temporarily halt use of the camera. If the camera will be used again in the next cycle, all you need to do is set "Activate NONE", instead of calling the CAMERA_End function.
199                         // If you set Activate to NONE prior to StopCapture, there is a risk that IsBusy will be TRUE until the next time you set "Activate ON".
200                         CAMERA_StopCapture();
201                         while(CAMERA_IsBusy())
202                         {
203                         }
204                         MI_StopNDma(NDMA_NO);
205                         result = CAMERA_I2CActivate(CAMERA_SELECT_NONE);
206                         if(result == CAMERA_RESULT_FATAL_ERROR)
207                             OS_Panic("CAMERA FATAL ERROR\n");
208 
209                         if(result == CAMERA_RESULT_ILLEGAL_STATUS)
210                             continue;
211 
212                         camera_notactivate_flag = TRUE;
213                     }
214                     else
215                     {
216                         result = CAMERA_I2CActivate(current);
217                         if(result == CAMERA_RESULT_FATAL_ERROR)
218                             OS_Panic("CAMERA FALTAL ERROR\n");
219                         if(result == CAMERA_RESULT_ILLEGAL_STATUS)
220                             continue;
221                         stabilizedCount = 0;
222 
223                         CAMERA_DmaRecvAsync(NDMA_NO, buffer[wp], CAMERA_GetBytesAtOnce(WIDTH), CAMERA_GET_FRAME_BYTES(WIDTH, HEIGHT), CameraDmaRecvIntr, NULL);
224                         CAMERA_ClearBuffer();
225                         CAMERA_StartCapture();
226                         while (CAMERA_IsBusy() == FALSE)
227                         {
228                         }
229                         camera_notactivate_flag = FALSE;
230                     }
231                     OS_TPrintf("%s\n", result == CAMERA_RESULT_SUCCESS ? "SUCCESS" : "FAILED");
232                 }
233             }
234             if (camera_notactivate_flag == FALSE)
235             {
236                 if (trg & PAD_BUTTON_START)
237                 {
238                     if (CAMERA_IsBusy())
239                     {
240                         // If you want to change parameters, you need to first start up the camera and then stop capturing. This makes the CAMERA_IsBusy function return FALSE.
241                         //
242                         // If you are about to stop using the camera, then it's all right to stop capturing with the camera in standby mode.
243                         result = CAMERA_I2CActivate(current);
244                         if(result == CAMERA_RESULT_FATAL_ERROR)
245                             OS_Panic("CAMERA FATAL ERROR\n");
246                         if(result == CAMERA_RESULT_ILLEGAL_STATUS)
247                             continue;
248 
249                         OS_TPrintf("%s\n", result == CAMERA_RESULT_SUCCESS ? "SUCCESS" : "FAILED");
250                         OS_TPrintf("call CAMERA_Stop()... ");
251                         CAMERA_StopCapture();
252                         while (CAMERA_IsBusy())
253                         {
254                         }
255                         OS_TPrintf("Camera was stopped.\n");
256                         MI_StopNDma(NDMA_NO);
257                         stabilizedCount = 0;
258                         camera_stop_capture_flag = TRUE;
259                     }
260                     else
261                     {
262                         CAMERA_DmaRecvAsync(NDMA_NO, buffer[wp], CAMERA_GetBytesAtOnce(WIDTH), CAMERA_GET_FRAME_BYTES(WIDTH, HEIGHT), CameraDmaRecvIntr, NULL);
263                         CAMERA_ClearBuffer();
264                         CAMERA_StartCapture();
265                         OS_TPrintf("Camera is shooting a movie...\n");
266                         while (CAMERA_IsBusy() == FALSE)
267                         {
268                         }
269                         camera_stop_capture_flag = FALSE;
270                     }
271                 }
272             }
273         }
274         // If you are not going to use the camera, just call CAMERA_I2CActivate(CAMERA_SELECT_NONE)
275         if (trg & PAD_BUTTON_SELECT)
276         {
277             if (camera_end_flag == FALSE)
278             {
279                 OS_TPrintf("call CAMERA_End()... ");
280                 CAMERA_StopCapture();
281                 while(CAMERA_IsBusy())
282                 {
283                 }
284                 MI_StopNDma(NDMA_NO);
285                 CAMERA_End();
286                 OS_TPrintf("Done.\n");
287                 camera_stop_capture_flag = TRUE;
288                 camera_notactivate_flag = TRUE;
289                 camera_end_flag = TRUE;
290             }
291             else
292             {
293                 if(CameraInit() == TRUE)
294                 {
295                     // Reregister the various callbacks that were registered at the first Init
296                     CAMERA_SetVsyncCallback(CameraIntrVsync);
297                     CAMERA_SetBufferErrorCallback(CameraIntrError);
298                     CAMERA_SetRebootCallback(CameraIntrReboot);
299 
300                     CAMERA_DmaRecvAsync(NDMA_NO, buffer[wp], CAMERA_GetBytesAtOnce(WIDTH), CAMERA_GET_FRAME_BYTES(WIDTH, HEIGHT), CameraDmaRecvIntr, NULL);
301                     CAMERA_ClearBuffer();
302                     CAMERA_StartCapture();
303                     OS_TPrintf("Camera is shooting a movie...\n");
304                     while (CAMERA_IsBusy() == FALSE)
305                     {
306                     }
307                     camera_end_flag = FALSE;
308                     camera_notactivate_flag = FALSE;
309                     camera_stop_capture_flag = FALSE;
310                 }
311             }
312         }
313     }
314 }
315 
316 //--------------------------------------------------------------------------------
317 //    V-Blank interrupt process
318 //
VBlankIntr(void)319 void VBlankIntr(void)
320 {
321     OS_SetIrqCheckFlag(OS_IE_V_BLANK);
322     if (wp == rp)
323     {
324         rp ^= 1;
325         GX_LoadBG3Scr(buffer[rp], 0, BYTES_PER_LINE * HEIGHT);
326         DC_InvalidateRange(buffer[rp], BYTES_PER_LINE * HEIGHT);
327     }
328 }
329 
330 //--------------------------------------------------------------------------------
331 //    Camera initialization (only the Init- and I2C-related initialization)
332 //
CameraInit(void)333 BOOL CameraInit(void)
334 {
335     CAMERAResult result;
336     OSTick begin = OS_GetTick();
337 
338     result = CAMERA_Init();
339     if(result == CAMERA_RESULT_FATAL_ERROR)
340         OS_TPanic("CAMERA_Init was failed.");
341     if(result == CAMERA_RESULT_ILLEGAL_STATUS)
342         return FALSE;
343 
344     OS_TPrintf("CAMERA_Init took %d msec\n", (int)OS_TicksToMilliSeconds(OS_GetTick()-begin));
345 
346     result = CAMERA_I2CFlip(CAMERA_SELECT_IN, flipIn);
347     if (result == CAMERA_RESULT_FATAL_ERROR)
348         OS_TPanic("CAMERA_I2CFlip was failed. (%d)\n", result);
349     if(result == CAMERA_RESULT_ILLEGAL_STATUS)
350         return FALSE;
351 
352     result = CAMERA_I2CFlip(CAMERA_SELECT_OUT, flipOut);
353     if (result == CAMERA_RESULT_FATAL_ERROR)
354         OS_TPanic("CAMERA_I2CFlip was failed. (%d)\n", result);
355     if(result == CAMERA_RESULT_ILLEGAL_STATUS)
356         return FALSE;
357 
358     result = CAMERA_I2CActivate(current);
359     if (result == CAMERA_RESULT_FATAL_ERROR)
360         OS_TPanic("CAMERA_I2CActivate was failed. (%d)\n", result);
361     if(result == CAMERA_RESULT_ILLEGAL_STATUS)
362         return FALSE;
363     stabilizedCount = 0;
364 
365     return TRUE;
366 }
367 
368 //--------------------------------------------------------------------------------
369 //    Camera interrupt process (Generated when there is an error and for Vsync)
370 //
CameraIntrError(CAMERAResult result)371 void CameraIntrError(CAMERAResult result)
372 {
373 #pragma unused(result)
374     OS_TPrintf("Error was occurred.\n");
375     // Camera Stop processing
376     CAMERA_StopCapture();
377     MI_StopNDma(NDMA_NO);
378     CAMERA_DmaRecvAsync(NDMA_NO, buffer[wp], CAMERA_GetBytesAtOnce(WIDTH), CAMERA_GET_FRAME_BYTES(WIDTH, HEIGHT), CameraDmaRecvIntr, NULL);
379     CAMERA_ClearBuffer();
380     wp_pending = TRUE;      // Also use same frame next time
381     CAMERA_StartCapture();
382 }
383 
384 // Restart completion callback
CameraIntrReboot(CAMERAResult result)385 void CameraIntrReboot(CAMERAResult result)
386 {
387     if(result == CAMERA_RESULT_FATAL_ERROR)
388     {
389         return; // Restore was not possible, even after restarting camera
390     }
391     CameraIntrError(result); // DMA synchronization might have drifted, so realign
392 }
393 
DoneCallback(CAMERAResult result,void * arg)394 static void DoneCallback(CAMERAResult result, void* arg)
395 {
396     const char* str = arg;
397     if (result != CAMERA_RESULT_SUCCESS)
398     {
399         OS_TPrintf("%s was failed. (%d)\n", arg, result);
400     }
401 }
402 
CameraIntrVsync(CAMERAResult result)403 void CameraIntrVsync(CAMERAResult result)
404 {
405 #pragma unused(result)
406     if(stabilizedCount <= 30)
407         stabilizedCount++;
408 }
409 
410 #define FPS_SAMPLES 4
CameraDmaRecvIntr(void * arg)411 void CameraDmaRecvIntr(void* arg)
412 {
413 #pragma unused(arg)
414     CAMERAResult result;
415 
416     MI_StopNDma(NDMA_NO);
417 
418     if(flipFlag == TRUE)
419     {
420         CAMERAFlip *pFlip = (current == CAMERA_SELECT_IN ? &flipIn : &flipOut);
421         *pFlip = (CAMERAFlip)((*pFlip + 1) % CAMERA_FLIP_MAX);
422         result = CAMERA_I2CFlipAsync(current, *pFlip, DoneCallback, "CAMERA_I2CFlipAsync");
423         if (result == CAMERA_RESULT_FATAL_ERROR)
424             OS_TPanic("CAMERA_I2CFlipAsync was failed. (%d)\n", result);
425         if(result == CAMERA_RESULT_ILLEGAL_STATUS)
426             return;
427 
428         flipFlag = FALSE;
429     }
430     else if(switchFlag == TRUE)
431     {
432         current = (current == CAMERA_SELECT_IN ? CAMERA_SELECT_OUT : CAMERA_SELECT_IN);
433         result = CAMERA_I2CActivateAsync(current, DoneCallback, "CAMERA_I2CActivateAsync");
434         if (result == CAMERA_RESULT_FATAL_ERROR)
435             OS_TPanic("CAMERA_I2CActivateAsync was failed. (%d)\n", result);
436         if(result == CAMERA_RESULT_ILLEGAL_STATUS)
437             return;
438         stabilizedCount = 0;
439         switchFlag = FALSE;
440     }
441 
442     // Start the next frame capture
443     if (wp_pending)
444     {
445         wp_pending = FALSE;
446     }
447     else
448     {
449         // Capture results are not displayed on screen until camera is stable
450         // This demo waits the minimum of four camera frames required to avoid chromatic aberration, but if you want to wait until auto-exposure is stable, you need to wait 14 frames for an indoor shot or 30 for an outdoor one, as indicated in the Function Reference.
451         //
452         if(stabilizedCount > 4)
453         {
454             wp ^= 1;
455         }
456     }
457     CAMERA_DmaRecvAsync(NDMA_NO, buffer[wp], CAMERA_GetBytesAtOnce(WIDTH), CAMERA_GET_FRAME_BYTES(WIDTH, HEIGHT), CameraDmaRecvIntr, NULL);
458 
459     // Display frame rate
460     {
461         static OSTick begin = 0;
462         static int uspf[FPS_SAMPLES] = { 0 };
463         static int count = 0;
464         int i;
465         int sum = 0;
466         OSTick end = OS_GetTick();
467         if (begin)  // Leave out the first time
468         {
469             uspf[count] = (int)OS_TicksToMicroSeconds(end - begin);
470             count = (count + 1) % FPS_SAMPLES;
471         }
472         begin = end;
473         // Calculate average value
474         for (i = 0; i < FPS_SAMPLES; i++)
475         {
476             if (uspf[i] == 0)  break;
477             sum +=  uspf[i];
478         }
479         if (sum)
480         {
481             int mfps = (int)(1000000000LL * i / sum);
482             PutString("%2d.%03d fps", mfps / 1000, mfps % 1000);
483         }
484     }
485 }
486