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