恩智浦的i.MX RT600是跨界處理器產品,同樣也是i.MX RTxxx系列的開山之作。不同於i.MX RT1xxx系列單片機,i.MX RT600 采用了雙核架構,將新一代Cortex-M33內核與高性能Cadence Tensilica HiFi 4 音頻DSP內核相結合,適用於32位沉浸式音頻播放和視頻用戶界面應用。i.MX RT600旨在通過安全、功率優化的嵌入式處理器充分挖掘語音輔助終端節點的潛力,因此針對音頻數據的采集、傳輸和處理,i.MX RT600都有豐富的硬件資源進行支持。其中,針對RT600的I2S外設,本文詳細地進行了介紹,並基於i.MX RT600 EVK開發板,在RT600的DSP端(HiFi4)實現了一個音頻數字回環的demo。
一、I2S介紹
1.1 I2S 接口
I2S總線為數字音頻流的傳輸提供了標准的通信接口,由飛利浦制定。I2S總線規范定義了一種3線串行總線,分別是:
1、串行時鍾SCK(也稱位時鍾BCLK),這是SDA線上數據的位時鍾。對應SDA的每一個數據位,SCLK都有產生一個脈沖。
2、幀時鍾WS(也稱LRCK,或FSYNC),以大多數單一立體聲格式的PDM數據來說,WS用於切換左右聲道的數據;在DSP或TDM模式下用作幀定界符。此外,I2S的采樣頻率是由WS頻率決定的。
3、串行數據(SDA),就是用二進制表示的音頻數據流,單個SDA提供一個音頻數據流,該數據流可能具有多種格式。
I2S接口除了以上3個總線外,在實際應用中還需要MCLK(Master CLK)時鍾。在某些I2S系統中,I2S從設備可能需要使用MCLK來構建位時鍾。一般情況下,它是采樣率(fs)的整倍數,例如256fs,64fs等。
1.2 RT600 I2S硬件架構
i.MX RT600一共包含8個可配置的通用串行接口模塊(Flexcomm接口),Flexcomms 0至6可以配置為I2S接口用於數字音頻數據的傳輸。下圖為I2S子系統的整體架構圖,每個I2S模塊都包含集成的FIFO和DMA支持, 每個I2S接口最多支持四個通道對的數據。

1.3 I2S幀格式
介紹完了I2S的接口定義,下面介紹在我們i.MX RT600上I2S外設提供了哪些幀格式。
首先是經典I2S模式(Classic I2S mode),經典I2S模式規范在SDA上定義了2聲道立體聲數據,其中WS狀態標識左(低)聲道和右(高)聲道,並且在WS轉換后,數據延遲1個SCK時鍾。 經典I2S模式的示意圖如下所示。

第二種模式是DSP模式(DSP mode)。在DSP模式中,不使用WS來標識左右聲道數據,而是將通道的音頻數據打包成位流的模式。在這種模式下,一包完整的音頻數據流從WS幀的上升沿開始。 通過WS的變化,DSP模式可以演變出如下圖所示的三種模式,分別為DSP mode with 50% WS,DSP mode with 1 SCK pulsed WS和DSP mode with 1 slot pulsed WS。



第三種模式為TDM模式。TDM模式能夠最多打包4個通道對(左右聲道)的音頻數據以位流的形式傳輸, 可以與DSP模式或經典I2S模式結合使用。 經典I2S模式下的TDM示意圖如下圖所示,一共有4對Slot音頻數據(共8聲道音頻數據),通過如圖所示的幀格式由一根SDA總線進行傳輸。

當然這邊需要注意的是,RT600上I2S的SDA總線能發送最多4對Slot音頻數據。另外寄存器”Configuration 1“和 ”Configuration register 1 for channel pairs 1, 2, and 3“ 中的ONECHANNEL位決定了I2S的數據通道是否為單通道模式,例如:如果ONECHANNEL設置為0,那么該通道的I2S數據流會被I2S總線視為左右聲道數據,如果ONECHANNEL設置為1,那么該通道對的I2S數據流被視為單個聲道數據。以上所有舉例的幀格式示意圖都是以ONECHANNEL=0為基礎的。
除了經典I2S模式下的TDM,還有在DSP模式下的TDM格式幀,下圖展示的是TDM在DSP modes with 1 SCK pulsed WS模式下的幀格式。因此,RT600能夠產生的I2S幀格式的種類是非常豐富的,用戶可以根據自己的需求進行配置。

二、應用
2.1 系統架構
在此應用中,音頻數字回環demo的整個架構如圖所示。WM8904編解碼器通過音頻數據線來接收音頻信號。音頻信號經過WM8904后會轉變成PCM信號。HiFi4通過一路I2S接口連續不斷地接收PCM信號,並使用另一個I2S接口將PCM信號發送回WM8904編解碼器,最后WM8904編解碼器輸出的音頻信號會給播放設備實時播放。另外,HiFi4利用一路I2C接口對WM8904進行初始化配置操作。

2.2 時鍾配置及采樣頻率計算
在RT600上使用I2S外設做音頻數字回環的關鍵一步是選擇I2S外設的時鍾源,以及配置I2S的采樣頻率。
首先需要確定MCLK和I2S的時鍾源。 如圖所示,MCLK有兩個時鍾源,分別是48/60m_irc和audio_pll-clk,而I2S至少有5個時鍾源。 在此應用中,我們把MCLK和I2S的時鍾源均設置為audio_pll_clk。

配置完MCLK和I2S外設的時鍾源后,下一步就是根據實際應用需求配置MCLK的時鍾頻率和I2S的時鍾頻率。 I2S的采樣頻率通常有兩大類,下表列出了這兩類典型的I2S采樣頻率。一旦I2S的采樣頻率確定了,那WS信號的頻率也就確定了。
| Typical Sample Frequency (Hz) | Typical Sample Frequency (Hz) |
|---|---|
| 11025 | 8000 |
| 22050 | 16000 |
| 44100 | 24000 |
| -------- | 32000 |
| -------- | 48000 |
| -------- | 96000 |
對於RT600來說,I2S采樣頻率是由I2S的時鍾源分頻得到的,而WS采樣頻率有上表所列的兩大類,為了能夠精確地通過時鍾分頻系數來計算得到這些典型的采樣頻率,筆者根據經驗總結,將I2S的時鍾輸入源配置為11.289MHz和24.576MHz是比較容易和推薦的。如果I2S的時鍾源為11.289MHZ,那么44.1KHz可以通過對其分頻得到;如果I2S的時鍾源為24.576MHZ,那么96KHz或者48KHz也可以通過對其分頻得到。
在音頻數字回環的demo中,選擇的信號源為PC端48KHz,16bit的立體聲(左右聲道)音頻信號。我們以此來計算並配置時鍾,過程如下:
- 將時鍾源audio_pll_clk配置為24.576MHZ,即audio_pll_clk = 24.576MHZ
- WS = 48KHz
- MCLK = audio_pll_clk = 24.576MHZ
- BCLK = WS * 聲道數 * 聲道位寬 = 48KHz * 2 * 16 = 1.536MHz
- I2S_DIV (I2S分頻系數) = audio_pll_clk / BCLK = 24.576MHz / 1.536MHz = 16
此外,在本應用中還需要將I2S模式配置為經典I2S模式。
以下代碼給出了I2S時鍾的具體配置:
#define DEMO_AUDIO_BIT_WIDTH (16)
#define DEMO_AUDIO_SAMPLE_RATE (48000)
#define DEMO_I2S_CLOCK_DIVIDER 24576000/2/16/48000
/* attach AUDIO PLL clock to FLEXCOMM1 (I2S1) */
CLOCK_AttachClk(kAUDIO_PLL_to_FLEXCOMM1);
/* attach AUDIO PLL clock to FLEXCOMM3 (I2S3) */
CLOCK_AttachClk(kAUDIO_PLL_to_FLEXCOMM3);
/* attach AUDIO PLL clock to MCLK */
CLOCK_AttachClk(kAUDIO_PLL_to_MCLK_CLK);
CLOCK_SetClkDiv(kCLOCK_DivMclkClk, 1);
SYSCTL1->MCLKPINDIR = SYSCTL1_MCLKPINDIR_MCLKPINDIR_MASK;
/* Set shared signal set 0: SCK, WS from Flexcomm1 */
SYSCTL1->SHAREDCTRLSET[0] = SYSCTL1_SHAREDCTRLSET_SHAREDSCKSEL(1) | SYSCTL1_SHAREDCTRLSET_SHAREDWSSEL(1);
/* Set flexcomm3 SCK, WS from shared signal set 0 */
SYSCTL1->FCCTRLSEL[3] = SYSCTL1_FCCTRLSEL_SCKINSEL(1) | SYSCTL1_FCCTRLSEL_WSINSEL(1);
s_TxConfig.divider = DEMO_I2S_CLOCK_DIVIDER;
s_RxConfig.divider = DEMO_I2S_CLOCK_DIVIDER;
2.3 WM8904 Codec介紹
WM8904是為便攜式音頻應用而優化的高性能超低功耗立體聲編解碼器。 WM8904使用標准的I2C總線控制接口,提供對WM8904所有功能的軟件配置。 在本應用中,WM8904作為I2C從設備,HiFi4可以通過I2C接口與編解碼器通信,並且可以使用I2C進行編解碼器初始化和配置。WM8904的單個寄存器的讀寫操作時序圖如下所示。 為了允許在同一接口上仲裁多個從機(或多個主機),WM8904通過使SDA引腳處於三態(而不是將其拉高)來發送邏輯1,因此需要一個外部上拉電阻來將SDA拉高。

同樣WM8904也需要被配置為經典I2S模式,其示意圖如下所示。可以對比發現,與我前文介紹的經典I2S模式是一致的。在檢測到LRCLK邊沿后,MSB在BCLK的第二個上升沿可用。 然后按順序傳輸直到最低位。另外,可以看到在一個采樣的LSB和下一個采樣的MSB之間有可能會存在未使用的BCLK時鍾信號,這個跟WM8904被配置的采樣位有關。

以下代碼給出了WM8904的具體配置:
wm8904_config_t wm8904Config = {
.i2cConfig = {.codecI2CInstance = BOARD_CODEC_I2C_INSTANCE, .codecI2CSourceClock
= 19000000U},
.recordSource = kWM8904_RecordSourceLineInput,
.recordChannelLeft = kWM8904_RecordChannelLeft2,
.recordChannelRight = kWM8904_RecordChannelRight2,
.playSource = kWM8904_PlaySourceDAC,
.slaveAddress = WM8904_I2C_ADDRESS,
.protocol = kWM8904_ProtocolI2S,
.format = {.sampleRate = kWM8904_SampleRate48kHz, .bitWidth
= kWM8904_BitWidth16},
.mclk_HZ = 24576000U,
.master = false,
};
static void I2C_Config(void)
{
PRINTF("Configure WM8904 codec\r\n");
/* protocol: i2s * sampleRate: 48K * bitwidth:16*/
if (CODEC_Init(codecHandle, &boardCodecConfig) != kStatus_Success)
{
PRINTF("WM8904_Init failed!\r\n");
}
/* Initial volume kept low for hearing safety. */
CODEC_SetVolume(codecHandle, kCODEC_PlayChannelHeadphoneLeft |kCODEC_PlayChannelHeadphoneRight, 0x0020);
}
2.4 DMA和中斷配置
在RT600上做音頻數據的處理推薦用DMA,從而減少對CPU資源的消耗。DMA在RT600上推薦的用法是CM33核使用DMA0,而HiFi4使用DMA1。此外,在HiFi4中使用DMA與在CM33端是有一定區別的,這個主要體現在以下幾點:
- 需要在XOS或XTOS中注冊並啟用HiFi4中斷。
- 在HiFi4中DMA操作的SRAM地址必須是non-cacheable。
- 需要由使用INPUTMUX注冊HiFi4中斷。
這些注冊的中斷與HiFi4的連接關系如下表所示。表中除了提供所需的中斷選擇之外,還顯示了各個中斷的中斷優先級。L1中斷的優先級最低,而L3中斷的優先級最高。

以下代碼給出了HiFi4 DMA和中斷的具體配置:
DMA_Init(DMA1);
/* XCHAL_EXTINT19_NUM, intlevel 2 */
INPUTMUX_AttachSignal(INPUTMUX, 18U, kINPUTMUX_Dmac1ToDspInterrupt);
xos_register_interrupt_handler(XCHAL_EXTINT19_NUM,
(XosIntFunc *) DMA_IRQHandle,
DMA1);
xos_interrupt_enable(XCHAL_EXTINT19_NUM);
DMA_EnableChannel(DMA1, DEMO_I2S_TX_CHANNEL);
DMA_SetChannelPriority(DMA1, DEMO_I2S_TX_CHANNEL, kDMA_ChannelPriority3);
DMA_CreateHandle(&s_DmaTxHandle, DMA1, DEMO_I2S_TX_CHANNEL);
DMA_EnableChannel(DMA1, DEMO_I2S_RX_CHANNEL);
DMA_SetChannelPriority(DMA1, DEMO_I2S_RX_CHANNEL, kDMA_ChannelPriority2);
DMA_CreateHandle(&s_DmaRxHandle, DMA1, DEMO_I2S_RX_CHANNEL);
2.5 音頻數據流的處理
MCU在同時接收和發送PCM數據並進行播放的應用場景中,容易出現播放音樂卡頓的情況,為了避免出現這種卡頓,一個好的傳輸機制是必不可少的,下圖就給出了一個處理PCM數據的思路。
如圖示,總共3個緩沖區用於PCM數據發送(TX)和接收(RX),數據的發送和接收都是通過I2S通道觸發DMA請求完成的,這3個緩沖區構成一個閉環。每當一個緩沖區中的PCM數據接收滿時,下一個緩沖區將立即開始接收,發送也同理,而且數據的接收和發送是同步的。通俗易懂的講,I2S的TX永遠追不上I2S的RX,並且TX和RX之間永遠有個緩沖區是准備好的。從初始框圖中可以看到,一開始連續兩次提交了I2S的TX和RX的DMA請求,這是為了能夠使PCM發送和接收的DMA請求之間從一開始就是無縫銜接的,這能夠保證PCM傳輸不出現時延。
當然,緩沖區的數量取決於用戶的實際需求。 當緩沖區為2時,這就構成了一個典型的乒乓緩沖區(ping-pong buffer)。為什么我這里采用了3個緩沖區構成1個ring buffer而不采用簡單的ping-pong buffer呢?肯定有人很疑惑,這是因為當PCM數據較為復雜時,例如我介紹的另一篇RT600之DMIC外設介紹及應用的文章中,就需要ring buffer的機制。
在本應用中,1幀PCM數據的位寬為32bit(左右聲道各16bit),每個緩沖區設置有8幀PCM數據,也就是說每個緩沖區的PCM數據長度為256bit。每個緩沖區的PCM數據長度也對應了I2S通道每次傳輸的數據長度,在RT600中I2S一次能夠傳輸的音頻數據長度最大支持2048bits,所以不能超過這個范圍。

三、RT600 硬件演示平台搭建
為了演示I2S音頻數字回環的demo,需要有一塊如下圖所示的RT600 EVK RevE板子,然后還需要注意以下幾點:
- JP7.1連接到JP7.2。
- JP8.1連接到JP8.2。
- J3口作為音頻信號的輸入,可以連接到PC端。
- J4口作為音頻信號的輸出,連到揚聲器。
- 將ISP開關(SW5)切換成0b010,即ON,OFF,ON
- 將USB插入板上J6口。

至此,RT600之I2S外設介紹完畢。
