K210开发板(三)—扬声器播放

  1. 一.实验准备
    1. 1.音频转化过程
    2. 2.I2S
    3. 3.PCM
    4. 4.函数介绍
      1. 4.1 i2s_tx_channel_config
      2. 4.2 i2s_play
  2. 二.实验步骤
    1. 1.配置
      1. 1.1.头文件
      2. 1.2.引脚配置
    2. 2.初始化
    3. 3.主体
  3. 三.实验总结

简介:
  本篇博客用于记录K210的学习和自己遇到的问题,主要内容为扬声器的播放。由于参考资料的教程比较齐全,本文会着重于讲述自己遇到的问题和解决方案


参考资料:http://www.yahboom.com/study/K210-Developer-Kit
提取码:moj1

一.实验准备

1.音频转化过程

  模拟量转成数字量过程,一般可以分为三个过程,分别为采样、量化、编码, 用一个比源声音频率高的采样信号去量化源声音,记录每个采样点的值, 最后如果把所有采样点数值连接起来与源声音曲线是互相吻合的,只是它不是连续的。 在图中两条蓝色虚线距离就是采样信号的周期,即对应一个采样频率(FS), 可以想象得到采样频率越高最后得到的结果就与源声音越吻合,但此时采样数据量越越大, 一般使用44.1KHz采样频率即可得到高保真的声音。每条蓝色虚线长度决定着该时刻源声音的量化值, 该量化值有另外一个概念与之挂钩,就是量化位数。量化位数表示每个采样点用多少位表示数据范围, 常用有16bit、24bit或32bit,位数越高最后还原得到的音质越好,数据量也会越大。

声音数字化过程

2.I2S

I2S 有 3 个主要信号:

  • 串行时钟 SCLK,也叫位时钟(BCLK),即对应数字音频的每一位数据,SCLK 都有 1 个脉冲。SCLK 的频率=2×采样频率×采样位数
  • 帧时钟 LRCK,用于切换左右声道的数据。LRCK 为“1”表示正在传输的 是左声道的数据,为“0”则表示正在传输的是右声道的数据。LRCK 的频率等于 采样频率。
  • 串行数据 SDATA,就是用二进制补码表示的音频数据。

  DAC 芯片将 I2S 信号转化成 RCH 信号输送给功放,功放将 RCH 信号发大,然 后扬声器播放出对应的声音。

3.PCM

  将每个数字化声音数据转换成二进制数据,该数据就是 PCM 数据。PCM 数据可以直接存储在介质上,也可以在经过编解码处理后进行存储或传输。

  PCM 数据常用量化指标:采样率位深、声道数、采样数据是否有符号(有符号的采样数据不能使用无符号的方式播放)、字节序(表示 PCM 数据是用大端存储还是小端存储,通常是小端存储)

  对于 8-bit PCM 数据而言,存储结构可能是左声道数据右声道数据交替存储

  PCM数据的音量调整由于数据溢出的可能性,不应当直接将数据*n,可通过对数变换处理x * tan(x/n)达到调整音量的效果

4.函数介绍

4.1 i2s_tx_channel_config

  设置I2S发送数据的通道参数

参数成员 描述 输入输出
device_num I2S号 输入
channel_num 通道号 输入
word_length 接收数据位数 输出
word_select_size 单个数据时钟数 输入
trigger_level DMA触发时FIFO深度 输入
word_mode 工作模式 输入

4.2 i2s_play

  发送 PCM 数据, 比如播放音乐

参数成员 描述 输入输出
device_num I2S号 输入
channel_num 通道号 输入
buf PCM PCM数据 输入
buf_len PCM数据长度 输入
frame 单次发送数量 输入
bits_per_sample 单次采样位宽 输入
track_num 声道数 输入

二.实验步骤

1.配置

1.1.头文件

1
2
//部分头文件
#include "pcm.h"

头文件pcm.h包含了变量uint16_t test_pcm[],其记录了要播放音频的PCM数据,参考资料直接给出了数据。这里我通过matlab将.wav文件转为PCM数据的方式实现播放任意音频。

1
2
3
4
5
6
7
8
9
10
%读取.wav文件
filename='name.wav';
[data,fs]=audioread(filename);
data=data(:,2);
%将数据归一化并转换为int16类型
pcm_data=int16(data * (2^15 - 1));
%将int16数据转换为uint16类型
pcm_data=typecast(pcm_data, 'uint16');
%将数据写入文件 以逗号分隔
dlmwrite('data.txt',pcm_data,'delimiter',',');

Tips:由于C语言不支持数组元素用空格分开,所以数据需要用逗号分隔再复制到pcm.h

1.2.引脚配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//定义硬件引脚(见原理图)
#define PIN_SPK_WS (30)
#define PIN_SPK_DATA (31)
#define PIN_SPK_BCK (32)
//定义软件GPIO口
/*FUNC_I2S2_WS
FUNC_I2S2_OUT_D0
FUNC_I2S2_SCLK*/
//将硬件引脚映射到软件GPIO功能
#define FUNC_SPK_WS FUNC_I2S2_WS
#define FUNC_SPK_DATA FUNC_I2S2_OUT_D0
#define FUNC_SPK_BCK FUNC_I2S2_SCLK

void hardware_init(void){
fpioa_set_function(PIN_SPK_WS, FUNC_SPK_WS);
fpioa_set_function(PIN_SPK_DATA, FUNC_SPK_DATA);
fpioa_set_function(PIN_SPK_BCK, FUNC_SPK_BCK);
}

2.初始化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//硬件引脚初始化
hardware_init();
//设置系统时钟
sysctl_pll_set_freq(SYSCTL_PLL0, 320000000UL);
sysctl_pll_set_freq(SYSCTL_PLL1, 160000000UL);
sysctl_pll_set_freq(SYSCTL_PLL2, 45158400UL);
uarths_init();
//初始化I2S,第三个参数为设置通道掩码,通道0:0x03,通道1:0x0C,通道2:0x30,通道3:0xC0
i2s_init(I2S_DEVICE_2, I2S_TRANSMITTER, 0x03);
//设置I2S发送数据的通道参数
i2s_tx_channel_config(
I2S_DEVICE_2, // I2S设备号
I2S_CHANNEL_0, // I2S通道
RESOLUTION_16_BIT, // 接收数据位数
SCLK_CYCLES_32, // 单个数据时钟数
TRIGGER_LEVEL_4, // DMA触发时FIFO深度
RIGHT_JUSTIFYING_MODE); // 工作模式

3.主体

1
2
3
4
5
6
7
8
i2s_play(
I2S_DEVICE_2, //I2S设备号
DMAC_CHANNEL0, //DMA通道号
(uint8_t *)test_pcm, //播放的PCM数据
sizeof(test_pcm), //PCM数据的长度
1024, //单次发送数量
16, //单次采样位宽
2); //声道数

三.实验总结

遇到问题:PCM数据的获取
解决方案:见二.1.1


结语:
  仅仅按照参考资料的程序实验是不够的,需要自己理解代码的含义,做出可拓展的尝试



转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 21009200039@stu.xidian.edu.cn

💰

×

Help us with donation