1371|4

44

帖子

2

TA的资源

一粒金砂(高级)

楼主
 

【Tang Primer25K Dock】七——I2S加麦克风阵列的基本驱动测试 [复制链接]

 
本帖最后由 Zhao_kar 于 2024-1-29 19:32 编辑

【Tang Primer25K Dock】七——I2S加麦克风阵列的基本驱动测试

备注:

  • 下一节第八节就是测评计划提到的数字时钟了,更新顺序比较乱,但是都会在备注里面简单说一下
  • 本节理论部分会多一点,毕竟涉及到一个新的概念
  • 事先声明,因为只是简单的测试,实际上的演示效果会比较差
  • 然后补充一下,实际学下来,实际上我数字时钟的代码甚至都没有分层,而这个I2S却是分了I2S的驱动部分和LED的驱动部分两个子模块。

一、I2S的原理

1、首先了解一下基本概念:

  • I2S(Inter-IC Sound)。I2S是一种用于在集成电路之间传输音频数据的串行通信接口标准。
  • 它通常用于连接音频编解码器、数字信号处理器(DSP)、微控制器和其他音频设备。
  • I2S接口使用了三个主要的信号线:时钟信号(SCK),帧同步信号(WS或LRCLK),和串行数据线(SD)。
  • 这种接口广泛用于数字音频传输,支持高质量的音频传输和处理。

2、协议介绍:

  • I2S是一种通过三根线(时钟线、数据线和帧选择线)进行全双工音频通信的协议。
  • 时钟线(SCK)用于同步数据传输。
  • 数据线(SD)传输左右声道的音频数据。
  • 帧选择线(WS,或LRCLK)用于指示是左声道还是右声道的数据。

3、时钟同步:

  • I2S协议需要时钟的同步,这是通过时钟线(SCK)实现的。所有设备都需要基于相同的时钟频率进行操作。
  • 其中时钟可以是主设备生成,也可以是外部提供。

4、数据格式:

  • I2S支持不同的数据格式,如标准I2S、左对齐(Left-Justified)和右对齐(Right-Justified)等。
  • 标准I2S数据格式是16位,每个帧有一个左声道和一个右声道的采样数据。

5、帧同步:

  • I2S协议中的帧同步线(WS)在每个音频帧的开始指示左右声道的数据。
  • 帧同步信号通常以低电平表示左声道,高电平表示右声道。

6、补充:

  • 上述的基本概念中,我们需要重点关注的是I2S的两个时钟,也就是实际麦克风阵列上的WS和MIC_CLK
  • 这里有一个注意的地方:下述两个时钟
  • 主时钟(mclk):频率是采样频率的256倍或384倍
  • 采样时钟(bclk):bclk的频率=2×采样频率×采样位数。
  • 同时,正常人可以听到的声音频率范围为20Hz~20KHz,这个也是一个需要了解的知识
  • 然后,后续代码的时钟分频部分,这里提前说一下,驱动这个模块前,先去看看手册,这里你可以看到详细的时序需求,这也是为什么后面代码乘了个64的原因
  • 然后在官方里面有提到这个部分:所以即可知道时钟的要求,那么我们就可以对他进行分频率

  • 接下来直接看代码部分

二、I2S的子模块部分

