7多摩川编码器总结.docx
《7多摩川编码器总结.docx》由会员分享,可在线阅读,更多相关《7多摩川编码器总结.docx(15页珍藏版)》请在冰豆网上搜索。
7多摩川编码器总结
2013.7-多摩川编码器总结
2013.7多摩川编码器总结
一、摘要
基于CPLD和DSP实现CPLD与多摩川编码器的通讯,通过对编码器发送请求,得到编码器发回的数据并进行解码,得到绝对位置值。
二、学习步骤:
1、熟悉工作环境,掌握Modelsim以及Quartus的使用。
2、阅读多摩川编码器的通讯协议。
3、根据协议编写testbench,并在Modelsim上进行仿真调试。
4、仿真通过后,通过Quartus编译后下载到CPLD上并与编码器通讯,实际情况下运行。
5、完成各项要求的功能。
6、对代码进行优化,尽可能减少资源占用。
7、验收。
三、总体结构
结构分三部分:
多摩川编码器,CPLD,DSP。
1、编码器跟CPLD之间通过MAX485电平转换进行连接。
2、CPLD与DSP则通过总线进行连接(这一部分结构编写学长已经完成并且提供了端口连接)
3、主要工作是CPLD的解码部分。
四、通讯协议
1、TS5668的技术指标:
(物理层)
精度:
单圈精度:
17位(131072)多圈精度:
16位(65536)
最高转速/(r·min-1):
6000】
输出:
差分NRZ编码二进制
传输速度/Mbps:
2.5
发送、接收电路:
差分形式
通信方式:
主从模式
接口:
3FG,4sig+,5sig-,7VCC,8DGND。
4和5为差分信号接口。
2、通信步骤如下图:
(逻辑链路层)
1)CPLD向编码器发送一个控制字CF
2)3us后编码器返回数据包。
3)CPLD对数据包进行解码,并将得到的数据放在总线上,等待DSP获取。
具体流程如下图:
3、字的结构:
下图分别为CF、DF、CRC字的结构。
1)CF
字的开始位为0,再是010的同步位,以及4位的控制位,1位奇偶校验位(对控制位进行奇偶校验),结束位为1,共十位。
通过不同的DataIDcode可以实现不同的功能,具体功能如下表:
2)SF
该字包含错误信息,如编码错误和通讯警报。
通过检测相应位置上的值,就可以确定编码器的工作状态是否正常。
3)CRC
进行CRC校验时,要对所有数据进行校验。
计算时除掉每字的起始位和分隔符。
4)数据传输
正如CF介绍中提到,不同的CF控制命令会对应不同的数据结构传输。
主要有三类,而我们用的是DataID0,绝对数据传输。
后面的空格表明没有数据传输。
数据传输中,低位在前,高位在后,每一字都是以0开始以1结束。
由于是17位精度编码器,DF2数据位的高7位都是0。
五、需求分析
1、启动
DSP每隔60us向CPLD发送一个启动脉冲,CPLD捕捉到上升沿后开始向编码器发送CF请求命令。
如果CPLD已经处于发送或接受状态,再接收到启动脉冲,不予响应。
2、485使能
由于CPLD与编码器的通讯需要MAX485进行电平转换,而MAX485是一个半双工器件,因此,需要提供一个端口控制485的使能端,决定485的读写控制。
3、频率要求
板子上提供10M频率的时钟,而多摩川编码器的通讯协议需要2.5M频率时钟,因此需要分频。
4、异常情况分析
考虑到传输过程中的异常情况(比如把“0”传输成“1”,或者反之),以及其他可能会出现的错误情况。
1)编码器接受到错误的CF,给出了相应的回应。
2)编码器接受到错误的CF,没有回应。
3)编码器自身出现错误(在SF中会给出错误类型)。
5、与DSP的通讯
得到绝对位置值之后,需要将读取的结果发送给DSP,而这一过程需要提供一个端口使CPLD与DSP连接。
六、整体设计
1、流程图
基于多摩川编码器的通讯协议以及需求分析,可以做出以下流程图。
有启动脉冲
无响应或无正确响应
正确完成一周期或
错误结束一周期
有正确响应
2、分配状态
根据流程图,可以通过“状态机”来完成各个状态之间的切换,因此,分配状态为:
Idle(闲置),Request(请求),Wait(等待),Receive(接收)四个状态。
状态机的编写有一段式、两段式和三段式,这里状态比较简单,可以采用结构简单的一段式。
(对于复杂的状态机,不推荐使用一段式)
具体写法如下:
reg[1:
0]state
parameter
Idle=2'b00,Request=2'b01,
Wait=2'b10,Receive=2'b11;
…
case(state)
Idle:
…
Request:
…
Wait:
…
Receive:
…
3、闲置状态
CPLD处于闲置状态,等待DSP发送命令。
当DSP发送启动脉冲后,CPLD检测到上升沿,即由闲置状态进入请求状态,同时,为请求状态做好初始化准备。
而在其它状态检测到上升沿时,则不予响应。
具体代码如下:
1)检测启动脉冲
always@(posedgestartorposedgestart_fg1)
begin
if(start==1)start_fg<=1'b1;
if(start_fg1==1)start_fg<=1'b0;
end
2)状态更改并为请求状态做好初始化准备
Idle:
begin
txd<=1;
if(start_fg==1'b1)
begin
state<=Request;
E_c<=1;//485使能,1向编码器写数据
start_fg1<=1'b1;
end
end
4、请求状态
CPLD处于请求状态时,每一个时钟周期发送一个高低电平。
请求命令为:
0010000001。
发送结束后,进入等待状态,等待编码器响应。
具体代码如下:
Request:
begin
if(i==8'd10)//发送结束
begin
state<=Wait;
start_fg1<=1'b0;
E_c<=0;//485使能,0接受编码器数据
CF_r<=10'd0;
txd<=1;
i<=0;
end
else
begin
txd<=CF[9-i];
i<=i+1'b1;
end
end
5、等待状态
CPLD处于等待状态时,等待编码器相应。
编码器一旦发送低电平过来,为避免传输干扰,设定了一个头部检测。
头部检测的方式为,每次左移并接受一个数据,检测开始的头4位,如果头4位正确,则进入到接受状态;如果头4位不正确,则继续左移并接受数据,在指定时间内,如果没有成功接受到数据,则认为这一次通讯失败,并给出错误类型erro<=2'd3,同时数据位报全1(数据位默认为全1),并且进入到闲置状态,等待下一次的请求命令。
在最初设计时,这里加入了一个“超时重发”的功能,即通讯失败后进入请求状态,重新发送请求命令,再次进入到等待状态,并且允许超时重发3次,3次都失败后则执行前面提到的错误处理办法。
只是后来由于时间以及资源的闲置,把这一个模块删掉了,如果资源以及时间允许的话,可以考虑加上这一模块。
具体代码如下:
Wait:
begin
txd<=1;
E_b<=1;
if(CF_r[3:
0]==4'b0010)//头检测
begin
state<=Receive;
i<=0;
CF_r<=(CF_r<<1)|re;
i<=4;
end
else
begin
CF_r<=(CF_r<<1)|re;
if(i==4'd14)
begin
state<=Idle;
erro<=2'd3;
end
else
i<=i+1'b1;
end
end
6、接受状态
这一状态是编码器的主要工作状态,同时由于接收的信息比较多,也是处理起来比较麻烦的一部分。
首先,利用计数器计数10次,读出CF信息,并且保存到CRC_buf里;再次利用计数器计数10次,读出SF信息,也保存在CRC_buf里;再利用计数器计数30次,取出位置信息,这里需注意一点,编码器发回的位置信息是低位在前,高位在后,因此,在接受数据时,需要将其调整一下。
最后,利用计数器计数10次,读出CRC的信息。
接收完数据后,就进行CRC校验。
由于这一部分工作是同组另一位学长完成,这里不做具体阐述。
如果CRC校验通过,则输出位置信息;如果CRC校验不通过,则输出错误信息,并且报全1。
以上是正常情况,同时还需要对CF进行检查。
如果发回的CF与发送出去的CF不同,则出错。
出错的话,按照协议,编码器可能会发回一个最长的数据包,与实际情况不符。
因此,也算作一种错误。
另外,SF的错误信息是编码器自身的错误,根据要求,如果有这种错误的话,需要断电处理。
因此,检测到这种错误时,给出错误信息,并不需要额外处理。
代码如下:
Receive:
begin
if(i<9)
begin
CF_r<=(CF_r<<1)|re;
i<=i+1'b1;
end
elseif(i==9)//CF检验
begin
if(CF_r==CF)
begin
CRC_buf<=CRC_buf|{CF_r[8:
1],40'd0};
CF_r<=(CF_r<<1)|re;
i<=i+1'b1;
end
else
begin
state<=Idle;
i<=0;
erro<=2'd0;
end
end
elseif(i<19)
begin
CF_r<=(CF_r<<1)|re;
i<=i+1'b1;
end
elseif(i==19)
begin
if(CF_r[4]|CF_r[3])//检测SF,出错应该断电处理begin
erro<=2'd2;
i<=i+1'b1;
end
else
begin
CRC_buf<=CRC_buf|{CF_r[8:
1],32'd0};
DATA1<=(DATA1>>1)|{re,29'd0};
i<=i+1'b1;
end
end
elseif(i<49)
begin
DATA1<=(DATA1>>1)|{re,29'd0};
i<=i+1'b1;
end
elseif(i<59)
begin
CF_r<=(CF_r<<1)|re;
i<=i+1'b1;
end
else
begin
DATA1[23:
0]={DATA1[28:
21],DATA1[18:
11],DATA1[8:
1]};
for(i=0;i<24;i=i+1)
begin
CRC_buf[31-i]=DATA1[i];
end
CRC_buf=CRC_buf|CF_r[8:
1];
for(i=47;i>7;i=i-1'b1)
begin
CRC_buf[47:
40]={CRC_buf[46:
40],(CRC_buf[47]^CRC_buf[39])};
CRC_buf[39:
0]=CRC_buf[38:
0]<<1;//shift
end
if(!
CRC_buf[47:
40])DATA=DATA1[23:
0];
state<=Idle;
E_b<=0;
i<=0;
end
end
7、错误状况汇总
对于运行状态中,可能出现的一些错误情况进行了一些汇总。
首先,根据错误的处理方式进行分类。
1)下一周期可以恢复正常的情况:
A、CF错误。
返回的CF与发送的CF不相同,此时按照程序逻辑继续运行。
B、通讯失败。
CPLD发送请求命令后,编码器没有相应。
C、CRC错误。
CRC校验不通过,此时按照程序逻辑继续运行。
2)下一周期不能恢复,需要断电处理:
SF错误。
8、代码优化经验积累
代码在实现了预定功能之后,需要的就是资源优化了。
在网上搜索了一些资料,并根据自己优化的情况,做了一些总结。
应该说,具体情况要具体分析,不过,也会有一些共性的问题。
1)寄存器的位数尽可能少。
在一开始编写过程中,可能会有一些寄存器冗余。
因此在资源优化时,要注意将这些冗余寄存器减掉。
2)尽可能不要用不等比较。
在if判断时,有时会用到不等比较,如if(i<9)。
这种情况下,尽量通过改变逻辑判断改成等于比较。
3)合理利用缓存寄存器。
对于位数较多的寄存器,直接进行处理可能会占用较多资源。
这时,可以多设立一些小寄存器,先行储存下来,进行处理之后,再赋值给大的寄存器。