• SPI学习笔记:DAC与ACD


    一、SPI协议简介

    SPI = Serial Peripheral Interface,是串行外围设备接口,是一种高速,全双工,同步的通信总线。常规只占用四根线,节约了芯片管脚,PCB的布局省空间。现在越来越多的芯片集成了这种通信协议,常见的有EEPROM、FLASH、AD转换器等。

    二、遵循SPI协议控制DAC:tlv5618

       1.时序分析

        从tlv5618芯片的数据手册中可找到其时序特性:

    没有Miso线时序,所以只需要设计三根线即可,因为此dac不需要主机发数据。由tsu(D)与th(D)均基于sclk的时钟下降沿描述,可以确定adc在时钟下降沿读取din数据,所以我们在sclk时钟上升沿改变数据即可。向由tw(不小于50ns)确定sclk时钟频率为20MHz;根据DIN时序可知在SCLK上升沿DIN改变,在SCLK下降沿读取DIN;tsu(D)建立时间,不小于10ns;th(D)保持时间,不小于10ns;故直接用20MHz时钟设计DIN长度即可满足;tsu用于限制CS的起止点,tsu(csh)应不小于50ns,tsu(cs-cl)应不小于10ns,tsu(c16-cs)应不小于10ns,在仿真时检验并调整使CS满足tsu即可。

    2.模块框图

            根据时序图可知,用线性序列机即可(LSM)实现。定义的信号如下:

    3.代码实现

    1. //采用线性序列机(LSM)来实现
    2. module dac_driver(
    3. input fpga_clk ,
    4. input rst_n ,
    5. input dac_pulse ,
    6. input [15:0]dac_data ,
    7. output reg cs_n ,//low level valid
    8. output reg sclk ,
    9. output reg mosi ,
    10. output reg dac_sig ,
    11. output reg dac_done
    12. );
    13. //供电电压不同,参数最小值不同,这里按照兼容VDD = 5V 和 VDD = 3V 的参数设计
    14. // tsu(cs-ck) : 20ns //
    15. // tsu(c16-cs): 20ns //
    16. // th(D): 10ns //
    17. // th(csh):50ns //
    18. // sclk : 20Mhz //
    19. //
    20. // D15 - D12 : 设置位 根据手册来设置 //
    21. // D11 - D0 : 数据位 需要转化的电压值 //
    22. wire clk_40m ;
    23. pll pll_inst(
    24. .inclk0(fpga_clk),
    25. .c0(clk_40m)
    26. );
    27. //detect dac_pulse
    28. reg dac_pulse_reg ;
    29. always@(posedge fpga_clk or negedge rst_n)
    30. if(!rst_n)
    31. dac_pulse_reg <= 0 ;
    32. else
    33. dac_pulse_reg <= dac_pulse ;
    34. //dac_sig
    35. always@(posedge fpga_clk or negedge rst_n)
    36. if(!rst_n)
    37. dac_sig <= 0 ;
    38. else if( (dac_pulse_reg == 0) & ( dac_pulse == 1) )
    39. dac_sig <= 1 ;
    40. else if( dac_done )
    41. dac_sig <= 0 ;
    42. // dac_cnt : 0 - 15
    43. reg [5:0]dac_cnt ;
    44. always@(posedge clk_40m or negedge rst_n)
    45. if(!rst_n)
    46. begin
    47. dac_cnt <= 0 ;
    48. end
    49. else if(dac_sig)
    50. begin
    51. dac_cnt <= dac_cnt + 1 ;
    52. if(dac_cnt == 34 )
    53. dac_cnt <= 0 ;
    54. end
    55. else
    56. dac_cnt <= 0 ;
    57. //dac_done:
    58. always@(posedge clk_40m or negedge rst_n)
    59. if(!rst_n)
    60. dac_done <= 0 ;
    61. else if( dac_cnt == 34 )
    62. dac_done <= 1 ;
    63. else if( dac_done )
    64. dac_done <= 0 ;
    65. // cs_n
    66. always@(posedge clk_40m or negedge rst_n)
    67. if(!rst_n)
    68. cs_n <= 1 ;
    69. else if(dac_sig)
    70. begin
    71. if(dac_cnt < 32 )
    72. cs_n <= 0 ;
    73. else
    74. cs_n <= 1 ;
    75. end
    76. // sclk
    77. always@(posedge clk_40m or negedge rst_n)
    78. if(!rst_n)
    79. sclk <= 0 ;
    80. else if(dac_sig)
    81. begin
    82. sclk <= !sclk ;
    83. end
    84. else
    85. sclk <= 0 ;
    86. //mosi
    87. always@(posedge clk_40m or negedge rst_n)
    88. if(!rst_n)
    89. mosi <= 0 ;
    90. else if(dac_sig)
    91. begin
    92. case(dac_cnt)
    93. 0 : mosi <= dac_data[15] ;
    94. 2 : mosi <= dac_data[14] ;
    95. 4 : mosi <= dac_data[13] ;
    96. 6 : mosi <= dac_data[12] ;
    97. 8 : mosi <= dac_data[11] ;
    98. 10 : mosi <= dac_data[10] ;
    99. 12 : mosi <= dac_data[9] ;
    100. 14 : mosi <= dac_data[8] ;
    101. 16 : mosi <= dac_data[7] ;
    102. 18 : mosi <= dac_data[6] ;
    103. 20 : mosi <= dac_data[5] ;
    104. 22 : mosi <= dac_data[4] ;
    105. 24 : mosi <= dac_data[3] ;
    106. 26 : mosi <= dac_data[2] ;
    107. 28 : mosi <= dac_data[1] ;
    108. 30 : mosi <= dac_data[0] ;
    109. 32 : mosi <= 0 ;
    110. default:;
    111. endcase
    112. end
    113. else
    114. mosi <= 0 ;
    115. endmodule
    1. `timescale 1ns/1ns
    2. module dac_driver_tb();
    3. reg fpga_clk ;
    4. reg rst_n ;
    5. reg dac_pulse ;
    6. reg [15:0]dac_data ;
    7. wire cs_n ;
    8. wire sclk ;
    9. wire mosi ;
    10. wire dac_done ;
    11. dac_driver dac_driver_inst(
    12. .fpga_clk(fpga_clk) ,
    13. .rst_n(rst_n) ,
    14. .dac_pulse(dac_pulse) ,
    15. .dac_data(dac_data) ,
    16. .cs_n(cs_n) ,//low level valid
    17. .sclk(sclk) ,
    18. .mosi(mosi) ,
    19. .dac_done(dac_done)
    20. );
    21. initial fpga_clk = 0 ;
    22. always #10 fpga_clk = ! fpga_clk ;
    23. initial
    24. begin
    25. rst_n = 0 ;
    26. dac_pulse = 0 ;
    27. dac_data = 0 ;
    28. #100 ;
    29. rst_n = 1 ;
    30. #100 ;
    31. dac_data = 16'h5a5a ;
    32. #100 ;
    33. dac_pulse = 1 ;
    34. #40 ;
    35. dac_pulse = 0 ;
    36. #100;
    37. wait(dac_done);
    38. #3000 ;
    39. dac_data = 16'ha5a5 ;
    40. #100 ;
    41. dac_pulse = 1 ;
    42. #40 ;
    43. dac_pulse = 0 ;
    44. #100;
    45. wait(dac_done);
    46. #3000 ;
    47. $stop;
    48. end
    49. endmodule

    4.仿真验证

    由图可知,tsu为25ns,大于tsu(c16-cs)(10ns),th(csh)为50ns,不小于50ns,满足要求。

    三、遵循SPI协议控制ADC:ADC128S022

    1.时序分析

            ADC128S022的spi接口时序图如下:

    根据时序图及对应参数数值作以下分析:

    1.fSCLK = 3.2 MHz to 8 MHz ,这里我们选取fsclk = 4Mhz进行设计,可以满足要求。

    2.mosi:通过sdstdh来判断,它是基于sclk的上升沿描述的,所以从机大概率是在时钟上升沿读取数据,因而可以选择在sclk的下降沿改变数据,从而到上升沿时数据已经保持稳定。

    3.miso:根据tdacctdhld以及时序图判断,它基于下降沿描述的,即从机在下降沿改变数据,故主机在上升沿进行读取。(读和写的变化沿大部分都是一致的)

    根据时序图分析画出如下波形:

    2.模块框图

            本模块通过一个adc_en脉冲开启一次adc转换读取;adc_channel用于选择八个ad通道中的一个;每次转换完成发出一个adc_done脉冲,并输出转换完成的数据adc_data;

    3.代码实现

    1. //****************************//
    2. //fsclk要求:3.2MHz - 8MHz 取:4MHz //
    3. //
    4. module adc_driver(
    5. input fpga_clk ,
    6. input rst_n ,
    7. input [2:0]adc_channel ,
    8. input adc_en ,
    9. //SPI
    10. input miso ,
    11. output reg mosi ,
    12. output reg cs_n ,
    13. output reg sclk ,
    14. output reg adc_done,
    15. output reg adc_state ,
    16. output reg [11:0]adc_data
    17. );
    18. wire clk_8m ;
    19. pll pll_inst(
    20. .inclk0(fpga_clk),
    21. .c0(clk_8m)
    22. );
    23. reg adc_en_reg ;
    24. always@(posedge fpga_clk or negedge rst_n)
    25. if(!rst_n)
    26. adc_en_reg <= 0 ;
    27. else
    28. adc_en_reg <= adc_en ;
    29. always@(posedge fpga_clk or negedge rst_n)
    30. if(!rst_n)
    31. adc_state <= 0 ;
    32. else if(adc_en == 1 & adc_en_reg == 0)
    33. adc_state <= 1 ;
    34. else if(adc_done)
    35. adc_state <= 0 ;
    36. reg [6:0]adc_cnt ;
    37. always@(posedge clk_8m or negedge rst_n)
    38. if(!rst_n)
    39. adc_cnt <= 0 ;
    40. else if ( adc_state == 1 )
    41. adc_cnt <= adc_cnt + 1 ;
    42. else if(adc_done)
    43. adc_cnt <= 0 ;
    44. //cs_n
    45. always@(posedge fpga_clk or negedge rst_n)
    46. if(!rst_n)
    47. cs_n <= 1 ;
    48. else if( adc_en )
    49. cs_n <= 0 ;
    50. else if( adc_done )
    51. cs_n <= 1 ;
    52. //sclk
    53. always@(posedge clk_8m or negedge rst_n)
    54. if(!rst_n)
    55. sclk <= 1 ;
    56. else if(adc_state)
    57. sclk <= ~sclk ;
    58. else
    59. sclk <= 1 ;
    60. //mosi miso
    61. reg [11:0]adc_data_reg ;
    62. always@(posedge clk_8m or negedge rst_n)
    63. if(!rst_n)
    64. begin
    65. mosi <= 0 ;
    66. adc_done <= 0 ;
    67. adc_data <= 0 ;
    68. adc_data_reg <= 0 ;
    69. end
    70. else if (adc_state)
    71. begin
    72. case(adc_cnt)
    73. 4 : mosi <= adc_channel[2] ;
    74. 6 : mosi <= adc_channel[1] ;
    75. 8 : begin mosi <= adc_channel[0] ; end
    76. 9 : adc_data_reg[11] <= miso ;
    77. 11 : adc_data_reg[10] <= miso ;
    78. 13 : adc_data_reg[9] <= miso ;
    79. 15 : adc_data_reg[8] <= miso ;
    80. 17 : adc_data_reg[7] <= miso ;
    81. 19 : adc_data_reg[6] <= miso ;
    82. 21 : adc_data_reg[5] <= miso ;
    83. 23 : adc_data_reg[4] <= miso ;
    84. 25 : adc_data_reg[3] <= miso ;
    85. 27 : adc_data_reg[2] <= miso ;
    86. 29 : adc_data_reg[1] <= miso ;
    87. 31 : adc_data_reg[0] <= miso ;
    88. 32 : begin adc_done <= 1 ; adc_data <= adc_data_reg ; end
    89. default : ;
    90. endcase
    91. end
    92. else
    93. begin
    94. adc_done <= 0 ;
    95. adc_data_reg <= 0 ;
    96. mosi <= 0 ;
    97. end
    98. endmodule
    1. `timescale 1ns/1ns
    2. /*注意,由于使用联合仿真的时候,modelsim的默认目录是当前Quartus工
    3. 程下的simulation目录下的modelsim文件夹,所以,需要在执行仿真前手
    4. 动将sin_12bit.txt文件拷贝到simulation/modelsim下。修改了
    5. sin_12bit.txt内容后也请记得重新覆盖modelsim下的sin_12bit.txt文件
    6. */
    7. `define sin_data_file "./sin_12bit.txt"
    8. module adc128s022_tb;
    9. reg Clk;
    10. reg Rst_n;
    11. reg [2:0]Channel;
    12. wire [11:0]Data;
    13. reg En_Conv;
    14. wire Conv_Done;
    15. wire ADC_State;
    16. wire [7:0]DIV_PARAM;
    17. wire ADC_SCLK;
    18. wire ADC_CS_N;
    19. reg ADC_DOUT;
    20. wire ADC_DIN;
    21. assign DIV_PARAM = 13;
    22. reg[11:0] memory[4095:0];//测试波形数据存储空间
    23. reg[11:0] address;//存储器地址
    24. adc_driver adc_driver_inst(
    25. .fpga_clk(Clk) ,
    26. .rst_n(Rst_n) ,
    27. .adc_channel(Channel) ,
    28. .adc_en(En_Conv) ,
    29. //SPI
    30. .miso(ADC_DOUT) ,
    31. .mosi(ADC_DIN) ,
    32. .cs_n(ADC_CS_N) ,
    33. .sclk(ADC_SCLK) ,
    34. .adc_done(Conv_Done),
    35. .adc_state(ADC_State) ,
    36. .adc_data(Data)
    37. );
    38. initial Clk = 1'b1;
    39. always #10 Clk = ~Clk;
    40. //将原始波形数据从文件读取到定义的存储器中
    41. initial $readmemh(`sin_data_file,memory);//读取原始波形数据读到memory中
    42. integer i;
    43. initial begin
    44. Rst_n = 0;
    45. Channel = 0;
    46. En_Conv = 0;
    47. ADC_DOUT = 0;
    48. address = 0;
    49. #101;
    50. Rst_n = 1;
    51. #100;
    52. Channel = 5;
    53. for(i=0;i<3;i=i+1)begin
    54. for(address=0;address<4095;address=address+1)begin
    55. En_Conv = 1;
    56. #20;
    57. En_Conv = 0;
    58. gene_DOUT(memory[address]); //依次将存储器中存储的波形读出,按照ADC的转换结果输出方式送到DOUT信号线上
    59. @(posedge Conv_Done); //等待转换完成信号
    60. #200;
    61. end
    62. end
    63. #20000;
    64. $stop;
    65. end
    66. //将并行数据按照ADC的数据输出格式,送到DOUT信号线上,供控制模块采集读取
    67. task gene_DOUT;
    68. input [15:0]vdata;
    69. reg [4:0]cnt;
    70. begin
    71. cnt = 0;
    72. wait(!ADC_CS_N);
    73. while(cnt<16)begin
    74. @(negedge ADC_SCLK) ADC_DOUT = vdata[15-cnt];
    75. cnt = cnt + 1'b1;
    76. end
    77. end
    78. endtask
    79. endmodule

    4.仿真验证

    仿真结果满足时序要求,可行。

  • 相关阅读:
    有手就行10——Jenkins+SonarQube代码审查
    如何将独立站与Facebook店铺同步绑定
    Java实战发包到远程maven仓库
    解剖—顺序表相关OJ练习题
    简谈计算机网络与网络编程
    Linux命令lsscsi详解
    【附源码】Python计算机毕业设计企业人事系统
    基于YOLOv8模型和UA-DETRAC数据集的车辆目标检测系统(PyTorch+Pyside6+YOLOv8模型)
    【NODE.JS】多进程架构(一)——基本概念
    QT基础第一天 (1)QT,GUI(图形用户接口)开发
  • 原文地址:https://blog.csdn.net/qq_44366923/article/details/133934761