module i2s_top(
    input clk,//输入时钟
    input rst_n,//复位信号
    output reg [23:0] ldata,//输出左声道数据
    output reg [23:0] rdata,//输出右声道数据
    output ready,//数据就绪一次给一个脉冲

    output mic_sck,//输出麦克风时钟,连麦克风阵列mic_sck
    output mic_ws,//输出麦克风左右声道信号,连麦克风阵列mic_ws
    input mic_sd//麦克风输入
);

    //对时钟分频
    localparam div_clk = 50;
    localparam div_ws = div_clk * 64;

    reg [15:0] sck_cnt;
    reg [21:0] ws_cnt;
    reg [23:0] shift_reg_l;
    reg [23:0] shift_reg_r;

    assign mic_sck = (sck_cnt > div_clk/2-1) ? 1'b1 : 1'b0;
    assign mic_ws = (ws_cnt > div_ws/2-1) ? 1'b1 : 1'b0;

    always @(posedge clk or negedge rst_n) begin
        if(~rst_n) begin
            sck_cnt <= 0;
        end
        else begin
            if(sck_cnt == div_clk-1) begin
                sck_cnt <= 0;
            end
            else begin
                sck_cnt <= sck_cnt + 1;
            end
        end
    end

    always @(posedge clk or negedge rst_n) begin
        if(~rst_n) begin
            ws_cnt <= 0;
        end
        else begin
            if(ws_cnt == div_ws-1) begin
                ws_cnt <= 0;
            end
            else begin
                ws_cnt <= ws_cnt + 1;
            end
        end
    end

    //根据I2S时序采集数据,并通过shift_reg更新
    always @(posedge clk or negedge rst_n) begin
        if(~rst_n) begin
            shift_reg_l <= 0;
            shift_reg_r <= 0;
        end
        else begin
            case(ws_cnt)
                div_clk*1+div_clk/2-1: begin shift_reg_l <= {shift_reg_l[22:0],mic_sd}; shift_reg_r <= shift_reg_r; end
                div_clk*2+div_clk/2-1: begin shift_reg_l <= {shift_reg_l[22:0],mic_sd}; shift_reg_r <= shift_reg_r; end
                div_clk*3+div_clk/2-1: begin shift_reg_l <= {shift_reg_l[22:0],mic_sd}; shift_reg_r <= shift_reg_r; end
                div_clk*4+div_clk/2-1: begin shift_reg_l <= {shift_reg_l[22:0],mic_sd}; shift_reg_r <= shift_reg_r; end
                div_clk*5+div_clk/2-1: begin shift_reg_l <= {shift_reg_l[22:0],mic_sd}; shift_reg_r <= shift_reg_r; end
                div_clk*6+div_clk/2-1: begin shift_reg_l <= {shift_reg_l[22:0],mic_sd}; shift_reg_r <= shift_reg_r; end
                div_clk*7+div_clk/2-1: begin shift_reg_l <= {shift_reg_l[22:0],mic_sd}; shift_reg_r <= shift_reg_r; end
                div_clk*8+div_clk/2-1: begin shift_reg_l <= {shift_reg_l[22:0],mic_sd}; shift_reg_r <= shift_reg_r; end
                div_clk*9+div_clk/2-1: begin shift_reg_l <= {shift_reg_l[22:0],mic_sd}; shift_reg_r <= shift_reg_r; end
                div_clk*10+div_clk/2-1: begin shift_reg_l <= {shift_reg_l[22:0],mic_sd}; shift_reg_r <= shift_reg_r; end
                div_clk*11+div_clk/2-1: begin shift_reg_l <= {shift_reg_l[22:0],mic_sd}; shift_reg_r <= shift_reg_r; end
                div_clk*12+div_clk/2-1: begin shift_reg_l <= {shift_reg_l[22:0],mic_sd}; shift_reg_r <= shift_reg_r; end
                div_clk*13+div_clk/2-1: begin shift_reg_l <= {shift_reg_l[22:0],mic_sd}; shift_reg_r <= shift_reg_r; end
                div_clk*14+div_clk/2-1: begin shift_reg_l <= {shift_reg_l[22:0],mic_sd}; shift_reg_r <= shift_reg_r; end
                div_clk*15+div_clk/2-1: begin shift_reg_l <= {shift_reg_l[22:0],mic_sd}; shift_reg_r <= shift_reg_r; end
                div_clk*16+div_clk/2-1: begin shift_reg_l <= {shift_reg_l[22:0],mic_sd}; shift_reg_r <= shift_reg_r; end
                div_clk*17+div_clk/2-1: begin shift_reg_l <= {shift_reg_l[22:0],mic_sd}; shift_reg_r <= shift_reg_r; end
                div_clk*18+div_clk/2-1: begin shift_reg_l <= {shift_reg_l[22:0],mic_sd}; shift_reg_r <= shift_reg_r; end
                div_clk*19+div_clk/2-1: begin shift_reg_l <= {shift_reg_l[22:0],mic_sd}; shift_reg_r <= shift_reg_r; end
                div_clk*20+div_clk/2-1: begin shift_reg_l <= {shift_reg_l[22:0],mic_sd}; shift_reg_r <= shift_reg_r; end
                div_clk*21+div_clk/2-1: begin shift_reg_l <= {shift_reg_l[22:0],mic_sd}; shift_reg_r <= shift_reg_r; end
                div_clk*22+div_clk/2-1: begin shift_reg_l <= {shift_reg_l[22:0],mic_sd}; shift_reg_r <= shift_reg_r; end
                div_clk*23+div_clk/2-1: begin shift_reg_l <= {shift_reg_l[22:0],mic_sd}; shift_reg_r <= shift_reg_r; end
                div_clk*24+div_clk/2-1: begin shift_reg_l <= {shift_reg_l[22:0],mic_sd}; shift_reg_r <= shift_reg_r; end

                div_clk*33+div_clk/2-1: begin shift_reg_l <= shift_reg_l; shift_reg_r <= {shift_reg_r[22:0],mic_sd}; end
                div_clk*34+div_clk/2-1: begin shift_reg_l <= shift_reg_l; shift_reg_r <= {shift_reg_r[22:0],mic_sd}; end
                div_clk*35+div_clk/2-1: begin shift_reg_l <= shift_reg_l; shift_reg_r <= {shift_reg_r[22:0],mic_sd}; end
                div_clk*36+div_clk/2-1: begin shift_reg_l <= shift_reg_l; shift_reg_r <= {shift_reg_r[22:0],mic_sd}; end
                div_clk*37+div_clk/2-1: begin shift_reg_l <= shift_reg_l; shift_reg_r <= {shift_reg_r[22:0],mic_sd}; end
                div_clk*38+div_clk/2-1: begin shift_reg_l <= shift_reg_l; shift_reg_r <= {shift_reg_r[22:0],mic_sd}; end
                div_clk*39+div_clk/2-1: begin shift_reg_l <= shift_reg_l; shift_reg_r <= {shift_reg_r[22:0],mic_sd}; end
                div_clk*40+div_clk/2-1: begin shift_reg_l <= shift_reg_l; shift_reg_r <= {shift_reg_r[22:0],mic_sd}; end
                div_clk*41+div_clk/2-1: begin shift_reg_l <= shift_reg_l; shift_reg_r <= {shift_reg_r[22:0],mic_sd}; end
                div_clk*42+div_clk/2-1: begin shift_reg_l <= shift_reg_l; shift_reg_r <= {shift_reg_r[22:0],mic_sd}; end
                div_clk*43+div_clk/2-1: begin shift_reg_l <= shift_reg_l; shift_reg_r <= {shift_reg_r[22:0],mic_sd}; end
                div_clk*44+div_clk/2-1: begin shift_reg_l <= shift_reg_l; shift_reg_r <= {shift_reg_r[22:0],mic_sd}; end
                div_clk*45+div_clk/2-1: begin shift_reg_l <= shift_reg_l; shift_reg_r <= {shift_reg_r[22:0],mic_sd}; end
                div_clk*46+div_clk/2-1: begin shift_reg_l <= shift_reg_l; shift_reg_r <= {shift_reg_r[22:0],mic_sd}; end
                div_clk*47+div_clk/2-1: begin shift_reg_l <= shift_reg_l; shift_reg_r <= {shift_reg_r[22:0],mic_sd}; end
                div_clk*48+div_clk/2-1: begin shift_reg_l <= shift_reg_l; shift_reg_r <= {shift_reg_r[22:0],mic_sd}; end
                div_clk*49+div_clk/2-1: begin shift_reg_l <= shift_reg_l; shift_reg_r <= {shift_reg_r[22:0],mic_sd}; end
                div_clk*50+div_clk/2-1: begin shift_reg_l <= shift_reg_l; shift_reg_r <= {shift_reg_r[22:0],mic_sd}; end
                div_clk*51+div_clk/2-1: begin shift_reg_l <= shift_reg_l; shift_reg_r <= {shift_reg_r[22:0],mic_sd}; end
                div_clk*52+div_clk/2-1: begin shift_reg_l <= shift_reg_l; shift_reg_r <= {shift_reg_r[22:0],mic_sd}; end
                div_clk*53+div_clk/2-1: begin shift_reg_l <= shift_reg_l; shift_reg_r <= {shift_reg_r[22:0],mic_sd}; end
                div_clk*54+div_clk/2-1: begin shift_reg_l <= shift_reg_l; shift_reg_r <= {shift_reg_r[22:0],mic_sd}; end
                div_clk*55+div_clk/2-1: begin shift_reg_l <= shift_reg_l; shift_reg_r <= {shift_reg_r[22:0],mic_sd}; end
                div_clk*56+div_clk/2-1: begin shift_reg_l <= shift_reg_l; shift_reg_r <= {shift_reg_r[22:0],mic_sd}; end

                default: begin shift_reg_l <= shift_reg_l; shift_reg_r <= shift_reg_r; end
            endcase
        end
    end

    //根据时序逻辑将shift_reg最终输出为左右声道音频值
    always @(posedge clk or negedge rst_n) begin
        if(~rst_n) begin
            ldata <= 0;
            rdata <= 0;
        end
        else begin
            if(ws_cnt == div_clk*56+div_clk/2) begin
                ldata <= shift_reg_l;
                rdata <= shift_reg_r;
            end
            else begin
                ldata <= ldata;
                rdata <= rdata;
            end
        end
    end

    assign ready = (ws_cnt == div_clk*56+div_clk/2) ? 1'b1 : 1'b0;//就绪脉冲

