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-06-15#$
14   $Rev: 10750 $
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 u32 stabilizedCount;
37 
38 static BOOL switchFlag;
39 
40 /////
41 //  Triple buffer sequence that assumes the display FPS rate is larger 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     // DMA is not used in GX (the old DMA conflicts with camera DMA)
163     (void)GX_SetDefaultDMA(GX_DMA_NOT_USE);
164 
165     // Clears VRAM
166     GX_SetBankForLCDC(GX_VRAM_LCDC_A);
167     GX_SetBankForLCDC(GX_VRAM_LCDC_B);
168     GX_SetBankForLCDC(GX_VRAM_LCDC_C);
169     GX_SetBankForLCDC(GX_VRAM_LCDC_D);
170     MI_CpuClearFast((void*)HW_LCDC_VRAM_A, 128 * 1024);
171     MI_CpuClearFast((void*)HW_LCDC_VRAM_B, 128 * 1024);
172     MI_CpuClearFast((void*)HW_LCDC_VRAM_C, 128 * 1024);
173     MI_CpuClearFast((void*)HW_LCDC_VRAM_D, 128 * 1024);
174 
175     // Direct bitmap display mode and text display
176     GX_SetBankForBG(GX_VRAM_BG_512_ABCD);         // Assign VRAM-A, B, C, and D banks to the background
177     GX_SetGraphicsMode(GX_DISPMODE_GRAPHICS, GX_BGMODE_4, GX_BG0_AS_2D);
178     GX_SetVisiblePlane(GX_PLANEMASK_BG1 | GX_PLANEMASK_BG3);
179 
180     G2_SetBG1Control(GX_BG_SCRSIZE_TEXT_256x256, GX_BG_COLORMODE_16,
181                      GX_BG_SCRBASE_0x0000, GX_BG_CHARBASE_0x04000, GX_BG_EXTPLTT_01);
182     G2_SetBG1Priority(1);
183     G2_BG1Mosaic(FALSE);
184 
185     wp = 0;
186     rp = 2;
187     wp_pending = TRUE;
188     stabilizedCount = 0;
189     switchFlag = FALSE;
190     G2_SetBG3ControlDCBmp(GX_BG_SCRSIZE_DCBMP_256x256, GX_BG_AREAOVER_XLU, GetNextDispSrc());
191     G2_SetBG3Priority(3);
192     G2_BG3Mosaic(FALSE);
193 
194     // Load text
195     {
196         static const GXRgb pal[16] = { GX_RGB(0, 0, 0), GX_RGB(31, 31, 31), };
197         GX_LoadBG1Char(DEMOAsciiChr, 0x00000, sizeof(DEMOAsciiChr));
198         GX_LoadBGPltt(pal, 0x0000, sizeof(pal));
199     }
200 
201     // V-Blank interrupt settings
202     OS_SetIrqFunction(OS_IE_V_BLANK, VBlankIntr);
203     (void)OS_EnableIrqMask(OS_IE_V_BLANK);
204     (void)OS_EnableIrq();
205     (void)GX_VBlankIntr(TRUE);
206     (void)OS_EnableInterrupts();
207 
208     OS_WaitVBlankIntr();
209     GX_DispOn();
210 
211     // Initialize camera
212     current = CAMERA_SELECT_IN;
213     (void)CameraInit();
214 
215     // Configure DMA interrupt
216     (void)OS_EnableIrqMask(OS_IE_NDMA1);
217 
218     // Camera VSYNC interrupt callback
219     CAMERA_SetVsyncCallback(CameraIntrVsync);
220 
221     // Camera error interrupt callback
222     CAMERA_SetBufferErrorCallback(CameraIntrError);
223 
224     // Camera restart completion callback
225     CAMERA_SetRebootCallback(CameraIntrReboot);
226 
227     CAMERA_SetOutputFormat(CAMERA_OUTPUT_RGB);
228     CAMERA_SetTransferLines(CAMERA_GET_MAX_LINES(WIDTH));
229 
230     // Start capturing
231     CAMERA_DmaRecvAsync(NDMA_NO, GetNextCaptureAddr(), CAMERA_GetBytesAtOnce(WIDTH), CAMERA_GET_FRAME_BYTES(WIDTH, HEIGHT), CameraDmaRecvIntr, NULL);
232     CAMERA_ClearBuffer();
233     CAMERA_StartCapture();
234     OS_TPrintf("Camera is shooting a movie...\n");
235 
236     while (1)
237     {
238         u16 pad;
239         u16 trg;
240         static u16 old = 0xffff;
241         static BOOL standby = FALSE;
242 
243         OS_WaitVBlankIntr();
244 
245         pad = PAD_Read();
246         trg = (u16)(pad & ~old);
247         old = pad;
248 
249         if (trg & PAD_BUTTON_A)
250         {
251             DebugReport();
252         }
253         if (trg & PAD_BUTTON_X)
254         {
255             switchFlag = TRUE;
256         }
257         if (trg & PAD_BUTTON_B)
258         {
259             standby ^= 1;
260             if (standby)
261             {
262                 // 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"
263                 CAMERA_StopCapture();
264                 while(CAMERA_IsBusy())
265                 {
266                 }
267                 MI_StopNDma(NDMA_NO);
268                 result = CAMERA_I2CActivate(CAMERA_SELECT_NONE);
269                 if(result == CAMERA_RESULT_FATAL_ERROR)
270                     OS_Panic("CAMERA FATAL ERROR\n");
271             }
272             else
273             {
274                 result = CAMERA_I2CActivate(current);
275                 if(result == CAMERA_RESULT_FATAL_ERROR)
276                     OS_Panic("CAMERA FATAL ERROR\n");
277                 stabilizedCount = 0;
278                 CAMERA_DmaRecvAsync(NDMA_NO, GetNextCaptureAddr(), CAMERA_GetBytesAtOnce(WIDTH), CAMERA_GET_FRAME_BYTES(WIDTH, HEIGHT), CameraDmaRecvIntr, NULL);
279                 CAMERA_ClearBuffer();
280                 CAMERA_StartCapture();
281             }
282             OS_TPrintf("%s\n", result == CAMERA_RESULT_SUCCESS ? "SUCCESS" : "FAILED");
283         }
284     }
285 }
286 
287 //--------------------------------------------------------------------------------
288 //    V-Blank interrupt process
289 //
VBlankIntr(void)290 void VBlankIntr(void)
291 {
292     OS_SetIrqCheckFlag(OS_IE_V_BLANK);
293     G2_SetBG3ControlDCBmp(GX_BG_SCRSIZE_DCBMP_256x256, GX_BG_AREAOVER_XLU, GetNextDispSrc());
294     GX_LoadBG1Scr(text_buffer, 0, sizeof(text_buffer));
295 }
296 
297 //--------------------------------------------------------------------------------
298 //    Camera initialization (only the Init- and I2C-related initialization)
299 //
CameraInit(void)300 BOOL CameraInit(void)
301 {
302     CAMERAResult result;
303     result = CAMERA_Init();
304     if(result == CAMERA_RESULT_FATAL_ERROR)
305         OS_TPanic("CAMERA_Init was failed.");
306     if(result == CAMERA_RESULT_ILLEGAL_STATUS)
307         return FALSE;
308 
309     result = CAMERA_I2CActivate(current);
310     if (result == CAMERA_RESULT_FATAL_ERROR)
311         OS_TPanic("CAMERA_I2CActivate was failed. (%d)\n", result);
312     if(result == CAMERA_RESULT_ILLEGAL_STATUS)
313         return FALSE;
314     stabilizedCount = 0;
315 
316     return TRUE;
317 }
318 //--------------------------------------------------------------------------------
319 //    Camera interrupt process (occurs both when there is an error and for Vsync)
320 //
321 #define FPS_SAMPLES 4
CameraIntrError(CAMERAResult result)322 void CameraIntrError(CAMERAResult result)
323 {
324 #pragma unused(result)
325     OS_TPrintf("Error was occurred.\n");
326     // Camera stop processing
327     CAMERA_StopCapture();
328     MI_StopNDma(NDMA_NO);
329     CAMERA_ClearBuffer();
330     PendingCapture();       // Also use same frame next time
331     CAMERA_DmaRecvAsync(NDMA_NO, GetNextCaptureAddr(), CAMERA_GetBytesAtOnce(WIDTH), CAMERA_GET_FRAME_BYTES(WIDTH, HEIGHT), CameraDmaRecvIntr, NULL);
332     CAMERA_StartCapture();
333 }
334 
CameraIntrReboot(CAMERAResult result)335 void CameraIntrReboot(CAMERAResult result)
336 {
337     if(result == CAMERA_RESULT_FATAL_ERROR)
338     {
339         return; // Restore was not possible, even after restarting camera
340     }
341     CameraIntrError(result); // DMA synchronization might have drifted, so realign
342 }
343 
CameraIntrVsync(CAMERAResult result)344 void CameraIntrVsync(CAMERAResult result)
345 {
346 #pragma unused(result)
347     if(stabilizedCount <= 30)
348         stabilizedCount++;
349     if(switchFlag)
350     {
351         current = (current == CAMERA_SELECT_IN ? CAMERA_SELECT_OUT : CAMERA_SELECT_IN);
352         OS_TPrintf("call CAMERA_I2CActivate(%s)... ", (current == CAMERA_SELECT_IN ? "CAMERA_SELECT_IN" : "CAMERA_SELECT_OUT"));
353         result = CAMERA_I2CActivate(current);
354         if(result == CAMERA_RESULT_FATAL_ERROR)
355             OS_Panic("CAMERA FATAL ERROR\n");
356         OS_TPrintf("%s\n", result == CAMERA_RESULT_SUCCESS ? "SUCCESS" : "FAILED");
357         stabilizedCount = 0;
358         switchFlag = FALSE;
359     }
360 }
361 
CameraDmaRecvIntr(void * arg)362 void CameraDmaRecvIntr(void* arg)
363 {
364 #pragma unused(arg)
365     MI_StopNDma(NDMA_NO);
366 
367     if (CAMERA_IsBusy() == TRUE)
368     {
369         if (MI_IsNDmaBusy(NDMA_NO)) // Check whether image transfer is complete
370         {
371             OS_TPrintf("DMA was not done until VBlank.\n");
372             MI_StopNDma(NDMA_NO);
373         }
374         // Start the next frame capture
375         CAMERA_DmaRecvAsync(NDMA_NO, GetNextCaptureAddr(), CAMERA_GetBytesAtOnce(WIDTH), CAMERA_GET_FRAME_BYTES(WIDTH, HEIGHT), CameraDmaRecvIntr, NULL);
376     }
377 
378     // Display frame rate
379     {
380         static OSTick begin = 0;
381         static int uspf[FPS_SAMPLES] = { 0 };
382         static int count = 0;
383         int i;
384         int sum = 0;
385         OSTick end = OS_GetTick();
386         if (begin)  // Leave out the first time
387         {
388             uspf[count] = (int)OS_TicksToMicroSeconds(end - begin);
389             count = (count + 1) % FPS_SAMPLES;
390         }
391         begin = end;
392         // Calculate average value
393         for (i = 0; i < FPS_SAMPLES; i++)
394         {
395             if (uspf[i] == 0)  break;
396             sum +=  uspf[i];
397         }
398         if (sum)
399         {
400             int mfps = (int)(1000000000LL * i / sum);
401             PutString("%2d.%03d fps", mfps / 1000, mfps % 1000);
402         }
403     }
404 }
405