基于MAC OSX的串口助手源代码讲解(上)

网上对于mac的资料相对于windows和linux来比杯水车薪,博主在大学是从单片机学到了linux的应用开发,刚刚工作,主要方向是mac系统上的应用开发。博主是菜鸡一枚,但是愿意分享我写的代码,有大神来交流我很是欢迎。
话不多说,先说一些基础的东西。首先mac osx和linux都属于从unix系统演变而来的,那么博主当时学习linux的时候看的书是《UNIX环境高级编程》,那么这里隐含的一个意思就是,书里面的大部分posix接口都是可以用的,最简单的open read write也是可以用的,并且OC里面的有的开源串口库的底层写函数也是用的write,由此可见,大部分的OC库里面也大量的封装了这些接口。
底层部分不难实现,只是对于串口的一些配置,这部分的代码,我直接用的网上的现成代码,以前在linux写过,这里懒了就直接copy了。那么我先展示一下我在XCODE写串口助手的文件架构。
![文件架构](https://img-blog.csdn.net/20170925184816913?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcXFfMzMzMjQ4Nzg=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
在有界面的编程世界中,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

界面部分的代码明天再讲,今天有点累了。谢谢各位看官!

猜你喜欢

转载自blog.csdn.net/qq_33324878/article/details/78088744