基于FPGA的DS2431 DS2408读写操作(1-wire多器件)

基于FPGA的DS2431 DS2408读写操作(1-wire多器件)1 Wire 总线是一种简单的信号交换架构 通过一条线路在主机与外围器件之间进行双向通信

综述

前段有个FPGA的开发任务,用到了ADI的DS2431和DS2408芯片,涉及到的难点有1-wire的读写操作和多个1-wire器件共用一根线进行通信,于是写一篇文章记录使用芯片中遇到的困难。

一、1-Wire概述

1-Wire总线是一种简单的信号交换架构,通过一条线路在主机与外围器件之间进行双向通信。所有的1-Wire总线都具有一个共同的特征:无论是芯片内还是iButton内,每个器件都有一个互不重复的、工厂光刻的序列号,因此,每个器件都是唯一的。这样就允许从众多连到同一总线的器件中独立选择任何一个器件。当1个、2个甚至多个1-Wire器件能共用一条线路进行通信,可以采用二进制位检索法依次查找每一个器件。一旦器件的序列号已知,通过寻址该序列号,就可以唯一地选出该器件进行通信。1-Wire软件资源指南和器件说明.

二、1-wire器件读写时序(DS2431 DS2408)

在这里插入图片描述

1-wire通信因为器件只有一根线,无法像SPI,IIC这样通过时钟同步数据来通信,只能通过一个判断周期内高低电平的时间来控制数据是0还是1。在正常速度下,发送1bit数据最小需要65us的时间,可以根据需要适当延长时间。
在这里插入图片描述

总线1-wire接口定义输入输出三态门接口, out_data_en为1时总线接口为输出, out_data_en为0时总线接口为输入。

inout ds2431_data; //总线输入输出定义 reg out_data_en; //输出使能 wire in_data; assign in_data = ds2431_data; //输入信号连续赋值 reg out_data; //输出数据寄存器 assign ds2431_data = out_data_en?out_data:1'bz; 

而在正常速度下主机发送1bit“0”数据的周期内,低电平需要有60-120us;发送1bit“1”数据的周期内,低电平需要有1-15us,控制好时间,使主机发送“0”和“1”的周期尽量相同。
在这里插入图片描述

因为在整个读写中涉及到的发送、接收比较多,本人是FPGA开发新人,理不清更复杂的时序逻辑,故整个读写过程用状态机配合调用任务来实现,以下为主机发送1字节任务示例。

task master_tx; //主机发送数据 input [7:0]tend_data; //要发送的数据 reg [5:0]tx_state; reg [9:0]tx_cnt; //发送计时寄存器 reg [3:0]i; //循环变量寄存器 case(tx_state) state_0:begin tx_over <= 0; tx_state <= state_1; i <= 0; tx_cnt <= 0; end state_1:begin if(i==8)begin //发送完毕 i <= 0; tx_state <= state_6; tx_over <= 1; out_data_en <= 0; end else begin //未发送完毕 out_data_en <= 1; out_data <= 0; if(tend_data[i]) tx_state <= state_2; else tx_state <= state_4; end end state_2:begin if(tx_cnt == 24)begin //5us低电平 tx_state <= state_3; tx_cnt <= 0; end else begin out_data <= 0; tx_state <= tx_state; tx_cnt <= tx_cnt + 1; end end state_3:begin if(tx_cnt == 324)begin //65us高电平 tx_state <= state_1; tx_cnt <= 0; i <= i + 1; end else begin out_data <= 1; tx_state <= tx_state; tx_cnt <= tx_cnt + 1; end end state_4:begin if(tx_cnt == 324)begin //65us低电平 tx_state <= state_5; tx_cnt <= 0; end else begin tx_state <= tx_state; tx_cnt <= tx_cnt + 1; out_data <= 0; end end state_5:begin if(tx_cnt == 24)begin //5us高电平 tx_state <= state_1; tx_cnt <= 0; i <= i + 1; end else begin tx_state <= tx_state; tx_cnt <= tx_cnt + 1; out_data <= 1; end end state_6:begin tx_over <= 0; tx_state <= state_7; end default:tx_state <= state_0; endcase endtask 

与之对应的是主机接收1-wire器件返回的数据,主机一个周期开始的15us读取数据,读到的电平高低即为1-wire器件发送的数据“1”或“0”。
在这里插入图片描述

以下为主机接收1字节任务示例。

