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