13-基于SPI的ADC芯片驱动
·
目录
一、ADC
1.1 ADC简介
定义:模数转换器,用于将模拟信号转换为数字信号的电子元件
常见指标参数:
分辨率:
指 ADC 能分辨的最小信号变化,以输出数字码的位数表示(如 12 位、16 位)。位数越高,可量化的信号细节越精细,例如 12 位 ADC 能将满量程信号分为 4096 个等级。
采样范围:
DC 能有效转换的信号范围,分单极性(如 0~5V)和双极性(如 - 10~+10V)。输入信号超出范围会导致失真,需根据实际信号选择匹配量程。
采样速率:
单位时间内的采样次数(单位:Hz 或 SPS),需满足奈奎斯特定理(采样率≥信号最高频率 2 倍)。例如,采样 10kHz 信号需至少 20kSPS 的速率,避免信号混叠失真。



1.2 ADC驱动
单次采集时序图

ADD2,ADD1,ADD0为通道选择信号

多次采集时序图





设计思路:
计数单元,定义两个计数器(①最小时间单位计数器,②对最小时间单位进行计数),用于表示时刻0~34;
对照时刻表,在0~34时刻,驱动信号进行相应的变化
1.3 线性序列机结构
- 计数单元,本质上就是一个不停计数的计数器,用以计时得到最小时间单位
- 序列计数器,用来标记每一个时间点
- 驱动部分,负责根据时刻表中各个信号的值,在对应时间点驱动信号变化
二、代码编写
2.1 设计文件
`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Create Date: 2025/07/04 10:44:08
// Design Name:
// Module Name: ADC128S102_Driver
module ADC128S102_Driver(
clk,
reset_n,
addr,
conv_go,
conv_done,
data,
ADC_SCLK,
ADC_CS_N,
ADC_DIN,
ADC_DOUT
);
input clk;
input reset_n;
input [2:0] addr;
input conv_go;
output reg conv_done;
output reg [11:0] data;
output reg ADC_CS_N;
output reg ADC_DIN;
output reg ADC_SCLK;
input ADC_DOUT;
reg [29:0] div_cnt;
reg [5:0] bit_cnt;
reg [2:0] r_addr;
reg [11:0] r_data;
reg conv_en;
parameter clk_freq = 50_000_000;
parameter SCLK_freq = 12_500_000;
parameter mcnt_div = clk_freq/(SCLK_freq * 2)-1; //最小时间单位为周期的一半
parameter mcnt_bit = 34;
always @(posedge clk or negedge reset_n)
if(!reset_n)
conv_en <= 1'b0;
else if(conv_go)
conv_en <= 1'b1;
else if((bit_cnt == mcnt_bit)&&(div_cnt == mcnt_div))
conv_en <= 1'b0;
else
conv_en <= conv_en;
//最小时间单元计数器
always @(posedge clk or negedge reset_n)
if(!reset_n)
div_cnt <= 30'd0;
else if(conv_en)begin
if(div_cnt == mcnt_div)
div_cnt <= 30'd0;
else
div_cnt <= div_cnt + 1'b1;
end
else
div_cnt <= 30'd0;
//对最小时间单位进行位计数
always @(posedge clk or negedge reset_n)
if(!reset_n)
bit_cnt <= 6'd0;
else if(div_cnt == mcnt_div) begin
if(bit_cnt == mcnt_bit)
bit_cnt <= 6'd0;
else
bit_cnt <= bit_cnt +1'b1;
end
else
bit_cnt <= bit_cnt;
//防止addr在驱动过程中发生变化,进行一级寄存
always @(posedge clk)
if(conv_go)
r_addr <= addr;
else
r_addr <= r_addr;
//驱动信号
always @(posedge clk or negedge reset_n)
if(!reset_n) begin
ADC_CS_N <= 1'b1;
ADC_SCLK <= 1'b1;
ADC_DIN <= 1'b1;
r_data <= 12'd0;
end
else if(div_cnt == mcnt_div) begin
case(bit_cnt)
0:begin
ADC_CS_N <= 1'b1;
ADC_SCLK <= 1'b1;
end
1:ADC_CS_N <= 1'b0;
2:ADC_SCLK <= 1'b0;
3:ADC_SCLK <= 1'b1;
4:ADC_SCLK <= 1'b0;
5:ADC_SCLK <= 1'b1;
6:begin
ADC_SCLK <= 1'b0;
ADC_DIN <= r_addr[2];
end
7:ADC_SCLK <= 1'b1;
8:begin
ADC_SCLK <= 1'b0;
ADC_DIN <= r_addr[1];
end
9:ADC_SCLK <= 1'b1;
10:begin
ADC_SCLK <= 1'b0;
ADC_DIN <= r_addr[0];
end
11:begin
ADC_SCLK <= 1'b1;
r_data[11] <= ADC_DOUT;
end
12:ADC_SCLK <= 1'b0;
13:begin
ADC_SCLK <= 1'b1;
r_data[10] <= ADC_DOUT;
end
14:ADC_SCLK <= 1'b0;
15:begin
ADC_SCLK <= 1'b1;
r_data[9] <= ADC_DOUT;
end
16:ADC_SCLK <= 1'b0;
17:begin
ADC_SCLK <= 1'b1;
r_data[8] <= ADC_DOUT;
end
18:ADC_SCLK <= 1'b0;
19:begin
ADC_SCLK <= 1'b1;
r_data[7] <= ADC_DOUT;
end
20:ADC_SCLK <= 1'b0;
21:begin
ADC_SCLK <= 1'b1;
r_data[6] <= ADC_DOUT;
end
22:ADC_SCLK <= 1'b0;
23:begin
ADC_SCLK <= 1'b1;
r_data[5] <= ADC_DOUT;
end
24:ADC_SCLK <= 1'b0;
25:begin
ADC_SCLK <= 1'b1;
r_data[4] <= ADC_DOUT;
end
26:ADC_SCLK <= 1'b0;
27:begin
ADC_SCLK <= 1'b1;
r_data[3] <= ADC_DOUT;
end
28:ADC_SCLK <= 1'b0;
29:begin
ADC_SCLK <= 1'b1;
r_data[2] <= ADC_DOUT;
end
30:ADC_SCLK <= 1'b0;
31:begin
ADC_SCLK <= 1'b1;
r_data[1] <= ADC_DOUT;
end
32:ADC_SCLK <= 1'b0;
33:begin
ADC_SCLK <= 1'b1;
r_data[0] <= ADC_DOUT;
end
34:ADC_CS_N <= 1'b1;
endcase
end
//为了避免数据接收的过程中,未完成的结果条件在输出端口上,使用内部存储器存储数据结枿
always @(posedge clk or negedge reset_n)
if(!reset_n)begin
conv_done <= 1'b0;
data <= 12'd0;
end
else if((bit_cnt == mcnt_bit)&&(div_cnt == mcnt_div)) begin
conv_done <= 1'b1;
data <= r_data;
end
else begin
conv_done <= 1'b0;
data <= data;
end
endmodule
2.2 激励文件
`timescale 1ns / 1ps
// Create Date: 2025/07/04 13:14:54
// Design Name:
// Module Name: ADC128S102_Driver_tb
module ADC128S102_Driver_tb;
reg clk;
reg reset_n;
reg [2:0] addr;
reg conv_go;
wire conv_done;
wire [11:0] data;
wire ADC_CS_N;
wire ADC_DIN;
wire ADC_SCLK;
reg ADC_DOUT;
ADC128S102_Driver ADC128S102_Driver_inst(
.clk(clk),
.reset_n(reset_n),
.addr(addr),
.conv_go(conv_go),
.conv_done(conv_done),
.data(data),
.ADC_SCLK(ADC_SCLK),
.ADC_CS_N(ADC_CS_N),
.ADC_DIN(ADC_DIN),
.ADC_DOUT(ADC_DOUT)
);
initial clk = 1'b1;
always #10 clk = ~clk;
initial begin
reset_n =1'b0;
addr = 3'd0;
conv_go = 1'b0;
#201;
reset_n = 1'b1;
#200;
conv_go = 1'b1;
addr = 3'd3;
#20;
conv_go = 1'b0;
wait(!ADC_CS_N);
//16'H0A58 0000_1010_0101_1000
@(negedge ADC_SCLK)
ADC_DOUT = 1'b0; //DB15
@(negedge ADC_SCLK)
ADC_DOUT = 1'b0; //DB14
@(negedge ADC_SCLK)
ADC_DOUT = 1'b0; //DB13
@(negedge ADC_SCLK)
ADC_DOUT = 1'b0; //DB12
@(negedge ADC_SCLK)
ADC_DOUT = 1'b1; //DB11
@(negedge ADC_SCLK)
ADC_DOUT = 1'b0; //DB10
@(negedge ADC_SCLK)
ADC_DOUT = 1'b1; //DB9
@(negedge ADC_SCLK)
ADC_DOUT = 1'b0; //DB8
@(negedge ADC_SCLK)
ADC_DOUT = 1'b0; //DB7
@(negedge ADC_SCLK)
ADC_DOUT = 1'b1; //DB6
@(negedge ADC_SCLK)
ADC_DOUT = 1'b0; //DB5
@(negedge ADC_SCLK)
ADC_DOUT = 1'b1; //DB4
@(negedge ADC_SCLK)
ADC_DOUT = 1'b1; //DB3
@(negedge ADC_SCLK)
ADC_DOUT = 1'b0; //DB2
@(negedge ADC_SCLK)
ADC_DOUT = 1'b0; //DB1
@(negedge ADC_SCLK)
ADC_DOUT = 1'b0; //DB0
wait(ADC_CS_N);
#20000;
conv_go = 1'b1;
addr = 3'd7;
#20;
conv_go = 1'b0;
wait(!ADC_CS_N);
//16'H089c 0000_1000_1001_1100
@(negedge ADC_SCLK)
ADC_DOUT = 1'b0; //DB15
@(negedge ADC_SCLK)
ADC_DOUT = 1'b0; //DB14
@(negedge ADC_SCLK)
ADC_DOUT = 1'b0; //DB13
@(negedge ADC_SCLK)
ADC_DOUT = 1'b0; //DB12
@(negedge ADC_SCLK)
ADC_DOUT = 1'b1; //DB11
@(negedge ADC_SCLK)
ADC_DOUT = 1'b0; //DB10
@(negedge ADC_SCLK)
ADC_DOUT = 1'b0; //DB9
@(negedge ADC_SCLK)
ADC_DOUT = 1'b0; //DB8
@(negedge ADC_SCLK)
ADC_DOUT = 1'b1; //DB7
@(negedge ADC_SCLK)
ADC_DOUT = 1'b0; //DB6
@(negedge ADC_SCLK)
ADC_DOUT = 1'b0; //DB5
@(negedge ADC_SCLK)
ADC_DOUT = 1'b1; //DB4
@(negedge ADC_SCLK)
ADC_DOUT = 1'b1; //DB3
@(negedge ADC_SCLK)
ADC_DOUT = 1'b1; //DB2
@(negedge ADC_SCLK)
ADC_DOUT = 1'b0; //DB1
@(negedge ADC_SCLK)
ADC_DOUT = 1'b0; //DB0
wait(ADC_CS_N);
#20000;
$stop;
end
endmodule
2.3 仿真图

