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