Kampis Elektroecke

SSM2603 Audio Codec Erweiterung

Auf dem ZYBO befindet sich ein SSM2603 Audio Codec, der genutzt werden kann um Töne über einen Lautsprecher auszugeben, oder um Töne mit Hilfe eines Mikrofons aufzunehmen. Schauen wir uns mal an, wie der aktuell verwendete CS4344 D/A Wandler durch einen SSM2603 Audio Codec ersetzt werden kann.

Dazu muss das Blockdesign angepasst werden. Es wird eine I2C-Schnittstelle für das Processing System, zwei EMIO für den MUTE-Pin des Audio Codec und eine LED, sowie ein AXI GPIO Block für einen Lautstärkeregler benötigt:

Als nächstes muss die vorhandene Software des I2S-Audioplayers um eine Ansteuerung für den SSM2603 erweitert werden. Dazu wird als erstes die Funktion AudioPlayer_Init angepasst, sodass während der Initialisierung auch die benötigten EMIO und GPIO, sowie der SSM2603 initialisiert werden. Die einzelnen Initialisierungssequenzen habe ich der Übersichtlichkeit halber zudem in einzelne Funktionen unterteilt:

u32 AudioPlayer_Init(void)
{
	if((AudioPlayer_InitGIC() != XST_SUCCESS) || (AudioPlayer_InitFIFO() != XST_SUCCESS) || (AudioPlayer_InitGPIO() != XST_SUCCESS))
	{
		return XST_FAILURE;
	}

        ...

	xil_printf("[INFO] Initialize SSM2603 Audio Codec...\r\n");
	if(SSM2603_Init(&_CodecConfiguration) != XST_SUCCESS)
	{
		xil_printf("[ERROR] Can not initialize Audio Codec!\n\r");
		return XST_FAILURE;
	}

        ...
}

static u32 AudioPlayer_InitGPIO(void)
{
	xil_printf("[INFO] Looking for GPIO configuration...\r\n");
	_Mute_ConfigPtr = XGpioPs_LookupConfig(XPAR_PS7_GPIO_0_DEVICE_ID);
	_IO_ConfigPtr = XGpio_LookupConfig(XPAR_IO_DEVICE_ID);
	if((_Mute_ConfigPtr == NULL) || (_IO_ConfigPtr == NULL))
	{
		xil_printf("[ERROR] Invalid GPIO configuration!\r\n");
		return XST_FAILURE;
	}

	xil_printf("[INFO] Initialize GPIO...\r\n");
	if((XGpioPs_CfgInitialize(&_Mute, _Mute_ConfigPtr, _Mute_ConfigPtr->BaseAddr) != XST_SUCCESS) || (XGpio_CfgInitialize(&_IO, _IO_ConfigPtr, _IO_ConfigPtr->BaseAddress) != XST_SUCCESS))
	{
		xil_printf("[ERROR] GPIO initialization failed!\n\r");
		return XST_FAILURE;
	}

	XGpioPs_SetDirectionPin(&_Mute, 54, 0x01);
	XGpioPs_SetOutputEnablePin(&_Mute, 54, 0x01);
	XGpioPs_SetDirectionPin(&_Mute, 55, 0x01);
	XGpioPs_SetOutputEnablePin(&_Mute, 55, 0x01);

	xil_printf("[INFO] Enable GPIO interrupts...\r\n");
	XGpio_InterruptClear(&_IO, XGPIO_IR_MASK);
	XGpio_InterruptEnable(&_IO, XGPIO_IR_MASK);
	XGpio_InterruptGlobalEnable(&_IO);

	return XST_SUCCESS;
}

In der Initialisierungsfunktion für den SSM2603 wird die I2C-Schnittstelle des Processing Systems initialisiert, die Konfiguration in den Audio Codec geschrieben und der DAC, sowie der Ausgangsbuffer des Audio Codecs aktiviert:

