# An audio-processor IC TDA8425

Another movement towards finishing my “pet” project – an audio amplifier with remote control.

First thing first – the datasheet reading should be always the step #1, at least I’m trying to keep this rule in place.

This processor (a preamplifier as well btw) allows to:

• adjust the volume in the range of -64..6 dB
• adjust the bass in the range of -15..12 dB
• adjust the treble in the range of -12..12 dB
• has two separate inputs which are digitally muxed
• might switch the output to different modes: mono, stereo, pseudo-stereo

Firstly I decided to understand how to send commands and which functions do we have, thus some digits and code strings about to come. The i2c transfer to tda8425 always should begin from the address 82h. The second byte is a sub-address (the name from the datasheet, not my invention), here we have some things which might vary:

0h – a volume of the left channel

1h – a volume of the right channel

2h – the bass

3h – the treble

8h – switching functions

The third byte is a command, here we’re passing values we want to transfer. Some words of caution related to the command byte – in dependence of what exactly we are doing, the msb bit # will be different:

All command details could be found in the datasheet, so not a lot of reasons to double it in the post. But there is a tricky one – the volume, when I first starred at it I was quite puzzled:

Initially, I had developed a weird conversion algorithm (which is probably propagated to my code already and I dont remember it at the moment of the post translation into English) but after on I realized that it is a quite simple – 1 bit of the change equal to 2dB of the gain. So, for instance, between +6 and -64 we have 70 dB, or 35 bits, then to get -64 we need to do: 63d – 35d = 28d = 0b00011100 which is exactly what we see in the table.

Okay, time to move to the schematic.

I simply used the application schematic from the datasheet, with one minor addition – I had added the Q1 MOS transistor to have the control over the supply voltage. I made a diy pcb, assembled everything and started to work on the code. The volume of left/right channels:

oid VL(signed char VLbyte) //Громкость левого канала
{

if (VLbyte>6) VLbyte = 6;            //6 дБ наш максимум
if (VLbyte<-64) VLbyte = -80;        //-80 дБ наш минимум
if (VLbyte<-80) VLbyte = -80;

if (!(VLbyte%2))                    //если идем с шагом 2 дБ
{
if (VLbyte == -80)               //если -80
{
I2CStart();
I2CSend(0x82);               //адрес тда8425
I2CSend(0x00);               //байт выбора левого канала
I2CSend(192 | 0);            //шлем нулевое значение
I2CStop();
} else
{
I2CStart();
I2CSend(0x82);
I2CSend(0x00);
I2CSend(192 | (63 - (6-VLbyte)/2));  //иначе шлем по формуле
I2CStop();
}
}//!VLbyte
}

The right channels is quite similar of course, with one exclusion – the command byte. The conversion equations are here as I thought. Now the bass function:

void BassSetup(signed char BassByte) //Установки басса - разрешенные значения от -12 до 15, шаг 3 дБ
{

if (BassByte > 15) BassByte = 15;            //максимум 15 дБ
if (BassByte < -12) BassByte = -12;          //минимум -12 дБ

if (!(BassByte%3))          //Если на три не делится, то нам прилетел какой-то шлак
{
unsigned char temp;
temp = 0;
switch(BassByte)          //Разгребаем что нам прилетело
{
case 15 : temp = 11;
break;
case 12 : temp = 10;
break;
case 9 : temp = 9;
break;
case 6 : temp = 8;
break;
case 3 : temp = 7;
break;
case 0 : temp = 6;
break;
case -3 : temp = 5;
break;
case -6 : temp = 4;
break;
case -9 : temp = 3;
break;
case -12 : temp = 2;
break;
}//switch
if (temp)  //Если темп не в нуле, то что-то адекватное
{
I2CStart();
I2CSend(0x82);
I2CSend(0x02);
I2CSend(240 | temp);
I2CStop();
}
}

}

There is no conversion for this procedure, just a couple of discrete values, so I organized the switch sequence. The same is done for the treble function as well.

void TrebleSetup(signed char TrebleByte) //тембр от -12 до 12, шаг 3 дБ
{

if (TrebleByte < -12) TrebleByte = -12;
if (TrebleByte > 12) TrebleByte = 12;

if (!(TrebleByte%3)) //Если на три не делится, то нам прилетел какой-то шлак
{
unsigned char temp;
temp = 0;
switch(TrebleByte)
{
case 12 : temp = 10;
break;
case 9 : temp = 9;
break;
case 6 : temp = 8;
break;
case 3 : temp = 7;
break;
case 0 : temp = 6;
break;
case -3 : temp = 5;
break;
case -6 : temp = 4;
break;
case -9 : temp = 3;
break;
case -12 : temp = 2;
break;
} //switch
if (temp) //Если темп не в нуле, то что-то адекватное
{
I2CStart();
I2CSend(0x82);
I2CSend(0x03);
I2CSend(240 | temp);
I2CStop();
}

}
}

In the audio amplifier project I’m going to save all the data (volumem treble, bass and other function states) in the EEPROM, thus the brain of the amplifier should include an init procedure where it will read values and transfer them to tda8425.

For the functions we have 5 bits, let’s start from the Mute mode.

unsigned char Mute(unsigned char SwByte, unsigned char MuteBit) // Mute, возвращает обновленное значение байта переключений
{
SwByte &= 0b11011111;                        //Обнуляем бит mute в байте
MuteBit <<= 5;                                        //сдвигаем нужное значение на правильную позицию

I2CStart();
I2CSend(0x82);
I2CSend(0x08);
I2CSend(SwByte | MuteBit);                   //Шлем весь новый байт
I2CStop();

return (SwByte | MuteBit);                      //И возвращаем его для последующих операций
}

The one can see that the whole byte is used already, but since it used not in a single function we are reading the byte first and then adding the mute state with OR. Also the function is returning the whole byte value if we wish to write it to EEPROM for example.

Next, the mode selection:

unsigned char ModeSel(unsigned char SwByte, unsigned char ModeByte)

{

/* 0 - моно
1 - линейное стерео
2 - псевдо стерео
3 - пространственное стерео
*/
SwByte &= 0b11100111;
ModeByte <<= 3;
I2CStart();
I2CSend(0x82);
I2CSend(0x08);
I2CSend(SwByte | ModeByte);
I2CStop();

return (SwByte | ModeByte);
}

The last one – the source selection:

unsigned char SourceSel(unsigned char SwByte, unsigned char SourceByte)
{
/* 2 - 1 канал sound A
3 - 2 канал sound A
4 - 1 канал sound B
5 - 2 канал sound B
6 - 1 канал стерео
7 - 2 канал стерео
*/
SwByte &= 0b11111000;
I2CStart();
I2CSend(0x82);
I2CSend(0x08);
I2CSend(SwByte | SourceByte);
I2CStop();

return (SwByte | SourceByte);
}

All is done, now the question is how to check that it really works? I made a uart based menu for this purpose:

The soures are here

Using this uart menu I can check values and transfer them obviously, some live example is below: