(300分)PC通过RS232与“画中画处理器”通信的问题(附资料)...(300分)

  • 主题发起人 selonboy
  • 开始时间
S

selonboy

Unregistered / Unconfirmed
GUEST, unregistred user!
[?]通过RS232串口发送控制信号到“画中画处理器”,[blue]难点在如何计算CRC-16数值[/blue],请高手帮忙解答,附资料如下:
*************************************
1.1 字格式
RSC电气连接同RS-232标准,采用的RS-232参数如下:通讯速度: 9.6kbps,38.4kbps,115.2kpbs;Data bits: 8;Start bit: 1;Stop bit: 1;Parity bit: No。不采用 XON/XOFF,RTS/CTS等流控制信号.
位的序列为:
起始位 1 2 3 4 5 6 7 8 停止位
1.2 信息帧结构
RSC的通讯以信息帧为最小单位。RSC支持二进制格式的数据传输,二进制信息帧标准结构为:
初始结构 ≥4字节的时间  
地址码 1字节  (地址域)
功能码 1字节  (功能域)
数据区 N字节  (数据域)
错误校验 16位CRC码  (错误检测域)
结束结构 ≥4字节的时间  
信息开始传送时至少需要3.5个字符的静止时间,依据使用的波特率,很容易计算这个静止时间。发送完最后一个字符后,也有一个至少3.5个字符的静止时间,然后才能发送一个新的信息。网络上的设备连续监测网络上的信息,包括静止时间。
*地址域
可能的设备地址是1-247,0是广播地址。主设备通过将要联络的从设备的地址放入地址域来选通从设备。从设备回应消息时,将自己的地址放入回应的地址域当中,告知主设备是哪个从设备作出回应。
*功能域
功能域告知从设备执行那些行为。从设备回应时,对正常回应仅功能代码。对异常回应则将最高位置为1。
*数据域
数据域定义了十六进制数集合,范围00…FF。
主设备到从设备的数据域包含附加的信息,规定了从设备执行由功能代码所定义的功能的具体行为。例如主设备要读取一组从设备的保持寄存器(功能代码03),数据域指定了起始寄存器以及要读的寄存器数量。
*错误检测域
错误检测域包含一16bits值,其值是对消息的内容进行循环冗长检测方法(CRC)得出。CRC域添加在整个消息的最后,添加时先是低字节然后是高字节。
≒≒≒≒≒≒≒≒ [:)]最关键的信息来了!≒≒≒≒≒≒≒≒
[red]1.3 CRC-16的实现
1.3.1 生成CRC-16校验字的步骤:
(1) 装入一个全1的16位寄存器
(2)该寄存器的低位字节与开始的8位字节“异或”,结果放入该16位寄存器
(3)将该16位寄存器右移一位
(4)若右移出的位的位值是1,则多项式10100000 00000001和移后的寄存器“异或”;若右移出的位值是0,则返回(3)。
(5)重复(3)和(4),直至移出8位。
(6)接下来的8位字节与该寄存器“异或”
(7)重复(3)~(6),直至该消息(报文)所有的字节均与16位寄存器进行了“异或”且移位8次。
(8)该16位寄存器的内容即2字节CRC错误校验,被加到报文的最后。添加时先是低字节后是高字节,故CRC的高位字节是发送消息的最后一个字节。
1.3.2 接收设备的CRC-16处理:
接收信息的设备将接收到的所有信息(含CRC码)重新计算CRC码,并判断该CRC码是否为0,如果为0,表示接收的信息帧正确无误,否则,则表明接收的信息帧有误,在进行CRC计算时只用8个数据位,起始位及停止位都不参与CRC计算。
例如:
对1号机的按键操作:
进入主菜单:0x01 0x41 0xFC 0x10 0x11
按MODE/MENU 键:0x01 0x41 0xFE 0x91 0xD0
按ESC/PIP HIDE键: 0x01 0x41 0xFD 0xD1 0xd1[/red]
......
≒≒≒≒≒≒≒≒ 最关键的信息结束 ≒≒≒≒≒≒≒≒
苦思半响仍不得其解,渴望有高人指点迷津(最好是写个函数计算过程,我用Delphi),不胜感激!
300分放在这儿了...
 
