【晶片手冊開發】Sil9136音訊開發詳細分析+原始碼實戰
阿新 • • 發佈:2020-11-21
[toc]
---
## 前言
* 預設在開發了視訊方面後
* 這方面的工作本來可以找技術支援拿個例程參考下,很快就可以的寫出來的,因為自己對HDMI協議不太瞭解,但是技術支援說沒有,所以沒辦法,只能自己搞了
* 看手冊不難,難的是找資料
* 記錄一下,也分享一下
## 參考
* sil9136暫存器手冊:**《Sil-PR-1060-C》**
* HDMI協議手冊:**《HDMI_1.4》**
* CEA標準手冊:**《CEA-861-D[安全]》**
* **使用參考例程**(*無音訊功能*)
## 手冊使用+實戰
* 以 I2S 介面為例開發
* 直接看手冊配置相關暫存器
* **記得輸入與輸出配對**
* 如編碼型別
* 取樣長度
* 取樣頻率
* 等等
### 配置
* 《Sil-PR-1060-C》手冊,28頁起
* ![](https://img2020.cnblogs.com/blog/2085252/202011/2085252-20201120213723759-111667844.png)
* 圖中說明 sil9136 支援 S/PDIF, I2S or DSD模式,主機可以通過配置TPI選擇不同的模式
* ![](https://img2020.cnblogs.com/blog/2085252/202011/2085252-20201120213731654-61667321.png)
* 這個暫存器表比較重要,說明了sil9136的暫存器配置
* 0x26 暫存器
* [7:6]
* 選擇模式,支援
* none
* S/PDIF
* I2S
* DSD
* [5]
* 通道數,支援
* 雙通道
* 8 通道
* [4]
* 靜音配置
* [3:0]
* 編碼型別,有
* Refer to Stream Header
* PCM (*本次使用 PCM*)
* AC-3
* MPEG1
* MP3
* MPEG2
* AAC
* DTS
* ATRAC
* 0x27 暫存器
* [7:6]
* 音訊取樣位元位長度 **SS**
* Refer to Stream Header
* 16 bit
* 20 bit
* 24 bit
* [5:3]
* 音訊取樣頻率 **SF**
* Refer to Stream Header
* 32 kHz
* 44.1 kHz
* 48 kHz
* 88.2 kHz
* 96 kHz
* 176.4 kHz
* 192 kHz
* [2]
* 是否支援高位元率
* **注意**:圖中說明的 0x24 和 0x25 暫存器只有在 S/PDIF 模式下有效,即是 0x26[7:6]=01 時。
### Configuring Audio Using I2S
* 直接跳到配置 I2S 流程,實現配置邏輯
* ![](https://img2020.cnblogs.com/blog/2085252/202011/2085252-20201120213743494-1600981779.png)
* 上圖已經很明顯地顯示出了配置出 I2S 的流程了
* 步驟:
1. 確保有有效的 I2S 訊號進入 sil9136
2. **設定 0x26[4] 為靜音模式**
* 直接呼叫例程介面:**`SetAudioMute(AUDIO_MUTE_MUTED);`**
3. **通過 0x20 來配置進來的 SD 格式**
* ![](https://img2020.cnblogs.com/blog/2085252/202011/2085252-20201120213752463-1751236004.png)
* *配置要和輸入的音訊配置搭配*
* 以下為個人選擇
* SCK Sample Edge :Rising
* MCLK Multiplier:256
* WS Polarity – Left when:WS is Low
* SD Justify Data is justified:Left
* SD Direction Byte shifted first:MSB
* WS to SD First Bit Shift:Yes
* 程式為:**`WriteByteTPI(TPI_I2S_IN_CFG, (0x80x10));`**
4. **通過多次設定 0x1F 來配置每一個 SD 輸入對映**
* ![](https://img2020.cnblogs.com/blog/2085252/202011/2085252-20201120213801164-848130155.png)
* SDx與FIFOn的對映
* 支援一對多
* **注意:必須順序對映,如如果要對映FIFO2,就必須先完成FIFO0和FIFO1的對映**
* 我的程式碼段:**SD0-FIFO0; SD1-FIFO1; SD2-FIFO2; SD3-FIFO3;**
```c
do{
WriteByteTPI(TPI_I2S_EN, 0x80);
Tmp = ReadByteTPI(TPI_I2S_EN);
}while(Tmp != 0x80);
do{
WriteByteTPI(TPI_I2S_EN, 0x91);
Tmp = ReadByteTPI(TPI_I2S_EN);
}while(Tmp != 0x91);
do{
WriteByteTPI(TPI_I2S_EN, 0xA2);
Tmp = ReadByteTPI(TPI_I2S_EN);
}while(Tmp != 0xA2);
do{
WriteByteTPI(TPI_I2S_EN, 0xB3);
Tmp = ReadByteTPI(TPI_I2S_EN);
}while(Tmp != 0xB3);
```
5. **通過設定 0x27[5:3] 來配置音訊取樣頻率**
* 配置為48kHz:**`ReadModifyWriteTPI(TPI_AUDIO_SAMPLE_CTRL, 0x38, 0x18);`**
6. **設定 0x21-0x25 來配置傳送到HDMI的頭資訊**
* ![](https://img2020.cnblogs.com/blog/2085252/202011/2085252-20201120213815694-1393027249.png)
* 上圖 0x21-0x25 的描述在 I2S 模式有效,即是 0x26[7:6] = 0x10
* 主要配置兩個引數
1. 取樣頻率:48 kHz
2. 取樣長度:24 bits
* 程式碼段
```c
WriteByteTPI(TPI_I2S_CHST_0, 0x00);
WriteByteTPI(TPI_I2S_CHST_1, 0x00);
WriteByteTPI(TPI_I2S_CHST_2, 0x00);
WriteByteTPI(TPI_I2S_CHST_3, 0x02);
WriteByteTPI(TPI_I2S_CHST_4, 0x0B);
```
7. **設定 0xBF-0xCD 來配置 audio infoframe**
* 這步驟先給出最終程式碼再分析:**`SetAudioInfoFrames(TWO_CHANNELS, 0x00, 0x00, 0x18, 0x00);`**
* ![](https://img2020.cnblogs.com/blog/2085252/202011/2085252-20201120213829449-1023336344.png)
* 說明了配置 audio infoframe 的必要性和需要參考的檔案 **HDMI Specification**,根據本檔案說明,瞭解到sil9136 支援 HDMI1.4 協議,所以準備好檔案《HDMI_1.4》,並找到關於 **audio infoframe** 的說明。
* 圖A
* ![](https://img2020.cnblogs.com/blog/2085252/202011/2085252-20201120213858023-1300178249.png)
* 圖B
* ![](https://img2020.cnblogs.com/blog/2085252/202011/2085252-20201120213903780-1784533537.png)
* 圖C
* ![](https://img2020.cnblogs.com/blog/2085252/202011/2085252-20201120213909903-1325677670.png)
* 圖Cc1,*《CEA-861-D安全》*
* ![](https://img2020.cnblogs.com/blog/2085252/202011/2085252-20201120213917708-1032554316.png)
* 圖Cc2,*《CEA-861-D安全》*
* ![](https://img2020.cnblogs.com/blog/2085252/202011/2085252-20201120213925424-1404842599.png)
* 圖D
* ![](https://img2020.cnblogs.com/blog/2085252/202011/2085252-20201120213930718-497220773.png)
* 結合例程原始碼 `bool SetAudioInfoFrames` 函式,得出只需要瞭解幾個引數配置即可。
* **byte ChannelCount**
* 參照*圖C和圖Cc1*,C0...C2,選擇雙通道,得出值為 **0x01**
* **byte CodingType**
* CT0...CT3: The CT bits shall always be set to a value of 0 (“Refer to Stream Header”).即是置為0即可
* **SS**
* The SS bits shall always be set to a value of 0 (“Refer to Stream Header”). 即是置為0即可
* **Fs**
* ***參考原始碼、0x27暫存器、圖C和圖Cc2***
* 推測 `Fs` 就是取樣頻率 SF0...SF2
* `B_Data[6] = (Fs >> 1) | (SS >> 6);`
* Fs在圖C中的PB2[4:2],而上述程式碼中右移一位,所以 Fs 的值佔用[5:3],參考0x27。
* 取樣頻率為 48kHz,得出 Fs=x018
* **SpeakerConfig**
* 參考原始碼 `B_Data[8] = SpeakerConfig;` 得出 SpeakerConfig 為 圖C中的PB4
* 這裡為 LPCM ,所以 SpeakerConfig = 0;
```c
//////////////////////////////////////////////////////////////////////////////
//
// FUNCTION : SetAudioInfoFrames()
//
// PURPOSE : Load Audio InfoFrame data into registers and send to sink
//
// INPUT PARAMS : (1) Channel count (2) speaker configuration per CEA-861D
// Tables 19, 20 (3) Coding type: 0x09 for DSD Audio. 0 (refer
// to stream header) for all the rest (4) Sample Frequency. Non
// zero for HBR only (5) Audio Sample Length. Non zero for HBR
// only.
//
// OUTPUT PARAMS : None
//
// GLOBALS USED : None
//
// RETURNS : TRUE
//
//////////////////////////////////////////////////////////////////////////////
bool SetAudioInfoFrames(byte ChannelCount, byte CodingType, byte SS, byte Fs, byte SpeakerConfig)
{
byte B_Data[SIZE_AUDIO_INFOFRAME]; // 14
byte i;
TPI_TRACE_PRINT((">>SetAudioInfoFrames()\n"));
for (i = 0; i < SIZE_AUDIO_INFOFRAME +1; i++)
B_Data[i] = 0;
B_Data[0] = EN_AUDIO_INFOFRAMES; // 0xC2
B_Data[1] = TYPE_AUDIO_INFOFRAMES; // 0x84
B_Data[2] = AUDIO_INFOFRAMES_VERSION; // 0x01
B_Data[3] = AUDIO_INFOFRAMES_LENGTH; // 0x0A
B_Data[5] = ChannelCount; // 0 for "Refer to Stream Header" or for 2 Channels. 0x07 for 8 Channels
B_Data[5] |= (CodingType << 4); // 0xC7[7:4] == 0b1001 for DSD Audio
B_Data[4] = 0x84 + 0x01 + 0x0A; // Calculate checksum
// B_Data[6] = (Fs << 2) | SS;
B_Data[6] = (Fs >> 1) | (SS >> 6);
//write Fs to 0x27[5:3] and SS to 0x27[7:6] to update the IForm with the current value.
// ReadModifyWriteTPI(TPI_AUDIO_SAMPLE_CTRL, BITS_7_6 | BITS_5_4_3, (B_Data[6] & BITS_1_0) << 6 | (B_Data[6] & 0x1C) << 1);
B_Data[8] = SpeakerConfig;
for (i = 5; i < SIZE_AUDIO_INFOFRAME; i++)
B_Data[4] += B_Data[i];
B_Data[4] = 0x100 - B_Data[4];
g_audio_Checksum = B_Data[4]; // Audio checksum for global use
WriteBlockTPI(TPI_AUDIO_BYTE_0, SIZE_AUDIO_INFOFRAME, B_Data);
#ifdef DEV_EMBEDDED
EnableEmbeddedSync();
#endif
return TRUE;
}
```
8. **I2S 模式, 設定音訊通道數,並關閉靜音**
1. 程式碼:**`WriteByteTPI(TPI_AUDIO_INTERFACE_REG, AUD_IF_I2S | TWO_CHANNEL_LAYOUT | 0x01);`**
2. ***注意:audio inframe中的通道數配置必須和 0x26 配置的一樣***
### 總結實現
* 得出一下程式碼,並把一下函式放到熱插拔的插入後執行即可
```c
/**
* @brief setPrivateAudio(void)
* @param
* @retval
* @author lzm
*/
void setPrivateAudio(void)
{
byte Tmp = 0;
/* Select I2S input mode using TPI 0x26[7:6], with Mute enabled (bit [4] = 1). */
SetAudioMute(AUDIO_MUTE_MUTED);
/* Write register TPI 0x20 to select the general incoming SD format. */
WriteByteTPI(TPI_I2S_IN_CFG, (SCK_SAMPLE_EDGE | 0x10));
/* Write register TPI 0x1F up to four times, to program each of the SD inputs. */
do{
WriteByteTPI(TPI_I2S_EN, 0x80);
Tmp = ReadByteTPI(TPI_I2S_EN);
}while(Tmp != 0x80);
do{
WriteByteTPI(TPI_I2S_EN, 0x91);
Tmp = ReadByteTPI(TPI_I2S_EN);
}while(Tmp != 0x91);
do{
WriteByteTPI(TPI_I2S_EN, 0xA2);
Tmp = ReadByteTPI(TPI_I2S_EN);
}while(Tmp != 0xA2);
do{
WriteByteTPI(TPI_I2S_EN, 0xB3);
Tmp = ReadByteTPI(TPI_I2S_EN);
}while(Tmp != 0xB3);
// /* Program register TPI 0x27 with the correct audio about. */
// WriteByteTPI(TPI_AUDIO_SAMPLE_CTRL, AUDIO_SAMPLE_SIZE_24BIT | AUDIO_SAMPLE_FREQ_48KHZ | AUDIO_SAMPLE_HBR_DISABLE);
/* Program register TPI 0x27[5:3] with the correct audio rate */
ReadModifyWriteTPI(TPI_AUDIO_SAMPLE_CTRL, 0x38, AUDIO_SAMPLE_FREQ_48KHZ);
/* Program registers TPI 0x21-x25 with the correct header information for the stream that will be sent over HDMI. */
WriteByteTPI(TPI_I2S_CHST_0, 0x00);
WriteByteTPI(TPI_I2S_CHST_1, 0x00);
WriteByteTPI(TPI_I2S_CHST_2, 0x00);
WriteByteTPI(TPI_I2S_CHST_3, 0x02);
WriteByteTPI(TPI_I2S_CHST_4, 0x0B);
/* Write registers TPI 0xBF-xCD with the appropriate Audio InfoFrame information. */
SetAudioInfoFrames(TWO_CHANNELS, 0x00, 0x00, 0x18, 0x00);
/* Set the audio packet header layout indicator to 2-channel or multi-channel mode as needed using the sequence described below.
Note that Audio InfoFrame byte 1 must also have this same setting. */
/* Again write register TPI 0x26 with I2S selected, this time with Mute disabled (bit [4] = 0). */
WriteByteTPI(TPI_AUDIO_INTERFACE_REG, AUD_IF_I2S | TWO_CHANNEL_LAYOUT | 0x01);
SetAudioMute(AUDIO_MUTE_NORMAL