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