最近由于国产化要求,需要把原来Windows
下的程序进行修改,主要是支持Linux,其中我们有一块采集卡是核心芯片是STM32F429,通讯采用usb cdc
方式,这块卡在windows 下采用的通用驱动程序 windows 自带驱动,或者是安装ST公司的驱动(实质依然采用的是windows
自带驱动),上下位机通讯,通讯方面不能说运行十分稳定,也能说真他妈的稳定,最长记录是5年连续运行。
但是好就好在Windows以后不能在工业方面用了,所以不得不在Linux 重新写通讯程序,不干不知道还是Windows好,在Linux
上USB CDC应用有一些问题,在Linux文档中已经支持了虚拟串口通用驱动程序,就是可以把USB CDC 设备模拟成一个串口
/dev/ttyACM0 ,本来应该很完美,但好就好在好像对BULK
传输有点问题,貌似读取没问题,但是写入就运行稳定了(有时可以,有时不行,同样的程序,开关一次机,可能结果不一样),开源的东西就是不可靠,郁闷了,终于陷入了Linux
中所谓不兼容需要重新适配的问题了,我完全没有精力在看看Linux CDC ACM
驱动源码,关键是有可能看不懂,另外也不会编译内核(因为我是一个搞水电工程的),问题无解了。
但是我们水电工程方面国产化是必须的,所以只有一个办法,就是不用 CDC ACM
驱动,或者把它绕过去,直接操作USB,能够选择的方法是使用libusb 安装方法请自行百度,里面有一万篇,关于如何安装的文档,ubuntu
就十分简单了,直接APT吧libusb 1.0.0,版本好像有点低,不过好就好在它跟libusb
官网中版本一样低,直接写程序操作就好,我习惯于使用FPC ,所以使用Typhon64 写了一个简单的封装,目前libusb1.0.0
不支持windows 虽然有接口但是没有实现,只实现了一点点功能,因为在win 下压根不需要libusb ,可以使用winlibusb
或者普通的串口驱动就好,linux 生态差一点吧,我把写的一些关键代码贴出,跟大家共享。 一切开始的前提是权限问题,要使用libusb
需要进行权限设置的操作,因为USB设备用户都是root,其他用户只有读取的权利,所以使用当打开的时候会出错,网上也分享了一些如何进行权限设置的问题,我觉得最直接的方法是一句话,比如我的用户是tr
sudo usermod -a -G root tr 这样免去了很多问题 ,把tr这个用户加入root
组,这样还有一些其他问题,没准都能解决。
代码如下:(水平不高,高手斧正)
在这里插入代码片
unit MyUSBLib;
{$mode ObjFPC}{$H+}
interface
uses
Classes, SysUtils, Forms, Controls, Graphics, Dialogs, ExtCtrls,
ctUtils,
LibUsbDyn, LibUsbOop, mytype;
type
Tr8000UsbStruct = record
Addr: byte;
Bus: byte;
Port: byte;
PortPath: string;
Speed: byte;
DevDesc: libusb_device_descriptor;
//DevDesc : libusb_device_descriptor;
end;
Tr8000UsbTransfer = class
private
FActive: boolean;
FContext: TLibUsbContext;
FEnable: boolean;
FTr8000DeviceExist: boolean;
FTr8000Usb: Tr8000UsbStruct;
FDevice: TLibUsbDevice;
fDescrEPreadTransfer: TLibUsbBulkInEndpoint;//Endpoint
fDescrEPsendTransfer: TLibUsbBulkOutEndpoint;//Endpoint;//Endpoint
fLibUsbInterface: TLibUsbInterface;
function reFoundTr8000Device: boolean;
procedure clearAll;
public
constructor Create;
destructor Destroy; override;
function init: boolean;
function Open: boolean;
function Reopen: boolean;
procedure Close;// :boolean;
function ReadBuffer(var Buffer; Len: integer): integer;
function WriteBuffer(var Buffer; Len: integer): integer;
published
property Tr8000DeviceExist: boolean read FTr8000DeviceExist;
property Enable: boolean read FEnable;
property Active: boolean read FActive;
end;
function initusbLib: boolean;
function FoundTr8000Device(var _Tr8000Usb: Tr8000UsbStruct; Context: TLibUsbContext): boolean;
implementation
//uses mytype;
{const
SpeedName: array[0..4] of string = (
'Unknown',
'1.5 Mbit/s (USB LowSpeed)',
'12 Mbit/s (USB FullSpeed)',
'480 Mbit/s (USB HighSpeed)',
'5000 Mbit/s (USB SuperSpeed)');
}
function FoundTr8000Device(var _Tr8000Usb: Tr8000UsbStruct; Context: TLibUsbContext): boolean;
var
// Version: Plibusb_version;
// Context : TLibUsbContext;
DevList: PPlibusb_device;
DevCount: integer;
// Addr: byte;
// Bus: byte;
// Port: byte;
PortPath: TDynByteArray;
// Speed: byte;
// DevDesc: libusb_device_descriptor;
I, J: integer;
var
s: string;
begin
// ...
Result := False;
// get library version
// Version := TLibUsbContext.GetVersion;
// create context
//Context := TLibUsbContext.Create;
try
DevCount := ELibUsb.Check(Context.GetDeviceList(DevList), 'GetDeviceList');
// InfoMemo.Append( 'Found ' +IntToStr( DevCount) +' devices:');
// list all devices
for I := 0 to DevCount - 1 do
begin
move(TLibUsbContext.GetDeviceDescriptor(DevList[I]), _Tr8000Usb.DevDesc, sizeof(libusb_device_descriptor));
if (_Tr8000Usb.DevDesc.idProduct = Tr8000idProduct) and (_Tr8000Usb.DevDesc.idVendor = Tr8000idVendor) then
begin
_Tr8000Usb.Addr := TLibUsbContext.GetDeviceAddress(DevList[I]);
_Tr8000Usb.Bus := TLibUsbContext.GetBusNumber(DevList[I]);
_Tr8000Usb.Port := TLibUsbContext.GetPortNumber(DevList[I]);
PortPath := Context.GetPortPath(DevList[I]);
_Tr8000Usb.Speed := TLibUsbContext.GetDeviceSpeed(DevList[I]);
// move(TLibUsbContext.GetDeviceDescriptor( DevList[I]), _Tr8000Usb.DevDesc,sizeof (libusb_device_descriptor)) ;
// _Tr8000Usb.DevDesc := TLibUsbContext.GetDeviceDescriptor( DevList[I]);
_Tr8000Usb.PortPath := '';
if Length(PortPath) > 0 then
begin
s := ' port path from HCD: ' + #9 + IntToStr(PortPath[0]) + ' ';
for J := 1 to Length(PortPath) - 1 do s := s + IntToStr(PortPath[J]);
// InfoMemo.Append( s);
end;
_Tr8000Usb.PortPath := s;
Result := True;
break;
end;
end;
Context.FreeDeviceList(DevList);
finally
// Context.Free;
end;
// finalize
// ButtonSearch.Visible:= TRUE;
end;
function initusbLib: boolean;
begin
Result := True;
if USBLib_Load = False then
Result := USBLib_Load(ctGetRuntimesCPUOSDir(True) + USBLibName);
end;
function Tr8000UsbTransfer.reFoundTr8000Device: boolean;
var
Version: Plibusb_version;
// Context : TLibUsbContext;
DevList: PPlibusb_device;
DevCount: integer;
Addr: byte;
Bus: byte;
Port: byte;
PortPath: TDynByteArray;
Speed: byte;
DevDesc: libusb_device_descriptor;
I, J: integer;
_Tr8000Usb: Tr8000UsbStruct;
var
s: string;
begin
// ...
Result := False;
// get library version
Version := TLibUsbContext.GetVersion;
// create context
if fContext <> nil then
FreeAndNil(fContext);
fContext := TLibUsbContext.Create;
try
DevCount := ELibUsb.Check(fContext.GetDeviceList(DevList), 'GetDeviceList');
// InfoMemo.Append( 'Found ' +IntToStr( DevCount) +' devices:');
// list all devices
for I := 0 to DevCount - 1 do
begin
move(TLibUsbContext.GetDeviceDescriptor(DevList[I]), _Tr8000Usb.DevDesc, sizeof(libusb_device_descriptor));
if (_Tr8000Usb.DevDesc.idProduct = Tr8000idProduct) and (_Tr8000Usb.DevDesc.idVendor = Tr8000idVendor) then
begin
_Tr8000Usb.Addr := TLibUsbContext.GetDeviceAddress(DevList[I]);
_Tr8000Usb.Bus := TLibUsbContext.GetBusNumber(DevList[I]);
_Tr8000Usb.Port := TLibUsbContext.GetPortNumber(DevList[I]);
PortPath := fContext.GetPortPath(DevList[I]);
_Tr8000Usb.Speed := TLibUsbContext.GetDeviceSpeed(DevList[I]);
// move(TLibUsbContext.GetDeviceDescriptor( DevList[I]), _Tr8000Usb.DevDesc,sizeof (libusb_device_descriptor)) ;
// _Tr8000Usb.DevDesc := TLibUsbContext.GetDeviceDescriptor( DevList[I]);
_Tr8000Usb.PortPath := '';
if Length(PortPath) > 0 then
begin
s := ' port path from HCD: ' + #9 + IntToStr(PortPath[0]) + ' ';
for J := 1 to Length(PortPath) - 1 do s := s + IntToStr(PortPath[J]);
// InfoMemo.Append( s);
end;
_Tr8000Usb.PortPath := s;
Result := True;
break;
end;
end;
fContext.FreeDeviceList(DevList);
finally
// Context.Free;
end;
end;
procedure Tr8000UsbTransfer.clearAll;
begin
if fDescrEPreadTransfer <> nil then
FreeAndNil(fDescrEPreadTransfer);
if fDescrEPsendTransfer <> nil then
FreeAndNil(fDescrEPsendTransfer);
if fLibUsbInterface <> nil then
FreeAndNil(fLibUsbInterface);
if FDevice <> nil then
FreeAndNil(FDevice);
// FDevice.Free;
end;
constructor Tr8000UsbTransfer.Create;
begin
FTr8000DeviceExist := False;
fLibUsbInterface := nil;//TLibUsbInterface.Create(FDevice,@fDescrIntf);
//TLibUsbInterfaceEndpoint.Create(fLibUsbInterface,)
fDescrEPsendTransfer := nil;//TLibUsbBulkOutEndpoint.Create (fLibUsbInterface,@fDescrEPsend);
fDescrEPreadTransfer := nil;//TLibUsbBulkInEndpoint.Create (fLibUsbInterface,@fDescrEPread);
FDevice := nil;
FActive := False;
//Freadaddr := 255;
//FSendaddr := 255;
FEnable := initusbLib;// then
end;
destructor Tr8000UsbTransfer.Destroy;
begin
clearAll;
if Assigned(FContext) then
FreeAndNil(FContext);
inherited Destroy;
end;
function Tr8000UsbTransfer.init: boolean;
begin
Result := False;
if FEnable then
begin
if FContext = nil then
FContext := TLibUsbContext.Create;
Result := FoundTr8000Device(FTr8000Usb, FContext);
FTr8000DeviceExist := Result;
end;
end;
function Tr8000UsbTransfer.Open: boolean;
var
DescrDev: libusb_device_descriptor;
NumConf: integer;
DescrCfg: array[0..1023] of byte;
CfgSize: word;
CfgIdx: word;
endtag: integer;
EP0: TLibUsbDeviceControlEndpoint;
fDescrConf: libusb_config_descriptor;
// NumConf : Byte;
fDescrIntf: libusb_interface_descriptor;
fDescrEP: libusb_endpoint_descriptor;
fDescrEPread: libusb_endpoint_descriptor;
fDescrEPsend: libusb_endpoint_descriptor;
begin
Result := False;
endtag := 0;
//clearAll;
if not FEnable then exit;
if not FTr8000DeviceExist then exit;
try
FDevice := TLibUsbDevice.Create(FContext, FTr8000Usb.DevDesc.idVendor, FTr8000Usb.DevDesc.idProduct);
EP0 := FDevice.Control;
fDescrEPread.bEndpointAddress := 255;
fDescrEPsend.bEndpointAddress := 255;
if ELibUsb.Check(EP0.GetDescriptor(LIBUSB_DT_DEVICE, 0, DescrDev, LIBUSB_DT_DEVICE_SIZE), 'GetDescriptor') <> LIBUSB_DT_DEVICE_SIZE then
raise Exception.Create('GetDescriptor: Didn''t get expected number of bytes');
for NumConf := 0 to DescrDev.bNumConfigurations - 1 do
begin
CfgSize := ELibUsb.Check(EP0.GetDescriptor(LIBUSB_DT_CONFIG, 0, DescrCfg, SizeOf(DescrCfg)), 'GetDescriptor');
CfgIdx := 0;
while CfgIdx < CfgSize do
begin
if (DescrCfg[CfgIdx + 1] = LIBUSB_DT_CONFIG) and (DescrCfg[CfgIdx] = LIBUSB_DT_CONFIG_SIZE) then
with fDescrConf do
begin
// copy config descriptor (inside "With" to save one level of indentation)
Move(DescrCfg[CfgIdx], fDescrConf, DescrCfg[CfgIdx]);
Inc(CfgIdx, fDescrConf.bLength);
end
else if (DescrCfg[CfgIdx + 1] = LIBUSB_DT_INTERFACE) and (DescrCfg[CfgIdx] = LIBUSB_DT_INTERFACE_SIZE) then
with fDescrIntf do
begin
// copy interface descriptor (inside "With" to save one level of indentation)
Move(DescrCfg[CfgIdx], fDescrIntf, DescrCfg[CfgIdx]);
Inc(CfgIdx, fDescrIntf.bLength);
end
else if (DescrCfg[CfgIdx + 1] = LIBUSB_DT_ENDPOINT) and (DescrCfg[CfgIdx] = LIBUSB_DT_ENDPOINT_SIZE) then
with fDescrEP do
begin
// copy endpoint descriptor (inside "With" to save one level of indentation)
if endtag = 0 then
begin
Move(DescrCfg[CfgIdx], fDescrEP, DescrCfg[CfgIdx]);
Inc(CfgIdx, fDescrEP.bLength);
Inc(endtag);
end
else if endtag = 1 then
begin
Move(DescrCfg[CfgIdx], fDescrEPread, DescrCfg[CfgIdx]);
Inc(CfgIdx, fDescrEPread.bLength);
Inc(endtag);
end
else if endtag = 2 then
begin
Move(DescrCfg[CfgIdx], fDescrEPsend, DescrCfg[CfgIdx]);
Inc(CfgIdx, fDescrEPsend.bLength);
Inc(endtag);
end;
end
else
Inc(CfgIdx, DescrCfg[CfgIdx]);
end;
end;
if (fDescrEPsend.bEndpointAddress <> 255) and (fDescrEPread.bEndpointAddress <> 255) then
begin
fLibUsbInterface := TLibUsbInterface.Create(FDevice, @fDescrIntf);
fDescrEPsendTransfer := TLibUsbBulkOutEndpoint.Create(fLibUsbInterface, @fDescrEPread); // i do not know why but is is right
fDescrEPreadTransfer := TLibUsbBulkInEndpoint.Create(fLibUsbInterface, @fDescrEPsend);
// fDescrEPsendTransfer := TLibUsbBulkOutEndpoint.Create(fLibUsbInterface, @fDescrEPsend); // i do not know why but is is right
// fDescrEPreadTransfer := TLibUsbBulkInEndpoint.Create(fLibUsbInterface, @fDescrEPread);
EP0.Addr := fDescrEP.bEndpointAddress;
FActive := True;
Result := True;
end;
except
if FDevice <> nil then
FreeAndNil(FDevice);
FActive := False;
exit;
end;
end;
function Tr8000UsbTransfer.Reopen: boolean;
begin
Result := False;
if Enable then
begin
clearAll;
FTr8000DeviceExist := reFoundTr8000Device;
if FTr8000DeviceExist then
begin
fActive := Open;
Result := True;
end;
end;
end;
//Result := True;
procedure Tr8000UsbTransfer.Close;
begin
FActive := False;
FTr8000DeviceExist := False;
clearAll;
end;
function Tr8000UsbTransfer.ReadBuffer(var Buffer; Len: integer): integer;
begin
Result := 0;
if not FTr8000DeviceExist then exit;
if not FEnable then exit;
if not FActive then exit;
if fDescrEPreadTransfer <> nil then
begin
Result := fDescrEPreadTransfer.Recv(Buffer, Len, 1000);
end;
end;
function Tr8000UsbTransfer.WriteBuffer(var Buffer; Len: integer): integer;
//var
// bytes: TDynByteArray;
begin
Result := 0;
if not FTr8000DeviceExist then exit;
if not FEnable then exit;
if not FActive then exit;
if fDescrEPsendTransfer <> nil then
begin
// SetLength(bytes, Len);
// Move(Buffer, bytes[0], Len);
Result := fDescrEPsendTransfer.Send(Buffer, len,1000);
end;
end;
end.