@implementation AppDelegate
-(instancetype)init
{
self=[superinit];
if(self)
{
My_Com = [[SeriportSettingalloc]init];
}
returnself;
}
以上是我初始化的代码,这段代码重写了父类的init,但是这里的类型我用的是instancetype,这个类型相比于ID类型来说是一种优化写法,能让编译器迅速的知道我们的类属于什么类型的。具体的话可以百度一下,有许多博客和大神讲解的比我更加的清楚。那么在这里我们把我们所写的串口类进行了实例化。
-(IBAction)SetStopbits:(id)sender
{
NSInteger stopbits = [Stopbits intValue];
My_Com->stopbits = stopbits;
}
-(IBAction)Setdatabit:(id)sender
{
NSInteger databits = [Databits intValue];
My_Com->databits=databits;
}
-(IBAction)SetBuratte:(id)sender
{
NSInteger burrate = [Burrate intValue];
My_Com->speed =burrate ;
}
上段代码我实现了三个触发事件,分别是设置停止位,数据位和波特率,这三个控件我是用的Combox控件来实现的,那么在这些简单控件上,只要[(控件实例) 数据类型],以这种方式我们就可以获得Combox 乃至许多简单控件上的数据,所以当事件触发的时候,就把值赋予我们所定义的串口类的实例里的成员变量就好了。
-(IBAction)Open:(id)sender
{
My_Com->DevName=[Device stringValue];
if([My_Com->DevName length]==0)
{
NSAlert *alert = [[NSAlert alloc] init];
[alert setMessageText:[NSString stringWithUTF8String:"Please Input your Seriport Name"]];
[alert setInformativeText:[NSString stringWithFormat:@"name is nil"]];
[alert setAlertStyle:NSAlertStyleWarning];
[alert runModal];
return;
}
My_Com->SerialPort_fd=OpenDev([My_Com->DevName UTF8String]);
if(My_Com->SerialPort_fd==-1)
{
NSAlert *alert = [[NSAlert alloc] init];
[alert setMessageText:[NSString stringWithUTF8String:"Open Serial error"]];
[alert setInformativeText:[NSString stringWithFormat:@"Unkown error"]];
[alert setAlertStyle:NSAlertStyleWarning];
[alert runModal];
return;
}
My_Com->parity='o';
set_speed(My_Com->SerialPort_fd, My_Com->speed);
set_Parity(My_Com->SerialPort_fd,My_Com->databits,My_Com->stopbits,My_Com->parity);
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(threadDemo) object:nil];
[thread start];
}
上面这段代码实现了打开串口的事件,这里使用的是按钮这个控件。由于我们需要在textField上写下我们串口的名字,其格式就是“/dev/...”,那么我们得到了这个字符串之后,需要进行判断,字符串的长度不能为空,所以我们判断字符串为空时,就弹出警告框, NSAlert 这个类就是警告框的应用,如果下次需要警告框,可以把我这段代码直接复制过去,你想提示什么讯息,就改变那两个字符串的内容,两个字符串,分别是大框的字符串和小框的字符串,大家自己去试的时候就知道了。判断结束之后,我们需要用open 这个系统调用来打开文件,但是由于open的第一个参数是const char *类型,属于c语言的字符串类型,所以这里我们用NString里面的方法把我们所接收到的字符串进行格式转换。当fd也就是文件描述符为-1时,再次弹出警告框。接下来,就开始设置我们的串口数据,这里注意的一点是,在我写的这个应用程序里,是要先设置这些数据,在进行打开串口的操作。在函数的最后,我们开启了一个线程,因为读动作,需要一直不断的读串口的数据,所以但凡执行超过0.2秒的代码段我们都要开启线程。所以这里的线程为读操作的线程。其实也可以用pthread,这也是我更加习惯用的,因为之前一直在linux下写代码。-(IBAction)add:(id)sender
{
if([sender state])
{
addstate=1;
}
}
上面这段代码的控件是一个勾选框,勾选框无非两种状态,一种是选中了,另一种是没有选中,那么在Cocoa框架里,参数sender里的state方法就可以判断勾选框是否勾选上了,当勾选框选上的时候,我们把addstate这个全局变量赋值为1,这里读者看了可能有些懵,这段代码是为了发送时的某一功能设定的,其实可以写的更优雅一些,但是本代码目的就是为了教会初学者,所以采用得是标志位的方式实现。下面来看发送函数的代码。
-(IBAction)Send:(id)sender
{
NSString *str=[Sendbuffer stringValue];
if([str length]==0)
{
NSAlert *alert = [[NSAlert alloc] init];
[alert setMessageText:[NSString stringWithUTF8String:"Please Input "]];
[alert setInformativeText:[NSString stringWithFormat:@"Unkown error"]];
[alert setAlertStyle:NSAlertStyleWarning];
[alert runModal];
return;
}
if(addstate==1)
{
if([[AddComBox stringValue] isEqualToString:@"\\n"])
{
NSString *str1=[str stringByAppendingString:@"\n"];
NSData* data = [str1 dataUsingEncoding:NSUTF8StringEncoding];
NSUInteger dataLen = [data length];
const char *dataBytes = (const char*)[data bytes];
write(My_Com->SerialPort_fd, dataBytes, dataLen);
}
else if ([[AddComBox stringValue] isEqualToString:@"\\r\\n"])
{
NSString *str1=[str stringByAppendingString:@"\r\n"];
NSData* data = [str1 dataUsingEncoding:NSUTF8StringEncoding];
NSUInteger dataLen = [data length];
const char *dataBytes = (const char*)[data bytes];
write(My_Com->SerialPort_fd, dataBytes, dataLen);
}
else if ([[AddComBox stringValue] isEqualToString:@"\\r"])
{
NSString *str1=[str stringByAppendingString:@"\r"];
NSData* data = [str1 dataUsingEncoding:NSUTF8StringEncoding];
NSUInteger dataLen = [data length];
const char *dataBytes = (const char*)[data bytes];
write(My_Com->SerialPort_fd, dataBytes, dataLen);
}
else{
NSData* data = [str dataUsingEncoding:NSUTF8StringEncoding];
NSUInteger dataLen = [data length];
const char *dataBytes = (const char*)[data bytes];
write(My_Com->SerialPort_fd, dataBytes, dataLen);
}
}
}
上面这段代码就是发送函数,这个功能就是以textfeild和一个按钮控件,加上Combox,ComBox的作用是为了在发送的时候我们可以选择add不add一个/r/n,这样我们就可以选择我们发送过去的数据到底换行还是不换行。这也就是我们勾选框的目的。
当发送区的textfield被写上数据时,我们先判断他是否是空字符串,如果是空字符串,那么我们一样要弹出警告框。接下来就是判断全局的addstate是否为1,如果为1,我们判断Combox里的值是否为/r/n或者/r或者/n,如果都不是那么我们就直接发送,如果是那么就在字符串的后面加上,这里面是有坑的,读者看我的/r/n不是正常的。至于为什么自己思考一下。NSdata是比特流的意思。也就是串口接收的元数据流,这里其实也可以直接发送,但是这么写更加的安全一些。
-(void)threadDemo
{
ssize_t nread;
char buffer[1024];
BOOL notstop=true;
while (notstop)
{
memset(buffer, 0, sizeof(buffer));
nread = read(My_Com->SerialPort_fd, buffer, 1024);
if (nread>0)
{
NSString * str = [NSString stringWithUTF8String:buffer];
[self performSelectorOnMainThread: @selector(view:) withObject:str waitUntilDone:NO];
}
[NSThread sleepForTimeInterval:0.01];
}
}
-(void)view:(NSString*)str
{
NSUInteger length = 0;
NSAttributedString *theString;
NSRange theRange;
theString = [[NSAttributedString alloc] initWithString:str];
[[My_textView textStorage] appendAttributedString: theString];
length = [[My_textView textStorage] length];
theRange = NSMakeRange(length, 0);
[My_textView scrollRangeToVisible:theRange];
}
上面这段代码结合起来实现了接收功能与视图的滚动显示。 [ self performSelectorOnMainThread : @selector (view:) withObject :str waitUntilDone : NO ];这句话的意思是刷新当前的界面,把数据传输到主线程里,如果不加这句话,你会发现,你的控件是不会更新数据的。上述就是我串口助手源码重要部分的讲解,博主也实现了自动遍历当前可用串口功能,只是这部分的功能是基于第三方库实现的。所以这部分就不写上了,如果调试过程之中发现有乱码,大部分是设置数据错误,可能是参数没对,主要也可能是波特率的问题。希望大家看了我的博客,能自己写出自己的串口助手!