在现代技术中,串口通讯是非常常用的一种通讯方式。Delphi这个强大的集成开发环境可以用来开发Android应用程序,并且支持串口通讯。那么如何在Delphi中实现Android串口通讯呢?下面我们来详细介绍一下。
1. 准备工作
首先,要在Delphi中安装Android开发环境,包括了Android SDK和Java JDK,为安装方案提供基础。然后,在Delphi中新建一个Android项目,并添加一个TTimer组件(用作定时器)。接着,从Android App Center获取"Android串口调试助手" 应用程序(或者从其他渠道下载)。"Android串口调试助手"在Android端既可以测试串口通讯,也可以充当数据的 发送者和接受者。在这里,它将作为测试工具。
2. 实现串口通讯
在Delphi中实现串口通讯需要利用Android系统提供的串口 API。串口 API的关键是Android的"串口管理器",该管理器主要包括以下几个步骤:
1) 获取串口设备
2) 设置串口参数
3) 打开串口
4) 发送和接受数据
下面,我们将详细讲解如何一步一步实现这个功能。
2.1 获取串口设备
获取串口设备最容易的方法是使用Android系统的File I/O API。通过遍历/dev/下的文件来获取串口设备。串口设备的设备文件通常命名为"/dev/ttyS0"、"/dev/ttyS1"、"/dev/ttyUSB0"。获取串口设备的代码片段如下:
```
function GetSerialList:TStrings;
var
f:TJavaFile;
dirlist:TJavaObjectArray
i:Integer;
begin
Result:=TStringList.Create;
try
f:=TJavaFile.JavaClass.Create('/dev');
dirlist:=f.listFiles;
for i:=0 to dirlist.Length - 1 do begin
if Pos('ttyS',TJFile.Wrap(dirlist.Items[i]).getName)>0 then
Result.Add(TJFile.Wrap(dirlist.Items[i]).getName);
if Pos('ttyUSB',TJFile.Wrap(dirlist.Items[i]).getName)>0 then
Result.Add(TJFile.Wrap(dirlist.Items[i]).getName);
end;
finally
f.Free;
end;
end;
```
2.2 设置串口参数
在调用串口设备前,需要先将串口的通讯参数设置为正确的状态。包括波特率、数据位、停止位、校验位。当然,你也可以根据需要,不设置校验位,比如不设置奇偶校验。最后通过串口的管理器,设置参数。下面是设置串口参数的完整代码:
```
procedure TAndroidSerial.SetSerialParam(port: String; baudrate: Integer; databits: Integer; stopbits: Integer; parity: Integer);
var
fd: Integer;
speed: LongInt;
begin
fd := open(StringToPansiChar('/dev/'+port), O_RDWR);//打开串口设备
if (fd <0) then exit;
tcgetattr(fd,@options);//读取串口设备参数//初始化options结构体,这应该被做到打开设备之后,然后就是用的操作应该都是老生常谈的。
options.c_cflag := TCSAFLUSH or TCSANOW;
options.c_cflag := options.c_cflag or CLOCAL or CREAD;//忽略掉接收中断、成为本地串口,不许任何进程占用串口
case baudrate of//设置波特率
9600:speed:=B9600;
19200:speed:=B19200;
38400:speed:=B38400;
57600:speed:=B57600;
115200:speed:=B115200;
230400:speed:=B230400;
end;
cfsetispeed(@options, speed);
cfsetospeed(@options, speed);
options.c_cflag := options.c_cflag or CS8;//数据位
case stopbits of//停止位
1: options.c_cflag:=options.c_cflag and not CSTOPB;
2: options.c_cflag:=options.c_cflag or CSTOPB;
end;
case parity of//偶校验/奇校验
0:begin//无校验
options.c_cflag:=options.c_cflag and not PARENB;
end;
1:begin//奇校验
options.c_cflag:=options.c_cflag or PARENB;
options.c_cflag:=options.c_cflag or PARODD;
options.c_iflag:=options.c_iflag or INPCK;
end;
2:begin//偶校验
options.c_cflag:=options.c_cflag or PARENB;
options.c_cflag:=options.c_cflag and not PARODD;
options.c_iflag:=options.c_iflag or INPCK;
end;
3:begin//Mark校验
options.c_cflag:=options.c_cflag or PARENB;
options.c_cflag:=options.c_cflag or PARODD;
options.c_cflag:=options.c_cflag or CMSPAR;
options.c_iflag:=options.c_iflag or INPCK;
end;
4:begin//Space校验
options.c_cflag:=options.c_cflag or PARENB;
options.c_cflag:=options.c_cflag and not PARODD;
options.c_cflag:=options.c_cflag or CMSPAR;
options.c_iflag:=options.c_iflag or INPCK;
end;
end;
tcflush(fd,TCIFLUSH);
tcsetattr(fd, TCSANOW, @options);
Close(fd);
end;
```
2.3 打开串口
在设置好串口参数后,就可以打开串口了。调用fcntl()函数来打开串口设备,并设置为非堵塞模式。同时,将串口数据实时输出到退出时调用的函数。这样就可以在代码中通过读取串口接收缓冲区的数据来判断串口接收到数据了。在这里,使用的是重绘主窗口的方法来实现读取串口接收缓冲区的数据。下面是打开串口的代码:
```
function TAndroidSerial.OpenSerial(port: String; baudrate: Integer; databits: Integer; parity: Integer; stopbits: Integer; hwfc:Boolean):Boolean;
var
fd: Integer;
begin
try
fd := open(StringToPansiChar('/dev/'+port), O_RDWR);
if (fd < 0) then begin
exit(False);
end;
options.c_cc[VMIN] := 0; //防止阻塞
options.c_cc[VTIME] := 30; //等待超时时间,单位10ms
tcflush(fd,TCIOFLUSH);
if fcntl(fd, F_SETFL, O_NONBLOCK) < 0 then begin
exit(False);
end;
isStop:=false;
TThread.CreateAnonymousThread(
procedure
var
n:Integer;
mstr:UTF8String;
begin
while not isStop do
begin
n := Read(fd, mbufferJS[0], BUFFER_SIZE - 1);
if (n > 0) then begin
mbufferJS[n] := #0;
mstr:=UTF8Encode(Trim(mbufferJS));
ShowMsg(mstr);//这里实现重绘主窗口,可以读取缓冲区中的数据
end;
Sleep(10);//过快的读取会让进程崩溃
end;
end).Start;
Result := True;
except
Result := False;
end;
end;
```
2.4 发送和接收数据
现在,我们已经将串口端口打开了,那么如何发送和接收数据呢?最简单的办法是使用Delphi中的Write和Read函数。但在本例中,使用System.IOUtils库的TBytesStream来创建一个缓冲区缓存数据包。在写入数据时,将需要发送的数据写入到流中,然后将流输出到端口。在读取数据时,读取端口的缓冲区中的字节并存储在TBytesStream中,然后从流中读取收到的数据。下面是发送和接收数据的完整代码:
函数SendData:(将byte类型的数据报文直接发送)
```
procedure TAndroidSerial.SendData(Data: TBytes);
begin
TThread.CreateAnonymousThread(
procedure
begin
while isOpen do
begin
try
TMonitor.Enter(FSerialLock);
try
if Write(fd, Data[0], Length(Data))<=0 then
break;
finally
TMonitor.Exit(FSerialLock);
end;
except
break;//发生错误时退出该线程
end;
break;//只发送一次
end;
end).Start;
end;
```
函数ReadData:(读取缓存区的数据,放入数据缓存TBytesStream中并返回读取的字节数)
```
function TAndroidSerial.ReadData(var Data: TBytes): Integer;
var
stream: TBytesStream;
c: byte;
i : integer;
begin
stream := TBytesStream.Create;
try
i:=0;
while i < BUFFER_SIZE do
begin
TMonitor.Enter(FSerialLock);
try
if (Read(fd, c, 1) > 0) then
begin
stream.Write(c, 1);
Inc(i);
end;
finally
TMonitor.Exit(FSerialLock);
end;
end;
SetLength(Data, stream.Size);
stream.Position := 0;
stream.ReadBuffer(Data[0], stream.Size);
finally
stream.Free;
end;
Result := Length(Data);
end;
```
到此,Android串口通讯的实现已经全部完成。这样实现后,你就可以很轻松地实现Android的串口通讯了。