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