还是书接上文,我列举了一个程序,表达如何在ubuntu 下通过LIBUSB 读取下位机数据(通过BLUK)传输方式,一般采用这种方式都是进行大数量传输的,我的程序也是这样,但是我发现了一个问题,如果每次读取比较大的数据块时候,好像问题不是很大,但是如果读取一个字节,或者比较小的数据块时会出现 LIBUSB_ERROR_OVERFLOW ,这个倒霉名字让我认为我的程序出错了,一直在调试我的程序,根本不管用,后来查阅了LIBUSB 有关于这个问题的描述才知道对这个问题的官方解释
- Problems may occur if the device attempts to send more data than can fit in
- the buffer. libusb reports LIBUSB_TRANSFER_OVERFLOW for this condition but
- other behaviour is largely undefined: actual_length may or may not be
- accurate, the chunk of data that can fit in the buffer (before overflow)
- may or may not have been transferred.
意思说,如果下位机需要试图传输比你要求数据更多的数据时,会产生这个问题,比如说一次BULK传输,下位机需要传输给你1000个字节,但是你使用了
recv= readbuffer(buffer,50) 你只需要传输50个字节,这时LIBUSB 会读取50个字节,但不会返回正确读取了50个字节而是出现LIBUSB_TRANSFER_OVERFLOW 的错误,因为还有1000-50 个字节没有读取成功,因为你开的buffer 太小了,不足以填充1000个字节,意思说,我没错,错误在你,谁让你开这么小的缓存,来完成本次的BLUK 传输,其实我有一个BULK 的buffer 挺大的,分手吧,你不适合我。这个跟我们对文件操作的认知是刚好相反的。
当然这个可以解决,比较粗暴的方法是
recv= readbuffer(buffer,1024*1024。。。。)基本就成功了,就是读取要求很大,但是返回可以是实际读取的大小了,也许是10 也许是100 或者某一个大约0 的数字,而且要不停地读取,才能保证只要有BLUK 传输 就不会丢包。貌似这个问题合理的解释过去了,但是同样带来一个问题,说如果我的客户端真的每次读取一个int,然后处理,岂不是很快就错位或者出现了LIBUSB_TRANSFER_OVERFLOW 吗?,读取会出现丢包,或者错位。想了想,还是Windows好,真完善,我想读多大就读多大,归根到底是Windows 驱动写了一个缓存,以便用户可以随便读取任何字节打下,对于windows驱动来说,仍然是靠大buffer读取,但是驱动建立了一个缓存,以便用户可以用任何大小读取,我安装这个思想做了一个缓冲区,让用户也可以用任何字节读取,而不再考虑出现LIBUSB_TRANSFER_OVERFLOW的错误,思想是用一个大Buffer 读取USB 然后把他放到一个队列里面,当然队列也应该比较大,当读取的时候,读取队列中的数据,这样就可以任意读取了,当然这种操作,有赖于要不停地写入和不停地读出。
在这里插入代码片
unit buffercheck;
// 主要用于Linux 下usb通讯 让他看起来很像windows 下的访问
{$mode ObjFPC}{$H+}
interface
uses
SysUtils, Classes, SyncObjs{$IFDEF MSWINDOWS} ,windows{$endif};
// 一个追赶式的循环buffer;
const buffer_ReadTimeout = -1;
type
Tbuffercheck = class
private
fCapcity: integer;
FData: array of Byte;
FLock: SyncObjs.TCriticalSection;
Fwriteindex: integer;
FReadindex: integer;
Fisfill: boolean;
function inc_Write_index(index: integer): integer;
function inc_Read_index(index: integer): integer;
function readbyte(var B: Byte): boolean;
function writebyte(B: Byte): boolean;
// function ReadgoforeWrite:boolean;
function ReadBuffer(var Buffer; Len: integer): integer; overload;
public
constructor Create(Capcity: integer);
destructor Destroy; override;
function WriteBuffer(var Buffer; Len: integer): integer;
function ReadBuffer(var Buffer; Len: integer; timeout: integer)
: integer; overload;
published
property REadindex :integer read FReadindex;
property Writeindex :integer read FWriteindex;
end;
implementation
{ Tbuffercheck }
function TickDelta(TickOld, TickNew: LongWord): LongWord;
begin
// if DWord is signed type (older Deplhi),
// then it not work properly on differencies larger then maxint!
Result := 0;
if TickOld <> TickNew then
begin
if TickNew < TickOld then
begin
TickNew := TickNew + LongWord(MaxInt) + 1;
TickOld := TickOld + LongWord(MaxInt) + 1;
end;
Result := TickNew - TickOld;
if TickNew < TickOld then
if Result > 0 then
Result := 0 - Result;
end;
end;
{$IFNDEF MSWINDOWS}
function GetTick: LongWord;
var
Stamp: TTimeStamp;
begin
Stamp := DateTimeToTimeStamp(Now);
Result := Stamp.Time;
end;
{$ELSE}
function GetTick: LongWord;
var
tick, freq: TLargeInteger;
{$IFDEF VER100}
x: TLargeInteger;
{$ENDIF}
begin
if windows.QueryPerformanceFrequency(freq) then
begin
windows.QueryPerformanceCounter(tick);
{$IFDEF VER100}
x.QuadPart := (tick.QuadPart / freq.QuadPart) * 1000;
Result := x.LowPart;
{$ELSE}
Result := Trunc((tick / freq) * 1000) and High(LongWord)
{$ENDIF}
end
else
Result := windows.GetTickCount;
end;
{$ENDIF}
constructor Tbuffercheck.Create(Capcity: integer);
begin
Fisfill := false;
fCapcity := Capcity;
SetLength(FData, fCapcity);
FLock := SyncObjs.TCriticalSection.Create;
Fwriteindex := -1; // integer;
FReadindex := -1; // integer;
end;
destructor Tbuffercheck.Destroy;
begin
Finalize(FData);
FLock.Free;
inherited;
end;
function Tbuffercheck.inc_Read_index(index: integer): integer;
begin
index := index + 1;
if index >= fCapcity then
begin
Result := 0;
end
else
Result := index;
end;
function Tbuffercheck.inc_Write_index(index: integer): integer;
begin
index := index + 1;
if index >= fCapcity then
begin
Result := 0;
Fisfill := true;
end
else
Result := index;
end;
function Tbuffercheck.ReadBuffer(var Buffer; Len: integer): integer;
var
i: integer;
// pb: integer;
pbuffer: pByte;
counter:integer;
begin
FLock.Enter;
counter:=0;
pbuffer := @Buffer;
for i := 0 to Len - 1 do
begin
if not readbyte(pbuffer^) then
Break;
inc(pbuffer);
counter :=counter +1;
end;
Result := counter;
FLock.Leave;
end;
function Tbuffercheck.ReadBuffer(var Buffer; Len: integer;
timeout: integer): integer;
var
pbuffer: pByte;
timeount: LongWord;
remin, read: integer;
ti: LongWord;
begin
remin := Len;
pbuffer := @Buffer;
ti := GetTick;
repeat
read := ReadBuffer(pbuffer^, remin);
remin := remin - read;
if remin <= 0 then
Break
else
inc(pbuffer, read);
timeount := timeout - integer(TickDelta(ti, GetTick));
if timeount <= 0 then
Break;
//睡一会 行吗
SysUtils.
Sleep(10); // Sleep(100);
until (false);
if remin > 0 then
Result := buffer_ReadTimeout
else
Result := Len;
end;
function Tbuffercheck.readbyte(var B: Byte): boolean;
var
pb: integer;
begin
// 如果读取到的 要覆盖 Fwriteindex 所定的数值,那一定是错误的,所以 完全不能再读取了 所以根本读不动,停下来
Result := false;
if Fwriteindex = FReadindex then
exit;
pb := inc_Read_index(FReadindex);
B := FData[pb];
FReadindex := pb;
// 如果读取到的
Result := true;
end;
(*function Tbuffercheck.ReadgoforeWrite: boolean;
begin
Result:= true;
// 这里可能存在覆盖的问题导致数据错误
if not Fisfill then
begin
if FReadindex <= Fwriteindex then
begin
Result := false;
exit;
end;
end
else
begin
//必须大于 Fwriteindex
{ if FReadindex >= Fwriteindex then
begin
Result := false;
exit;
end;
}
Result := false;
end;
end;
*)
function Tbuffercheck.WriteBuffer(var Buffer; Len: integer): integer;
var
i: integer;
// pb: integer;
pbuffer: pByte;
begin
FLock.Enter;
pbuffer := @Buffer;
for i := 0 to Len - 1 do
begin
writebyte(pbuffer^);
inc(pbuffer);
end;
Result := Len;
FLock.Leave;
end;
function Tbuffercheck.writebyte(B: Byte): boolean;
var
pb: integer;
begin
pb := inc_Write_index(Fwriteindex);
FData[pb] := B;
Fwriteindex := pb;
//如果超越了 读 那就把以前的覆盖了,所以把读取的指针也往前移动一下,保证读取的是最新的数值
if Fwriteindex = FReadindex then
FReadindex := inc_Read_index(FReadindex);
Result := true;
end;
end.
``这样终于安静了,我们在ubuntu 下获得了一个跟windows 下一样操作习惯的USB 读取方法,
最后还是说一句,不过微软有什么错误,他的操作系统还是照顾了大多数人的应用场景,Linux 从稳定性方面虽然也不差,但是这种靠草台班子组建的开发队伍,
还是有很多没有考虑到的问题,这并不是在侮辱开源精神,
而是希望更多的专业团队来从事Linux 相关领域方面的应用开发,而不是把问题交给用户,
让用户各个都成为了开发的高手,或者是解决Linux 问题的高手`