0

由于项目需要,需要由FPGA把4路由ADC芯片采集的I2S数据进行合并成1路I2S,最后输出到主控CPU芯片,FPGA在这里起到数据中转的作用。项目的整体结构如下图

(待画)

1.I2S协议

参考文档:I2S bus specification.pdf  见下面博客

参考博客:I2S接口规范时序以及其同DSP的连接

I2S音频总线学习中的一、二、四

采样频率、时间、声道、量化与储存大学的关系

2.实现方案

1.方案一:FIFO的思想

即采用8个FIFO,分成两组,组1的4个FIFO负责存储4路采样数据的左声道的32bit,组二的4个FIFO负责存储4路采样数据的左声道的32bit;然后使用乒乓操作进行2个组的读和写的切换,即读组1的同时对组2进行写,读组2的同时对组1进行写,读时钟的频率是写时钟的4倍,这样可以保证写完4路数据的时间和读完4路并输出成1路的数据相同。

2.方案二:RAM的思想

采用2个32X4(地址深度32,数据宽度为4,)的RAM分别存储4路采样数据的左右声道数据(32bit)。其中RAM1的地址n中存储的是4路左声道数据的第n位,正好4位,其中RAM2的地址n中存储的是4路右声道数据的第n位。写时钟是Fbclk1=32X2X16K(bit时钟),Flsclk1=16k(采样频率=帧时钟);读时钟Fbclk0=32X2X4X16K(主控CPU的bit时钟),Flsclk0=4X16k(主控CPU帧时钟)。读和写RAM采用乒乓操作进行切换。其中RAM采用SDP(伪双端口RAM),这里写代码需要参考SDP的具体手册。
主要参数的时序:Fbclk0、Flsclk0、Fbclk1、Flsclk1、data0、data1、data2、data3、data4(待续)

3.硬件实现

1.FPGA最小系统

1.Micro USB 供电
2.LDO生成核心电压VCC=1.2V,所有的VCCIO=3.3V
3.JTAG下载电路,复位引脚

2.I2S输入和输出接口

1.主控CPU输入Fbclk0、Flsclk0;FPGA输出到主控时钟CPU:data0;
2.FPGA输出到ADC芯片:Fbclk1、Flsclk1、Fmclk;ADC输入到FPGA:data1、data2、data3、data4

3.I2C接口

1.FPGA留出I2C接口,用来进行通过FPGA对ADC进行配置

4.代码结构及实现

1.整体端口

module I2S_64K
(//main controllerinput   i_rst,input   i_bit_clk,                        input   i_fs_clk,output   o_data,//adc output reg o_bit_clk,output    reg o_fs_clk,output      o_main_clk,  //---------------------------------->now, it have not be useddinput   i_data0,input   i_data1,input   i_data2,input   i_data3,//input   i_data4,//input   i_data5//i2c_config_done//input   i2c_config_done
);

2.生成ADC芯片的Fbclk1和Flsclk1

对输入的主控CPU的I2S时钟(Fbclk0、Flsclk0)进行4分频生成ADC的Fbclk1、Flsclk1
参见(奇偶分频、小数分频),这里计数器分频
parameter DIVIDE=4;                 //共4路数据,所以4分频
//**********************************************************************************************************************
//************************   o_bit_clk = i_bit_clk / DIVIDE ;   o_fs_clk = i_fs_clk / DIVIDE   *************************
//**********************************************************************************************************************
//1.o_bit_clk = i_bit_clk / DIVIDE
reg [1:0] cnt_bit_divide;always@(negedge i_bit_clk or negedge i_rst)     //下降沿时钟触发是为了下降沿和o_fs_clk对齐
if(!i_rst)begincnt_bit_divide <= 2'd0;o_bit_clk <= 1'b0;end
else if(cnt_bit_divide == (DIVIDE/2-1))     begincnt_bit_divide <= 2'd0;o_bit_clk <= ~o_bit_clk;end
elsebegincnt_bit_divide <= cnt_bit_divide + 1'b1;o_bit_clk <= o_bit_clk;end//**********************************************
//2.o_fs_clk = i_fs_clk / DIVIDE
reg [1:0] cnt_fs_divide;always@(negedge i_fs_clk or negedge i_rst)
if(!i_rst)begincnt_fs_divide <= 2'd0;o_fs_clk <= 1'b1;                        end
else if(cnt_fs_divide == (DIVIDE/2-1))           begincnt_fs_divide <= 2'd0;o_fs_clk <= ~o_fs_clk;end
elsebegincnt_fs_divide <= cnt_fs_divide + 1'b1;o_fs_clk <= o_fs_clk;end

