1 /*---------------------------------------------------------------------------*
2   Project:  TwlSDK - demos.TWL - camera - camera-4
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: 9736 $
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 BOOL startRequest = FALSE;
37 
38 /////
39 //  Sample configuration with double buffer in main memory and 1 layer of VRAM
40 /////
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 u32 stabilizedCount;
46 static BOOL switchFlag;
47 
48 static CAMERASelect current;
49 static CAMERAFlip   flipIn, flipOut;
50 static u16 buffer[2][WIDTH*HEIGHT] ATTRIBUTE_ALIGN(32);
DebugReport(void)51 static void DebugReport(void)
52 {
53     OS_TPrintf("\nCapture to No.%d\tDisplay from No.%d\n", wp, rp);
54 }
PendingCapture(void)55 static void PendingCapture(void)
56 {
57     wp_pending = TRUE;
58 }
59 
60 // Display text (one line only)
61 static u16 text_buffer[ 32 ] ATTRIBUTE_ALIGN(32);
PutString(char * format,...)62 static void PutString( char *format, ... )
63 {
64     u16             *dest = text_buffer;
65     char            temp[32+1];
66     int             i;
67     va_list         va;
68 
69     va_start(va, format);
70     (void)OS_VSNPrintf(temp, sizeof(temp), format, va);
71     va_end(va);
72 
73     MI_CpuClearFast(text_buffer, sizeof(text_buffer));
74     for (i = 0; i < 32 && temp[i]; i++)
75     {
76         dest[i] = (u16)((u8)temp[i] | (1 << 12));
77     }
78     DC_StoreRange(text_buffer, sizeof(text_buffer));
79 }
80 
81 /*---------------------------------------------------------------------------*
82   Name:         TwlMain
83 
84   Description:  Main
85 
86   Arguments:    None.
87 
88   Returns:      None.
89  *---------------------------------------------------------------------------*/
TwlMain()90 void TwlMain()
91 {
92     CAMERAResult result;
93 
94     // Initialization
95     OS_Init();
96     OS_InitThread();
97     GX_Init();
98     OS_InitTick();
99     OS_InitAlarm();
100     MI_InitNDmaConfig();
101 
102     // DMA is not used in GX (the old DMA conflicts with camera DMA)
103     (void)GX_SetDefaultDMA(GX_DMA_NOT_USE);
104 
105     // Clear VRAM
106     GX_SetBankForLCDC(GX_VRAM_LCDC_A);
107     GX_SetBankForLCDC(GX_VRAM_LCDC_B);
108     MI_CpuClearFast((void*)HW_LCDC_VRAM_A, 128 * 1024);
109     MI_CpuClearFast((void*)HW_LCDC_VRAM_B, 128 * 1024);
110 
111     // Direct bitmap display mode and text display
112     GX_SetBankForBG(GX_VRAM_BG_256_AB);         // Allocate VRAM-A, B banks to BG
113     GX_SetGraphicsMode(GX_DISPMODE_GRAPHICS, GX_BGMODE_4, GX_BG0_AS_2D);
114     GX_SetVisiblePlane(GX_PLANEMASK_BG1 | GX_PLANEMASK_BG3);
115 
116     G2_SetBG1Control(GX_BG_SCRSIZE_TEXT_256x256, GX_BG_COLORMODE_16,
117                      GX_BG_SCRBASE_0x0000, GX_BG_CHARBASE_0x04000, GX_BG_EXTPLTT_01);
118     G2_SetBG1Priority(1);
119     G2_BG1Mosaic(FALSE);
120 
121     G2_SetBG3ControlDCBmp(GX_BG_SCRSIZE_DCBMP_256x256, GX_BG_AREAOVER_XLU, GX_BG_BMPSCRBASE_0x20000);
122     G2_SetBG3Priority(3);
123     G2_BG3Mosaic(FALSE);
124 
125     // Load text
126     {
127         static const GXRgb pal[16] = { GX_RGB(0, 0, 0), GX_RGB(31, 31, 31), };
128         GX_LoadBG1Char(DEMOAsciiChr, 0x00000, sizeof(DEMOAsciiChr));
129         GX_LoadBGPltt(pal, 0x0000, sizeof(pal));
130     }
131     wp = 0;
132     rp = 1;
133     wp_pending = TRUE;
134     stabilizedCount = 0;
135     switchFlag = FALSE;
136 
137     // V-Blank interrupt settings
138     OS_SetIrqFunction(OS_IE_V_BLANK, VBlankIntr);
139     (void)OS_EnableIrqMask(OS_IE_V_BLANK);
140     (void)OS_EnableIrq();
141     (void)GX_VBlankIntr(TRUE);
142     (void)OS_EnableInterrupts();
143 
144     OS_WaitVBlankIntr();
145     GX_DispOn();
146 
147     // Initialize camera
148     current = CAMERA_SELECT_IN;
149     flipIn = CAMERA_FLIP_HORIZONTAL;
150     flipOut = CAMERA_FLIP_NONE;
151     (void)CameraInit();
152 
153     // Configure DMA interrupt
154     (void)OS_EnableIrqMask(OS_IE_NDMA1);
155 
156     // Camera VSYNC interrupt callback
157     CAMERA_SetVsyncCallback(CameraIntrVsync);
158 
159     // Camera error interrupt callback
160     CAMERA_SetBufferErrorCallback(CameraIntrError);
161 
162     // Camera restart completion callback
163     CAMERA_SetRebootCallback(CameraIntrReboot);
164 
165     CAMERA_SetOutputFormat(CAMERA_OUTPUT_RGB);
166     CAMERA_SetTransferLines(CAMERA_GET_MAX_LINES(WIDTH));
167 
168     // Camera start (substituted for by interrupt handler)
169     startRequest = TRUE;
170     CameraIntrVsync(CAMERA_RESULT_SUCCESS);
171     OS_TPrintf("Camera is shooting a movie...\n");
172 
173     while (1)
174     {
175         u16 pad;
176         u16 trg;
177         static u16 old = 0xffff;
178         static BOOL standby = FALSE;
179 
180         OS_WaitVBlankIntr();
181 
182         pad = PAD_Read();
183         trg = (u16)(pad & ~old);
184         old = pad;
185 
186         if (trg & PAD_BUTTON_Y)
187         {
188             DebugReport();
189         }
190         if (trg & PAD_BUTTON_X)
191         {
192             switchFlag = TRUE;
193         }
194         if (trg & PAD_BUTTON_B)
195         {
196             standby ^= 1;
197             if (standby)
198             {
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             else
210             {
211                 result = CAMERA_I2CActivate(current);
212                 if(result == CAMERA_RESULT_FATAL_ERROR)
213                     OS_Panic("CAMERA FATAL ERROR\n");
214                 stabilizedCount = 0;
215                 startRequest = TRUE;    // Camera restart request
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     startRequest = TRUE;    // Camera restart request
322 }
323 
CameraIntrReboot(CAMERAResult result)324 void CameraIntrReboot(CAMERAResult result)
325 {
326     if(result == CAMERA_RESULT_FATAL_ERROR)
327     {
328         return; // Restore was not possible, even after restarting camera
329     }
330     CameraIntrError(result); // DMA synchronization might have drifted, so realign
331 }
332 
CameraIntrVsync(CAMERAResult result)333 void CameraIntrVsync(CAMERAResult result)
334 {
335 #pragma unused(result)
336     if(stabilizedCount <= 30)
337         stabilizedCount++;
338     if(switchFlag)
339     {
340         current = (current == CAMERA_SELECT_IN ? CAMERA_SELECT_OUT : CAMERA_SELECT_IN);
341         OS_TPrintf("call CAMERA_I2CActivate(%s)... ", (current == CAMERA_SELECT_IN ? "CAMERA_SELECT_IN" : "CAMERA_SELECT_OUT"));
342         result = CAMERA_I2CActivate(current);
343         if(result == CAMERA_RESULT_FATAL_ERROR)
344             OS_Panic("CAMERA FATAL ERROR\n");
345         stabilizedCount = 0;
346         switchFlag = FALSE;
347         OS_TPrintf("%s\n", result == CAMERA_RESULT_SUCCESS ? "SUCCESS" : "FAILED");
348     }
349     if (startRequest)
350     {
351         CAMERA_DmaRecvAsync(NDMA_NO, buffer[wp], CAMERA_GetBytesAtOnce(WIDTH), CAMERA_GET_FRAME_BYTES(WIDTH, HEIGHT), CameraDmaRecvIntr, NULL);
352         CAMERA_ClearBuffer();
353         CAMERA_StartCapture(); // The CAMERA_IsBusy function will immediately return TRUE because it is being called during a camera VSYNC
354         startRequest = FALSE;
355     }
356 }
357 
CameraDmaRecvIntr(void * arg)358 void CameraDmaRecvIntr(void* arg)
359 {
360 #pragma unused(arg)
361     MI_StopNDma(NDMA_NO);
362 
363     if (CAMERA_IsBusy() == TRUE)
364     {
365         if (MI_IsNDmaBusy(NDMA_NO)) // Check whether image transfer is complete
366         {
367             OS_TPrintf("DMA was not done until VBlank.\n");
368             MI_StopNDma(NDMA_NO);
369         }
370         // Start the next frame capture
371         if (wp_pending)
372         {
373             wp_pending = FALSE;
374         }
375         else
376         {
377             // Capture results are not displayed on screen until camera is stable
378             // 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.
379             //
380             if(stabilizedCount > 4)
381             {
382                 wp ^= 1;
383             }
384         }
385         CAMERA_DmaRecvAsync(NDMA_NO, buffer[wp], CAMERA_GetBytesAtOnce(WIDTH), CAMERA_GET_FRAME_BYTES(WIDTH, HEIGHT), CameraDmaRecvIntr, NULL);
386     }
387 
388     // Display frame rate
389     {
390         static OSTick begin = 0;
391         static int uspf[FPS_SAMPLES] = { 0 };
392         static int count = 0;
393         int i;
394         int sum = 0;
395         OSTick end = OS_GetTick();
396         if (begin)  // Leave out the first time
397         {
398             uspf[count] = (int)OS_TicksToMicroSeconds(end - begin);
399             count = (count + 1) % FPS_SAMPLES;
400         }
401         begin = end;
402         // Calculate average value
403         for (i = 0; i < FPS_SAMPLES; i++)
404         {
405             if (uspf[i] == 0)  break;
406             sum +=  uspf[i];
407         }
408         if (sum)
409         {
410             int mfps = (int)(1000000000LL * i / sum);
411             PutString("%2d.%03d fps", mfps / 1000, mfps % 1000);
412         }
413     }
414 }
415