endmodule

三、顶层模块部分

module top(
    input clk50M,//系统输入时钟,H11
    input rst_n,//系统复位信号,低电平复位

    output mic_sck,//输出麦克风时钟,连麦克风阵列mic_sck
    output mic_ws,//输出麦克风左右声道信号,连麦克风阵列mic_ws
    input mic_sd0,//麦克风输入,连mic_d0
    input mic_sd1,//麦克风输入,连mic_d1
    input mic_sd2,//麦克风输入,连mic_d2
    input mic_sd3,//麦克风输入,连mic_d3

    output led_sck,//输出灯带时钟,连led_ck
    output led_dat//输出灯带数据,连led_da
);

    wire signed [23:0] mic_data00;//麦克风1数据(有符号整数)
    wire signed [23:0] mic_data01;//麦克风2数据(有符号整数)
    wire signed [23:0] mic_data10;//麦克风3数据(有符号整数)
    wire signed [23:0] mic_data11;//麦克风4数据(有符号整数)
    wire signed [23:0] mic_data20;//麦克风5数据(有符号整数)
    wire signed [23:0] mic_data21;//麦克风6数据(有符号整数)
    wire signed [23:0] mic_data3;//麦克风7数据(有符号整数)

    wire [23:0] abs_mic_data00;//麦克风1数据绝对值
    wire [23:0] abs_mic_data01;//麦克风2数据绝对值
    wire [23:0] abs_mic_data10;//麦克风3数据绝对值
    wire [23:0] abs_mic_data11;//麦克风4数据绝对值
    wire [23:0] abs_mic_data20;//麦克风5数据绝对值
    wire [23:0] abs_mic_data21;//麦克风6数据绝对值
    wire [23:0] abs_mic_data3;//麦克风7数据绝对值

    //对接收到的麦克风数据取绝对值
    assign abs_mic_data00 = (mic_data00 >= 0) ? $unsigned(mic_data00) : $unsigned(-mic_data00);
    assign abs_mic_data01 = (mic_data01 >= 0) ? $unsigned(mic_data01) : $unsigned(-mic_data01);
    assign abs_mic_data10 = (mic_data10 >= 0) ? $unsigned(mic_data10) : $unsigned(-mic_data10);
    assign abs_mic_data11 = (mic_data11 >= 0) ? $unsigned(mic_data11) : $unsigned(-mic_data11);
    assign abs_mic_data20 = (mic_data20 >= 0) ? $unsigned(mic_data20) : $unsigned(-mic_data20);
    assign abs_mic_data21 = (mic_data21 >= 0) ? $unsigned(mic_data21) : $unsigned(-mic_data21);
    assign abs_mic_data3  = (mic_data3  >= 0) ? $unsigned(mic_data3 ) : $unsigned(-mic_data3 );

    reg [11:0] dir;//12个灯亮灭控制寄存器

    reg [31:0] set_cnt;//灯带刷新计数寄存器

    reg [2:0] comp_state;//麦克风强度比较状态机state
    reg [31:0] maixv;//麦克风最大强度值缓存
    reg [2:0] maixn;//强度麦克风序号缓存
    reg [31:0] maxv;//麦克风最大强度值
    reg [2:0] maxn;//强度麦克风序号

    //例化麦克风驱动,麦克风1 2
    i2s_top i2s_u0(
        .clk(clk50M),
        .rst_n(rst_n),
        .ldata(mic_data00),
        .rdata(mic_data01),
        .ready(),

        .mic_sck(mic_sck),
        .mic_ws(mic_ws),
        .mic_sd(mic_sd0)
    );

    //例化麦克风驱动,麦克风3 4
    i2s_top i2s_u1(
        .clk(clk50M),
        .rst_n(rst_n),
        .ldata(mic_data10),
        .rdata(mic_data11),
        .ready(),

        .mic_sck(),
        .mic_ws(),
        .mic_sd(mic_sd1)
    );

    //例化麦克风驱动,麦克风5 6
    i2s_top i2s_u2(
        .clk(clk50M),
        .rst_n(rst_n),
        .ldata(mic_data20),
        .rdata(mic_data21),
        .ready(),

        .mic_sck(),
        .mic_ws(),
        .mic_sd(mic_sd2)
    );

    //例化麦克风驱动,麦克风7
    i2s_top i2s_u3(
        .clk(clk50M),
        .rst_n(rst_n),
        .ldata(),
        .rdata(mic_data3),
        .ready(),

        .mic_sck(),
        .mic_ws(),
        .mic_sd(mic_sd3)
    );

    //灯带驱动
    led12 led12_u(
        .clk(clk50M),
        .rst_n(rst_n),

        .direct(dir),
        .bgr(24'h0000fc),//颜色红色 蓝:24'hfc0000 绿:24'h00hc00
        .refresh(1'b1),

        .led_sck(led_sck),
        .led_dat(led_dat)
    );

    //比较7个麦克风的声音强度,获取最强的麦克风序号
    always @(posedge clk50M or negedge rst_n) begin
        if(~rst_n) begin
            comp_state <= 0;
            maixv <= 0;
            maixn <= 0;
            maxv <= 0;
            maxn <= 0;
        end
        else begin
            case(comp_state)
                0: begin
                    if(abs_mic_data00 > maixv) begin
                        maixv <= abs_mic_data00;
                        maixn <= 1;
                    end
                    else begin
                        maixv <= maixv;
                        maixn <= maixn;
                    end
                    comp_state <= comp_state + 1;
                end
                1: begin
                    if(abs_mic_data01 > maixv) begin
                        maixv <= abs_mic_data01;
                        maixn <= 2;
                    end
                    else begin
                        maixv <= maixv;
                        maixn <= maixn;
                    end
                    comp_state <= comp_state + 1;
                end
                2: begin
                    if(abs_mic_data10 > maixv) begin
                        maixv <= abs_mic_data10;
                        maixn <= 3;
                    end
                    else begin
                        maixv <= maixv;
                        maixn <= maixn;
                    end
                    comp_state <= comp_state + 1;
                end
                3: begin
                    if(abs_mic_data11 > maixv) begin
                        maixv <= abs_mic_data11;
                        maixn <= 4;
                    end
                    else begin
                        maixv <= maixv;
                        maixn <= maixn;
                    end
                    comp_state <= comp_state + 1;
                end
                4: begin
                    if(abs_mic_data20 > maixv) begin
                        maixv <= abs_mic_data20;
                        maixn <= 5;
                    end
                    else begin
                        maixv <= maixv;
                        maixn <= maixn;
                    end
                    comp_state <= comp_state + 1;
                end
                5: begin
                    if(abs_mic_data21 > maixv) begin
                        maixv <= abs_mic_data21;
                        maixn <= 6;
                    end
                    else begin
                        maixv <= maixv;
                        maixn <= maixn;
                    end
                    comp_state <= comp_state + 1;
                end
                6: begin
                    if(abs_mic_data3 > maixv) begin
                        maixv <= abs_mic_data3;
                        maixn <= 7;
                    end
                    else begin
                        maixv <= maixv;
                        maixn <= maixn;
                    end
                    comp_state <= comp_state + 1;
                end
                7: begin
                    maxv <= maixv;
                    if(maixv>9000)//设置个阈值,声音太小全都忽略,都不亮
                        maxn <= maixn;
                    else
                        maxn <= 0;
                    maixv <= 0;
                    maixn <= 0;
                    comp_state <= 0;
                end
                default: begin
                    comp_state <= 0;
                end
            endcase
        end
    end

    //根据最强的麦克风序号亮相应的指示灯
    always @(posedge clk50M or negedge rst_n) begin
        if(~rst_n) begin
            set_cnt <= 0;
            dir <= 0;
        end
        else begin
            if(set_cnt == 2699999) begin
                set_cnt <= 0;
                case(maxn)
                    0: dir <= 12'b000000000000;
                    1: dir <= 12'b000000000001;
                    2: dir <= 12'b000000000100;
                    3: dir <= 12'b000000010000;
                    4: dir <= 12'b000001000000;
                    5: dir <= 12'b000100000000;
                    6: dir <= 12'b010000000000;
                    7: dir <= 12'b111111111111;
                    default: dir <= 12'b000000000000;
                endcase
            end
            else begin
                set_cnt <= set_cnt + 1;
            end
        end
    end

endmodule

四、实际视频演示

PS:因为比较简单,所以实际演示效果并不好,且中间那个麦克风的声音最大时,全部的灯都会亮,加上延时问题,会出现经常全部亮的情况,可以在判别的部分降低频率进行测试,之前试过,就不演示了

i2s

最新回复

哈哈,多分享分享经验~   详情 回复 发表于 2024-2-4 09:17
点赞(1) 关注(1)
 
 

回复
举报

6809

帖子

0

TA的资源

五彩晶圆(高级)

沙发
 

好吧,驱动这个模块前,先去看看手册,这里你可以看到详细的时序需求,这也是为什么后面代码乘了个64的原因,

效果还可以吧

 
 
 

回复

7193

帖子

2

TA的资源

版主

板凳
 

有研究过无线麦克风吗,最近刚好有需求。

点评

额没有,这个帖子也是最近研究麦克风现学的  详情 回复 发表于 2024-2-1 19:03
 
 
 

回复

44

帖子

2

TA的资源

一粒金砂(高级)

4
 
wangerxian 发表于 2024-1-31 18:54 有研究过无线麦克风吗,最近刚好有需求。

额没有,这个帖子也是最近研究麦克风现学的

点评

哈哈,多分享分享经验~  详情 回复 发表于 2024-2-4 09:17
 
 
 

回复

7193

帖子

2

TA的资源

版主

5
 
Zhao_kar 发表于 2024-2-1 19:03 额没有,这个帖子也是最近研究麦克风现学的

哈哈,多分享分享经验~

 
 
 

回复
您需要登录后才可以回帖 登录 | 注册

查找数据手册?

EEWorld Datasheet 技术支持

相关文章 更多>>
关闭
站长推荐上一条 1/8 下一条

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

站点相关: 国产芯 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网

北京市海淀区中关村大街18号B座15层1530室 电话:(010)82350740 邮编:100190

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2025 EEWORLD.com.cn, Inc. All rights reserved
快速回复 返回顶部 返回列表