没有验证,你自己测试然后告诉结果。
function CRC16(data:array of Byte):word;
var CRC16Lo,CRC16Hi:Byte;
//CRC寄存器
CL,CH:Byte;
//多项式码&HA001
SaveHi,SaveLo:Byte;
i,Flag:Integer;
begin
CRC16Lo:=$FF;
CRC16Hi:=$FF;
CL:=$1;
CH:=$A0;
for i:=0 to High(data)-1do
begin
CRC16Lo:=CRC16Lo xor data;
//每一个数据与CRC寄存器进行异或
for Flag:=0 to 7do
begin
SaveHi:=CRC16Hi;
SaveLo:=CRC16Lo;
CRC16Hi:=CRC16Hi shr 1;
// 高位右移一位
CRC16Lo:=CRC16Lo shr 1;
//低位右移一位
if ((SaveHi and $1)=$1) then
//如果高位字节最后一位为1
CRC16Lo:=CRC16Lo or $80;
//则低位字节右移后前面补1
//否则自动补0
if ((SaveLo and $1)=$1) then
//如果LSB为1,则与多项式码进行异或
begin
CRC16Hi:=CRC16Hi xor CH;
CRC16Lo:=CRC16Lo xor CL;
end;
end;
CRC16:=CRC16Hi*256+CRC16Lo;
end;
end;
 
zywcd:
富翁论坛果然高手倍出,贴子刚发出不久,老兄出手好快,感谢!
 等我试过后明晚回帖告知结果....
 
