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-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 /////
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     // When in NITRO mode, stopped by Panic
100     DEMOCheckRunOnTWL();
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     // Clears 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 and 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     // Start capturing
169     CAMERA_DmaRecvAsync(NDMA_NO, buffer[wp], CAMERA_GetBytesAtOnce(WIDTH), CAMERA_GET_FRAME_BYTES(WIDTH, HEIGHT), CameraDmaRecvIntr, NULL);
170     CAMERA_ClearBuffer();
171     CAMERA_StartCapture();
172     OS_TPrintf("Camera is shooting a movie...\n");
173 
174     while (1)
175     {
176         u16 pad;
177         u16 trg;
178         static u16 old = 0xffff;
179         static BOOL standby = FALSE;
180 
181         OS_WaitVBlankIntr();
182 
183         pad = PAD_Read();
184         trg = (u16)(pad & ~old);
185         old = pad;
186 
187         if (trg & PAD_BUTTON_Y)
188         {
189             DebugReport();
190         }
191         if (trg & PAD_BUTTON_X)
192         {
193             switchFlag = TRUE;
194         }
195         if (trg & PAD_BUTTON_B)
196         {
197             standby ^= 1;
198             if (standby)
199             {
200                 // 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"
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             else
211             {
212                 result = CAMERA_I2CActivate(current);
213                 if(result == CAMERA_RESULT_FATAL_ERROR)
214                     OS_Panic("CAMERA FATAL ERROR\n");
215                 stabilizedCount = 0;
216                 CAMERA_DmaRecvAsync(NDMA_NO, buffer[wp], CAMERA_GetBytesAtOnce(WIDTH), CAMERA_GET_FRAME_BYTES(WIDTH, HEIGHT), CameraDmaRecvIntr, NULL);
217                 CAMERA_ClearBuffer();
218                 CAMERA_StartCapture();
219             }
220             OS_TPrintf("%s\n", result == CAMERA_RESULT_SUCCESS ? "SUCCESS" : "FAILED");
221         }
222 
223         if (trg & PAD_BUTTON_SELECT)
224         {
225             result = CAMERA_I2CFlip(current, CAMERA_FLIP_VERTICAL);
226             if (result != CAMERA_RESULT_SUCCESS)
227             {
228                 OS_TPrintf("CAMERA_I2CEffect was failed. (%d)\n", result);
229             }
230             *(current == CAMERA_SELECT_IN ? &flipIn : &flipOut) = CAMERA_FLIP_VERTICAL;
231         }
232         if (trg & PAD_BUTTON_L)
233         {
234             result = CAMERA_I2CFlip(current, CAMERA_FLIP_HORIZONTAL);
235             if (result != CAMERA_RESULT_SUCCESS)
236             {
237                 OS_TPrintf("CAMERA_I2CEffect was failed. (%d)\n", result);
238             }
239             *(current == CAMERA_SELECT_IN ? &flipIn : &flipOut) = CAMERA_FLIP_HORIZONTAL;
240         }
241         if (trg & PAD_BUTTON_R)
242         {
243             result = CAMERA_I2CFlip(current, CAMERA_FLIP_REVERSE);
244             if (result != CAMERA_RESULT_SUCCESS)
245             {
246                 OS_TPrintf("CAMERA_I2CEffect was failed. (%d)\n", result);
247             }
248             *(current == CAMERA_SELECT_IN ? &flipIn : &flipOut) = CAMERA_FLIP_REVERSE;
249         }
250         if (trg & PAD_BUTTON_START)
251         {
252             result = CAMERA_I2CFlip(current, CAMERA_FLIP_NONE);
253             if (result != CAMERA_RESULT_SUCCESS)
254             {
255                 OS_TPrintf("CAMERA_I2CEffect was failed. (%d)\n", result);
256             }
257             *(current == CAMERA_SELECT_IN ? &flipIn : &flipOut) = CAMERA_FLIP_NONE;
258         }
259     }
260 }
261 
262 //--------------------------------------------------------------------------------
263 //    V-Blank interrupt process
264 //
VBlankIntr(void)265 void VBlankIntr(void)
266 {
267     OS_SetIrqCheckFlag(OS_IE_V_BLANK); // Checking V-Blank interrupt
268     if (wp == rp)
269     {
270         rp ^= 1;
271         // This is just a simple copy, but format conversion and so on could be included
272         GX_LoadBG3Scr(buffer[rp], 0, BYTES_PER_LINE * HEIGHT);
273         DC_InvalidateRange(buffer[rp], BYTES_PER_LINE * HEIGHT);
274     }
275     GX_LoadBG1Scr(text_buffer, 0, sizeof(text_buffer));
276 }
277 
278 //--------------------------------------------------------------------------------
279 //    Camera initialization
280 //
CameraInit(void)281 BOOL CameraInit(void)
282 {
283     CAMERAResult result;
284 
285     result = CAMERA_Init();
286     if(result == CAMERA_RESULT_FATAL_ERROR)
287         OS_TPanic("CAMERA_Init was failed.");
288     if(result == CAMERA_RESULT_ILLEGAL_STATUS)
289         return FALSE;
290 
291     result = CAMERA_I2CFlip(CAMERA_SELECT_IN, flipIn);
292     if (result != CAMERA_RESULT_SUCCESS)
293     {
294         OS_TPrintf("CAMERA_I2CFlip was failed. (%d)\n", result);
295     }
296     result = CAMERA_I2CFlip(CAMERA_SELECT_OUT, flipOut);
297     if (result != CAMERA_RESULT_SUCCESS)
298     {
299         OS_TPrintf("CAMERA_I2CFlip was failed. (%d)\n", result);
300     }
301     result = CAMERA_I2CActivate(current);
302     if (result == CAMERA_RESULT_FATAL_ERROR)
303         OS_TPanic("CAMERA_I2CActivate was failed. (%d)\n", result);
304     if(result == CAMERA_RESULT_ILLEGAL_STATUS)
305         return FALSE;
306     stabilizedCount = 0;
307 
308     return TRUE;
309 }
310 
311 //--------------------------------------------------------------------------------
312 //    Camera interrupt process (occurs both when there is an error and for Vsync)
313 //
314 #define FPS_SAMPLES 4
CameraIntrError(CAMERAResult result)315 void CameraIntrError(CAMERAResult result)
316 {
317 #pragma unused(result)
318     OS_TPrintf("Error was occurred.\n");
319     // Camera stop processing
320     CAMERA_StopCapture();
321     MI_StopNDma(NDMA_NO);
322     CAMERA_ClearBuffer();
323     wp_pending = TRUE;      // Also use same frame next time
324     CAMERA_DmaRecvAsync(NDMA_NO, buffer[wp], CAMERA_GetBytesAtOnce(WIDTH), CAMERA_GET_FRAME_BYTES(WIDTH, HEIGHT), CameraDmaRecvIntr, NULL);
325     CAMERA_StartCapture();
326 }
327 
CameraIntrReboot(CAMERAResult result)328 void CameraIntrReboot(CAMERAResult result)
329 {
330     if(result == CAMERA_RESULT_FATAL_ERROR)
331     {
332         return; // Restore was not possible, even after restarting camera
333     }
334     CameraIntrError(result); // DMA synchronization might have drifted, so realign
335 }
336 
CameraIntrVsync(CAMERAResult result)337 void CameraIntrVsync(CAMERAResult result)
338 {
339 #pragma unused(result)
340     if(stabilizedCount <= 30)
341         stabilizedCount++;
342     if(switchFlag)
343     {
344         current = (current == CAMERA_SELECT_IN ? CAMERA_SELECT_OUT : CAMERA_SELECT_IN);
345         OS_TPrintf("call CAMERA_I2CActivate(%s)... ", (current == CAMERA_SELECT_IN ? "CAMERA_SELECT_IN" : "CAMERA_SELECT_OUT"));
346         result = CAMERA_I2CActivate(current);
347         if(result == CAMERA_RESULT_FATAL_ERROR)
348             OS_Panic("CAMERA FATAL ERROR\n");
349         stabilizedCount = 0;
350         switchFlag = FALSE;
351         OS_TPrintf("%s\n", result == CAMERA_RESULT_SUCCESS ? "SUCCESS" : "FAILED");
352     }
353 }
354 
CameraDmaRecvIntr(void * arg)355 void CameraDmaRecvIntr(void* arg)
356 {
357 #pragma unused(arg)
358     MI_StopNDma(NDMA_NO);
359 
360     if (CAMERA_IsBusy() == TRUE)
361     {
362         if (MI_IsNDmaBusy(NDMA_NO)) // Check whether image transfer is complete
363         {
364             OS_TPrintf("DMA was not done until VBlank.\n");
365             MI_StopNDma(NDMA_NO);
366         }
367         // Start the next frame capture
368         if (wp_pending)
369         {
370             wp_pending = FALSE;
371         }
372         else
373         {
374             // Capture results are not displayed on screen until camera is stable
375             // 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.
376             //
377             if(stabilizedCount > 4)
378             {
379                 wp ^= 1;
380             }
381         }
382         CAMERA_DmaRecvAsync(NDMA_NO, buffer[wp], CAMERA_GetBytesAtOnce(WIDTH), CAMERA_GET_FRAME_BYTES(WIDTH, HEIGHT), CameraDmaRecvIntr, NULL);
383     }
384 
385     // Display frame rate
386     {
387         static OSTick begin = 0;
388         static int uspf[FPS_SAMPLES] = { 0 };
389         static int count = 0;
390         int i;
391         int sum = 0;
392         OSTick end = OS_GetTick();
393         if (begin)  // Leave out the first time
394         {
395             uspf[count] = (int)OS_TicksToMicroSeconds(end - begin);
396             count = (count + 1) % FPS_SAMPLES;
397         }
398         begin = end;
399         // Calculate average value
400         for (i = 0; i < FPS_SAMPLES; i++)
401         {
402             if (uspf[i] == 0)  break;
403             sum +=  uspf[i];
404         }
405         if (sum)
406         {
407             int mfps = (int)(1000000000LL * i / sum);
408             PutString("%2d.%03d fps", mfps / 1000, mfps % 1000);
409         }
410     }
411 }
412