📄 wr_com.pas
字号:
unit wr_com;
interface
const
//return value indicate
RC_FAILURE = $00;
RC_SUCCESS = $01;
RC_RX_DATE_ERROR = $02; //状态寄存器指示收数据错;--物理层错
RC_NO_RX_DATE = $03; //超时仍未从串口接收数据寄存器收到数据;
RC_RX_CHECK_ERROR = $04; //收到返回帧校验错 --用户层错
RC_TX_SEND_OVERTIME = $11; //发送超时
const
{//8250共有10个寄存器
COM1_STATUS_REG = $2fd; //线状态寄存器 LSR
COM1_CONTROL_REG = $2fb; //线控制寄存器 LCR
COM1_RX_DATA_REG = $2f8; //接收数据寄存器
COM1_TX_DATA_REG = $2f8; //发送保持寄存器
COM1_MODEM_STATUS_REG = $2fe; //modem状态寄存器
COM1_MODEM_CONTROL_REG = $2fc; //modem 控制寄存器
COM1_DIVIDEFREQ_LOW_REG = $2f8; //分频寄存器(L)
COM1_DIVIDEFREQ_HIGH_REG = $2f9; //分频寄存器(H)
COM1_INTERUPPT_PERMIT_REG = $2f9; //中断允许寄存器
COM1_INTERUPPT_STATUS_REG = $2fa; //中断标识寄存器 }
COM1_STATUS_REG = $3fd; //线状态寄存器 LSR
COM1_CONTROL_REG = $3fb; //线控制寄存器 LCR
COM1_RX_DATA_REG = $3f8; //接收数据寄存器
COM1_TX_DATA_REG = $3f8; //发送保持寄存器
COM1_MODEM_STATUS_REG = $3fe; //modem状态寄存器
COM1_MODEM_CONTROL_REG = $3fc; //modem 控制寄存器
COM1_DIVIDEFREQ_LOW_REG = $3f8; //分频寄存器(L)
COM1_DIVIDEFREQ_HIGH_REG = $3f9; //分频寄存器(H)
COM1_INTERUPPT_PERMIT_REG = $3f9; //中断允许寄存器
COM1_INTERUPPT_STATUS_REG = $3fa; //中断标识寄存器
type
TComNum = (stCOM1, stCOM2, stCOM3, stCOM4);
TUartServo = class //(TSerialPortUart)
private
FBuadRate: integer; //波特率
FOddEvenCheck: Integer; //=0,无校验位;=1,形成奇校验; =3, 形成偶校验位;
FDataBitNum: integer; //数据位数;=3,8位;
FComNum: TComNum; //哪一个串口:com1,com2,com3,com4
protected
Function GetComStatus(): byte; // override; //读线状态寄存器的状态值
Function GetReadyComValue(): byte; //override; //串口接收数据已准备好,可以读取一个字节
Procedure PutReadyComValue(value: byte); //override; //发送保持寄存器空,可以发送一个字节
function GetByte(var InputValue: byte): integer; // override;//先查询线状态寄存器的状态值,若接收准备好,从串口读单个字节
function SendByte(output: byte): integer; //override; //先查询线状态寄存器的状态值,若发送准备好,向串口写单个字节
//发一次下传帧, 下传帧五个字节
function SendFrame(Command: byte; Data: word): integer;
//读一次返回帧, 返回帧五个字节
function ReceiveFrame(var Command,Status: byte; var Data: word): integer;
public
//Constructor Create(WhichCom: TComNum, BuadRate,OddEvenCheck,DataBitNum:integer); //override;
//Destructor Destroy;// override;
//发一个5byte下传帧,取回一个5byte返回帧的状态数据和命令号对应数据
function TxRxUartCycle(SendCommand: byte; SendData: word; var RecieveStatus: byte;
var RecieveData: word): integer;
procedure InitCom();
end;
//var
//Procedure PushStack();
//Procedure PopStack();
implementation
//其他pas文件不能调用
Procedure PushStack();
Begin
asm
push dx
push ax
end;
End;
Procedure PopStack();
Begin
asm
pop ax
pop dx
end;
End;
//取补码; 如果正数 和零不变; 负数 减1 d0-d6取反;
function ChangeCode(RecieveData: word): Smallint;
var
temp:word;
begin
if (RecieveData = $80) or (RecieveData = $00) then
begin
Result := 0;
end
else
if ( (RecieveData and $80) = 0) then Result := RecieveData
else
begin
temp := not ( (RecieveData and $7f)-1 );
Result := -temp;
end;
end;
// 初始化串口:波特率,起始和停止位,有无奇偶校验
//baudrate 4800 0018; 9600 000c; 19200 0006; 38400 0003;
procedure TUartServo.InitCom();
begin
asm
mov dx,COM1_CONTROL_REG //2fbh
mov al,80h //线路控制寄存器LCR口地址2FBH,80h允许访问除数寄存器低字节DLL
out dx,al
mov dx,COM1_DIVIDEFREQ_LOW_REG //2f8h
mov al,06H //置除数寄存器低字节DLL口地址2F8H,置波特率(DLL),0cH=9600bits/sec
out dx,al
mov dx,COM1_DIVIDEFREQ_HIGH_REG //2f9h
mov al, 00h //00h 置除数寄存器高字节DLH口地址2F9H,置波特率(DLH)=4
out dx,al
mov dx,COM1_CONTROL_REG //2fbh
mov al,03h //1bh $置通信数据格式:起始位(1位),数据8位,无奇偶校验,停止位1位
out dx,al
mov dx,COM1_INTERUPPT_PERMIT_REG
mov al,0 //设中断允许寄存器为0,屏蔽四种中断
out dx, al
{mov dx,COM1_MODEM_CONTROL_REG;
mov al ,13h //Modem控制寄存器:发DTR和RTS信号,内部输出输入反接,中断方式被禁止
out dx, al }
end;
end;
{ function TxRxUartCycle(SendCommand: byte; SendData: word; var RecieveStatus: byte;
var ReceiveData: word): integer;
功能:发一个5byte下传帧,取回一个5byte返回帧的状态数据和命令号对应数据
输入参数: SendCommand, 单字节,发送的下传帧的命令号值;
SendData, 双字节,发送的下传帧的数据值;
输出参数: RecieveStatus,双字节,接收的返回帧的状态值;
ReceiveData, 双字节,接收的返回帧的数据值;
返回值: RC_FAILURE 在规定时间和重发次数内,上位机仍收不到合法的返回帧;
RC_SUCCESS 在规定时间和重发次数内, 上位机成功接收到合法的返回帧;
RC_TX_SEND_OVERTIME 超时数据仍不能发送
RC_RX_DATE_ERROR 状态寄存器指示收数据错;
RC_NO_RX_DATE 超时仍未从串口接收数据寄存器收到数据;
RC_RX_CHECK_ERROR 收到返回帧校验错
}
function TUartServo.TxRxUartCycle(SendCommand: byte; SendData: word; var RecieveStatus: byte; var RecieveData: word): integer;
var
PollResult: integer;
SendStatus: integer;
PollCount: integer;
begin
PollResult := RC_FAILURE;
PollCount := 0;
//PushStack(); //DX ,al值保留
//如果发送下传帧过1ms后未取到返回帧,且轮询次数少于10次,开始下一轮发和收.下传帧后收到返回帧的时间间隔定为10*100us,待测试;
While ( (PollResult <> RC_SUCCESS) and (PollCount <= 3) ) do
begin
PollCount := PollCount + 1;
SendStatus := SendFrame(SendCommand, SendData);
if (SendStatus = RC_TX_SEND_OVERTIME) then continue;
PollResult := ReceiveFrame(SendCommand, RecieveStatus, RecieveData);
end;
//PopStack();
if (SendStatus = RC_TX_SEND_OVERTIME) then
result:= SendStatus
else
result := PollResult // 轮询成功;
end;
{ function SendFrame(Command: byte; Data: word): integer;
功能: 向下位机发4个字节的下传帧
输入参数: Command: byte, 下传帧的命令字节;
Data: word, 下传帧的数据字;
输出参数:
返回值: RC_SEND_OVERTIME: 上位机产生的下传帧发不下去;
RC_SUCCESS: 上位机成功发送了下传帧
}
function TUartServo.SendFrame(Command: byte; Data: word): integer;
var
arrOutput: Array[0..4] of byte;
begin
//发送命令字节;
arrOutput[0] := Command;
if (SendByte(arrOutput[0]) = RC_TX_SEND_OVERTIME) then
begin
Result := RC_TX_SEND_OVERTIME;
exit;
end;
//发送命令非字节;
arrOutput[1] := (not command);
//if (SendByte(command ->大错特错,不是arrOutput[1]) = RC_TX_SEND_OVERTIME) then
if (SendByte(arrOutput[1]) = RC_TX_SEND_OVERTIME) then
begin
Result := RC_TX_SEND_OVERTIME;
exit;
end;
//发送数据高字节
arrOutput[2] := (data and $ff00) shr 8;
if (SendByte(arrOutput[2]) = RC_TX_SEND_OVERTIME) then
begin
Result := RC_TX_SEND_OVERTIME;
exit;
end;
//发送数据低字节;
arrOutput[3] := data and $00ff;
if (SendByte(arrOutput[3]) = RC_TX_SEND_OVERTIME) then
begin
Result := RC_TX_SEND_OVERTIME;
exit;
end;
//发送校验字节,校验字节取校验字节之前的3字节无符号数求和后的和的低字节;
arrOutput[4] := (arrOutput[0] + arrOutput[1] + arrOutput[2] + arrOutput[3]) and $00ff ;
if (SendByte(arrOutput[4]) = RC_TX_SEND_OVERTIME) then
begin
Result := RC_TX_SEND_OVERTIME;
exit;
end;
Result := RC_SUCCESS;
end;
//读一次返回帧, 返回帧五个字节
{function ReceiveFrame(var Command,Status: byte; var Data: word): integer;
功能:
输入参数: Command: byte,命令号,一轮收发内返回帧和下传帧的命令值相同
输出参数: Status:word,读取的返回帧的状态值;
Data:word,读取的返回帧的数据值;
返回值: RC_FAILURE 等待超时或校验错,收不到合法的返回帧.
RC_RX_CHECK_ERROR 收到返回帧校验错
RC_RX_DATE_ERROR 状态寄存器指示收数据错;
RC_NO_RX_DATE 等待超时,仍未从串口接收数据寄存器读到数据;
RC_SUCCESS 规定时间内收到了合法的返回帧;
}
function TUartServo.ReceiveFrame(var Command,Status: byte; var Data: word): integer;
var
arrInput: Array[0..4] of byte;
i: integer;
GetResult:integer;
begin
//连续从com1取五个字节
for i := 0 to 4 do
begin
GetResult := GetByte(arrInput[i]) ;
//如果接收格式错,或接收数据被冲掉;马上重发应答帧
if (GetResult = RC_RX_DATE_ERROR) then
begin
Result := RC_RX_DATE_ERROR;
exit;
end;
if (GetResult = RC_NO_RX_DATE) then
begin
Result := RC_NO_RX_DATE;
exit;
end;
{GetCount := 1;
//如果长时间没收到数且查询次数少于2轮,开始下一轮查询
while ( (GetResult = RC_NO_RX_DATE) and (GetCount <= 2) ) do
begin
GetCount := GetCount+1;
GetResult := GetByte(arrInput[i]);
if (GetResult = RC_RX_DATE_ERROR) then return RC_FAILURE;
end;
if (GetResult = RC_NO_RX_DATE) then return RC_FAILURE; }
end;
//前4字节无符号数求和后的和的低字节与最后一个字节不等,则校验出错,返回失败值
if ( ( (arrInput[0]+ arrInput[1]+arrInput[2]+ arrInput[3]) and $00ff) <> arrInput[4] ) then
begin
Result := RC_RX_CHECK_ERROR;
exit;
end;
//接收命令字节;
Command := arrInput[0];
//接收状态字节
Status := arrInput[1];
//接收数据字
Data := (arrInput[2] shl 8)+arrInput[3];
result := RC_SUCCESS;
end;
{
function TUartServo.SendByte(output:byte ):integer;
功能: 等待发送保持寄存器空, 以发送单个字节;
输入参数: output: byte,准备向发送保持寄存器发送的单字节
输出参数: 无
返回值: RC_SEND_OVERTIME 发送保持寄存器保持非空, 超时仍旧发不出去;
RC_SUCCESS 成功发送单个字节
}
function TUartServo.SendByte(output:byte): integer;
var
GetCount: integer;
begin
// 读8250芯片状态寄存器,若D5=1,发送保持寄存器空,可以发送数据;否则等待.
GetCount := 0;
while ( (GetComStatus() and $20) = 0) and (GetCount<=100000) do
begin
GetCount := GetCount+1;
end;
If ( GetCount>100000) then
begin
Result := RC_TX_SEND_OVERTIME;//RC_FAILURE;
exit;
end;
PutReadyComValue(output);
Result := RC_SUCCESS;
end;
{function GetByte(var InputValue: byte): integer;
功能: 等待数据到达串口, 若到达检查收字节是否正确,正确则从串口取单个字节;
输入参数:
输出参数: InputValue:byte,从串口接收数据寄存器读取到的字节;
返回值: RC_RX_DATE_ERROR 状态寄存器指示收数据错;
RC_NO_RX_DATE 等待超时,仍未从串口接收数据寄存器读到数据;
RC_SUCCESS 成功从串口接收数据寄存器读取一字节
}
function TUartServo.GetByte(var InputValue: byte) :integer;
var
ComStatus: byte;
GetCount: integer;
i:integer;
Begin
ComStatus :=GetComStatus();
//测是否d4=1接收格式错;d2=1数接收据丢失,重叠错.无错,读数;否则放弃接收
If ( (ComStatus and $0a) <> 0) then
begin
result := RC_RX_DATE_ERROR;
exit;
end;
GetCount := 0;
//d0=1, 接收准备好;d0=0, 未准备好
While ( ((ComStatus and $01)= 0) and (GetCount<= 100000) ) do
Begin
ComStatus :=GeTComStatus();
If ( (ComStatus and $0a) <> 0 ) then
begin
Result := RC_RX_DATE_ERROR;
exit;
end;
GetCount:=GetCount + 1;
end;
If ( GetCount > 100000) then
begin
Result := RC_NO_RX_DATE;
exit;
end;
InputValue := GetReadyComValue();
Result := RC_SUCCESS;
end;
{function GetComStatus():byte;
功能: 读取线状态寄存器的值, 此值指示当前串口收发的状态信息
输入参数:
输出参数:
返回值: integer; 读取的线状态寄存器的值,指示收发的状态信息。
}
Function TUartServo.GetComStatus():byte;
Var
Value:byte;
Begin
Asm
mov dx, COM1_STATUS_REG //2fdh;
in al, dx
mov Value,al
end;
Result := Value;
End;
//从接收数据寄存器读取一个字节
Function TUartServo.GetReadyComValue(): byte;
var
value: byte;
Begin
Asm
mov dx,COM1_RX_DATA_REG //2f8h
in al,dx
mov Value,al
end;
Result := Value;
End;
//向发送保持寄存器发送单字节
Procedure TUartServo.PutReadyComValue(value: byte);
Begin
asm
mov dx,COM1_TX_DATA_REG //2f8h {2F8H口地址}
mov al,value {数据发送}
out dx,al
end;
End;
end.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -