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