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