/*---------------------------------------------------------------------------* Project: TwlSDK - demos.TWL - spi - pmAmpGainLevel File: main.c Copyright 2008 Nintendo. All rights reserved. These coded instructions, statements, and computer programs contain proprietary information of Nintendo of America Inc. and/or Nintendo Company Ltd., and are protected by Federal copyright law. They may not be disclosed to third parties or copied or duplicated in any form, in whole or in part, without the prior written consent of Nintendo. $Date:: 2009-09-24#$ $Rev: 11063 $ $Author: okubata_ryoma $ *---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------* A sample that controls the amplifier gain for microphone input. USAGE: UP, DOWN: Control amplifier gain LEFT: Minimize amplifier gain RIGHT: Maximize amplifier gain HOWTO: 1. Initialize MIC library. 2. Start auto sampling of MIC by default configuration. 3. When you change amplifier gain, you don't have to stop sampling. *---------------------------------------------------------------------------*/ #include #include "DEMO.h" /*---------------------------------------------------------------------------* Constant definitions *---------------------------------------------------------------------------*/ #define TEST_BUFFER_SIZE (4 * 1024) // 4 KB #define RETRY_MAX_COUNT 8 /*---------------------------------------------------------------------------* Internal Function Definitions *---------------------------------------------------------------------------*/ static void StepUpAmpGain(void); static void StepDownAmpGain(void); static void MaximizeAmpGain(void); static void MinimizeAmpGain(void); static u32 GetDefaultMicSamplingRate(void); static SNDEXFrequency GetI2SFrequency(void); static void StartMicSampling(const MICAutoParam* param); static void SetDrawData(void *address, MICSamplingType type); static void VBlankIntr(void); static void Init3D(void); static void Draw3D(void); static void DrawLine(s16 sx, s16 sy, s16 ex, s16 ey); /*---------------------------------------------------------------------------* Internal Variable Definitions *---------------------------------------------------------------------------*/ static MICAutoParam gMicAutoParam; static u8 gDrawData[192]; static u8 gMicData[TEST_BUFFER_SIZE] ATTRIBUTE_ALIGN(HW_CACHE_LINE_SIZE); /*---------------------------------------------------------------------------* Name: TwlMain Description: Initialization and main loop. Arguments: None. Returns: None. *---------------------------------------------------------------------------*/ void TwlMain(void) { /* Various types of initialization */ OS_Init(); GX_Init(); GX_DispOff(); GXS_DispOff(); // When in NITRO mode, stopped by Panic DEMOCheckRunOnTWL(); /* Initializes display settings */ GX_SetBankForLCDC(GX_VRAM_LCDC_ALL); MI_CpuClearFast((void *)HW_LCDC_VRAM, HW_LCDC_VRAM_SIZE); (void)GX_DisableBankForLCDC(); MI_CpuFillFast((void *)HW_OAM, GX_LCD_SIZE_Y, HW_OAM_SIZE); MI_CpuClearFast((void *)HW_PLTT, HW_PLTT_SIZE); MI_CpuFillFast((void *)HW_DB_OAM, GX_LCD_SIZE_Y, HW_DB_OAM_SIZE); MI_CpuClearFast((void *)HW_DB_PLTT, HW_DB_PLTT_SIZE); /* 3D-related initialization */ Init3D(); /* Interrupt settings */ OS_SetIrqFunction(OS_IE_V_BLANK, VBlankIntr); (void)OS_EnableIrqMask(OS_IE_V_BLANK); (void)OS_EnableIrq(); (void)OS_EnableInterrupts(); (void)GX_VBlankIntr(TRUE); /* Initialize related libraries */ { (void)PM_SetAmp(PM_AMP_ON); (void)PM_SetAmpGain(PM_AMPGAIN_80); MIC_Init(); gMicAutoParam.type = MIC_SAMPLING_TYPE_8BIT; gMicAutoParam.buffer = (void *)gMicData; gMicAutoParam.size = TEST_BUFFER_SIZE; gMicAutoParam.loop_enable = TRUE; gMicAutoParam.full_callback = NULL; gMicAutoParam.rate = GetDefaultMicSamplingRate(); StartMicSampling((const MICAutoParam *)&gMicAutoParam); } /* Initialize internal variables */ MI_CpuFill8(gDrawData, 0x80, sizeof(gDrawData)); /* LCD display start */ GX_DispOn(); GXS_DispOn(); /* Debug string output */ OS_Printf("pmAmpGainLevel demo started.\n"); OS_Printf(" up/down -> change amplifier gain\n"); OS_Printf(" left -> minimize amplifier gain\n"); OS_Printf(" right -> maximize amplifier gain\n"); OS_Printf("\n"); /* Empty call for getting key input data (strategy for pressing A Button in the IPL) */ (void)PAD_Read(); { u16 keyOld = 0; u16 keyTrg; u16 keyNow; /* Main loop */ while (TRUE) { /* Get key input data */ keyNow = PAD_Read(); keyTrg = (u16)((keyOld ^ keyNow) & keyNow); keyOld = keyNow; /* Change the amp gain */ if (keyTrg & PAD_KEY_UP) { StepUpAmpGain(); } if (keyTrg & PAD_KEY_DOWN) { StepDownAmpGain(); } if (keyTrg & PAD_KEY_LEFT) { MinimizeAmpGain(); } if (keyTrg & PAD_KEY_RIGHT) { MaximizeAmpGain(); } /* Render waveform */ SetDrawData(MIC_GetLastSamplingAddress(), gMicAutoParam.type); Draw3D(); /* Waiting for the V-Blank */ OS_WaitVBlankIntr(); } } } /*---------------------------------------------------------------------------* Name: StepDownAmpGain Description: Raises the amp gain by one level. Arguments: None. Returns: None. *---------------------------------------------------------------------------*/ static void StepUpAmpGain(void) { u8 gain; if (PM_GetAmpGainLevel(&gain) == PM_RESULT_SUCCESS) { if (OS_IsRunOnTwl() == TRUE) { gain ++; if ((gain >= PM_AMPGAIN_LEVEL_MIN) && (gain <= PM_AMPGAIN_LEVEL_MAX)) { if (PM_SetAmpGainLevel(gain) == PM_RESULT_SUCCESS) { OS_Printf("Amplifier gain level was changed to %d\n", gain); } } } else { switch (gain) { case PM_AMPGAIN_LEVEL_DS0: gain = PM_AMPGAIN_LEVEL_DS1; break; case PM_AMPGAIN_LEVEL_DS1: gain = PM_AMPGAIN_LEVEL_DS2; break; case PM_AMPGAIN_LEVEL_DS2: gain = PM_AMPGAIN_LEVEL_DS3; break; default: return; } if (PM_SetAmpGainLevel(gain) == PM_RESULT_SUCCESS) { OS_Printf("Amplifier gain level was changed to %d\n", gain); } } } } /*---------------------------------------------------------------------------* Name: StepDownAmpGain Description: Lowers the amp gain by one level. Arguments: None. Returns: None. *---------------------------------------------------------------------------*/ static void StepDownAmpGain(void) { u8 gain; if (PM_GetAmpGainLevel(&gain) == PM_RESULT_SUCCESS) { if (OS_IsRunOnTwl() == TRUE) { gain --; if ((gain >= PM_AMPGAIN_LEVEL_MIN) && (gain <= PM_AMPGAIN_LEVEL_MAX)) { if (PM_SetAmpGainLevel(gain) == PM_RESULT_SUCCESS) { OS_Printf("Amplifier gain level was changed to %d\n", gain); } } } else { switch (gain) { case PM_AMPGAIN_LEVEL_DS3: gain = PM_AMPGAIN_LEVEL_DS2; break; case PM_AMPGAIN_LEVEL_DS2: gain = PM_AMPGAIN_LEVEL_DS1; break; case PM_AMPGAIN_LEVEL_DS1: gain = PM_AMPGAIN_LEVEL_DS0; break; default: return; } if (PM_SetAmpGainLevel(gain) == PM_RESULT_SUCCESS) { OS_Printf("Amplifier gain level was changed to %d\n", gain); } } } } /*---------------------------------------------------------------------------* Name: MaximizeAmpGain Description: Maximizes amp gain. Arguments: None. Returns: None. *---------------------------------------------------------------------------*/ static void MaximizeAmpGain(void) { u8 gain; if (PM_GetAmpGainLevel(&gain) == PM_RESULT_SUCCESS) { if (OS_IsRunOnTwl() == TRUE) { if (gain != PM_AMPGAIN_LEVEL_MAX) { gain = PM_AMPGAIN_LEVEL_MAX; if (PM_SetAmpGainLevel(gain) == PM_RESULT_SUCCESS) { OS_Printf("Amplifier gain level was changed to %d\n", gain); } } } else { if (gain != PM_AMPGAIN_LEVEL_DS3) { gain = PM_AMPGAIN_LEVEL_DS3; if (PM_SetAmpGainLevel(gain) == PM_RESULT_SUCCESS) { OS_Printf("Amplifier gain level was changed to %d\n", gain); } } } } } /*---------------------------------------------------------------------------* Name: MinimizeAmpGain Description: Minimizes amp gain. Arguments: None. Returns: None. *---------------------------------------------------------------------------*/ static void MinimizeAmpGain(void) { u8 gain; if (PM_GetAmpGainLevel(&gain) == PM_RESULT_SUCCESS) { if (OS_IsRunOnTwl() == TRUE) { if (gain != PM_AMPGAIN_LEVEL_MIN) { gain = PM_AMPGAIN_LEVEL_MIN; if (PM_SetAmpGainLevel(gain) == PM_RESULT_SUCCESS) { OS_Printf("Amplifier gain level was changed to %d\n", gain); } } } else { if (gain != PM_AMPGAIN_LEVEL_DS0) { gain = PM_AMPGAIN_LEVEL_DS0; if (PM_SetAmpGainLevel(gain) == PM_RESULT_SUCCESS) { OS_Printf("Amplifier gain level was changed to %d\n", gain); } } } } } /*---------------------------------------------------------------------------* Name: GetDefaultMicSamplingRate Description: Determines the sampling rate for the microphone's auto-sampling. Arguments: None. Returns: u32: Returns an appropriate sampling rate. *---------------------------------------------------------------------------*/ static u32 GetDefaultMicSamplingRate(void) { if ((OS_IsRunOnTwl() == TRUE) && (GetI2SFrequency() == SNDEX_FREQUENCY_47610)) { return MIC_SAMPLING_RATE_15870; } else { return MIC_SAMPLING_RATE_16360; } } #include /*---------------------------------------------------------------------------* Name: GetI2SFrequency Description: Gets the I2S operating frequency. Arguments: None. Returns: SNDEXFrequency: Returns the I2S operating frequency. *---------------------------------------------------------------------------*/ static SNDEXFrequency GetI2SFrequency(void) { SNDEXResult result; SNDEXFrequency freq; s32 retry = 0; SNDEX_Init(); while (TRUE) { result = SNDEX_GetI2SFrequency(&freq); switch (result) { case SNDEX_RESULT_SUCCESS: // Success return freq; case SNDEX_RESULT_EXCLUSIVE: // Exclusion error case SNDEX_RESULT_PXI_SEND_ERROR: // PXI queue is full if (++ retry <= RETRY_MAX_COUNT) { OS_Sleep(1); continue; } OS_TWarning("%s: Retry count overflow.\n", __FUNCTION__); break; case SNDEX_RESULT_BEFORE_INIT: // Pre-initialization case SNDEX_RESULT_ILLEGAL_STATE: // Abnormal state OS_TWarning("%s: Illegal state to get I2S frequency.\n", __FUNCTION__); break; case SNDEX_RESULT_FATAL_ERROR: // Fatal error default: // Other fatal error OS_Panic("Fatal error (%d).\n", result); /* Never return */ } } return SNDEX_FREQUENCY_32730; } #include /*---------------------------------------------------------------------------* Name: StartMicSampling Description: Calls the microphone auto-sampling start function and performs error processing. Arguments: param: Parameters passed to the microphone functions Returns: None. *---------------------------------------------------------------------------*/ static void StartMicSampling(const MICAutoParam* param) { MICResult result; s32 retry = 0; while (TRUE) { result = MIC_StartLimitedSampling(param); switch (result) { case MIC_RESULT_SUCCESS: // Success return; case MIC_RESULT_BUSY: // Another thread is using the microphone case MIC_RESULT_SEND_ERROR: // PXI queue is full if (++retry <= RETRY_MAX_COUNT) { OS_Sleep(1); continue; } OS_TWarning("%s: Retry count overflow.\n", __FUNCTION__); return; case MIC_RESULT_ILLEGAL_STATUS: // Auto-sampling is not currently paused OS_TWarning("%s: Already started sampling.\n", __FUNCTION__); return; case MIC_RESULT_ILLEGAL_PARAMETER: // Unsupported parameter OS_TWarning("%s: Illegal parameter to start automatic sampling.\n", __FUNCTION__); return; default: // Other fatal error OS_Panic("Fatal error (%d).\n", result); /* Never return */ } } } /*---------------------------------------------------------------------------* Name: SetDrawData Description: Stores the current newest sampled data in the buffer that puts it on the display. Arguments: address: Location in main memory where the most recent sampling data was stored by the component type: Sampling type (bit width) Returns: None. *---------------------------------------------------------------------------*/ static void SetDrawData(void *address, MICSamplingType type) { s32 i; /* If sampling has never been performed, do nothing and stop. (Because it would delete memory cache(s) unrelated to the microphone) */ if ((address < gMicData) || (address >= (gMicData + TEST_BUFFER_SIZE))) { return; } switch (type) { case MIC_SAMPLING_TYPE_8BIT: case MIC_SAMPLING_TYPE_SIGNED_8BIT: /* In the case of 8-bit sampling */ { u8 *p; p = (u8 *)((u32)address - 191); if (p < gMicData) { p = (u8 *)((u32)p + TEST_BUFFER_SIZE); } DC_InvalidateRange((void *)((u32)p & ~(HW_CACHE_LINE_SIZE - 1)), HW_CACHE_LINE_SIZE); for (i = 0; i < 192; i++) { gDrawData[i] = *p; p++; if ((u32)p >= (u32)(gMicData + TEST_BUFFER_SIZE)) { p -= TEST_BUFFER_SIZE; } if (((u32)p % HW_CACHE_LINE_SIZE) == 0) { DC_InvalidateRange(p, HW_CACHE_LINE_SIZE); } } } break; case MIC_SAMPLING_TYPE_12BIT: case MIC_SAMPLING_TYPE_SIGNED_12BIT: case MIC_SAMPLING_TYPE_12BIT_FILTER_OFF: case MIC_SAMPLING_TYPE_SIGNED_12BIT_FILTER_OFF: /* For 12-bit sampling */ { u16 *p; p = (u16 *)((u32)address - 382); if ((u32)p < (u32)gMicData) { p = (u16 *)((u32)p + TEST_BUFFER_SIZE); } DC_InvalidateRange((void *)((u32)p & ~(HW_CACHE_LINE_SIZE - 1)), HW_CACHE_LINE_SIZE); for (i = 0; i < 192; i++) { gDrawData[i] = (u8)((*p >> 8) & 0x00ff); p++; if ((u32)p >= (u32)(gMicData + TEST_BUFFER_SIZE)) { p = (u16 *)((u32)p - TEST_BUFFER_SIZE); } if (((u32)p % HW_CACHE_LINE_SIZE) == 0) { DC_InvalidateRange(p, HW_CACHE_LINE_SIZE); } } } break; } } /*---------------------------------------------------------------------------* Name: VBlankIntr Description: V-Blank interrupt vector. Arguments: None. Returns: None. *---------------------------------------------------------------------------*/ static void VBlankIntr(void) { /* Sets the IRQ check flag */ OS_SetIrqCheckFlag(OS_IE_V_BLANK); } /*---------------------------------------------------------------------------* Name: Init3D Description: Initialization for 3D display Arguments: None. Returns: None. *---------------------------------------------------------------------------*/ static void Init3D(void) { G3X_Init(); G3X_InitMtxStack(); GX_SetGraphicsMode(GX_DISPMODE_GRAPHICS, GX_BGMODE_0, GX_BG0_AS_3D); GX_SetVisiblePlane(GX_PLANEMASK_BG0); G2_SetBG0Priority(0); G3X_AlphaTest(FALSE, 0); G3X_AntiAlias(TRUE); G3X_EdgeMarking(FALSE); G3X_SetFog(FALSE, (GXFogBlend)0, (GXFogSlope)0, 0); G3X_SetClearColor(0, 0, 0x7fff, 63, FALSE); G3_ViewPort(0, 0, 255, 191); G3_MtxMode(GX_MTXMODE_POSITION_VECTOR); } /*---------------------------------------------------------------------------* Name: Draw3D Description: Displays waveforms in 3D. Arguments: None. Returns: None. *---------------------------------------------------------------------------*/ static void Draw3D(void) { G3X_Reset(); G3_Identity(); G3_PolygonAttr(GX_LIGHTMASK_NONE, GX_POLYGONMODE_MODULATE, GX_CULL_NONE, 0, 31, 0); { s32 i; if ((gMicAutoParam.type == MIC_SAMPLING_TYPE_SIGNED_8BIT) || (gMicAutoParam.type == MIC_SAMPLING_TYPE_SIGNED_12BIT) || (gMicAutoParam.type == MIC_SAMPLING_TYPE_SIGNED_12BIT_FILTER_OFF)) { for (i = 0; i < 191; i++) { DrawLine((s16)((s8)gDrawData[i]), (s16)i, (s16)((s8)gDrawData[i + 1]), (s16)(i + 1)); } } else { for (i = 0; i < 191; i++) { DrawLine((s16)(gDrawData[i]), (s16)i, (s16)(gDrawData[i + 1]), (s16)(i + 1)); } } } G3_SwapBuffers(GX_SORTMODE_AUTO, GX_BUFFERMODE_W); } /*---------------------------------------------------------------------------* Name: DrawLine Description: Renders lines with triangular polygons Arguments: sx: x coordinate of line's starting point sy: y coordinate of line's starting point ex: x coordinate of line's ending point ey: y coordinate of line's ending point Returns: None. *---------------------------------------------------------------------------*/ static void DrawLine(s16 sx, s16 sy, s16 ex, s16 ey) { fx16 fsx = (fx16)(((sx - 128) * 0x1000) / 128); fx16 fsy = (fx16)(((96 - sy) * 0x1000) / 96); fx16 fex = (fx16)(((ex - 128) * 0x1000) / 128); fx16 fey = (fx16)(((96 - ey) * 0x1000) / 96); G3_Begin(GX_BEGIN_TRIANGLES); { G3_Color(GX_RGB(31, 31, 31)); G3_Vtx(fsx, fsy, 0); G3_Color(GX_RGB(31, 31, 31)); G3_Vtx(fex, fey, 0); G3_Color(GX_RGB(31, 31, 31)); G3_Vtx(fsx, fsy, 1); } G3_End(); }