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-09-24#$
14   $Rev: 11063 $
15   $Author: okubata_ryoma $
16  *---------------------------------------------------------------------------*/
17 
18 #include <twl.h>
19 #include <twl/camera.h>
20 #include "DEMO.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     // When in NITRO mode, stopped by Panic
90     DEMOCheckRunOnTWL();
91 
92     // DMA is not used in GX (the old DMA conflicts with camera DMA)
93     (void)GX_SetDefaultDMA(GX_DMA_NOT_USE);
94 
95     // Clears VRAM
96     GX_SetBankForLCDC(GX_VRAM_LCDC_A);
97     GX_SetBankForLCDC(GX_VRAM_LCDC_B);
98     MI_CpuClearFast((void*)HW_LCDC_VRAM_A, 128 * 1024);
99     MI_CpuClearFast((void*)HW_LCDC_VRAM_B, 128 * 1024);
100 
101     // Direct bitmap display mode and text display
102     GX_SetBankForBG(GX_VRAM_BG_256_AB);         // Allocate VRAM-A and B banks to BG
103     GX_SetGraphicsMode(GX_DISPMODE_GRAPHICS, GX_BGMODE_4, GX_BG0_AS_2D);
104     GX_SetVisiblePlane(GX_PLANEMASK_BG1 | GX_PLANEMASK_BG3);
105 
106     G2_SetBG1Control(GX_BG_SCRSIZE_TEXT_256x256, GX_BG_COLORMODE_16,
107                      GX_BG_SCRBASE_0x0000, GX_BG_CHARBASE_0x04000, GX_BG_EXTPLTT_01);
108     G2_SetBG1Priority(1);
109     G2_BG1Mosaic(FALSE);
110 
111     G2_SetBG3ControlDCBmp(GX_BG_SCRSIZE_DCBMP_256x256, GX_BG_AREAOVER_XLU, GX_BG_BMPSCRBASE_0x20000);
112     G2_SetBG3Priority(3);
113     G2_BG3Mosaic(FALSE);
114 
115     // Load text
116     {
117         static const GXRgb pal[16] = { GX_RGB(0, 0, 0), GX_RGB(31, 31, 31), };
118         GX_LoadBG1Char(DEMOAsciiChr, 0x00000, sizeof(DEMOAsciiChr));
119         GX_LoadBGPltt(pal, 0x0000, sizeof(pal));
120     }
121     wp = 0;
122     rp = 1;
123     wp_pending = TRUE;
124     stabilizedCount = 0;
125 
126     // V-Blank interrupt settings
127     OS_SetIrqFunction(OS_IE_V_BLANK, VBlankIntr);
128     (void)OS_EnableIrqMask(OS_IE_V_BLANK);
129     (void)OS_EnableIrq();
130     (void)GX_VBlankIntr(TRUE);
131     (void)OS_EnableInterrupts();
132 
133     OS_WaitVBlankIntr();
134     GX_DispOn();
135 
136     // Initialize camera
137     current = CAMERA_SELECT_IN;
138     flipIn  = CAMERA_FLIP_HORIZONTAL;
139     flipOut = CAMERA_FLIP_NONE;
140     flipFlag = FALSE;
141 
142     (void)CameraInit();
143 
144     CAMERA_SetOutputFormat(CAMERA_OUTPUT_RGB);
145     CAMERA_SetTransferLines(CAMERA_GET_MAX_LINES(WIDTH));
146 
147     // Configure DMA interrupt
148     (void)OS_EnableIrqMask(OS_IE_NDMA1);
149 
150     // Camera VSYNC interrupt callback
151     CAMERA_SetVsyncCallback(CameraIntrVsync);
152 
153     // Camera error interrupt callback
154     CAMERA_SetBufferErrorCallback(CameraIntrError);
155 
156     // Camera restart completion callback
157     CAMERA_SetRebootCallback(CameraIntrReboot);
158 
159     // Prepare the DMA to use for the capture
160     MI_StopNDma(NDMA_NO);
161     CAMERA_DmaRecvAsync(NDMA_NO, buffer[wp], CAMERA_GetBytesAtOnce(WIDTH), CAMERA_GET_FRAME_BYTES(WIDTH, HEIGHT), CameraDmaRecvIntr, NULL);
162 
163     // Camera start
164     CAMERA_ClearBuffer();
165     CAMERA_StartCapture();
166     OS_TPrintf("Camera is shooting a movie...\n");
167 
168     while (1)
169     {
170         u16 pad;
171         u16 trg;
172         static u16 old = 0xffff;    // Ignore the trigger by first data
173         static int camera_end_flag = FALSE;
174         static int camera_stop_capture_flag = FALSE;
175         static int camera_notactivate_flag = FALSE;
176 
177         OS_WaitVBlankIntr();
178 
179         pad = PAD_Read();
180         trg = (u16)(pad & ~old);
181         old = pad;
182 
183         if (camera_end_flag == FALSE)
184         {
185             if (trg & PAD_BUTTON_Y)
186             {
187                 flipFlag = TRUE;
188             }
189             if (trg & PAD_BUTTON_X)
190             {
191                 switchFlag = TRUE;
192             }
193             if (camera_stop_capture_flag == FALSE)
194             {
195                 if (trg & PAD_BUTTON_B)
196                 {
197                     static BOOL standby = FALSE;
198                     standby ^= 1;
199                     if (standby)
200                     {
201                         // 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.
202                         // If you set Activate to NONE before StopCapture, there is a risk that IsBusy will be TRUE until the next time you set "Activate ON"
203                         CAMERA_StopCapture();
204                         while(CAMERA_IsBusy())
205                         {
206                         }
207                         MI_StopNDma(NDMA_NO);
208                         result = CAMERA_I2CActivate(CAMERA_SELECT_NONE);
209                         if(result == CAMERA_RESULT_FATAL_ERROR)
210                             OS_Panic("CAMERA FATAL ERROR\n");
211 
212                         if(result == CAMERA_RESULT_ILLEGAL_STATUS)
213                             continue;
214 
215                         camera_notactivate_flag = TRUE;
216                     }
217                     else
218                     {
219                         result = CAMERA_I2CActivate(current);
220                         if(result == CAMERA_RESULT_FATAL_ERROR)
221                             OS_Panic("CAMERA FALTAL ERROR\n");
222                         if(result == CAMERA_RESULT_ILLEGAL_STATUS)
223                             continue;
224                         stabilizedCount = 0;
225 
226                         CAMERA_DmaRecvAsync(NDMA_NO, buffer[wp], CAMERA_GetBytesAtOnce(WIDTH), CAMERA_GET_FRAME_BYTES(WIDTH, HEIGHT), CameraDmaRecvIntr, NULL);
227                         CAMERA_ClearBuffer();
228                         CAMERA_StartCapture();
229                         while (CAMERA_IsBusy() == FALSE)
230                         {
231                         }
232                         camera_notactivate_flag = FALSE;
233                     }
234                     OS_TPrintf("%s\n", result == CAMERA_RESULT_SUCCESS ? "SUCCESS" : "FAILED");
235                 }
236             }
237             if (camera_notactivate_flag == FALSE)
238             {
239                 if (trg & PAD_BUTTON_START)
240                 {
241                     if (CAMERA_IsBusy())
242                     {
243                         // 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.
244                         //
245                         // If you are about to stop using the camera, then it's all right to stop capturing with the camera in standby mode.
246                         result = CAMERA_I2CActivate(current);
247                         if(result == CAMERA_RESULT_FATAL_ERROR)
248                             OS_Panic("CAMERA FATAL ERROR\n");
249                         if(result == CAMERA_RESULT_ILLEGAL_STATUS)
250                             continue;
251 
252                         OS_TPrintf("%s\n", result == CAMERA_RESULT_SUCCESS ? "SUCCESS" : "FAILED");
253                         OS_TPrintf("call CAMERA_Stop()... ");
254                         CAMERA_StopCapture();
255                         while (CAMERA_IsBusy())
256                         {
257                         }
258                         OS_TPrintf("Camera was stopped.\n");
259                         MI_StopNDma(NDMA_NO);
260                         stabilizedCount = 0;
261                         camera_stop_capture_flag = TRUE;
262                     }
263                     else
264                     {
265                         CAMERA_DmaRecvAsync(NDMA_NO, buffer[wp], CAMERA_GetBytesAtOnce(WIDTH), CAMERA_GET_FRAME_BYTES(WIDTH, HEIGHT), CameraDmaRecvIntr, NULL);
266                         CAMERA_ClearBuffer();
267                         CAMERA_StartCapture();
268                         OS_TPrintf("Camera is shooting a movie...\n");
269                         while (CAMERA_IsBusy() == FALSE)
270                         {
271                         }
272                         camera_stop_capture_flag = FALSE;
273                     }
274                 }
275             }
276         }
277         // If you are not going to use the camera, just call CAMERA_I2CActivate(CAMERA_SELECT_NONE)
278         if (trg & PAD_BUTTON_SELECT)
279         {
280             if (camera_end_flag == FALSE)
281             {
282                 OS_TPrintf("call CAMERA_End()... ");
283                 CAMERA_StopCapture();
284                 while(CAMERA_IsBusy())
285                 {
286                 }
287                 MI_StopNDma(NDMA_NO);
288                 CAMERA_End();
289                 OS_TPrintf("Done.\n");
290                 camera_stop_capture_flag = TRUE;
291                 camera_notactivate_flag = TRUE;
292                 camera_end_flag = TRUE;
293             }
294             else
295             {
296                 if(CameraInit() == TRUE)
297                 {
298                     // Reregister the various callbacks that were registered at the first Init
299                     CAMERA_SetVsyncCallback(CameraIntrVsync);
300                     CAMERA_SetBufferErrorCallback(CameraIntrError);
301                     CAMERA_SetRebootCallback(CameraIntrReboot);
302 
303                     CAMERA_DmaRecvAsync(NDMA_NO, buffer[wp], CAMERA_GetBytesAtOnce(WIDTH), CAMERA_GET_FRAME_BYTES(WIDTH, HEIGHT), CameraDmaRecvIntr, NULL);
304                     CAMERA_ClearBuffer();
305                     CAMERA_StartCapture();
306                     OS_TPrintf("Camera is shooting a movie...\n");
307                     while (CAMERA_IsBusy() == FALSE)
308                     {
309                     }
310                     camera_end_flag = FALSE;
311                     camera_notactivate_flag = FALSE;
312                     camera_stop_capture_flag = FALSE;
313                 }
314             }
315         }
316     }
317 }
318 
319 //--------------------------------------------------------------------------------
320 //    V-Blank interrupt process
321 //
VBlankIntr(void)322 void VBlankIntr(void)
323 {
324     OS_SetIrqCheckFlag(OS_IE_V_BLANK);
325     if (wp == rp)
326     {
327         rp ^= 1;
328         GX_LoadBG3Scr(buffer[rp], 0, BYTES_PER_LINE * HEIGHT);
329         DC_InvalidateRange(buffer[rp], BYTES_PER_LINE * HEIGHT);
330     }
331 }
332 
333 //--------------------------------------------------------------------------------
334 //    Camera initialization (only the Init- and I2C-related initialization)
335 //
CameraInit(void)336 BOOL CameraInit(void)
337 {
338     CAMERAResult result;
339     OSTick begin = OS_GetTick();
340 
341     result = CAMERA_Init();
342     if(result == CAMERA_RESULT_FATAL_ERROR)
343         OS_TPanic("CAMERA_Init was failed.");
344     if(result == CAMERA_RESULT_ILLEGAL_STATUS)
345         return FALSE;
346 
347     OS_TPrintf("CAMERA_Init took %d msec\n", (int)OS_TicksToMilliSeconds(OS_GetTick()-begin));
348 
349     result = CAMERA_I2CFlip(CAMERA_SELECT_IN, flipIn);
350     if (result == CAMERA_RESULT_FATAL_ERROR)
351         OS_TPanic("CAMERA_I2CFlip was failed. (%d)\n", result);
352     if(result == CAMERA_RESULT_ILLEGAL_STATUS)
353         return FALSE;
354 
355     result = CAMERA_I2CFlip(CAMERA_SELECT_OUT, flipOut);
356     if (result == CAMERA_RESULT_FATAL_ERROR)
357         OS_TPanic("CAMERA_I2CFlip was failed. (%d)\n", result);
358     if(result == CAMERA_RESULT_ILLEGAL_STATUS)
359         return FALSE;
360 
361     result = CAMERA_I2CActivate(current);
362     if (result == CAMERA_RESULT_FATAL_ERROR)
363         OS_TPanic("CAMERA_I2CActivate was failed. (%d)\n", result);
364     if(result == CAMERA_RESULT_ILLEGAL_STATUS)
365         return FALSE;
366     stabilizedCount = 0;
367 
368     return TRUE;
369 }
370 
371 //--------------------------------------------------------------------------------
372 //    Camera interrupt process (Generated when there is an error and for Vsync)
373 //
CameraIntrError(CAMERAResult result)374 void CameraIntrError(CAMERAResult result)
375 {
376 #pragma unused(result)
377     OS_TPrintf("Error was occurred.\n");
378     // Camera stop processing
379     CAMERA_StopCapture();
380     MI_StopNDma(NDMA_NO);
381     CAMERA_DmaRecvAsync(NDMA_NO, buffer[wp], CAMERA_GetBytesAtOnce(WIDTH), CAMERA_GET_FRAME_BYTES(WIDTH, HEIGHT), CameraDmaRecvIntr, NULL);
382     CAMERA_ClearBuffer();
383     wp_pending = TRUE;      // Also use same frame next time
384     CAMERA_StartCapture();
385 }
386 
387 // Restart completion callback
CameraIntrReboot(CAMERAResult result)388 void CameraIntrReboot(CAMERAResult result)
389 {
390     if(result == CAMERA_RESULT_FATAL_ERROR)
391     {
392         return; // Recovery was not possible, even after restarting camera
393     }
394     CameraIntrError(result); // DMA synchronization might have drifted, so realign
395 }
396 
DoneCallback(CAMERAResult result,void * arg)397 static void DoneCallback(CAMERAResult result, void* arg)
398 {
399     const char* str = arg;
400     if (result != CAMERA_RESULT_SUCCESS)
401     {
402         OS_TPrintf("%s was failed. (%d)\n", arg, result);
403     }
404 }
405 
CameraIntrVsync(CAMERAResult result)406 void CameraIntrVsync(CAMERAResult result)
407 {
408 #pragma unused(result)
409     if(stabilizedCount <= 30)
410         stabilizedCount++;
411 }
412 
413 #define FPS_SAMPLES 4
CameraDmaRecvIntr(void * arg)414 void CameraDmaRecvIntr(void* arg)
415 {
416 #pragma unused(arg)
417     CAMERAResult result;
418 
419     MI_StopNDma(NDMA_NO);
420 
421     if(flipFlag == TRUE)
422     {
423         CAMERAFlip *pFlip = (current == CAMERA_SELECT_IN ? &flipIn : &flipOut);
424         *pFlip = (CAMERAFlip)((*pFlip + 1) % CAMERA_FLIP_MAX);
425         result = CAMERA_I2CFlipAsync(current, *pFlip, DoneCallback, "CAMERA_I2CFlipAsync");
426         if (result == CAMERA_RESULT_FATAL_ERROR)
427             OS_TPanic("CAMERA_I2CFlipAsync was failed. (%d)\n", result);
428         if(result == CAMERA_RESULT_ILLEGAL_STATUS)
429             return;
430 
431         flipFlag = FALSE;
432     }
433     else if(switchFlag == TRUE)
434     {
435         current = (current == CAMERA_SELECT_IN ? CAMERA_SELECT_OUT : CAMERA_SELECT_IN);
436         result = CAMERA_I2CActivateAsync(current, DoneCallback, "CAMERA_I2CActivateAsync");
437         if (result == CAMERA_RESULT_FATAL_ERROR)
438             OS_TPanic("CAMERA_I2CActivateAsync was failed. (%d)\n", result);
439         if(result == CAMERA_RESULT_ILLEGAL_STATUS)
440             return;
441         stabilizedCount = 0;
442         switchFlag = FALSE;
443     }
444 
445     // Start the next frame capture
446     if (wp_pending)
447     {
448         wp_pending = FALSE;
449     }
450     else
451     {
452         // Capture results are not displayed on screen until camera is stable
453         // 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.
454         //
455         if(stabilizedCount > 4)
456         {
457             wp ^= 1;
458         }
459     }
460     CAMERA_DmaRecvAsync(NDMA_NO, buffer[wp], CAMERA_GetBytesAtOnce(WIDTH), CAMERA_GET_FRAME_BYTES(WIDTH, HEIGHT), CameraDmaRecvIntr, NULL);
461 
462     // Display frame rate
463     {
464         static OSTick begin = 0;
465         static int uspf[FPS_SAMPLES] = { 0 };
466         static int count = 0;
467         int i;
468         int sum = 0;
469         OSTick end = OS_GetTick();
470         if (begin)  // Leave out the first time
471         {
472             uspf[count] = (int)OS_TicksToMicroSeconds(end - begin);
473             count = (count + 1) % FPS_SAMPLES;
474         }
475         begin = end;
476         // Calculate average value
477         for (i = 0; i < FPS_SAMPLES; i++)
478         {
479             if (uspf[i] == 0)  break;
480             sum +=  uspf[i];
481         }
482         if (sum)
483         {
484             int mfps = (int)(1000000000LL * i / sum);
485             PutString("%2d.%03d fps", mfps / 1000, mfps % 1000);
486         }
487     }
488 }
489