我是靠谱客的博主 糊涂高跟鞋,这篇文章主要介绍FPGA驱动CS1237(Verilog),现在分享给大家,希望可以做个参考。

最近用到CS1237(24位ADC)采集锂电池电压,也是本人第一次用Verilog自己按照数据手册的时序图写通讯协议,内容可能会有不足之处,也欢迎大家在评论区指正。

话不多说,开始整活

(CS1237数据手册的时序图我就不贴了,大家如果要用这个芯片,最开始肯定有找数据手册)

硬件电路图:

 正采集端用电阻分压的原因是,芯片自己的数据转换公式里会给基准电压除2 ,为方便数据处理,硬件上做个电阻分压。

Verilog代码:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
module CS1237 ( input wire sys_clk , //系统时钟,频率25MHz input wire sys_rst_n , //复位信号,低电平有效 inout reg adc , //数据端口 output reg sclk , //时钟端口 output reg [7:0] bat_power ); parameter S_STOP = 3'd0, //时钟高电平,休眠状态 S_WAIT_R = 3'd1, //时钟低电平,等待数据转换完成 S_READ = 3'd2, //1-24时钟,读取数据 S_WAIT_W = 3'd3, //25-29时钟,准备写 S_WRITE = 3'd4, //30-37时钟,发送写寄存器指令,37时钟用于过渡 S_CONFIG = 3'd5; //38-47时钟,写寄存器,47时钟是为了让46时钟完整 parameter T_STOP = 8'd250; //us,休眠状态时长,根据自己需求修改 reg [2:0] state ; //状态机状态 reg adc_d1 ; //总线信号打一拍 wire adc_fall ; //总线下降沿 reg [4:0] cnt ; //时钟分频计数器 reg clk_1us ; //1us时钟 reg [7:0] cnt_us ; //us计数器 reg en_sclk ; //输出时钟使能 reg [5:0] bit_cnt ; //字节计数器 reg [23:0] data_adc ; //adc采集的数据 //对adc信号打拍,检测总线信号的下降沿 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) adc_d1 <= 1'b0 ; else adc_d1 <= adc ; assign adc_fall = (adc_d1) & (~adc) ; //cnt:分频计数器 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) cnt <= 5'd0; else if(cnt == 5'd24) cnt <= 5'd0; else cnt <= cnt + 1'b1; //clk_1us:产生单位时钟为1us的时钟 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) clk_1us <= 1'b0; else if(cnt == 5'd24) clk_1us <= 1'b0; else if(cnt == 5'd12) clk_1us <= 1'b1; else clk_1us <= clk_1us; //微秒计数器 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) cnt_us <= 8'd0; else if(state == S_STOP && cnt_us == T_STOP) cnt_us <= 8'd0; else if(state == S_STOP) cnt_us <= cnt_us + 8'd1; else cnt_us <= 8'd0; //输出sclk管脚输出1us时钟使能,确保输出的时钟的第一个周期完整 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) en_sclk <= 1'b0; else if(state == S_STOP) en_sclk <= 1'b0; else if(state == S_READ && clk_1us == 1'b0) en_sclk <= 1'b1; else en_sclk <= en_sclk; //状态机状态跳转 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) state <= S_STOP ; else case(state) S_STOP: if(cnt_us == T_STOP) state <= S_WAIT_R; else state <= S_STOP; S_WAIT_R: if(adc_fall == 1'b1 && sclk == 1'b0) state <= S_READ; else state <= S_WAIT_R; S_READ: if(bit_cnt == 6'd25) state <= S_WAIT_W; else state <= S_READ; S_WAIT_W: if(bit_cnt == 6'd29) state <= S_WRITE; else state <= S_WAIT_W; S_WRITE: if(bit_cnt == 6'd37) state <= S_CONFIG; else state <= S_WRITE; S_CONFIG: if(bit_cnt == 6'd47) state <= S_STOP; else state <= S_CONFIG; default: state <= S_STOP; endcase //数据端口控制,默认高阻态 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) adc <= 1'dz; else case(state) S_STOP: adc <= 1'dz; S_WAIT_R: adc <= 1'dz; S_READ: adc <= 1'dz; S_WAIT_W: adc <= 1'dz; S_WRITE: if(bit_cnt == 6'd30) //写入0x65,写寄存器指令 adc <= 1'd1; else if(bit_cnt == 6'd32) adc <= 1'd0; else if(bit_cnt == 6'd34) adc <= 1'd1; else if(bit_cnt == 6'd35) adc <= 1'd0; else if(bit_cnt == 6'd36) adc <= 1'd1; else adc <= adc; S_CONFIG: adc <= 1'd0; //寄存器写入全0,将128倍的PGA改为1倍,其他不变 default: adc <= 1'dz; endcase //sclk时钟输出 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) sclk <= 1'b0; else case(state) S_STOP : sclk <= 1'b1; S_WAIT_R : sclk <= 1'b0; default : if(en_sclk == 1'b1) sclk <= clk_1us; else sclk <= 1'b0; endcase //bit_cnt:读出数据bit位数计数器 always@(posedge clk_1us or negedge sys_rst_n) if(sys_rst_n == 1'b0) bit_cnt <= 6'd0; else if(bit_cnt == 6'd47) bit_cnt <= 6'd0; else if(state != S_STOP && state != S_WAIT_R && en_sclk == 1'b1) bit_cnt <= bit_cnt + 1'b1; else bit_cnt <= 6'd0; //data_adc:将读出的数据寄存在data_adc中 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) data_adc <= 24'd0; else if(cnt == 6'd12 && en_sclk == 1'b1 && bit_cnt <= 6'd24) data_adc[24 - bit_cnt] <= adc; else data_adc <= data_adc; //数据转换:锂电池电量显示1%-99% //(这里认定电池电量与电压关系为线性,实际情况为非线性) //认定正常使用锂电池情况下,满电量电压为4.2V,最低使用电压为3.2V //差值4.2-3.2=1V,分100份,每份10mV //ADC采集的电量减去3.2V,再除10mV,得到当前所占份数,即百分比电量 //CS1237采集满量程为7FFFFFh(5V基准) //10mV对应(5V)比例数值为4189h(0100 0001 1000 1001b) //近似忽略后几位数值,看成4000h(0100 0000 0000 0000b)误差2.34% //除以4000h即可看成除以2的14次方,即data_adc[23:0]的后14位数忽略即可 always@(posedge clk_1us or negedge sys_rst_n) if(sys_rst_n == 1'b0) bat_power <= 8'd0; else if(bit_cnt == 6'd25) //读取完数据后更新数据 if(data_adc[23] == 1'b1) //负电压显示1%电量 bat_power <= 8'd1; else if(data_adc[22:14]<9'h147) //低于3.2V显示1%电量 bat_power <= 8'd1; else if(data_adc[22:14]>9'h1AE) //高于4.2V显示99%电量 bat_power <= 8'd99; else bat_power <= (data_adc[22:14]-9'h147); //显示1-99%电量 else bat_power <= bat_power; endmodule

 2022/6/3更新

得知锂电池电压正常工作电压范围是3V-4.2V,锂电池电压低于3V会对电池造成损耗。

锂电池电压与电量的关系表可自行百度。

代码已更新。


如果哪里有错误,欢迎在评论区指出 

如果这篇文章能帮助到你,顺手点个赞支持一下吧

最后

以上就是糊涂高跟鞋最近收集整理的关于FPGA驱动CS1237(Verilog)的全部内容,更多相关FPGA驱动CS1237(Verilog)内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(97)

评论列表共有 0 条评论

立即
投稿
返回
顶部