折腾这个折腾了两天,功夫不负有心人,终于还是折腾出来了。关于串口的实现,涉及的比较多,不过也没有想象中的那么难。之所以折腾那么就,主要还是犯了一个很低级的出错,就是在读数据的时候老出错(写数据老早就可以了,幸庆自己还有那么一点完美主义)。一开是使用的是网上广泛流传的那个文件包叫“qextserialport-1.2win-alpha”,用到了里面的几个文件。 posix_qextserialport.h qextserialbase.h qetserialport.h 以及其对应的.cpp文件。 pre.cjk { font-family: "DejaVu Sans",monospace; }p { margin-bottom: 0.21cm; } 这些文件里面已经写好了对串口的初始化,打开,关闭,读写等等。可是我觉得内容太多,很多功能和我要实现的简单收发功能不相关。先给出我的实现函数
mainwindow.h文件,该文件主要定义一些Textbrowser之类的类和引用头文件“posix_qextserialport.h”,以及一些初始化串口,打开串口的槽函数。
pre.cjk { font-family: "DejaVu Sans",monospace; }p { margin-bottom: 0.21cm; }
#ifndef MAINWINDOW_H #define MAINWINDOW_H #include #include #include #include #include #include #include #include #include #include #include #include #include #include "posix_qextserialport.h"namespace Ui { class MainWindow; } class MainWindow : public QMainWindow { Q_OBJECT public: explicit MainWindow(QWidget *parent = 0); ~MainWindow(); private: Ui::MainWindow *ui; public slots: void init_serial(); void read_serial(); void open_serial(); void close_serial(); void send_serial(); public: Posix_QextSerialPort *myCom; QSocketNotifier *notifier; }; #endif // MAINWINDOW_H
mainwindow.cpp文件,该文件具体实现各个函数的功能以及信号与槽之间的关系。
pre.cjk { font-family: "DejaVu Sans",monospace; }p { margin-bottom: 0.21cm; }
#include "mainwindow.h"#include "ui_mainwindow.h"//static int serial_fd;struct PortSettings myComSetting={BAUD115200,DATA_8,PAR_NONE,STOP_1,FLOW_OFF,500};MainWindow::MainWindow(QWidget *parent) :QMainWindow(parent),ui(new Ui::MainWindow){ init_serial();//初始化串口//开始时三个按钮的状态ui->setupUi(this);ui->closeButton->setEnabled(false);ui->sendButton->setEnabled(false);//各个信号与槽之间的关系//notifier=new QSocketNotifier()connect(myCom,SIGNAL(readyRead()),this,SLOT(read_serial()));//connect(ui->openButton,SIGNAL(clicked()),this,SLOT(open_serial()));connect(ui->closeButton,SIGNAL(clicked()),this,SLOT(close_serial()));connect(ui->sendButton,SIGNAL(clicked()),this,SLOT(send_serial()));}MainWindow::~MainWindow(){delete ui;}void MainWindow::init_serial(){myCom=new Posix_QextSerialPort("/dev/ttyUSB1",myComSetting,QextSerialBase::EventDriven);//如果在电脑端则选择ttyUSB0,如果在开发板端则选择ttySAC0}void MainWindow::read_serial(){ QByteArray temp=myCom->readAll();//全部读取ui->textEdit->insertPlainText(temp);//显示读取到的数据}void MainWindow::open_serial(){myCom->open(QIODevice::ReadWrite);//打开串口ui->openButton->setEnabled(false);ui->closeButton->setEnabled(true);ui->sendButton->setEnabled(true);}void MainWindow::close_serial(){myCom->close();//关闭串口ui->openButton->setEnabled(true);ui->sendButton->setEnabled(false);ui->closeButton->setEnabled(false);}void MainWindow::send_serial(){myCom->write(ui->sendlineEdit->text().toAscii());//转化为ASCII码}至此总结:该工程可以实现,串口的发送,可是接收老出现问题,我还尝试这像前面一样加入QSocketNotifier,在PC端可以接收,可是
很容易出现乱码,可能是跟终端共有一个串口的原因,于是我又两边都换了串口2,可是问题就来了,在PC端的软件打不开了。可是一旦
设置minicom为串口2软件又可以打开,不知道什么原因。于是干脆狠下心来自己写串口的应用程序,具体内容见下面。
在这里有必要先了解一下底层串口的知识,
在这里用到的关于串口的分别是:termios,tcgetattr,tcsetattr,tcsendbreak,tcdrain,tcflush,tcflow,cfmakeraw,
cfgetospeed,cfgetispeed,cfsetispeed,cfsetospeed。
分别对其进行分析:
其原型为:
int tcgetattr(int fd, struct termios *termios_p);
int tcsetattr(int fd, int optional_actions, struct termios *termios_p);
int tcsendbreak(int fd, int duration);
int tcdrain(int fd);
int tcflush(int fd, int queue_selector);
int tcflow(int fd, int action);
int cfmakeraw(struct termios *termios_p);
speed_t cfgetispeed(struct termios *termios_p);
speed_t cfgetospeed(struct termios *termios_p);
int cfsetispeed(struct termios *termios_p, speed_t speed);
int cfsetospeed(struct termios *termios_p, speed_t speed);
作用:
tcgetattr() 得到与 fd 指向的对象相关的参数,将它们保存于 termios_p 引用的 termios 结构中。函数可以从后台进程中调用;但是,终端属性可能被后来的前台进程所改变。
tcsetattr() 设置与终端相关的参数 (除非需要底层支持却无法满足)。
tcsendbreak() 传送连续的 0 值比特流,持续一段时间,如果终端使用异步串行数据传输的话。如果 duration 是 0,它至少传输 0.25 秒,不会超过 0.5 秒。如果 duration 非零,它发送的时间长度由实现定义。
如果终端并非使用异步串行数据传输,tcsendbreak() 什么都不做。
tcdrain() 等待直到所有写入 fd 引用的对象的输出都被传输。
tcflush() 丢弃要写入 引用的对象,但是尚未传输的数据,或者收到但是尚未读取的数据,取决于 queue_selector 的值:
刷新收到的数据但是不读 刷新写入的数据但是不传送 同时刷新收到的数据但是不读,并且刷新写入的数据但是不传送。
tcflow() 挂起 fd 引用的对象上的数据传输或接收,取决于 action 的值:
挂起输出 重新开始被挂起的输出 发送一个 STOP 字符,停止终端设备向系统传送数据 发送一个 START 字符,使终端设备向系统传输数据 。
cfmakeraw()设置终端属性,就是设置termios结构中的各个参数。
cfgetospeed() 返回
termios 结构中存储的输出波特率。
cfsetospeed() 设置
termios 结构中存储的输出波特率为 speed,必须是这样的格式“B115200”。
cfsetispeed() 设置
termios 结构中存储的输入波特率为 speed。如果输入波特率被设为0,实际输入波特率将等于输出波特率。
其实就是围绕着termios这个结构捣鼓来捣鼓去,把它的数据写到寄存器中去(当然得结合硬件底层驱动)。
pre.cjk { font-family: "DejaVu Sans",monospace; }p { margin-bottom: 0.21cmycom.h头文件,声明各个函数,以及加入需要的头文件
pre.cjk { font-family: "DejaVu Sans",monospace; }p { margin-bottom: 0.21c
#ifndef MYCOM_H #define MYCOM_H #include //printf等用到 #include //file control #include //bzero等用到 #include //exit(0)等用到 #include //sleep等用到 #include //串口应用程序必须添加的头文件 #include //定义pid_t,size等 #include //read,write,getpid等等 #include //ioctl typedef struct //define the uart value struct { char prompt; //prompt after reciving data int baudrate; //baudrate char databit; //data bits, 5, 6, 7, 8 char debug; //debug mode, 0: none, 1: debug char echo; //echo mode, 0: none, 1: echo char fctl; //flow control, 0: none, 1: hardware, 2: software char tty; //tty: 0, 1, 2, 3, 4, 5, 6, 7 char parity; //parity 0: none, 1: odd, 2: even char stopbit; //stop bits, 1, 2 //const int reserved; //reserved, must be zero int reserved; //reserved, must be zero }portinfo_t; typedef portinfo_t *pportinfo_t; //redefine the portinfo_t /**************** declaration the fuction to set the uart *******************/ int PortOpen(pportinfo_t pportinfo); //open the uart int PortSet(int fdcom, const pportinfo_t pportinfo); //set the uart void PortClose(int fdcom); //close the uart int PortSend(int fdcom, char *data, int datalen); //send the datas int PortRecv(int fdcom, char *data, int datalen, int baudrate);//receive the datas #endif // MYCOM_H
mycom.h的具体.cpp文件,具体去实现各个函数的功能。需要注意的地方是,对应不同的平台TTY_DEV的定义是不同的。pre.cjk { font-family: "DejaVu Sans",monospace; }p { margin-bottom: 0.21c
#include "mycom.h"#define TTY_DEV "/dev/ttyUSB" //端口路径 如果是在电脑端选择ttyUSB,在mini2440端则选择ttySAC#define TIMEOUT_SEC(buflen,baud) (buflen*20/baud 2)//接收超时#define TIMEOUT_USEC 0/*************************************************fuction: char *get_ptty(pportinfo_t pportinfo)description: get thd uart namereturn: ptty**************************************************/static char *get_ptty(pportinfo_t pportinfo){char *ptty=NULL;switch(pportinfo->tty){case '0':ptty=TTY_DEV"0";break;case '1':ptty=TTY_DEV"1";break;case '2':ptty=TTY_DEV"2";break;}return ptty;}/*************************************************fuction: convbaud(unsigned long int baudrate)description: baud changereturn: ptty**************************************************/static int convbaud(unsigned long int baudrate){switch(baudrate){case 2400:return B2400;case 4800:return B4800;case 9600:return B9600;case 19200:return B19200;case 38400:return B38400;case 57600:return B57600;case 115200:return B115200;default:return B9600;}}/****************************************************************fuction: convbaud(unsigned long int baudrate)description: set the uartparameters: fdcom: 串口文件描述符,pportinfo: 待设置的串口信息return: tmp******************************************************************/int PortSet(int fdcom, const pportinfo_t pportinfo){struct termios termios_old, termios_new;int baudrate, tmp;char databit, stopbit, parity, fctl;bzero(&termios_old, sizeof(termios_old));//置字符串前sizeof个字节为零bzero(&termios_new, sizeof(termios_new));cfmakeraw(&termios_new);//设置终端属性tcgetattr(fdcom, &termios_old);//get the serial port attributions/*------------设置端口属性----------------*///baudratesbaudrate = convbaud(pportinfo -> baudrate);//得到波特率cfsetospeed(&termios_new, baudrate);//填入串口输出端的波特率termios_new.c_cflag |= CLOCAL;//控制模式,保证程序不会成为端口的占有者// 控制模式,flow controlfctl = pportinfo-> fctl;switch(fctl){case '0':{termios_new.c_cflag &= ~CRTSCTS;//no flow control}break;case '1':{termios_new.c_cflag |= CRTSCTS;//hardware flow control}break;case '2':{termios_new.c_iflag |= IXON | IXOFF |IXANY; //software flow control}break;}//控制模式,data bitstermios_new.c_cflag &= ~CSIZE;//控制模式,屏蔽字符大小位databit = pportinfo -> databit;switch(databit){case '5':termios_new.c_cflag |= CS5;case '6':termios_new.c_cflag |= CS6;case '7':termios_new.c_cflag |= CS7;default:termios_new.c_cflag |= CS8;}//控制模式 parity checkparity = pportinfo -> parity;switch(parity){case '0':{termios_new.c_cflag &= ~PARENB;//no parity check}break;case '1':{termios_new.c_cflag |= PARENB;//odd checktermios_new.c_cflag &= ~PARODD;}break;case '2':{termios_new.c_cflag |= PARENB;//even checktermios_new.c_cflag |= PARODD;}break;}//控制模式,stop bitsstopbit = pportinfo -> stopbit;if(stopbit == '2'){termios_new.c_cflag |= CSTOPB;//2 stop bits}else{termios_new.c_cflag &= ~CSTOPB;//1 stop bits}//other attributions defaulttermios_new.c_lflag &= ~(ICANON