uint32_t SSM2603_Init(const SSM2603_Configuration_t* Config)
{
	uint16_t Data;
	SSM2603_PowerDown_t PowerMask;

	xil_printf("[INFO] Looking for I2C configuration...\r\n");
	_Iic_ConfigPtr = XIicPs_LookupConfig(XPAR_XIICPS_0_DEVICE_ID);
	if(_Iic_ConfigPtr == NULL)
	{
		xil_printf("[ERROR] Invalid I2C configuration!\r\n");
		return XST_FAILURE;
	}

	xil_printf("[INFO] Initialize I2C...\r\n");
	if(XIicPs_CfgInitialize(&_Iic, _Iic_ConfigPtr, _Iic_ConfigPtr->BaseAddress) != XST_SUCCESS)
	{
		xil_printf("[ERROR] I2C initialization failed!\n\r");
		return XST_FAILURE;
	}

	XIicPs_SetSClk(&_Iic, SSM2603_SPEED);
        XIicPs_SetOptions(&_Iic, XIICPS_7_BIT_ADDR_OPTION);

        if(SSM2603_Reset())
        {
    	    xil_printf("[ERROR] Can not reset SSM2603!\n\r");
    	    return XST_FAILURE;
        }

	Data = (Config->DivClockOut << 0x07) | (Config->DivClock << 0x06) | (Config->ClockSettings << 0x05) | (Config->UseOversampling << 0x01) | (Config->UseUSB << 0x00);
	PowerMask = SSM2603_PWRDOWN_CLKOUT | SSM2603_PWRDOWN_OSC | SSM2603_PWRDOWN_OUT | SSM2603_PWRDOWN_DAC | SSM2603_PWRDOWN_ADC | SSM2603_PWRDOWN_MIC | SSM2603_PWRDOWN_LINEIN;

	if(SSM2603_SetPowerDown(PowerMask) || SSM2603_SetAudioPath(Config->AudioPath) || SSM2603_SetAudioFormat(Config->AudioFormat, Config->DataLength) || SSM2603_WriteRegister(SSM2603_REGISTER_R8, Data))
        {
    	    xil_printf("[ERROR] Can not configure SSM2603!\n\r");
    	    return XST_FAILURE;
        }

	if(Config->AudioPath & SSM2603_PATH_BYPASS)
	{
		PowerMask &= SSM2603_PWRDOWN_LINEIN;
	}

	if(Config->AudioPath & SSM2603_PATH_DAC)
	{
		PowerMask &= ~SSM2603_PWRDOWN_DAC;
	}

	if(Config->AudioPath & SSM2603_PATH_SIDE)
	{
		PowerMask &= SSM2603_PWRDOWN_MIC;
	}

	PowerMask &= ~SSM2603_PWRDOWN_OUT;

	return SSM2603_SetPowerDown(PowerMask) || SSM2603_SwitchEnable(true);
}

Nach der Initialisierung der einzelnen Komponenten wird die Lautstärke des SSM2603 auf 0 dB gesetzt und der aktuelle Zustand des MUTE-Schalters eingelesen.

if((SSM2603_SetVolume(SSM2603_CHANNEL_SPEAKER_L | SSM2603_CHANNEL_SPEAKER_R, AUDIOPLAYER_DEFAULT_SPEAKER_VOLUME) != XST_SUCCESS) || (SSM2603_GetVolume(SSM2603_CHANNEL_SPEAKER_L, &_SpeakerVolume[0]) != XST_SUCCESS) || (SSM2603_GetVolume(SSM2603_CHANNEL_SPEAKER_R, &_SpeakerVolume[1]) != XST_SUCCESS))
{
	return XST_FAILURE;
}

_MuteState = AudioPlayer_GetMute();

Info:

Das MUTE-Signal des Audio Codecs ist Active Low.


Zum Schluss wird der Zustand des internen Signals _MuteState auf den Zustand des MUTE-Schalters gesetzt und die Initialisierung beendet.

Ab jetzt kann über die Taster die Lautstärke der einzelnen Kanäle des ausgegebenen Signals eingestellt werden, bzw. das Audiosignal über den Schalter SW0 gemutet werden. Die Abarbeitung erfolgt in dem Callback der GPIO:

static void AudioPlayer_GpioHandler(void* CallbackRef)
{
    XGpio* InstancePtr = (XGpio*)CallbackRef;
    u32 Status = XGpio_InterruptGetStatus(InstancePtr);

    if(Status & XGPIO_IR_CH1_MASK)
    {
        if(XGpio_DiscreteRead(&_IO, 1) & 0x01)
        {
            AudioPlayer_Mute(true);
        }
        else
        {
            AudioPlayer_Mute(false);
        }
    }
    else if(Status & XGPIO_IR_CH2_MASK)
    {
        if(XGpio_DiscreteRead(&_IO, 2) & 0x01)
        {
            SSM2603_SetVolume(SSM2603_CHANNEL_SPEAKER_L, ++_SpeakerVolume[0]);
        }
        else if(XGpio_DiscreteRead(&_IO, 2) & 0x02)
        {
            SSM2603_SetVolume(SSM2603_CHANNEL_SPEAKER_L, --_SpeakerVolume[0]);
        }
        else if(XGpio_DiscreteRead(&_IO, 2) & 0x04)
        {
            SSM2603_SetVolume(SSM2603_CHANNEL_SPEAKER_R, ++_SpeakerVolume[1]);
        }
        else if(XGpio_DiscreteRead(&_IO, 2) & 0x08)
        {
            SSM2603_SetVolume(SSM2603_CHANNEL_SPEAKER_R, --_SpeakerVolume[1]);
        }
    }

    XGpio_InterruptClear(InstancePtr, Status);
}

Jetzt ist euer ZYBO in der Lage Musik über den integrierten Audio Codec auszugeben :)

Zurück

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert