yangjiaxu 发表于 2022-9-28 10:48

灵动的I2S音频播放-基于MM32F5270

I2S简介:
I2S ( Inter—IC Sound ) 总线是飞利浦公司为数字音频设备之间的音频数据传输而制定的一种总线标准,该总线专责于音频设备之间的数据传输,广泛应用于各种多媒体系统。它采用了沿独立的导线传输时钟与数据信号的设计,通过将数据和时钟信号分离,避免了因时差诱发的失真,为用户节省了购买抵抗音频抖动的专业设备的费用。在飞利浦公司的 I2S 标准中,既规定了硬件接口规范,也规定了数字音频数据的格式。

在 MM32F5270 中,SPI 与 I2S 共用引脚。在 I2S 的描述中,支持半双工通信,也支持全双工模式。I2S 与 SPI 区别在于,I2S 主模式的控制逻辑使用独立的时钟分频处理单元。在半双工模式下,可使用 MCK 引脚输出驱动时钟,用于驱动外部音频组件。在全双工模式下,为了能同时进行数据的收发,MCK 驱动时钟只能从专用的 MCK 功能引脚输出。I2S 的功能框图如图 1 所示,包括时钟单元,寄存器控制单元,主从模式控制单元等。

yangjiaxu 发表于 2022-9-28 10:48


MM32F5270 中 I2S 主要特征

支持半双工通信(仅发射机或接收机)和全双工通信(SD,extSD 的数据方向根据主/从模式配置决定)两种通信方式

9 位可编程线性预分频器,以达到精确的音频采样频率( 8KHz 到 192KHz)

数据帧格式可以是 16 位、 24 位或 32 位

数据包帧固定为 16 位(16 位有效数据)或 32 位(16 位、 24 位、 32 位有效数据)

可编程时钟极性(稳定状态)

发射模式下的下溢标志(仅从机),接收模式下的上溢标志(主和从机)和接收/发射模式下的帧错误标志(仅从机)

用于传输和接收的 32 位寄存器为两个声道分时复用

支持 I2S 协议

▶飞利浦标准

▶MSB 对齐标准(左对齐)

▶LSB 对齐标准(右对齐)

▶PCM 标准(在 16 位信道帧上具有短帧和长帧同步或扩展到 32 位信道帧的 16 位数据帧)

数据方向始终是 MSB 优先

DMA 传输能力

可配置输出 MCLK 来驱动外部音频组件,比率固定在 256× FS(其中 FS 为音频采样频率

yangjiaxu 发表于 2022-9-28 10:49


I2S 信号接口

I2S 和 SPI 共用三个公共管脚:

串行时钟 CK

映射在 SCK 引脚上,也叫位时钟 BCLK,是主模式下的串行时钟输出以及从机模式下的串行时钟输入。SCLK 频率 = 2 x 采样频率 x 采样位数

帧时钟 WS

映射在 NSS 引脚上,是主模式下的串行时钟输出以及从机模式下的串行时钟输入。用于切换左右声道,LRCK 频率 = 采样频率

串行数据 SD

映射在 MOSI 管脚上,二进制补码表示的音频数据,用于发送或接收两次多路数据通道(仅在半双工模式下)。

当某些外部设备需要主时钟输入时,可以使用一个附加的管脚输出时钟到音频设备

主时钟 MCLK

映射在 MISO 引脚或专用 MCK 引脚上,当 I2S 配置为主模式时使用此时钟。MCLK 频率 = 256 x 采样频率 Fs

yangjiaxu 发表于 2022-9-28 10:49


数据格式

三线总线处理音频数据,必须经过分时复用两个声道:右声道和左声道。因为只有一个 32 位寄存器用于传输或接收,所以软件应依次配置寄存器 TXREG 为各声道的数据,或依次读取寄存器 RXREG 为各声道的数据。按照 I2S 协议,总是先发送左声道,然后发送右声道。

数据格式可以采用以下格式进行发送:

16 位数据打包在 16 位帧中

16 位数据打包在 32 位帧中

24 位数据打包在 32 位帧中

32 位数据打包在 32 位帧中

当使用 32 位帧上发送 16 位数据时,前 16 位(MSB)是有效的位,16 位 LSB 制为 0,无需任何软件操作,通过硬件实现,其他格式相似

yangjiaxu 发表于 2022-9-28 10:49


通信标准

I2S 接口支持四种音频标准,通过配置寄存器 SPI_I2S_I2SCFGR 中的 I2SSTD、PCMSYNC 位进行切换;数据格式则通过配置 DATLEN、CHLEN 来进行选择。对于所有通信标准及数据格式,总是先发送最高位(MSB 优先)。

yangjiaxu 发表于 2022-9-28 10:52


飞利浦标准

对于飞利浦标准,WS 信号用于指示正在传输的声道。发射器在 CK 的下降沿锁存数据,接收器并在 CK 的上升读取数据。WS 信号也在 CK 的下降沿被锁定。对于这种标准 I2S 格式的信号,无论有多少位有效数据,数据的最高位总是出现在 WS 变化(也就是一帧开始)后的第 2 个 CK 脉冲处

yangjiaxu 发表于 2022-9-28 10:53

接下来进入正题-基于 MM32F5270 的声音播放实验

CS4344 芯片是实现本次实验功能的重要器件之一。CS4344 是一种立体声音频数模转换器(DAC) ,可使用单个 +3.3 V 或 +5 V 电源,仅需要最小的支持电路。该系列线性模拟低通滤波器和自动速度模式检测,当自动选择 2 kHz 和 200 kHz 之间的采样率,使用采样率和主时钟速率方法。

本实验的基本原理是 MM32F3270 读取正弦波采样计算出的左右声道的数组数据,通过 I2S 接口将 PCM 信号传输给 CS4344,再经过 TS4871(音频功率放大器)连接到耳机接口,可以接入耳机等音频播放装置。

yangjiaxu 发表于 2022-9-28 10:53

GPIO 初始化 BOARD_InitPins()

配置 I2S 引脚,I2S_CK 为 PD3 引脚,I2S_SD 为 PE6 引脚,I2S_WS 为 PE4 引脚,I2S_MCK 为 PE5 引脚,复用通道为 AF5 。void BOARD_InitPins(void)
{
    /* PB7 - UART1_TX. */
    GPIO_Init_Type gpio_init;
    gpio_init.Pins= GPIO_PIN_6;
    gpio_init.PinMode= GPIO_PinMode_AF_PushPull; //GPIO_PinMode_AF_PushPull
    gpio_init.Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB, &gpio_init);
    GPIO_PinAFConf(GPIOB, gpio_init.Pins, GPIO_AF_7);

    /* PB6 - UART1_RX. */
    gpio_init.Pins= GPIO_PIN_7;
    gpio_init.PinMode= GPIO_PinMode_In_Floating; //GPIO_PinMode_In_Floating
    gpio_init.Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB, &gpio_init);
    GPIO_PinAFConf(GPIOB, gpio_init.Pins, GPIO_AF_7);

    /* PD3 - I2S_CK. */
    gpio_init.Pins= GPIO_PIN_3;
    gpio_init.PinMode= GPIO_PinMode_AF_PushPull; //GPIO_PinMode_In_PushPull
    gpio_init.Speed = GPIO_Speed_10MHz;
    GPIO_Init(GPIOD, &gpio_init);
    GPIO_PinAFConf(GPIOD, gpio_init.Pins, GPIO_AF_5);

    /* PE6 - I2S_SD. */
    gpio_init.Pins= GPIO_PIN_6;
    gpio_init.PinMode= GPIO_PinMode_AF_PushPull; //GPIO_PinMode_In_PushPull
    gpio_init.Speed = GPIO_Speed_10MHz;
    GPIO_Init(GPIOE, &gpio_init);
    GPIO_PinAFConf(GPIOE, gpio_init.Pins, GPIO_AF_5);

    /* PE4 - I2S_WS. */
    gpio_init.Pins= GPIO_PIN_4;
    gpio_init.PinMode= GPIO_PinMode_AF_PushPull; //GPIO_PinMode_In_PushPull
    gpio_init.Speed = GPIO_Speed_10MHz;
    GPIO_Init(GPIOE, &gpio_init);
    GPIO_PinAFConf(GPIOE, gpio_init.Pins, GPIO_AF_5);

    /* PE5 - I2S_MCK. */
    gpio_init.Pins= GPIO_PIN_5;
    gpio_init.PinMode= GPIO_PinMode_AF_PushPull; //GPIO_PinMode_In_PushPull
    gpio_init.Speed = GPIO_Speed_10MHz;
    GPIO_Init(GPIOE, &gpio_init);
    GPIO_PinAFConf(GPIOE, gpio_init.Pins, GPIO_AF_5);
}

yangjiaxu 发表于 2022-9-28 10:54

I2S 配置初始化 app_i2s_master_init()

初始化 I2S,配置时钟频率、采样率、数据长度、通信协议、传输模式及是否使能 MCLK 。
void app_i2s_master_init(void)
{
    /* setup I2S master module. */
    I2S_Master_Init_Type i2s_master_init;

    i2s_master_init.ClockFreqHz= BOARD_I2S_FREQ;
    i2s_master_init.SampleRate   = BOARD_I2S_SAMPLE_RATE;
    i2s_master_init.DataWidth    = BOARD_I2S_DATA_WIDTH;
    i2s_master_init.Protocol   = BOARD_I2S_PROTOCOL;
    i2s_master_init.EnableMCLK   = true;
    i2s_master_init.Polarity   = BOARD_I2S_CPOL;
    i2s_master_init.XferMode   = I2S_XferMode_TxOnly;

    I2S_InitMaster(BOARD_I2S_PORT, &i2s_master_init);

    /* enable I2S. */
    I2S_Enable(BOARD_I2S_PORT, true);
}
Protocol
通信标准格式选择,可选 I2S Philips 标准、左对齐标准、右对齐标准、 PCM 短帧标准或 PCM 长帧标准,它设定 SPI\_I2S\_I2SCFGR 寄存器 I2SSTD位和 PCMSYNC位的值。一般设置为 I2S Philips 标准即可。
DataWidth
数据格式选择,设定有效数据长度和帧长度,可选标准 16bit 格式、扩展 16bit( 32bit 帧长度) 格式、 24bit 格式和 32bit 格式,它设定 SPI\_I2SCFGR 寄存器 DATLEN 位和CHLEN 位的值。对应 16bit 数据长度可选 16bit 或 32bit 帧长度,其他都是 32bit 帧长度。
EnableMCLK
主时钟输出使能控制,可选使能输出或禁止输出,它设定 SPI_I2SPR 寄存器 MCKOE 位的值。为提高系统性能一般使能主时钟输出。
SampleRate
采样频率设置,标准库提供采样采样频率选择,分别为 4KHz、8kHz、 11kHz、12KHz、16kHz、22kHz、32kHz、44kHz、48kHz、96kHz、192kHz 以及默认 2Hz,它设定 SPI\_I2S\_SPBRG 寄存器的值。
Polarity
空闲状态的 CK 线电平,可选高电平或低电平,它设定 SPI\_I2S\_CCTL 寄存器 CPOL位的值。一般设置为低电平即可。

yangjiaxu 发表于 2022-9-28 10:55


左右声道数据

采样函数 f (t) = A *sin( B*t ) ,t 为采样时间,单位: s。采样位宽为 16bit,采样频率为 8 khz,系数 A 为10000,B 为2000π。/* i2s left channel xfer data. */
const int16_t sound_buf_left =
{
    0, 3535, 5000, 3535, 0, -3535, -5000, -3535,
    0, 3535, 5000, 3535, 0, -3535, -5000, -3535,
};

/* i2s right channel xfer data. */
const int16_t sound_buf_right =
{
    0,3827,7071,9238,10000,9238,7071,3827,
    0, -3827, -7071, -9238, -10000, -9238, -7071, -3827
};

yangjiaxu 发表于 2022-9-28 10:55


左右声道数据传输
/* put data into left channel. */
void app_i2s_put_data_left(void)
{
    I2S_PutData(BOARD_I2S_PORT, (uint32_t)sound_buf_left);
    sound_buf_index_left++;
    if (sound_buf_index_left >= SOUND_BUF_SIZE)
    {
      sound_buf_index_left = 0;
    }
}

/* put data into right channel. */
void app_i2s_put_data_right(void)
{
    I2S_PutData(BOARD_I2S_PORT, (uint32_t)sound_buf_right);
    sound_buf_index_right++;
    if (sound_buf_index_right >= SOUND_BUF_SIZE)
    {
      sound_buf_index_right = 0;
    }
}

yangjiaxu 发表于 2022-9-28 10:56

主函数
main() 函数结合上述操作,串口打印 "i2s_master_basic" ,初始化 I2S 后,将发送数组数据到左右声道缓冲区,进行声音播放。int main(void)
{
    BOARD_Init();

    printf("i2s_master_basic.\r\n");

    app_i2s_master_init();

    while (1)
    {
      while(0u != (I2S_GetStatus(BOARD_I2S_PORT) & SPI_I2S_CSTAT_TXFULL_MASK) )
      {}

      app_i2s_put_data_left(); /* sending left channel data. */

      while(0u != (I2S_GetStatus(BOARD_I2S_PORT) & SPI_I2S_CSTAT_TXFULL_MASK) )
      {}
      app_i2s_put_data_right(); /* sending right channel data. */
    }
}

zerorobert 发表于 2022-10-5 18:46

MCK 驱动时钟有什么要求?      

belindagraham 发表于 2022-10-5 18:59

MM32F5270的性能怎么样            

yeates333 发表于 2022-10-5 19:19

I2S和SPDIF的数据格式区别?

saservice 发表于 2022-10-5 19:47

ms4344比cs4344好在哪里

kkzz 发表于 2022-10-9 20:05

这个如何解码呢?解码的速度是多少呢   

benjaminka 发表于 2022-10-9 20:52

接口使用什么通信协议               

jackcat 发表于 2022-10-9 21:46

I2S 和 SPI 可以分时复用吗?         

sdCAD 发表于 2022-10-9 22:37

MM32F5270 使用什么芯片驱动的外部电路呢?
页: [1] 2
查看完整版本: 灵动的I2S音频播放-基于MM32F5270