task master_rx; //主机接收数据任务 output reg [7:0]read_data; //接收数据 reg [5:0]rx_state; reg [20:0]rx_cnt; //接收数据寄存器 reg [3:0]i; //循环寄存器 case(rx_state) state_0:begin rx_over <= 0; i <= 0; rx_state <= state_1; rx_cnt <= 0; end state_1:begin if(i<8)begin //未读取完毕 out_data_en <= 1; out_data <= 1; rx_state <= state_2; end else begin //读取数据完毕 rx_state <= state_5; i <= 0; rx_over <= 1; end end state_2:begin if(rx_cnt == 25)begin out_data_en <= 0; rx_state <= state_3; rx_cnt <= 0; end else begin out_data <= 0; rx_cnt <= rx_cnt + 1; rx_state <= rx_state; end end state_3:begin if(rx_cnt == 50)begin //10us采样 rx_state <= state_4; rx_cnt <= 0; if(in_data) read_data[i] <= 1; else if(!in_data) read_data[i] <= 0; else read_data[i] <= 1'bz; end else begin rx_cnt <= rx_cnt + 1; rx_state <= rx_state; end end state_4:begin if(rx_cnt == 300)begin //60us延迟 rx_state <= state_1; //重复7次 rx_cnt <= 0; i <= i + 1; end else begin rx_cnt <= rx_cnt + 1; rx_state <= rx_state; end end state_5:begin rx_over <= 0; out_data_en <= 1; out_data <= 1; rx_state <= state_6; end default:rx_state <= state_0; endcase endtask 

三、1-wire器件复位操作

