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