嵌入式Qt5.6应用编程之嵌入式Linux串口收发应用-JZ2440

2019-07-13 06:18发布

linux内核版本:3.4.2 qt 版本:5.6.0 交叉编译工具:arm-linuxgcc 4.4.3 开发板:JZ2440V3 本博客接上篇博客(嵌入式Qt5.6应用编程之触摸屏控制LED灯):https://mp.csdn.net/postedit/87923875 推荐一篇Linux串口应用编程的博客:https://blog.csdn.net/morixinguan/article/details/80898172 该博客源码分享: (内含可执行文件qt5_jz2440,可直接在jz2440上运行) serial.ui 界面设计文件,下图2 serial.c 跟界面空间紧密相连,界面各控件的槽函数都在此函数中实现。 uartrev.cpp 主要用于实现串口接受数据,新建了一个class继承QThread实现多线程编程.

目标:最终效果图如下

1.通过点击图1中的”串口助手“图标进入串口应用程序界面。 2.可以配置串口各参数 3.点击”打开“,”保存“,”发送“按钮可以进行提示。 4.可以接受串口数据,并显示接受数据的时间。 5.可以保存接受到的串口数据。 6.可以清空接受到的数据。 7.可以在发送框中输入数字,并通过串口发送。

通过该例子,可以收获以下知识点

1.  串口相关的一些基本知识

设备属于字符设备,Linux下默认设置的串口设置为:9600  8  n  1,一般命名为/dev/ttySn(n = 0、1、2......),如果该串口为USB转串口,可能名称为/dev/ttyUSBn(n = 0、1、2......),不同的平台下串口的名称是不同的,由底层驱动决定。linux自带串口驱动,驱动在根文件系统的/dev目录下,名为ttySAC0、ttySAC1、ttySAC2的三个设备.

2.  当qt函数与linux函数发生冲突时的解决方式

如果需要调用Qt库的close函数,在函数加::, 例如 ::close(fd_uart); 如果需要调用linux库的close函数,在函数前加QT::,例如 QT::close();

3. 显示一个消息对话框

