完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
1)实验平台:正点原子开拓者FPGA 开发板
2)摘自《开拓者FPGA开发指南》关注官方微信号公众号,获取更多资料:正点原子 3)全套实验源码+手册+视频下载地址:http://www.openedv.com/thread-13912-1-1.html UDP(udp)模块是对以太网接收模块(ip_receive)、以太网发送模块(ip_send)、CRC 校验模块(crc32_d4)的例化,其中crc32_d4模块是对发送模块做CRC校验。下面着重介绍各 个子模块代码的实现。 我们在前面介绍过,以太网接收模块实现的是4位转32位的功能以及解析数据的顺序,可 以发现,解析数据的顺序很适合使用状态机来实现,下图为以太网接收模块的状态跳转图。 图 43.4.5 ip_receive模块的状态跳转图 接收模块使用三段式状态机来解析以太网包,从上图可以比较直观的看到每个状态实现的 功能以及跳转到下一个状态的条件。这里需要注意的一点是,在中间状态如前导码错误、MAC 地址错误以及IP地址错误时跳转到st_rx_end状态而不是跳转到st_idle转态。因为中间状态在 解析到数据错误时,单包数据的接收还没有结束,如果此时跳转到st_idle状态会误把有效数 据当成前导码来解析,所以状态跳转到st_rx_end。而eth_rxdv信号为0时,单包数据才算接收 结束,所以st_rx_end跳转到st_idle的条件是eth_rxdv=0,准备接收下一包数据。因为代码较 长,只粘贴了第三段状态机的接收数据状态和接收结束状态源代码,代码如下: 265 st_rx_data : begin 266 //接收数据,转换成32bit 267 if(rx_byte_val) begin 268 data_cnt <= data_cnt + 16'd1; 269 rec_en_cnt <= rec_en_cnt + 2'd1; 270 if(data_cnt == data_byte_num - 16'd1) begin 271 skip_en <= 1'b1; //有效数据接收完成 272 data_cnt <= 16'd0; 273 rec_en_cnt <= 2'd0; 274 rec_pkt_done <= 1'b1; 275 rec_en <= 1'b1; 276 rec_byte_num <= data_byte_num; 277 end 278 //先收到的数据放在了rec_data的高位,所以当数据不是4的倍数时, 279 //低位数据为无效数据,可根据有效字节数来判断(rec_byte_num) 280 if(rec_en_cnt == 2'd0) 281 rec_data[31:24] <= rx_data; 282 else if(rec_en_cnt == 2'd1) 283 rec_data[23:16] <= rx_data; 284 else if(rec_en_cnt == 2'd2) 285 rec_data[15:8] <= rx_data; 286 else if(rec_en_cnt==2'd3) begin 287 rec_en <= 1'b1; 288 rec_data[7:0] <= rx_data; 289 end 290 end 291 end 292 st_rx_end : begin //单包数据接收完成 293 if(eth_rxdv == 1'b0 && skip_en == 1'b0) 294 skip_en <= 1'b1; 295 end 图 43.4.6为接收过程中SignalTap抓取的波形图,上位机通过网口调试助手发送 http://www.alientek.com/(十六进制为:68 74 74 70 3A 2F 2F 77 77 77 2E 61 6C 69 65 6E 74 65 6B 2E 63 6F 6D 2F),图中eth_rxdv和eth_rx_data为MII接口的接收信号,skip_en 为状态机的跳转信号。每次单包数据接收完成都会产生rec_pkt_done信号,rec_en和rec_data 为收到的数据有效信号和数据。 图 43.4.6 接收过程SignalTap波形图 以太网发送模块实际上完成的是32位数据转4位数据的功能,也就是接收模块的逆过程, 同样也非常适合使用状态机来完成发送数据的功能,状态跳转图如下图所示: 图 43.4.7 ip_send模块的状态跳转图 发送模块和接收模块有很多相似之处,同样使用三段式状态机来发送以太网包,从上图可 以比较直观的看到每个状态实现的功能以及跳转到下一个状态的条件。 发送模块的代码中定义了数组来存储以太网的帧头、IP首部以及UDP的首部,在复位时初 始化数组的值,部分源代码如下。 68 reg [7:0] preamble[7:0] ; //前导码 69 reg [7:0] eth_head[13:0] ; //以太网首部 70 reg [31:0] ip_head[6:0] ; //IP首部 + UDP首部 省略部分代码…… 198 //初始化数组 199 //前导码 7个8'h55 + 1个8'hd5 200 preamble[0] <= 8'h55; 201 preamble[1] <= 8'h55; 202 preamble[2] <= 8'h55; 203 preamble[3] <= 8'h55; 204 preamble[4] <= 8'h55; 205 preamble[5] <= 8'h55; 206 preamble[6] <= 8'h55; 207 preamble[7] <= 8'hd5; 208 //目的MAC地址 209 eth_head[0] <= DES_MAC[47:40]; 210 eth_head[1] <= DES_MAC[39:32]; 211 eth_head[2] <= DES_MAC[31:24]; 212 eth_head[3] <= DES_MAC[23:16]; 213 eth_head[4] <= DES_MAC[15:8]; 214 eth_head[5] <= DES_MAC[7:0]; 215 //源MAC地址 216 eth_head[6] <= BOARD_MAC[47:40]; 217 eth_head[7] <= BOARD_MAC[39:32]; 218 eth_head[8] <= BOARD_MAC[31:24]; 219 eth_head[9] <= BOARD_MAC[23:16]; 220 eth_head[10] <= BOARD_MAC[15:8]; 221 eth_head[11] <= BOARD_MAC[7:0]; 222 //以太网类型 223 eth_head[12] <= ETH_TYPE[15:8]; 224 eth_head[13] <= ETH_TYPE[7:0]; 我们前面讲过以太网帧格式的数据部分最少是46个字节,去掉IP首部字节和UDP首部字节 后,有效数据至少为18个字节,程序设计中已经考虑到这种情况,当发送的有效数据少于18个 字节时,会在有效数据后面填补充位,第三段状态机发送状态源代码如下所示: 344 st_tx_data : begin //发送数据 345 crc_en <= 1'b1; 346 eth_tx_en <= 1'b1; 347 tx_bit_sel <= tx_bit_sel + 3'd1; 348 if(tx_bit_sel[0] == 1'b0) begin 349 if(data_cnt < tx_data_num - 16'd1) 350 data_cnt <= data_cnt + 16'd1; 351 else if(data_cnt == tx_data_num - 16'd1)begin 352 //如果发送的有效数据少于18个字节,在后面填补充位 353 //补充的值为最后一次发送的有效数据 354 if(data_cnt + real_add_cnt < real_tx_data_num - 16'd1) 355 real_add_cnt <= real_add_cnt + 5'd1; 356 else 357 skip_en <= 1'b1; 358 end 359 end 360 if(tx_bit_sel == 3'd0) begin 361 eth_tx_data <= tx_data[27:24]; 362 end 363 else if(tx_bit_sel == 3'd1) 364 eth_tx_data <= tx_data[31:28]; 365 else if(tx_bit_sel == 3'd2) 366 eth_tx_data <= tx_data[19:16]; 367 else if(tx_bit_sel == 3'd3) 368 eth_tx_data <= tx_data[23:20]; 369 else if(tx_bit_sel == 3'd4) 370 eth_tx_data <= tx_data[11:8]; 371 else if(tx_bit_sel == 3'd5) 372 eth_tx_data <= tx_data[15:12]; 373 else if(tx_bit_sel == 3'd6) begin 374 eth_tx_data <= tx_data[3:0]; 375 if(data_cnt != tx_data_num - 16'd1) 376 tx_req <= 1'b1; 377 end 378 else if(tx_bit_sel == 3'd7) 379 eth_tx_data <= tx_data[7:4]; 380 if(skip_en) begin 381 data_cnt <= 16'd0; 382 real_add_cnt <= 5'd0; 383 tx_bit_sel <= 3'd0; 384 end 385 end 发送模块的CRC校验是由crc32_d4模块完成的,发送模块将输入的crc的计算结果每4位高 低位互换,按位取反发送出去,crc计算部分在后面阐述,第三段状态机发送CRC校验源代码如 下所示: 386 st_crc : begin //发送CRC校验值 387 eth_tx_en <= 1'b1; 388 tx_bit_sel <= tx_bit_sel + 3'd1; 389 if(tx_bit_sel == 3'd0) 390 //注意是crc_next 391 eth_tx_data <= {~crc_next[0], ~crc_next[1], ~crc_next[2], 392 ~crc_next[3]}; 393 else if(tx_bit_sel == 3'd1) 394 eth_tx_data <= {~crc_data[24],~crc_data[25],~crc_data[26], 395 ~crc_data[27]}; 396 else if(tx_bit_sel == 3'd2) 397 eth_tx_data <= {~crc_data[20],~crc_data[21],~crc_data[22], 398 ~crc_data[23]}; 399 else if(tx_bit_sel == 3'd3) 400 eth_tx_data <= {~crc_data[16],~crc_data[17],~crc_data[18], 401 ~crc_data[19]}; 402 else if(tx_bit_sel == 3'd4) 403 eth_tx_data <= {~crc_data[12],~crc_data[13],~crc_data[14], 404 ~crc_data[15]}; 405 else if(tx_bit_sel == 3'd5) 406 eth_tx_data <= {~crc_data[8],~crc_data[9],~crc_data[10], 407 ~crc_data[11]}; 408 else if(tx_bit_sel == 3'd6) begin 409 eth_tx_data <= {~crc_data[4],~crc_data[5],~crc_data[6], 410 ~crc_data[7]}; 411 skip_en <= 1'b1; 412 end 413 else if(tx_bit_sel == 3'd7) begin 414 eth_tx_data <= {~crc_data[0],~crc_data[1],~crc_data[2], 415 ~crc_data[3]}; 416 tx_done_t <= 1'b1; 417 end 418 end 图 43.4.8为发送过程中SignalTap抓取的波形图,图中tx_start_en作为开始发送的启动 信号,eth_tx_en和eth_tx_data即为MII接口的发送接口。在开始发送以太网帧头时crc_en拉 高开始CRC校验的计算,在将要发送有效数据时拉高tx_req(发送数据请求)信号,tx_data即 为待发送的有效数据,在所有数据发送完成后输出tx_done(发送完成)信号和crc_clr(CRC 校验值复位)信号。 图 43.4.8 发送过程SignalTap波形图 以太网发送模块CRC校验代码如下所示: 1 module crc32_d4( 2 input clk , //时钟信号 3 input rst_n , //复位信号,低电平有效 4 input [3:0] data , //输入待校验4位数据 5 input crc_en , //crc使能,开始校验标志 6 input crc_clr , //crc数据复位信号 7 output reg [31:0] crc_data, //CRC校验数据 8 output [31:0] crc_next //CRC下次校验完成数据 9 ); 10 11 //***************************************************** 12 //** main code 13 //***************************************************** 14 15 //输入待校验4位数据,需要先将高低位互换 16 wire [3:0] data_t; 17 18 assign data_t = {data[0],data[1],data[2],data[3]}; 19 20 //CRC32的生成多项式为:G(x)= x^32 + x^26 + x^23 + x^22 + x^16 + x^12 + x^11 21 //+ x^10 + x^8 + x^7 + x^5 + x^4 + x^2 + x^1 + 1 22 assign crc_next[0] = crc_en & (data_t[0] ^ crc_data[28]); 23 assign crc_next[1] = crc_en & (data_t[1] ^ data_t[0] ^ crc_data[28] 24 ^ crc_data[29]); 25 assign crc_next[2] = crc_en & (data_t[2] ^ data_t[1] ^ data_t[0] ^ crc_data[28] 26 ^ crc_data[29] ^ crc_data[30]); 27 assign crc_next[3] = crc_en & (data_t[3] ^ data_t[2] ^ data_t[1] ^ crc_data[29] 28 ^ crc_data[30] ^ crc_data[31]); 29 assign crc_next[4] = (crc_en & (data_t[3] ^ data_t[2] ^ data_t[0] ^ crc_data[28] 30 ^ crc_data[30] ^ crc_data[31])) ^ crc_data[0]; 31 assign crc_next[5] = (crc_en & (data_t[3] ^ data_t[1] ^ data_t[0] ^ crc_data[28] 32 ^ crc_data[29] ^ crc_data[31])) ^ crc_data[1]; 33 assign crc_next[6] = (crc_en & (data_t[2] ^ data_t[1] ^ crc_data[29] 34 ^ crc_data[30])) ^ crc_data[ 2]; 35 assign crc_next[7] = (crc_en & (data_t[3] ^ data_t[2] ^ data_t[0] ^ crc_data[28] 36 ^ crc_data[30] ^ crc_data[31])) ^ crc_data[3]; 37 assign crc_next[8] = (crc_en & (data_t[3] ^ data_t[1] ^ data_t[0] ^ crc_data[28] 38 ^ crc_data[29] ^ crc_data[31])) ^ crc_data[4]; 39 assign crc_next[9] = (crc_en & (data_t[2] ^ data_t[1] ^ crc_data[29] 40 ^ crc_data[30])) ^ crc_data[5]; 41 assign crc_next[10] = (crc_en & (data_t[3] ^ data_t[2] ^ data_t[0] ^ crc_data[28] 42 ^ crc_data[30] ^ crc_data[31])) ^ crc_data[6]; 43 assign crc_next[11] = (crc_en & (data_t[3] ^ data_t[1] ^ data_t[0] ^ crc_data[28] 44 ^ crc_data[29] ^ crc_data[31])) ^ crc_data[7]; 45 assign crc_next[12] = (crc_en & (data_t[2] ^ data_t[1] ^ data_t[0] ^ crc_data[28] 46 ^ crc_data[29] ^ crc_data[30])) ^ crc_data[8]; 47 assign crc_next[13] = (crc_en & (data_t[3] ^ data_t[2] ^ data_t[1] ^ crc_data[29] 48 ^ crc_data[30] ^ crc_data[31])) ^ crc_data[9]; 49 assign crc_next[14] = (crc_en & (data_t[3] ^ data_t[2] ^ crc_data[30] 50 ^ crc_data[31])) ^ crc_data[10]; 51 assign crc_next[15] = (crc_en & (data_t[3] ^ crc_data[31])) ^ crc_data[11]; 52 assign crc_next[16] = (crc_en & (data_t[0] ^ crc_data[28])) ^ crc_data[12]; 53 assign crc_next[17] = (crc_en & (data_t[1] ^ crc_data[29])) ^ crc_data[13]; 54 assign crc_next[18] = (crc_en & (data_t[2] ^ crc_data[30])) ^ crc_data[14]; 55 assign crc_next[19] = (crc_en & (data_t[3] ^ crc_data[31])) ^ crc_data[15]; 56 assign crc_next[20] = crc_data[16]; 57 assign crc_next[21] = crc_data[17]; 58 assign crc_next[22] = (crc_en & (data_t[0] ^ crc_data[28])) ^ crc_data[18]; 59 assign crc_next[23] = (crc_en & (data_t[1] ^ data_t[0] ^ crc_data[29] 60 ^ crc_data[28])) ^ crc_data[19]; 61 assign crc_next[24] = (crc_en & (data_t[2] ^ data_t[1] ^ crc_data[30] 62 ^ crc_data[29])) ^ crc_data[20]; 63 assign crc_next[25] = (crc_en & (data_t[3] ^ data_t[2] ^ crc_data[31] 64 ^ crc_data[30])) ^ crc_data[21]; 65 assign crc_next[26] = (crc_en & (data_t[3] ^ data_t[0] ^ crc_data[31] 66 ^ crc_data[28])) ^ crc_data[22]; 67 assign crc_next[27] = (crc_en & (data_t[1] ^ crc_data[29])) ^ crc_data[23]; 68 assign crc_next[28] = (crc_en & (data_t[2] ^ crc_data[30])) ^ crc_data[24]; 69 assign crc_next[29] = (crc_en & (data_t[3] ^ crc_data[31])) ^ crc_data[25]; 70 assign crc_next[30] = crc_data[26]; 71 assign crc_next[31] = crc_data[27]; 72 73 always @(posedge clk or negedge rst_n) begin 74 if(!rst_n) 75 crc_data <= 32'hff_ff_ff_ff; 76 else if(crc_clr) //CRC校验值复位 77 crc_data <= 32'hff_ff_ff_ff; 78 else if(crc_en) 79 crc_data <= crc_next; 80 end 81 82 endmodule 这里要注意的是代码的第18行,输入待校验的4位数据,高低位数据需要互换再进行CRC校 验。CRC32校验在FPGA实现的原理是线性反馈移位寄存器,其思想是各个寄存器储存着上一次 CRC32运算的结果,寄存器的输出即为CRC32的值。CRC32的原理与公式推导较复杂,在此可不 比深究, CRC 校 验 的源代码可 直 接 通 过 网页生成 工 具 直 接 下 载 , 网 址 : http://www.easics.com/webtools/crctool,CRC32的生成多项式为:G(x)= x^32 + x^26 + x^23 + x^22 + x^16 + x^12 + x^11 + x^10 + x^8 + x^7 + x^5 + x^4 + x^2 + x^1 + 1, 设置界面如下图所示: 图 43.4.9 生成CRC代码设置界面 下载之后对比我们提供的源代码会发现,只需稍作修改就可以直接使用。 下载验证 首先我们打开以太网通信实验工程,在工程所在的路径下打开eth_pc_loop/par文件夹, 在里面找到“eth_pc_loop.qpf”并双击打开。注意工程所在的路径名只能由字母、数字以及 下划线组成,不能出现中文、空格以及特殊字符等。工程打开后如图 43.5.1所示: 图 43.5.1 以太网通信实验工程 然后将网线一端连接电脑网口,另一端与开发板上的网口连接。再将下载器一端连接电脑, 另一端与开发板上对应端口连接,最后连接电源线并打开电源开关。 开拓者开发板实物图如下所示: 图 43.5.2 开拓者开发板网口 接下来我们下载程序,验证以太网通信环回的功能。工程打开后通过点击工具栏中的“Programmer” 图 标 打 开 下 载 界 面 , 通 过 “Add File” 按钮选择 eth_pc_loop/par/output_files目录下的“eth_pc_loop.sof”文件。开发板电源打开后, 在 程序下载界面点击“Hardware Setup”,在弹出的对话框中选择当前的硬件连接为“USB Blaster[USB-0]”。然后点击“Start”将工程编译完成后得到的sof文件下载到开发板中,如 图 43.5.3所示: 图 43.5.3 程序下载完成界面 程序下载完成后,PHY芯片就会和电脑网卡进行通信(自协商),如果程序下载正确并且 硬件连接无误的话,我们打开电脑右下角的网络和共享中心,点击更改适配器设置,会看到本 地连接刚开始显示的是正在识别,一段时间之后显示未识别的网络,打开方式如下图: 图 43.5.4 准备打开电脑的网络和共享中心 图 43.5.5 网络和共享中心界面 图 43.5.6 更改适配器界面 如果看到上图“本地连接”显示未识别的网络之后,说明硬件连接和程序都是没有问题的, 在开始使用网口调试助手之前,先把程序的目的IP地址与电脑的网口IP地址保持一致。查询电 脑的IP地址的方法是首先打开电脑的DOS命令窗口(可在电脑的左下角(或者WIN+R)搜索cmd) 为了避免操作失败,以管理员的身份运行,如下图所示: 图 43.5.7 搜索cmd界面 图 43.5.8 打开DOS命令窗口界面 首先查询本地连接的网卡ID号,运行命令:netsh i i show in,如下图所示: 图 43.5.9 查询本地连接网卡ID 从上图可以知道,“本地连接”的网卡ID为12,注意是Idx一栏而不是Met。接下来查询本 地连接的IP地址,运行命令:arp -a,运行界面如下: 图 43.5.10 查询本地IP地址 运行查询IP地址命令之后可能会出现多个接口,选择与网卡ID号一致的那个接口,如上图 所示,网卡ID 0x0c(十进制为12),从上图中可以看到本地连接的IP地址为192.168.1.102, 和程序里面写入的目的IP地址是一致的,源代码如下(eth_pc_loop模块): 1 //parameter define 2 //开发板MAC地址 00-11-22-33-44-55 3 parameter BOARD_MAC = 48'h00_11_22_33_44_55; 4 //开发板IP地址 192.168.1.123 5 parameter BOARD_IP = {8'd192,8'd168,8'd1,8'd123}; 6 //目的MAC地址 ff_ff_ff_ff_ff_ff 7 parameter DES_MAC = 48'hff_ff_ff_ff_ff_ff; 8 //目的IP地址 192.168.1.102 9 parameter DES_IP = {8'd192,8'd168,8'd1,8'd102}; 可以看到程序里面的DES_IP是和电脑的本地IP是一致的,可以通过修改代码DES_IP值改成 电脑的IP地址,或者将电脑的本地IP地址改成和程序一样的地址。修改电脑IP地址方法是打开 更改适配器的界面,右击本地连接,选择属性,打开后双击Internet协议版本4(TCP/Ipv4) 打开界面如下图所示: 图 43.5.11 本地连接属性界面 图 43.5.12 IPv4属性界面 按照上图的设置即可和开发板目的IP地址设置一致。当然也可以直接修改程序里面的目的 IP地址,如果修改程序的话需要编译工程,重新下载程序。 IP地址设置完成之后,接下来电脑需要绑定开发板的MAC地址和IP地址,因为网口调试助手只能设置发送目标的IP地址,MAC地址是从DOS界面绑定的,绑定的方法是在DOS界面运行命 令:netsh -c i i add neighbors 12 192.168.1.123 00-11-22-33-44-55(这里的12就是查 询到的本地网卡的ID),运行界面如下: 图 43.5.13 绑定开发板目的MAC、IP地址界面 此时可运行命令:arp -a查询是否绑定成功,查询界面如下: 图 43.5.14 查询绑定的地址界面 从上图可以看到,开发板的MAC地址和IP地址已经出现在列表里,接下来就可以使用网口 调试助手进行通信了,该工具位于开发板所随附的资料“6_软件资料/1_软件/网口调试助手” 目录下(打开网口调试助手前开发板必须硬件连接正确并且程序下载完成)。网口调试助手打 开界面如图 43.5.15所示: 图 43.5.15 网口调试助手界面 打开网口调试助手后,协议类型选择:UDP;本地主机地址选择:本地连接的IP地址(在 这里是192.168.1.102);本地主机端口号:1234;设置完成后点击【打开】按钮。如下图所 示: 图 43.5.16 网口调试助手打开界面 远程主机选择:192.168.1.123 : 1234 (开发板的IP地址和端口号),在这里本机主端 口号和远程主机端口号都为1234,见ip_send模块,源代码如下所示: 252 //16位源端口号:1234 16位目的端口号:1234 253 ip_head[5] <= {16'd1234,16'd1234}; 网口调试助手打开后,在发送文本框中输入数据“http://www.alientek.com/”并点击发 送,如下图所示: 图 43.5.17 网口调试助手收发数据界面 可以看到网口调试助手中接收到数据“http://www.alientek.com/”,接收到的数据与发 送的数据一致。 在这里介绍一个以太网通信时经常使用的抓包软件,该软件位于开发板所随附的资料“6_ 软件资料/1_软件/Wireshark”目录下,也可以直接在网上搜索下载,我们现在打开Wireshark, 界面如下图所示: 图 43.5.18 wireshark打开界面 双击上图所示的本地连接或者先选中本地连接,再点击红框选中的蓝色按钮,即可开始抓 取本地连接的数据包,抓取界面如下图所示: 图 43.5.19 wireshark本地连接打开界面 从上图可以看到,已经抓取到其它应用程序使用本地连接发送的数据包,但是这些数据包 并不是网口调试助手发送的,我们这个时候重新使用网口调试助手发送数据,就可以在 wireshark中抓取到数据包了,抓取到的数据包如下图所示: 图 43.5.20 wireshark本地连接抓包界面 从上图可以看到以太网数据包的源IP地址和目的IP地址,第71行是网口调试助手发送给开 发板的数据包,第72行是开发板返回给网口调试助手的数据包,双击第72行可以看到返回的具 体内容,如下图所示: 图 43.5.21 wireshark抓取到的详细数据 我们可以看到,网口调试助手和wireshark都可以接收到数据,程序所实现的以太网数据 环回功能验证成功。 |
|
相关推荐
|
|
飞凌嵌入式ElfBoard ELF 1板卡-CAN编程示例之开发板测试
727 浏览 0 评论
该问题是用APP给芯海科技的CST92F25芯片发指令是出现的
2430 浏览 1 评论
828 浏览 0 评论
1600 浏览 1 评论
2423 浏览 1 评论
浏览过的版块 |
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-11-25 01:12 , Processed in 0.718012 second(s), Total 68, Slave 50 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号