unit Main;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls;
type
TForm1 = class(TForm)
Memo1: TMemo;
CalCRC16Btn: TButton;
CRC16ResultEdit: TEdit;
Edit1: TEdit;
Edit2: TEdit;
Edit3: TEdit;
Edit4: TEdit;
Edit5: TEdit;
Edit6: TEdit;
procedure FormCreate(Sender: TObject);
procedure CalCRC16BtnClick(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;

var
Form1: TForm1;
srbuf:array[0..2] of word;
date1:array[0..3] of byte;
// 生成多项式的值(多项式因子)
// 注意:CRC16是数据流的高位先计算,多项式因子不变
GenPoly16: Word;
// CRC-CCITT16 = X16+X12+X5+1, 00010000 00100001($1021) 高位先行
CRC16Tab: array [0..255] of Word;
// CRC16快速计算查表
implementation
{$R *.DFM}
///////////////////////////////////////////////////////////
// 16位CRC:按位计算,速度最慢,占用空间最少
// 注:数据流是高位先行
///////////////////////////////////////////////////////////
function CalCRC16(data, crc, genpoly: Word): Word;
var i: Integer;
begin
crc := crc xor (data shl 8);
for i:=0 to 7do
if (crc and $8000) <> 0 then
// 只测试最高位
crc := (crc shl 1) xor genpoly // 最高位为1,移位和异或处理
else
crc := crc shl 1;
// 否则只移位(乘2)
Result := crc;
end;

// 初始化
procedure TForm1.FormCreate(Sender: TObject);
begin
GenPoly16 := word($1021);
InitCRC16Tab(GenPoly16);
// 先生成CRC16表(256项),用于快速查表计算
end;
//从Memo中取数据,并格式化。
function GetDataFromText(str: String): String;
var i, p1, p2: Integer;
begin
Result := '';
while str <> ''do
begin
i := 0;
p1 := Pos(' ',str);
p2 := Pos(#13#10,str);
if p1=1 then
// 空格在最前面
begin
Delete(str,1,1);
continue;
end;
if p2=1 then
// #13#10在最前面
begin
Delete(str,1,2);
continue;
end;

if (p1=0) and (p2=0) and (str<>'') then
// 都没有找到,结束
begin
i := StrToIntDef('$'+str,0);
Delete(str,1,Length(str));
end;

if ((p1>0) and (p2=0)) or // 找到空格
((p1>0) and (p2>0) and (p1<p2)) then
// 或都找到,但空格在前
begin
i := StrToIntDef('$'+Copy(str,1,p1-1),0);
Delete(str,1,p1);
end;

if ((p1=0) and (p2>0)) or // 找到#13#10
((p1>0) and (p2>0) and (p1>p2)) then
// 或都找到,但空格在后
begin
i := StrToIntDef('$'+Copy(str,1,p2-1),0);
Delete(str,1,p2+1);
end;
Result := Result + Chr(i);
// 8位数据转为字符
end;
// while
end;

// 计算一组数据的CRC16结果
// 数据来源于文本框中的数据字串
procedure TForm1.CalCRC16BtnClick(Sender: TObject);
var
i: Integer;
databuf: String;
data, crc: Word;
begin
databuf := GetDataFromText(Memo1.Text);
// 从文本中读取数据。
crc := 0;
// CRC初始值
for i:=1 to Length(databuf)do
begin
data := Ord(databuf);
// 字符转为8位数据
crc := CalCRC16(data,crc,GenPoly16);
// 按位计算CRC
end;
CRC16ResultEdit.Text :=IntToHex(crc,4);
// 显示结果
srbuf[0]:=crc;
edit1.Text:=inttohex((srbuf[0] and $FF00) shr 8,2);
//高字节赋给edit1.
edit2.Text:=inttohex(srbuf[0] and $00FF,2);
// 低字节赋给edit2.
date1[0]:=byte((srbuf[0] and $F000) shr 12);//高字节的高四位赋给date1[0]
date1[1]:=byte((srbuf[0] and $0F00) shr 8);
//高字节的低四位赋给date1[1]
date1[2]:=byte((srbuf[0] and $00F0) shr 4);
//低字节的高四位赋给date1[2]
date1[3]:=byte(srbuf[0] and $000F) ;
//低字节的低四位赋给date1[3]
if date1[0]>=byte($0A) then
//对高字节的高四位进行“2字节拆分处理”。
edit5.text:=inttohex(date1[0]+byte($37),2)
else
edit5.text:=inttohex(date1[0]+byte($30),2);
if date1[1]>=byte($0A) then
//对高字节的低四位进行“2字节拆分处理”。
edit6.text:=inttohex(date1[1]+byte($37),2)
else
edit6.text:=inttohex(date1[1]+byte($30),2);
if date1[2]>=byte($0A) then
//对低字节的高四位进行“2字节拆分处理”。
edit3.text:=inttohex(date1[2]+byte($37),2)
else
edit3.text:=inttohex(date1[2]+byte($30),2);
if date1[3]>=byte($0A) then
//对低字节的低四位进行“2字节拆分处理”。
edit4.text:=inttohex(date1[3]+byte($37),2)
else
edit4.text:=inttohex(date1[3]+byte($30),2);
end;
end.
使用Delphi 7.0在Windows 2000下通过测试。
 
不能上传图片,所以不能把运行界面贴上去。
 
系统提示:
图片可以贴自己的 富翁笔记 里,然后在这里发个连接[:D]
 
贴什么贴啊,帐号等好好几天都不给确认哦!!~~~~~
 
首先感谢朋友们热心跟贴,更感谢帮助我的朋友。
zywcd用移位的方法,代码精简,思路清晰,但语句中有不少错误之处,后经本人修改一二终于通过。
LimitVideo用查表法,无疑这种方法是极快的,而且给出的代码最为详细,这对于菜鸟们的确是最贴心的帮助 :) 不过,思考再三,我还是采用了zywcd的思路,下面给出我测试通过的代码:
CRC16函数:
function CRC16(const AData: array of Byte): string;
var
I, Flag : Integer;
CRC16High, CRC16Low, SaveHigh, SaveLow: Byte;
begin
CRC16High := $FF;
CRC16Low := $FF;
for I := 0 to High(AData)do
begin
CRC16Low := CRC16Low xor AData;
for Flag := 0 to 7do
begin
SaveHigh := CRC16High;
SaveLow := CRC16Low;
CRC16High := CRC16High shr 1;
CRC16Low := CRC16Low shr 1;
if (SaveHigh and $1) = $1 then
CRC16Low := CRC16Low or $80;
if (SaveLow and $1) = $1 then
begin
CRC16High := CRC16High xor $A0;
CRC16Low := CRC16Low xor $1;
end;
end;
end;
Result := IntToHex(CRC16Low, 2) + IntToHex(CRC16High, 2);
end;

调用:
var
shuju : array [0..3] of Byte;
begin
shuju[0] := $01;
shuju[1] := $41;
shuju[2] := $FD;
shuju[3] := $D1;
caption := CRC16(shuju);
end;

结贴放分,希望大富翁会永远继续下去!
 
没有测试。你修改以后的我收藏了。
 

Similar threads

S
回复
0
查看
3K
SUNSTONE的Delphi笔记
S
S
回复
0
查看
2K
SUNSTONE的Delphi笔记
S
S
回复
0
查看
949
SUNSTONE的Delphi笔记
S
顶部