1 /*---------------------------------------------------------------------------*
2   Project:  TwlSDK - demos.TWL - camera - camera-4
3   File:     main.c
4 
5   Copyright 2007-2009 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 /////
37 //  Sample configuration with double buffer in main memory and 1 layer of VRAM
38 /////
39 
40 static int wp;  // Buffer while capturing data from camera
41 static int rp;  // Buffer most recently copied to VRAM
42 static BOOL wp_pending; // Data capture was cancelled (recapture to same buffer)
43 static u32 stabilizedCount;
44 static BOOL switchFlag;
45 
46 static CAMERASelect current;
47 static CAMERAFlip   flipIn, flipOut;
48 static u16 buffer[2][WIDTH*HEIGHT] ATTRIBUTE_ALIGN(32);
DebugReport(void)49 static void DebugReport(void)
50 {
51     OS_TPrintf("\nCapture to No.%d\tDisplay from No.%d\n", wp, rp);
52 }
PendingCapture(void)53 static void PendingCapture(void)
54 {
55     wp_pending = TRUE;
56 }
57 
58 // Display text (one row only)
59 static u16 text_buffer[ 32 ] ATTRIBUTE_ALIGN(32);
PutString(char * format,...)60 static void PutString( char *format, ... )
61 {
62     u16             *dest = text_buffer;
63     char            temp[32+1];
64     int             i;
65     va_list         va;
66 
67     va_start(va, format);
68     (void)OS_VSNPrintf(temp, sizeof(temp), format, va);
69     va_end(va);
70 
71     MI_CpuClearFast(text_buffer, sizeof(text_buffer));
72     for (i = 0; i < 32 && temp[i]; i++)
73     {
74         dest[i] = (u16)((u8)temp[i] | (1 << 12));
75     }
76     DC_StoreRange(text_buffer, sizeof(text_buffer));
77 }
78 
79 /*---------------------------------------------------------------------------*
80   Name:         TwlMain
81 
82   Description:  Main.
83 
84   Arguments:    None.
85 
86   Returns:      None.
87  *---------------------------------------------------------------------------*/
TwlMain()88 void TwlMain()
89 {
90     CAMERAResult result;
91 
92     // Initialization
93     OS_Init();
94     OS_InitThread();
95     GX_Init();
96     OS_InitTick();
97     OS_InitAlarm();
98 
99     // DMA is not used in GX (the old DMA conflicts with camera DMA)
100     (void)GX_SetDefaultDMA(GX_DMA_NOT_USE);
101 
102     // Clears VRAM
103     GX_SetBankForLCDC(GX_VRAM_LCDC_A);
104     GX_SetBankForLCDC(GX_VRAM_LCDC_B);
105     MI_CpuClearFast((void*)HW_LCDC_VRAM_A, 128 * 1024);
106     MI_CpuClearFast((void*)HW_LCDC_VRAM_B, 128 * 1024);
107 
108     // Direct bitmap display mode and text display
109     GX_SetBankForBG(GX_VRAM_BG_256_AB);         // Allocate VRAM-A and B banks to BG
110     GX_SetGraphicsMode(GX_DISPMODE_GRAPHICS, GX_BGMODE_4, GX_BG0_AS_2D);
111     GX_SetVisiblePlane(GX_PLANEMASK_BG1 | GX_PLANEMASK_BG3);
112 
113     G2_SetBG1Control(GX_BG_SCRSIZE_TEXT_256x256, GX_BG_COLORMODE_16,
114                      GX_BG_SCRBASE_0x0000, GX_BG_CHARBASE_0x04000, GX_BG_EXTPLTT_01);
115     G2_SetBG1Priority(1);
116     G2_BG1Mosaic(FALSE);
117 
118     G2_SetBG3ControlDCBmp(GX_BG_SCRSIZE_DCBMP_256x256, GX_BG_AREAOVER_XLU, GX_BG_BMPSCRBASE_0x20000);
119     G2_SetBG3Priority(3);
120     G2_BG3Mosaic(FALSE);
121 
122     // Load text
123     {
124         static const GXRgb pal[16] = { GX_RGB(0, 0, 0), GX_RGB(31, 31, 31), };
125         GX_LoadBG1Char(DEMOAsciiChr, 0x00000, sizeof(DEMOAsciiChr));
126         GX_LoadBGPltt(pal, 0x0000, sizeof(pal));
127     }
128     wp = 0;
129     rp = 1;
130     wp_pending = TRUE;
131     stabilizedCount = 0;
132     switchFlag = FALSE;
133 
134     // V-Blank interrupt settings
135     OS_SetIrqFunction(OS_IE_V_BLANK, VBlankIntr);
136     (void)OS_EnableIrqMask(OS_IE_V_BLANK);
137     (void)OS_EnableIrq();
138     (void)GX_VBlankIntr(TRUE);
139     (void)OS_EnableInterrupts();
140 
141     OS_WaitVBlankIntr();
142     GX_DispOn();
143 
144     // Initialize camera
145     current = CAMERA_SELECT_IN;
146     flipIn = CAMERA_FLIP_HORIZONTAL;
147     flipOut = CAMERA_FLIP_NONE;
148     (void)CameraInit();
149 
150     // Configure DMA interrupt
151     (void)OS_EnableIrqMask(OS_IE_NDMA1);
152 
153     // Camera VSYNC interrupt callback
154     CAMERA_SetVsyncCallback(CameraIntrVsync);
155 
156     // Camera error interrupt callback
157     CAMERA_SetBufferErrorCallback(CameraIntrError);
158 
159     // Camera restart completion callback
160     CAMERA_SetRebootCallback(CameraIntrReboot);
161 
162     CAMERA_SetOutputFormat(CAMERA_OUTPUT_RGB);
163     CAMERA_SetTransferLines(CAMERA_GET_MAX_LINES(WIDTH));
164 
165     // Start capturing
166     CAMERA_DmaRecvAsync(NDMA_NO, buffer[wp], CAMERA_GetBytesAtOnce(WIDTH), CAMERA_GET_FRAME_BYTES(WIDTH, HEIGHT), CameraDmaRecvIntr, NULL);
167     CAMERA_ClearBuffer();
168     CAMERA_StartCapture();
169     OS_TPrintf("Camera is shooting a movie...\n");
170 
171     while (1)
172     {
173         u16 pad;
174         u16 trg;
175         static u16 old = 0xffff;
176         static BOOL standby = FALSE;
177 
178         OS_WaitVBlankIntr();
179 
180         pad = PAD_Read();
181         trg = (u16)(pad & ~old);
182         old = pad;
183 
184         if (trg & PAD_BUTTON_Y)
185         {
186             DebugReport();
187         }
188         if (trg & PAD_BUTTON_X)
189         {
190             switchFlag = TRUE;
191         }
192         if (trg & PAD_BUTTON_B)
193         {
194             standby ^= 1;
195             if (standby)
196             {
197                 // 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"
198                 CAMERA_StopCapture();
199                 while(CAMERA_IsBusy())
200                 {
201                 }
202                 MI_StopNDma(NDMA_NO);
203                 result = CAMERA_I2CActivate(CAMERA_SELECT_NONE);
204                 if(result == CAMERA_RESULT_FATAL_ERROR)
205                     OS_Panic("CAMERA FATAL ERROR\n");
206             }
207             else
208             {
209                 result = CAMERA_I2CActivate(current);
210                 if(result == CAMERA_RESULT_FATAL_ERROR)
211                     OS_Panic("CAMERA FATAL ERROR\n");
212                 stabilizedCount = 0;
213                 CAMERA_DmaRecvAsync(NDMA_NO, buffer[wp], CAMERA_GetBytesAtOnce(WIDTH), CAMERA_GET_FRAME_BYTES(WIDTH, HEIGHT), CameraDmaRecvIntr, NULL);
214                 CAMERA_ClearBuffer();
215                 CAMERA_StartCapture();
216             }
217             OS_TPrintf("%s\n", result == CAMERA_RESULT_SUCCESS ? "SUCCESS" : "FAILED");
218         }
219 
220         if (trg & PAD_BUTTON_SELECT)
221         {
222             result = CAMERA_I2CFlip(current, CAMERA_FLIP_VERTICAL);
223             if (result != CAMERA_RESULT_SUCCESS)
224             {
225                 OS_TPrintf("CAMERA_I2CEffect was failed. (%d)\n", result);
226             }
227             *(current == CAMERA_SELECT_IN ? &flipIn : &flipOut) = CAMERA_FLIP_VERTICAL;
228         }
229         if (trg & PAD_BUTTON_L)
230         {
231             result = CAMERA_I2CFlip(current, CAMERA_FLIP_HORIZONTAL);
232             if (result != CAMERA_RESULT_SUCCESS)
233             {
234                 OS_TPrintf("CAMERA_I2CEffect was failed. (%d)\n", result);
235             }
236             *(current == CAMERA_SELECT_IN ? &flipIn : &flipOut) = CAMERA_FLIP_HORIZONTAL;
237         }
238         if (trg & PAD_BUTTON_R)
239         {
240             result = CAMERA_I2CFlip(current, CAMERA_FLIP_REVERSE);
241             if (result != CAMERA_RESULT_SUCCESS)
242             {
243                 OS_TPrintf("CAMERA_I2CEffect was failed. (%d)\n", result);
244             }
245             *(current == CAMERA_SELECT_IN ? &flipIn : &flipOut) = CAMERA_FLIP_REVERSE;
246         }
247         if (trg & PAD_BUTTON_START)
248         {
249             result = CAMERA_I2CFlip(current, CAMERA_FLIP_NONE);
250             if (result != CAMERA_RESULT_SUCCESS)
251             {
252                 OS_TPrintf("CAMERA_I2CEffect was failed. (%d)\n", result);
253             }
254             *(current == CAMERA_SELECT_IN ? &flipIn : &flipOut) = CAMERA_FLIP_NONE;
255         }
256     }
257 }
258 
259 //--------------------------------------------------------------------------------
260 //    V-Blank interrupt process
261 //
VBlankIntr(void)262 void VBlankIntr(void)
263 {
264     OS_SetIrqCheckFlag(OS_IE_V_BLANK); // Checking V-Blank interrupt
265     if (wp == rp)
266     {
267         rp ^= 1;
268         // This is just a simple copy, but format conversion and so on could be included
269         GX_LoadBG3Scr(buffer[rp], 0, BYTES_PER_LINE * HEIGHT);
270         DC_InvalidateRange(buffer[rp], BYTES_PER_LINE * HEIGHT);
271     }
272     GX_LoadBG1Scr(text_buffer, 0, sizeof(text_buffer));
273 }
274 
275 //--------------------------------------------------------------------------------
276 //    Camera initialization
277 //
CameraInit(void)278 BOOL CameraInit(void)
279 {
280     CAMERAResult result;
281 
282     result = CAMERA_Init();
283     if(result == CAMERA_RESULT_FATAL_ERROR)
284         OS_TPanic("CAMERA_Init was failed.");
285     if(result == CAMERA_RESULT_ILLEGAL_STATUS)
286         return FALSE;
287 
288     result = CAMERA_I2CFlip(CAMERA_SELECT_IN, flipIn);
289     if (result != CAMERA_RESULT_SUCCESS)
290     {
291         OS_TPrintf("CAMERA_I2CFlip was failed. (%d)\n", result);
292     }
293     result = CAMERA_I2CFlip(CAMERA_SELECT_OUT, flipOut);
294     if (result != CAMERA_RESULT_SUCCESS)
295     {
296         OS_TPrintf("CAMERA_I2CFlip was failed. (%d)\n", result);
297     }
298     result = CAMERA_I2CActivate(current);
299     if (result == CAMERA_RESULT_FATAL_ERROR)
300         OS_TPanic("CAMERA_I2CActivate was failed. (%d)\n", result);
301     if(result == CAMERA_RESULT_ILLEGAL_STATUS)
302         return FALSE;
303     stabilizedCount = 0;
304 
305     return TRUE;
306 }
307 
308 //--------------------------------------------------------------------------------
309 //    Camera interrupt process (occurs both when there is an error and for Vsync)
310 //
311 #define FPS_SAMPLES 4
CameraIntrError(CAMERAResult result)312 void CameraIntrError(CAMERAResult result)
313 {
314 #pragma unused(result)
315     OS_TPrintf("Error was occurred.\n");
316     // Camera stop processing
317     CAMERA_StopCapture();
318     MI_StopNDma(NDMA_NO);
319     CAMERA_ClearBuffer();
320     wp_pending = TRUE;      // Also use same frame next time
321     CAMERA_DmaRecvAsync(NDMA_NO, buffer[wp], CAMERA_GetBytesAtOnce(WIDTH), CAMERA_GET_FRAME_BYTES(WIDTH, HEIGHT), CameraDmaRecvIntr, NULL);
322     CAMERA_StartCapture();
323 }
324 
CameraIntrReboot(CAMERAResult result)325 void CameraIntrReboot(CAMERAResult result)
326 {
327     if(result == CAMERA_RESULT_FATAL_ERROR)
328     {
329         return; // Restore was not possible, even after restarting camera
330     }
331     CameraIntrError(result); // DMA synchronization might have drifted, so realign
332 }
333 
CameraIntrVsync(CAMERAResult result)334 void CameraIntrVsync(CAMERAResult result)
335 {
336 #pragma unused(result)
337     if(stabilizedCount <= 30)
338         stabilizedCount++;
339     if(switchFlag)
340     {
341         current = (current == CAMERA_SELECT_IN ? CAMERA_SELECT_OUT : CAMERA_SELECT_IN);
342         OS_TPrintf("call CAMERA_I2CActivate(%s)... ", (current == CAMERA_SELECT_IN ? "CAMERA_SELECT_IN" : "CAMERA_SELECT_OUT"));
343         result = CAMERA_I2CActivate(current);
344         if(result == CAMERA_RESULT_FATAL_ERROR)
345             OS_Panic("CAMERA FATAL ERROR\n");
346         stabilizedCount = 0;
347         switchFlag = FALSE;
348         OS_TPrintf("%s\n", result == CAMERA_RESULT_SUCCESS ? "SUCCESS" : "FAILED");
349     }
350 }
351 
CameraDmaRecvIntr(void * arg)352 void CameraDmaRecvIntr(void* arg)
353 {
354 #pragma unused(arg)
355     MI_StopNDma(NDMA_NO);
356 
357     if (CAMERA_IsBusy() == TRUE)
358     {
359         if (MI_IsNDmaBusy(NDMA_NO)) // Check whether image transfer is complete
360         {
361             OS_TPrintf("DMA was not done until VBlank.\n");
362             MI_StopNDma(NDMA_NO);
363         }
364         // Start the next frame capture
365         if (wp_pending)
366         {
367             wp_pending = FALSE;
368         }
369         else
370         {
371             // Capture results are not displayed on screen until camera is stable
372             // 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.
373             //
374             if(stabilizedCount > 4)
375             {
376                 wp ^= 1;
377             }
378         }
379         CAMERA_DmaRecvAsync(NDMA_NO, buffer[wp], CAMERA_GetBytesAtOnce(WIDTH), CAMERA_GET_FRAME_BYTES(WIDTH, HEIGHT), CameraDmaRecvIntr, NULL);
380     }
381 
382     // Display frame rate
383     {
384         static OSTick begin = 0;
385         static int uspf[FPS_SAMPLES] = { 0 };
386         static int count = 0;
387         int i;
388         int sum = 0;
389         OSTick end = OS_GetTick();
390         if (begin)  // Leave out the first time
391         {
392             uspf[count] = (int)OS_TicksToMicroSeconds(end - begin);
393             count = (count + 1) % FPS_SAMPLES;
394         }
395         begin = end;
396         // Calculate average value
397         for (i = 0; i < FPS_SAMPLES; i++)
398         {
399             if (uspf[i] == 0)  break;
400             sum +=  uspf[i];
401         }
402         if (sum)
403         {
404             int mfps = (int)(1000000000LL * i / sum);
405             PutString("%2d.%03d fps", mfps / 1000, mfps % 1000);
406         }
407     }
408 }
409