int serial::set_show_msg_box(const QString msg) { QMessageBox msgBox(this);//对话框设置父组件 msgBox.setWindowTitle("保存数据");//对话框标题 msgBox.setIcon(QMessageBox::Information);//设置图标类型 msgBox.setStandardButtons(QMessageBox::Ok);//对话框上包含的按钮 msgBox.setText(msg);//对话框提示文本 return msgBox.exec(); }

4.将Qstring 类型转换成const char *

const char* ch_dev; QString uart_dev = ui->comboBox_uart_dev->currentText();//合成完整的设备号路径 QByteArray ba = uart_dev.toLatin1();//将QString格式转化为QByteArray格式 ch_dev = ba.data();//最终将QString转化成const char *型,例如”/dev/ttySAC0“

5. 获取linux系统时间

struct timeval tv; struct timezone tz; struct tm *ptm; gettimeofday(&tv, &tz); ptm = localtime(&tv.tv_sec);//获取当前时间 ui->text_uart_rev->setText(QString::number(1900+ptm->tm_year) + '-' + QString::number(ptm->tm_mon + 1) + '-' + QString::number(ptm->tm_mday) + ' ' + QString::number(ptm->tm_hour) + ':' + QString::number(ptm->tm_min) + ':' + QString::number(ptm->tm_sec));//显示时间

6.  QKeyEvent(子类)转QEvent(基类)使用dynamic_cast

QKeyEvent *evt_del = new QKeyEvent(QEvent::KeyPress, Qt::Key_Backspace, Qt::NoModifier);//新建一个键盘事件 QEvent *evt = dynamic_cast(evt_del);//子类转基类

7. 使用termios结构体进行串口设置

#include static struct termios opt; int set_uart(int fd,int baud,int databits,char parity,int stopbits,int flow_ctrl) { int i; speed_t baud_rates[] = { B9600, B19200, B38400, B57600, B115200 }; int speed_rates[] = {9600, 19200, 38400, 57600, 115200}; /*得到与fd指向对象的相关参数,并保存*/ tcgetattr(fd, &opt); for ( i= 0; i < 5; i++){ if (baud == speed_rates[i]){ cfsetispeed(&opt, baud_rates[i]); cfsetospeed(&opt, baud_rates[i]); } } opt.c_cflag |= CLOCAL;/*修改控制模式,保证程序不会占用串口*/ opt.c_cflag |= CREAD;/*修改控制模式,使得能够从串口中读取输入数据 */ /*设置数据流控制 */ if(flow_ctrl == 0){ opt.c_cflag &= ~CRTSCTS;//不使用流控制 }else if(flow_ctrl == 1){ opt.c_cflag |= CRTSCTS;//使用硬件流控制 }else if(flow_ctrl == 2){ opt.c_cflag |= IXON | IXOFF | IXANY;//使用软件流控制 }else{ qDebug() << "Unsupported mode flow "; } /*屏蔽其他标志位 */ opt.c_cflag &= ~CSIZE; /*设置数据位*/ if(databits == 5){ opt.c_cflag |= CS5; }else if(databits == 6){ opt.c_cflag |= CS6; }else if(databits == 7){ opt.c_cflag |= CS7; }else if(databits == 8){ opt.c_cflag |= CS8; }else{ qDebug() << "Unsupported data size "; } /*设置校验位 */ switch (parity){ case 'N': //无奇偶校验位。 opt.c_cflag &= ~PARENB; opt.c_iflag &= ~INPCK; break; case 'O'://设置为奇校验 opt.c_cflag |= (PARODD | PARENB); opt.c_iflag |= INPCK; break; case 'E'://设置为偶校验 opt.c_cflag |= PARENB; opt.c_cflag &= ~PARODD; opt.c_iflag |= INPCK; break; case 'S': //设置为空格 opt.c_cflag &= ~PARENB; opt.c_cflag &= ~CSTOPB; break; default: qDebug() << "Unsupported parity "; break; } /* 设置停止位 */ if(stopbits == 1){ opt.c_cflag &= ~CSTOPB; }else if(stopbits == 2){ opt.c_cflag |= CSTOPB; }else{ qDebug() << "Unsupported stop bits "; } tcflush(fd, TCIFLUSH);/*如果发生数据溢出,接收数据,但是不再读取*/ /*如果有数据可用,则read最多返回所要求的字节数。如果无数据可用,则read立即返回0*/ opt.c_cc[VTIME] = 0; //设置超时 opt.c_cc[VMIN] = 0; //Update the Opt and do it now /* *使能配置 *TCSANOW:立即执行而不等待数据发送或者接受完成 *TCSADRAIN:等待所有数据传递完成后执行 *TCSAFLUSH:输入和输出buffers 改变时执行 */ tcsetattr(fd,TCSANOW,&opt); return 0; }

8. 保存数据到文件

void serial::on_bt_save_data_clicked() { QFile file("uart_log.txt");//链接文件 //以只写方式打开该文件,并以追加的方式写入数据 if (!file.open(QIODevice::WriteOnly | QIODevice::Append)){ set_show_msg_box("打开文件失败"); return; } QTextStream out(&file); out<text_uart_rev->toPlainText()<

9. 实现一个简单的数字软键盘

  • 在界面上托放一个Grid Layout,然后在该Layout摆放按钮,形成一个数字键盘,每个按键都需要转到槽
  • 选中Grid Layout ,右键选择相应菜单,将其转换为QFrame,这样就可以调用hide()与show()函数对数字键盘进行隐藏与显示
  • 数字0-9的槽函数调用ui->textEdit_uart_send->insertPlainText(" ")函数,对输入文本框插入数字;回车按键的槽函数模拟del按键,代码如下:
/*数字键盘“删除”按键的槽函数*/ void serial::on_pushButton_del_clicked() { QKeyEvent *evt_del = new QKeyEvent(QEvent::KeyPress, Qt::Key_Backspace, Qt::NoModifier);//新建一个键盘事件 QEvent *evt = dynamic_cast(evt_del);//子类转基类 ui->textEdit_uart_send->setFocus(); QApplication::sendEvent(focusWidget(),evt); }
  • 输入文本框使用selectionChanged()槽函数,该函数中调用ui->gridFrame->show();显示键盘

10. 新建一个uartrev类继承QThread用于新建线程

  • 通过静态类成员变量在类与类之间传递参数fd_uart
----------serial.h文件,在类中声明变量fd_uart------------------------------------------ class serial : public QDialog { public: static int fd_uart; } ----------serial.cpp文件进行初始化---------------------------------------------------- int serial::fd_uart = -1; ----------uartrev.cpp文件就可以使用了(serial::fd_uart)------------------------------- void uartrev::run() { read(serial::fd_uart, uart_rev_buf, sizeof(uart_rev_buf)-1); }
  • 串口接受数据功能在重写的run函数中运行
//重写run函数 void uartrev::run() { int fs_sel; fd_set fs_read; FD_ZERO(&fs_read);// 将fs_read清零使集合中不含任何fd FD_SET(serial::fd_uart,&fs_read);//将fd加入uart_read集合 while(1){ //使用select实现串口的多路通信 fs_sel = select(serial::fd_uart+1,&fs_read,NULL,NULL,NULL); if(fs_sel){ memset(uart_rev_buf,0,sizeof(uart_rev_buf)); read(serial::fd_uart, uart_rev_buf, sizeof(uart_rev_buf)-1); emit signalUpdateText(uart_rev_buf); }else{ qDebug("receive data error"); } qDebug() <<"rev_uart:线程id"<
  • 自定义信号与槽,当接受到串口数据时通过emit signalUpdateText(uart_rev_buf)发送信号 ----------uartrev.h代码片段:完整的请下载源码---------------------------------------- class uartrev : public QThread { //只有加入了Q_OBJECT,你才能使用QT中的signal和slot机制。 Q_OBJECT signals: void signalUpdateText(char *uart_buf);//信号 } ----------uartrev.cpp代码片段:完整的请下载源码--------------------------------------- //重写run函数 void uartrev::run() { while(1){ if(接受到串口数据){ emit signalUpdateText(uart_rev_buf); } } } ----------serial.h代码片段:完整的请下载源码------------------------------------------ #include "uartrev.h" class serial : public QDialog { Q_OBJECT private slots: void slotUpdateText(char *uart_buf);//槽函数 private: uartrev *c_uart_rev; } ----------serial.cpp代码片段:完整的请下载源码---------------------------------------- serial::serial(QWidget *parent) : QDialog(parent), ui(new Ui::serial) { //连接信号与槽 c_uart_rev = new uartrev(); connect(c_uart_rev,SIGNAL(signalUpdateText(char*)),this,SLOT(slotUpdateText(char*))); } void serial::slotUpdateText(char *uart_buf) { //在此处更新接受到的串口数据 } void serial::on_bt_uart_open_clicked() { if(ui->bt_uart_open->text() == "关闭"){//检测到按钮当前显示为关闭 c_uart_rev->terminate();//终止串口接受数据线程 c_uart_rev->wait();//等待终止完成 return ;//退出函数 } c_uart_rev->start();//启用线程接受串口数据 }