网上对于mac的资料相对于windows和linux来比杯水车薪,博主在大学是从单片机学到了linux的应用开发,刚刚工作,主要方向是mac系统上的应用开发。博主是菜鸡一枚,但是愿意分享我写的代码,有大神来交流我很是欢迎。
话不多说,先说一些基础的东西。首先mac osx和linux都属于从unix系统演变而来的,那么博主当时学习linux的时候看的书是《UNIX环境高级编程》,那么这里隐含的一个意思就是,书里面的大部分posix接口都是可以用的,最简单的open read write也是可以用的,并且OC里面的有的开源串口库的底层写函数也是用的write,由此可见,大部分的OC库里面也大量的封装了这些接口。
底层部分不难实现,只是对于串口的一些配置,这部分的代码,我直接用的网上的现成代码,以前在linux写过,这里懒了就直接copy了。那么我先展示一下我在XCODE写串口助手的文件架构。
![文件架构]()
在有界面的编程世界中,MVC模式是流行的,也就是说,界面部分只负责处理控件。也就是MVC里的V,也就是view。而真正的难点在于contreller部分。这部分是负责界面控件与你底层数据结构之间的通讯。所以我的文件架构,就是底层的数据结构放在support文件里,而appdelete部分是界面部分的处理。
前面也说了我底层代码这一部分是基于网上的代码改的,由于第一天发博客不知道怎么引用,所以这里跟原创道个歉。代码如下:
//
// SeriportSetting.h
// SerialPort
//
// Created by gdlocal on 19/09/2017.
// Copyright © 2017 yang. All rights reserved.
//
#import <Foundation/Foundation.h>
#import <stdio.h> /*标准输入输出定义*/
#import <stdlib.h> /*标准函数库定义*/
#import <unistd.h> /*Unix 标准函数定义*/
#import <sys/types.h>
#import <sys/stat.h>
#import "string.h"
#import <fcntl.h> /*文件控制定义*/
#import <termios.h> /*PPSIX 终端控制定义*/
#import <errno.h> /*错误号定义*/
@interface SeriportSetting : NSObject
{
@public
int SerialPort_fd;
NSString*DevName;
int speed;
int databits;
int stopbits;
char parity;
}
int OpenDev(char *DevName);
void set_speed(int fd, int speed);
int set_Parity(int SerialPort_fd,int databits,int stopbits,int parity);
@end
上面这部分代码我用的是OC与C的混合,定义了SeriportSetting类其实就是串口的底层函数封装。
//
// SeriportSetting.m
// SerialPort
//
// Created by gdlocal on 19/09/2017.
// Copyright © 2017 yang. All rights reserved.
//
#import "SeriportSetting.h"
//#define FALSE -1
//#define TRUE 0
int SerialPort_fd;
@implementation SeriportSetting
-(id)init
{
self=[super init];
if(self)
{
}
return self;
}
int OpenDev(char *DevName)
{
SerialPort_fd = open(DevName, O_RDWR | O_NOCTTY ); //| O_NOCTTY | O_NDELAY
if (-1 == SerialPort_fd)
{
perror("Can't Open Serial Port");
return -1;
}
else
return SerialPort_fd;
}
int speed_arr[] = { B38400, B19200, B9600, B4800, B2400, B1200, B300,
B38400, B19200, B9600, B4800, B2400, B1200, B300, };
int name_arr[] = {38400, 19200, 9600, 4800, 2400, 1200, 300, 38400,
19200, 9600, 4800, 2400, 1200, 300, };
void set_speed(int fd, int speed)
{
int i;
int status;
struct termios Opt;
tcgetattr(SerialPort_fd, &Opt);
for ( i= 0; i < sizeof(speed_arr) / sizeof(int); i++) {
if (speed == name_arr[i]) {
tcflush(fd, TCIOFLUSH);
cfsetispeed(&Opt, speed_arr[i]);
cfsetospeed(&Opt, speed_arr[i]);
status = tcsetattr(SerialPort_fd, TCSANOW, &Opt);
if (status != 0) {
perror("tcsetattr fd1");
return;
}
tcflush(SerialPort_fd,TCIOFLUSH);
}
}
}
int set_Parity(int SerialPort_fd,int databits,int stopbits,int parity)
{
struct termios options;
options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); /*Input*/
options.c_oflag &= ~OPOST; /*Output*/
if ( tcgetattr(SerialPort_fd,&options) != 0) {
perror("SetupSerial 1");
return 0;
}
options.c_cflag &= ~CSIZE;
switch (databits) /*设置数据位数*/
{
case 7:
options.c_cflag |= CS7;
break;
case 8:
options.c_cflag |= CS8;
break;
default:
// fprintf(stderr,"Unsupported data size/n");
return 0;
}
switch (parity)
{
case 'n':
case 'N':
options.c_cflag &= ~PARENB; /* Clear parity enable */
options.c_iflag &= ~INPCK; /* Enable parity checking */
break;
case 'o':
case 'O':
options.c_cflag |= (PARODD | PARENB); /* 设置为奇效验*/
options.c_iflag |= INPCK; /* Disnable parity checking */
break;
case 'e':
case 'E':
options.c_cflag |= PARENB; /* Enable parity */
options.c_cflag &= ~PARODD; /* 转换为偶效验*/
options.c_iflag |= INPCK; /* Disnable parity checking */
break;
case 'S':
case 's': /*as no parity*/
options.c_cflag &= ~PARENB;
options.c_cflag &= ~CSTOPB;break;
default:
//fprintf(stderr,"Unsupported parity/n");
return 0;
}
/* 设置停止位*/
switch (stopbits)
{
case 1:
options.c_cflag &= ~CSTOPB;
break;
case 2:
options.c_cflag |= CSTOPB;
break;
default:
// fprintf(stderr,"Unsupported stop bits/n");
return 0;
}
/* Set input parity option */
if (parity != 'n')
options.c_iflag |= INPCK;
tcflush(SerialPort_fd,TCIFLUSH);
options.c_cc[VTIME] = 150; /* 设置超时15 seconds*/
options.c_cc[VMIN] = 0; /* Update the options and do it NOW */
if (tcsetattr(SerialPort_fd,TCSANOW,&options) != 0)
{
perror("SetupSerial 3");
return 0;
}
return 1;
}
@end
.m文件在OC里的意思是OC与C的混合文件,由于OC是完全兼容C的,所以熟悉C的朋友可以很快的上手。底层这部分代码主要是设置波特率,数据位,停止位,奇偶校验位。以及串口的打开操作,这部分只要是懂linux得朋友基本都能看懂,网上关于这部分的代码也是非常之多的。那么底层文件写完之后最主要的部分来了,就是界面部分。那么我先把我的xib文件的界面布局用图片表示出来。
![界面布局](http://img.blog.csdn.net/20170925192849790?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcXFfMzMzMjQ4Nzg=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
介绍完界面布局,我们重点来讲解一下界面是如何实现的。
//
// AppDelegate.h
// SerialPort
//
// Created by gdlocal on 19/09/2017.
// Copyright © 2017 yang. All rights reserved.
//
#import <Cocoa/Cocoa.h>
#import "SeriportSetting.h"
#import <CoreLib/SerialportEx.h>
@interface AppDelegate : NSObject<NSApplicationDelegate>
IBOutlet NSComboBox * Stopbits;
IBOutlet NSComboBox * Databits;
IBOutlet NSComboBox * Burrate;
IBOutlet NSButton * Check;
IBOutlet NSButton *Open;
IBOutlet NSComboBox * Device;
IBOutlet NSButton *Close;
IBOutlet NSButton *Send;
IBOutlet NSTextField * Sendbuffer;
IBOutlet NSButton *Add;
IBOutlet NSButton *RepelyReceive;
IBOutlet NSTextView *My_textView;
IBOutlet NSComboBox *AddComBox;
IBOutlet NSButton *add;
NSMutableDictionary *dicConfiguration;
SeriportSetting *My_Com;
NSMutableDictionary *dic;
SerialportEx* My_Scom;
}
-(IBAction)SetStopbits:(id)sender;
-(IBAction)SetBuratte:(id)sender;
-(IBAction)Close:(id)sender;
-(IBAction)Send:(id)sender;
-(IBAction)RepelyReceive:(id)sender;
-(void)My_textView;
-(IBAction)Open:(id)sender;
-(IBAction)Setdatabit:(id)sender;
-(IBAction)add:(id)sender;
@end
网上串口助手的界面布局有很多,可以看出在所有的界面之中,波特率,停止位,数据位,校验位大多数都是以选择形式来提供的,所以这几个的控件我选的combox控件。开始,关闭,发送,加末尾换行符这些控件我选择了按钮,如果各位看官想直接COPY我代码去自己试试的时候一定要把 SerialportEx* My_Scom注释掉,这部分涉及的功能可以先不写。用的是闭源的库。NSTextField控件表示得是文本编辑区,发送功能可以用这个控件,但是接收就不可以用了,因为一般的串口助手在接收的时候是要显示所有你收到的数据的,如果你再用NSTextField控件的话,你会发现在你收到第二次数据的时候,就会把第一次的数据覆盖了,但是OC的控件是丰富的。 NSTextView控件完美的解决了这个问题。以后会讲。至于上面h文件的方法部分,都是设置以及action,方法名字也都是见名知意。所以就不多说了。下面来看实现的m文件。
#import "AppDelegate.h"
#import "SeriportSetting.h"
NSInteger addstate=0;
@interface AppDelegate ()
@property (weak) IBOutlet NSWindow *window;
@end
@implementation AppDelegate
-(instancetype)init
{
self=[super init];
if(self)
{
My_Com = [[SeriportSetting alloc]init];
dicConfiguration=[[NSMutableDictionary alloc]init];
My_Scom = [[SerialportEx alloc]init];
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
}
return self;
}
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
}
- (void)applicationWillTerminate:(NSNotification *)aNotification {
// Insert code here to tear down your application
}
-(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 ;
}
-(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];
}
}
-(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];
}
-(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);
}
}
}
-(IBAction)Close:(id)sender
{
int fd=close(My_Com->SerialPort_fd);
if(fd==-1)
{
NSAlert *alert = [[NSAlert alloc] init];
[alert setMessageText:[NSString stringWithUTF8String:"Close error "]];
[alert setInformativeText:[NSString stringWithFormat:@"Unkown error"]];
[alert setAlertStyle:NSAlertStyleWarning];
[alert runModal];
return;
}
}
-(IBAction)add:(id)sender
{
if([sender state])
{
addstate=1;
}
}
-(IBAction)Scanf:(id)sender
{
[Device removeAllItems] ;
NSEnumerator *enumerator = [AMSerialPortList portEnumerator];
AMSerialPort *aPort;
while (aPort = [enumerator nextObject]) {
[Device addItemWithObjectValue:[aPort bsdPath]];
}
return ;
}
-(void)view:(NSString*)str
{
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:@"yyyy/MM/dd HH:mm:ss.SSS : "];
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];
}
@end
界面部分的代码明天再讲,今天有点累了。谢谢各位看官!