3.生成ADC芯片的Fmclk

用PLL对主控时钟Fbclk0进行倍频生成Fmclk
wire clkout_o,lock_o;GW_PLL PLL_3(.clkout(clkout_o), //output clkout.lock(lock_o), //output lock.clkin(i_bit_clk) //input clkin);
assign o_main_clk = (lock_o == 1'b1)    ?   clkout_o    :   1'b0;




4.I2S接收电路和4路并行数据写入RAM(写入时钟Fbclk1)

接收电路参考文档:I2S bus specification.pdf。这个文档的figure5、figure6和figure7有详细的RTL图,这个是我写这段代码的基础。下面的是上升沿(时钟)边沿检测,并触发了下降沿(时钟)的数据发送就是从这里参考的 

1.接收电路上升沿时钟便要检测和下降沿时钟开始接收数据

// dectect o_fs_clk  edge change
reg o_fs_clk1,o_fs_clk2;always@(posedge o_bit_clk or negedge i_rst)
if(!i_rst)begino_fs_clk1 <= 1'b0;o_fs_clk2 <= 1'b0;end
else begino_fs_clk1 <= o_fs_clk;o_fs_clk2 <= o_fs_clk1;endwire wsp = o_fs_clk1 ^ o_fs_clk2;     // edge detect

2.接收数据写入RAM

这里需要计数器对32bit的写入进行计数(即计数最大值31)
//*******************************************
// SRAM store count register
reg [4:0] cnt_rec; //SRAM store count register always@(negedge o_bit_clk or negedge i_rst)    //下降沿时钟,wsp上升沿开始计时(see the I2S bus specification.pdf,the last two figure)
if(!i_rst)begincnt_rec <= 5'd0;                end
else if(wsp)                        //begincnt_rec <= 5'd1;                end
elsebegincnt_rec <= cnt_rec + 1'b1;     end

RAM写入使能,RAM写入就是一个周期,即立马写入

//*******************************************
// SRAM write enable signal
wire cea1 = o_fs_clk;
wire cea2 = ~ o_fs_clk;

RAM数据写入并编通道号

//*******************************************
//ADC data receive ,then store the data in SRAM ,
wire [3:0] data_adc;
//assign data_adc = {i_data0,i_data1,i_data2,i_data3};assign data_adc =   ((cnt_rec >= 5'd25) | (cnt_rec <= 5'd20)) ?   {i_data0,i_data1,i_data2,i_data3}   :((cea1 == 1'b1) & (cnt_rec == 5'd21))    ?   (4'b0000)  :((cea1 == 1'b1) & (cnt_rec == 5'd22))    ?   (4'b0001)  :((cea1 == 1'b1) & (cnt_rec == 5'd23))    ?   (4'b0110)  :((cea1 == 1'b1) & (cnt_rec == 5'd24))    ?   (4'b1010)  :((cea1 == 1'b0) & (cnt_rec == 5'd21))    ?   (4'b0001)  :((cea1 == 1'b0) & (cnt_rec == 5'd22))    ?   (4'b1110)  :((cea1 == 1'b0) & (cnt_rec == 5'd23))    ?   (4'b0110)  :((cea1 == 1'b0) & (cnt_rec == 5'd24))    ?   (4'b1010)  :   (4'b0000); 

5.主控CPU读出RAM(读RAM时钟Fbclk0)

1.读时钟边沿检测

reg i_fs_clk1,i_fs_clk2;always@(posedge i_bit_clk or negedge i_rst)
if(!i_rst)begini_fs_clk1 <= 1'b0;i_fs_clk2 <= 1'b0;end
else begini_fs_clk1 <= i_fs_clk;i_fs_clk2 <= i_fs_clk1;endwire wsp3 = i_fs_clk1 ^ i_fs_clk2; //边沿检测

2.读RAM的计数器

reg [4:0] cnt_read; //计数器只是用来计数,当o_bit_clk变化时它就清零,然后依次加一
always@(negedge i_bit_clk or negedge i_rst)    //the same resson as 99 line
if(!i_rst)begincnt_read <= 5'd0;end
else if(wsp3)                    //i_fs_clk edge detect                                      begincnt_read <= 5'd3;        // attention !!!  here is important,  to keep same with the left-justified audio data   end
else begin     cnt_read <= cnt_read + 1'b1;end

3.读出的4个通道数据区分

因为RAM的bypass输出需要两个周期,并且需要左对齐输出,
reg [3:0] cnt_read_cnt;  //这个累加器是为了把o_fs_clk分成四份,用来区别通道数的
always@(negedge i_bit_clk or negedge i_rst)    //the same resson as 99 line
if(!i_rst)begincnt_read_cnt <= 4'b0000;end
else if((cnt_read == 5'd0)  & (cnt_rec != 5'd31))      //begin     cnt_read_cnt <= cnt_read_cnt << 1;end
else if((cnt_read == 5'd0)  & (cnt_rec == 5'd31))  //begin     cnt_read_cnt <= 4'b0001;              //attention !!!    here is delay for a i_bit_clk period !end
elsebegincnt_read_cnt <= cnt_read_cnt;end

4.读RAM使能

因为RAM的bypass输出需要两个周期,并且需要左对齐输出,
reg ceb11,ceb22;always@(negedge i_bit_clk or negedge i_rst)    //the same resson as 99 line
if(!i_rst)beginceb11 <= 1'b0;ceb22 <= 1'b0;end
else if((cnt_read == 5'd30) & (cnt_rec == 5'd31) & (o_fs_clk == 1'b1) )      // here is good ,and there are no delay ,begin     ceb11 <= 1'b1;ceb22 <= 1'b0;end
else if((cnt_read == 5'd30) & (cnt_rec == 5'd31) & (o_fs_clk == 1'b0) )  //begin     ceb11 <= 1'b0;ceb22 <= 1'b1;end
elsebeginceb11 <= ceb11;ceb22 <= ceb22;end

6.两个RAM例化

RAM1存储左声道数据,RAM2储存右声道数据
 GW_SDP sram_left(.dout(data_sram1), //output [3:0] dout                                read.clka(o_bit_clk), //input clka              write.cea(cea1), //input cea                     write.reseta(!i_rst), //input reseta             write.clkb(!i_bit_clk), //input clkb                              read.ceb(ceb11), //input ceb                                         read.resetb(!i_rst), //input resetb                             read.oce(1'b0), //input oce                                                         .ada(cnt_rec), //input [4:0] ada            write.din(data_adc), //input [3:0] din           write.adb(cnt_read) //input [4:0] adb                                    read);GW_SDP sram_right(.dout(data_sram2), //output [3:0] dout                                read.clka(o_bit_clk), //input clka              write.cea(cea2), //input cea                     write.reseta(!i_rst), //input reseta             write.clkb(!i_bit_clk), //input clkb                              read.ceb(ceb22), //input ceb                                         read.resetb(!i_rst), //input resetb                             read.oce(1'b0), //input oce                                                        .ada(cnt_rec), //input [4:0] ada            write.din(data_adc), //input [3:0] din           write.adb(cnt_read) //input [4:0] adb                                    read);

7.I2S数据输出

assign o_data = (ceb11 & (cnt_read_cnt == 4'b0001))?  data_sram1[3] :(ceb11 & (cnt_read_cnt == 4'b0010))?  data_sram1[2] :(ceb11 & (cnt_read_cnt == 4'b0100))?  data_sram1[1] :(ceb11 & (cnt_read_cnt == 4'b1000))?  data_sram1[0] :(ceb22 & (cnt_read_cnt == 4'b0001))?  data_sram2[3] :(ceb22 & (cnt_read_cnt == 4'b0010))?  data_sram2[2] :(ceb22 & (cnt_read_cnt == 4'b0100))?  data_sram2[1] :(ceb22 & (cnt_read_cnt == 4'b1000))?  data_sram2[0] : 1'b0;

5.modelsim仿真

主要参数的时序:
i_bit_clk、i_fs_clk、o_bit_clk、o_fs_clk、o_data、i_data0、i_data1、i_data2、i_data3

6.总结

1.知识点

1.I2S协议:I2S bus specification.pdf 
2.modelsim独立仿真的流程
3.SDP(伪双端口RAM的具体使用细节和读写的时序查看细节)
4.本代码I2S接收是normal格式,发送时I2S的左对齐模式

FPGA的I2S采集数据处理相关推荐

  1. 202- K7 +C6678学习资料:基于TI DSP TMS320C6678、Xilinx K7 FPGA XC7K325T的高速数据处理核心板

    基于TI DSP TMS320C6678.Xilinx K7 FPGA XC7K325T的高速数据处理核心板 一.板卡概述 该DSP+FPGA高速信号采集处理板由我公司自主研发,包含一片TI DSP ...

  2. 202-基于TI DSP TMS320C6678、Xilinx K7 FPGA XC7K325T的高速数据处理核心板

    基于TI DSP TMS320C6678.Xilinx K7 FPGA XC7K325T的高速数据处理核心板 一.板卡概述 该DSP+FPGA高速信号采集处理板由我公司自主研发,包含一片TI DSP ...

  3. FPGA控制AD7768采集

    1.1 FPGA控制AD7768采集 1.1.1 本节目录 1)本节目录: 2)本节引言: 3)FPGA简介: 4) FPGA控制AD7768采集: 5)结束语. 1.1.2 本节引言 "不 ...

  4. FPGA实现AD采集

    1 理论学习(废话篇)   ADC 模拟数字转换器(额谈到这个,真的很荣幸在ADI实习的时光,打住不扯了),凡是涉及到模拟信号转数字信号的时候,都会用到ADC.   ADC的种类很多,有积分型,逐次比 ...

  5. C6678板卡学习资料:202-基于TI DSP TMS320C6678、Xilinx K7 FPGA XC7K325T的高速数据处理核心板

    一.板卡概述 该DSP+FPGA高速信号采集处理板由我公司自主研发,包含一片TI DSP TMS320C6678和一片Xilinx FPGA K7 XC72K325T-1ffg900.包含1个千兆网口 ...

  6. 直升机FPGA多路视频采集叠加OSD字符VGA-HDMI-DVI-SDI采集融合板卡设计

    视频融合技术是虚拟现实技术的一个分支,也可以说是虚拟现实的一个发展阶段.视频融合技术指将一个或多个由视频采集设备采集的关于某场景或模型的图像序列视频与一个与之相关的虚拟场景加以融合,以生成一个新的关于 ...

  7. FPGA对高速采集ADC(8路并行数据)进行峰值检测,并记录峰值位置

    本模块主要是ADC(2Gsps)采集信号波形进行峰值检测,主要是检测单音信号或者脉冲信号中的所有峰峰值信号(对噪声大信号适用性不是很好),并记录峰值点的位置. 1. 峰值检测8路并行数据端口 modu ...

  8. 基于fpga的单线激光雷达数据处理

    激光雷达: 首先来给大家稍微介绍以下激光雷达,激光雷达,即Light Detation and ranging,它相比于其他雷达,优点非常明确,包括 1).具有极高的分辨率: 2).抗干扰能力强: 3 ...

  9. 【ESP32音视频传输】②通过I2S采集SPH0645麦克风音频数据并上传到服务端实时播放

    提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言 一.I2S型麦克风SPH0645 二.使用步骤 1.连线图 2.Arduino主文件代码 3.服务端利用UDP接收音频 ...

  10. TC260-001《汽车采集数据处理安全指南》

    来源:信安标委