从空闲状态唤醒时,1-Wire总线电压需要从VPUP降到VTL门限电压以下。从工作状态返回空闲状态时,电压需要从VLMAX上升至VTH门限电压以上。电压上升时间在图10中用ε表示,持续时间取决于所使用的上拉电阻(RPUP和1-Wire网络的附加电容。DS2431根据VMAx电压判断逻辑电平,不会触发任何事件。

在这里插入图片描述

这里复位的方式是在主机拉低1-wire总线的过程中,看是否接收到1-wire器件返回的高电平,来确定器件是否复位成功。正常速度下主机拉低1-wire总线在480-640us之间,1-wire器件返回的高电平时间在15-60us之间。
在这里插入图片描述

task reset; //复位任务 reg [3:0]rst_state; reg [20:0]rst_cnt; //定义复位计时器 case(rst_state) state_0:begin rst_over <= 0; out_data_en <= 1; //总线输出模式 out_data <= 0; //初始总线拉低 rst_cnt <= 0; rst_state <= state_1; end state_1:begin if(rst_cnt==2499)begin //拉低500us rst_cnt <= 0; rst_state <= state_2; end else begin rst_cnt <= rst_cnt + 1; rst_state <= rst_state; end end state_2:begin if(rst_cnt==74)begin //拉高15us out_data_en <= 0; //总线改为输入模式 rst_cnt <= 0; rst_state <= state_3; end else begin out_data <= 1; rst_cnt <= rst_cnt + 1; end end state_3:begin if(!in_data)begin rst_state <= state_4; end else begin if(rst_cnt==1199)begin //检测240us后仍无脉冲应答 rst_state <= state_0; rst_cnt <= 0; end else begin rst_cnt <= rst_cnt + 1; rst_state <= rst_state; end end end state_4:begin if(rst_cnt == 1499)begin //持续300us rst_state <= state_5; rst_cnt <= 0; end else begin rst_cnt <= rst_cnt + 1; rst_state <= rst_state; end end state_5:begin rst_over <= 1; rst_state <= state_6; end default:begin rst_state <= 0; rst_over <= 0; end endcase endtask 

四、1-wire器件通讯协议

以下为DS2431的通讯协议,兼容ADI的其他1-wire器件;针对1-wire有多个器件来说的话,初次的操作指令应当为:复位-ROM寻址-指定某一器件ROM操作,在寄存器内存储ROM地址之后,即可进行:复位-指定某一器件ROM操作

在这里插入图片描述

五、ROM寻址

每个DS2431都有一个唯一的64位ROM码,其中前8位是1-Wire家族码,中间48位是唯一的序列号,最后8位是前56位的循环冗余校验(CRC)码,详见图3所示。

在这里插入图片描述

对于1-wire器件来说,每个系列的器件都有各自独立的家族码,DS2431为2Dh,DS2408则为29h。

Search ROM【F0h】系统刚启动时,总线主机可能并不知道1-Wire总线上挂接的器件数量及它们的注册码。主机可利用总线的线与特性,采用排除法来识别总线上所有从机的注册码。针对最低有效位在前的注册码的每一位,总线主机都发送三个时隙。在第一个时隙,每个参与搜索的从机都输出各自注册码位的原码。在第二个时隙,每个参与搜索的从机都输出各自注册码位的补码值。在第三个时隙,主机写人所选位的原码。所有与由主机写人的该位不匹配的从机都不再参加搜索。如果主机两次读到的值均是0,则说明从机该位的两个状态都存在。总线主机通过写人的状态值来选择搜索ROM码树的不同分支。经过一次完整搜索过程,总线主机即可知道某个从机的注册码。另外的搜索过程可以识别其余从机的注册码。1-Wire搜索算法 | Analog Devices

reg [1:0]return_state; //1bit数据 reg [6:0]rom_cnt1,rom_cnt2; //1-wire 器件rom码计时器 reg [63:0]rom_number1,rom_number2; //1wire 器件64位rom reg [63:0]DS2408ROM,DS2431ROM; //两芯片ROM reg roms_over; task rom_sch; //rom搜寻 reg [4:0]rom_state; reg [20:0]rom_cnt; case(rom_state) state_0:begin rom_state <= state_1; end state_1:begin reset; //复位 if(rst_over)begin rom_state <= state_2; end else rom_state <= rom_state; end state_2:begin master_tx(8'hf0); //发出读取rom命令 if(tx_over) rom_state <= state_3; else rom_state <= rom_state; end state_3:begin //而如果读到的位格式为 01、10 或 00,则表明1 wire总线有器件 master_rx_2bit(return_state);//发送2 if(rx_over)begin rom_state <= state_4; end else rom_state <= rom_state; end state_4:begin if(return_state==2'b01|return_state==2'b10)begin rom_state <= state_5; //检测到按键按下 end else if(return_state==0)begin rom_state <= state_5; //记录分叉的数据 rom_number2 <= rom_number1; rom_cnt2 <= rom_cnt1; end else begin //检测未按下按键 rom_state <= state_1; end end state_5:begin master_tx_1bit(return_state[0]); //发送读取rom地址原码 if(tx_over)begin if(rom_cnt1 < 63)begin rom_cnt1 <= rom_cnt1 + 1; rom_number1[rom_cnt1] <= return_state[0]; rom_state <= state_3; end else if(rom_cnt1 == 63)begin rom_number1[rom_cnt1] <= return_state[0]; rom_state <= state_6; end else begin rom_cnt1 <= rom_cnt1; rom_state <= state_0; end end else rom_state <= rom_state; end state_6:begin if(rom_cnt2 > 0)begin rom_cnt1 <= 0; rom_state <= state_7; //若有分岔口,返回寻找分岔口 end else begin rom_cnt1 <= 0; rom_state <= state_13; //ROM选择 //调试 //rom_state <= state_0; end end state_7:begin //100 reset; //复位 if(rst_over)begin rom_state <= state_8; end else rom_state <= rom_state; end state_8:begin master_tx(8'hf0); //发出读取rom命令 if(tx_over) rom_state <= state_9; //调试 //rom_state <= state_6; else rom_state <= rom_state; end state_9:begin //而如果读到的位格式为 01、10 或 00,则表明1 wire总线有器件 master_rx_2bit(return_state); if(rx_over) rom_state <= state_10; else rom_state <= rom_state; end state_10:begin if(return_state==2'b01|return_state==2'b10)begin rom_state <= state_11; //检测到按键按下 end else if(return_state==2'b00)begin rom_state <= state_12; //记录分叉的数据 rom_number2 <= rom_number1; rom_cnt2 <= rom_cnt1; end else begin //检测未按下按键 rom_state <= state_1; end end state_11:begin master_tx_1bit(return_state[0]); //发送读取rom地址原码 if(tx_over)begin if(rom_cnt1 < 63)begin rom_cnt1 <= rom_cnt1 + 1; rom_number2[rom_cnt1] <= return_state[0]; rom_state <= state_9; end else if(rom_cnt1 == 63)begin rom_number2[rom_cnt1] <= return_state[0]; rom_state <= state_6; rom_cnt2 <= 0; end else begin rom_cnt1 <= rom_cnt1; rom_state <= state_0; end end else rom_state <= rom_state; end state_12:begin master_tx_1bit(1); //发送读取rom地址反码 if(tx_over)begin if(rom_cnt1 < 63)begin rom_cnt1 <= rom_cnt1 + 1; rom_number2[rom_cnt2] <= 1; rom_state <= state_9; end else if(rom_cnt1 == 63)begin rom_number2[rom_cnt2] <= 1; rom_state <= state_6; rom_cnt2 <= 0; end else begin rom_cnt1 <= rom_cnt1; rom_state <= state_0; end end else rom_state <= rom_state; end state_13:begin//rom赋值 if((rom_number1[7:0]==8'h2D)&(rom_number2[7:0]==8'h29))begin DS2431ROM <= rom_number1; DS2408ROM <= rom_number2; rom_state <= state_14; roms_over <= 1; end else if((rom_number1[7:0]==8'h29)&(rom_number2[7:0]==8'h2D))begin DS2431ROM <= rom_number2; DS2408ROM <= rom_number1; rom_state <= state_14; roms_over <= 1; end else begin rom_state <= state_1; //重新复位 end end state_14:begin roms_over <= 0; rom_state <= 0; end default:begin roms_over <= 0; rom_state <= 0; end endcase endtask 

六、主状态机

在搜寻完ROM地址后,在ROM命令里即可发送特定命令55h来指定某一器件;本文仅指定DS2431的ROM进行操作。

reg [6:0]ds_state; //主状态机0-127 always@(posedge clk or negedge rst_n)begin//主状态机变换 if(!rst_n)begin ds_state <= 0; end else begin if(rst_over&ds_state!=1)//寻址中状态机不变 ds_state <= ds_state + 1; else if(tx_over&ds_state!=1) ds_state <= ds_state + 1; else if(rx_over&ds_state!=1) ds_state <= ds_state + 1; else if(skip_over&ds_state!=1) ds_state <= ds_state + 1; else if(roms_over&ds_state==1)//寻址完成 ds_state <= state_2; else ds_state <= ds_state; end end reg [20:0]state_cnt; //主状态机定时器 reg [15:0]crc_write; //写crc校验 reg [7:0]ff_write; //ff校验 reg [63:0]reg_data; //暂存器数据 reg [20:0]aa_cnt; //读aa循环前计时器 reg [7:0]AA_loop; //AA循环数据 always@(posedge clk or negedge rst_n)begin if(!rst_n)begin crc_write <= 0; ff_write <= 0; rom_data <= 0; output_data <= 0; out_data <= 1; return_state <= 0; rom_cnt1 <= 0; rom_cnt2 <= 0; rom_number1 <= 0; rom_number2 <= 0; DS2431ROM <= 0; DS2408ROM <= 0; end else begin case(ds_state) state_0:reset; state_1:rom_sch; // state_2:reset; //复位 state_3:master_tx(8'h55); //发出读取特定ROM命令 state_4:master_tx_64bit(DS2431ROM); //发出特定ROM地址 state_5:master_tx(8'h0f); //DS2431开始操作 state_6:master_tx(8'h10); state_7:master_tx(8'h00); state_8:master_tx(data1); //数据1-8字节 state_9:master_tx(data2); state_10:master_tx(data3); state_11:master_tx(data4); state_12:master_tx(data5); state_13:master_tx(data6); state_14:master_tx(data7); state_15:master_tx(data8); state_16:master_rx(crc_write[15:8]); //crc校验 state_17:master_rx(crc_write[7:0]); //crc校验 state_18:master_rx(ff_write); //ff校验 // state_19:reset; //复位 state_20:master_tx(8'h55); //发出读取特定ROM命令 state_21:master_tx_64bit(DS2431ROM); //发出特定ROM地址 state_22:master_tx(8'h55); //发出Issue “Copy Scratchpad” command state_23:master_tx(8'h10); //地址1 state_24:master_tx(8'h00); //地址2 state_25:master_tx(8'h07); //发出ES state_26: if(aa_cnt < 29999) //等待数据复制 aa_cnt <= aa_cnt + 1; else master_skip; state_27:begin aa_cnt <= 0;master_rx(AA_loop); end //接收AA循环数据 state_28: if(AA_loop==8'haa) master_skip; else master_skip; default:; // endcase end end 
今天的文章 基于FPGA的DS2431 DS2408读写操作(1-wire多器件)分享到此就结束了,感谢您的阅读。
编程小号
上一篇 2024-12-07 22:40
下一篇 2024-12-07 22:33

相关推荐

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/bian-cheng-ji-chu/80019.html