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

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实现多线程编程.

目标:最终效果图如下

嵌入式Qt5.6应用编程之嵌入式Linux串口收发应用-JZ2440_第1张图片嵌入式Qt5.6应用编程之嵌入式Linux串口收发应用-JZ2440_第2张图片

嵌入式Qt5.6应用编程之嵌入式Linux串口收发应用-JZ2440_第3张图片

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\n";
    }

    /*屏蔽其他标志位  */
     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\n";
     }

    /*设置校验位 */
     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\n";
         break;
     }

     /* 设置停止位 */
     if(stopbits == 1){
        opt.c_cflag &= ~CSTOPB;
     }else if(stopbits == 2){
        opt.c_cflag |= CSTOPB;
     }else{
        qDebug() << "Unsupported stop bits\n";
     }

    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();//启用线程接受串口数据
    }

     

你可能感兴趣的:(嵌入式linux)