三、ADC驱动模块调用
3.1 模块调用代码
`timescale 1ns / 1ps
// Create Date: 2025/07/04 15:35:36
// Design Name:
// Module Name: ADC128S102_test
module ADC128S102_test(
clk,
reset_n,
key,
addr,
led,
HEX8_DIO,
HEX8_SRCLK,
HEX8_RCLK,
ADC_SCLK,
ADC_CS_N,
ADC_DIN,
ADC_DOUT,
);
input clk;
input reset_n;
input key;
input [2:0] addr;
output reg led;
output HEX8_DIO;
output HEX8_SRCLK;
output HEX8_RCLK;
output ADC_SCLK;
output ADC_CS_N;
output ADC_DIN;
input ADC_DOUT;
wire [31:0] disp_data;
wire conv_go;
wire conv_done;
wire [11:0] data;
wire key_p_flag;
assign disp_data={20'd0,data};
//数码管驱动模块调用,将采集到的数据显示在数码管上
hex8_hc595(
.clk(clk),
.reset_n(reset_n),
.disp_data(disp_data),
.DIO(HEX8_DIO),
.SRCLK(HEX8_SRCLK),
.RCLK(HEX8_RCLK)
);
//ADC模块调用
ADC128S102_Driver ADC128S102_Driver_inst(
.clk(clk),
.reset_n(reset_n),
.addr(addr),
.conv_go(conv_go),
.conv_done(conv_done),
.data(data),
.ADC_SCLK(ADC_SCLK),
.ADC_CS_N(ADC_CS_N),
.ADC_DIN(ADC_DIN),
.ADC_DOUT(ADC_DOUT)
);
//按键消抖模块调用,使用按键指示conv_go开始采集信号
key_filter(
.clk(clk),
.reset_n(reset_n),
.key(key),
.key_p_flag(key_p_flag),
.key_r_flag(),
.key_state()
);
assign conv_go =key_p_flag;
//LED指示模块
always @(posedge clk or negedge reset_n)
if(!reset_n)
led <= 1'b0;
else if(conv_done)
led <=~led;
else
led <= led;
endmodule
3.2 板级测试
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐


所有评论(0)