最新文章

  1. bzoj千题计划303:bzoj4827: [Hnoi2017]礼物
  2. 北京大学AI写作机器人来了,会替代记者?
  3. NOI前总结:点分治
  4. 本地环境用eclipse搭建spring源码环境
  5. 查看docker容器日志
  6. 谈一谈自己对依赖、关联、聚合和组合之间区别的理解
  7. 【Python】python学习笔记day1
  8. matlab图片包微盘,如何用Matlab绘制二维图形资料下载
  9. Web Activities 简介
  10. DeadObjectException
  11. C#中的==、Equal、ReferenceEqual
  12. 使用阿帕奇服务器配置多个网站站点的方法
  13. cms php 免费,十大免费PHP内容管理系统(CMS)
  14. Spring Cloud与Dubbo怎么选择?
  15. 阿里云---云开发平台的创建与部署
  16. React Native仿美团下拉菜单
  17. Linux 用ssh远程登录及scp传输文件
  18. 中国金融科技50强之“百度金融”技术基因研究
  19. 台灯的品质,决定了你的阅读质量
  20. cGAN/cDCGAN,MNIST数据集初体验(内含原理,代码)

热门文章

  1. Office 2016 简体中文批量授权版镜像下载
  2. 《极客时间:代码精进之路》学习笔记
  3. php基础教程 第五章,VFP基础教程 第五章 创建查询和视图
  4. win10 联想键盘快捷键关闭_如何关闭联想台式机电脑USB键盘的FN功能
  5. kali局域网扫描ip_kali 扫描局域网的QQ
  6. 诺顿误杀事件造成“疑似病毒大爆发”恐慌
  7. 计算机维护工作周报,运维周报怎么写呀,这一周没什么事做
  8. 2022年6月25日PMP考试通关宝典-5
  9. PHP/PHPStudy所需的VC9-VC14的运行库
  10. 数模美赛准备——Numpy