嵌入式--通过串口上位机-下位机数据通信:传输命令控制,原始数据传输
协议:单片机端typedef struct{uint8_tbuff[1024];uint32_tindex;enum{UART_IDLE = 0,UART_RECV_CMD_1,UART_RECV_CMD_2,UART_RECV_CMD_START,UART_RECV_CMD_STOP,UART_RECV_DATA_RAW,UART
·
本科时嵌入式有一个课堂设计,“上位机”桌面端串口软件发命令给“下位机”单片机,然后单片机执行相应得操作。
协议:

单片机端
typedef struct
{
uint8_t buff[1024];
uint32_t index;
enum
{
UART_IDLE = 0,
UART_RECV_CMD_1,
UART_RECV_CMD_2,
UART_RECV_CMD_START,
UART_RECV_CMD_STOP,
UART_RECV_DATA_RAW,
UART_RECV_DATA_RAW_OK,
UART_ERROR = 0xFF,
} uartState;
} rectBlock_t;
rectBlock_t _g_uart_Block = {0, 0, UART_IDLE};
static bool my_strcmp(const char* src, const uint8_t* dst)
{
uint8_t len = strlen(src);
while(len--)
{
if(src[len] != dst[len])
return false;
}
return true;
}
// 串口中断处理函数
void uart_event_handle(app_uart_evt_t * p_event)
{
switch (p_event->evt_type)
{
case APP_UART_DATA_READY:
UNUSED_VARIABLE(app_uart_get(&_g_uart_Block.buff[_g_uart_Block.index]));
_g_uart_Block.index++;
if(_g_uart_Block.uartState == UART_IDLE)
{
if((_g_uart_Block.buff[0] == '{') &&
(_g_uart_Block.buff[_g_uart_Block.index-1] == '}'))
{
if(my_strcmp("{cmd1}", &_g_uart_Block.buff[0]))
{
// test cmd1
_g_uart_Block.uartState = UART_RECV_CMD_1;
}
else if(my_strcmp("{cmd2}", &_g_uart_Block.buff[0]))
{
// test cmd2
_g_uart_Block.uartState = UART_RECV_CMD_2;
}
else if(my_strcmp("{stop}", &_g_uart_Block.buff[0]))
{
_g_uart_Block.uartState = UART_RECV_CMD_STOP;
}
else if(my_strcmp("{start", &_g_uart_Block.buff[0]))
{
_g_image_file_index = _g_uart_Block.buff[_g_uart_Block.index-2];
_g_uart_Block.uartState = UART_RECV_CMD_START;
}
_g_uart_Block.index = 0;
}
if(_g_uart_Block.buff[0] != '{')
{
_g_uart_Block.index = 0;
}
}
else if(_g_uart_Block.uartState == UART_RECV_DATA_RAW)
{
if(_g_uart_Block.index == 1024)
{
_g_uart_Block.uartState = UART_RECV_DATA_RAW_OK;
}
else
{
if((_g_uart_Block.buff[0] == '{') &&
(_g_uart_Block.buff[_g_uart_Block.index-1] == '}'))
{
if(my_strcmp("{stop}", &_g_uart_Block.buff[0]))
{
_g_uart_Block.uartState = UART_RECV_CMD_STOP;
}
_g_uart_Block.index = 0;
}
}
}
break;
default:
break;
}
}
void uart_response(unsigned char* str)
{
printf("%s\r\n", str);
}
// 主循环中的串口数据处理函数
void Uart_Data_Handler(void)
{
switch((uint8_t)_g_uart_Block.uartState)
{
case UART_IDLE:
// Nothing stay here
break;
case UART_RECV_CMD_1:
// do cmd1
memset(_g_uart_Block.buff, 0, 1024);
_g_uart_Block.uartState = UART_IDLE;
_g_uart_Block.index = 0;
uart_response("{cmd1_ok}");
break;
case UART_RECV_CMD_2:
// do cmd2
memset(_g_uart_Block.buff, 0, 1024);
_g_uart_Block.uartState = UART_IDLE;
_g_uart_Block.index = 0;
uart_response("{cmd2_ok}");
break;
case UART_RECV_CMD_START:
// to do start cmd
memset(_g_uart_Block.buff, 0, 1024);
_g_uart_Block.uartState = UART_RECV_DATA_RAW;
_g_uart_Block.index = 0;
uart_response("{start_ok}");
break;
case UART_RECV_CMD_STOP:
// to do stop cmd
memset(_g_uart_Block.buff, 0, 1024);
_g_uart_Block.uartState = UART_IDLE;
_g_uart_Block.index = 0;
uart_response("{stop_ok}");
break;
case UART_RECV_DATA_RAW:
break;
case UART_RECV_DATA_RAW_OK:
// write raw data
memset(_g_uart_Block.buff, 0, 1024);
_g_uart_Block.uartState = UART_RECV_DATA_RAW;
_g_uart_Block.index = 0;
uart_response("{write_ok}");
break;
}
}
桌面端软件–Qt
界面:
serialfiletransfer.pro
QT += core gui
QT += serialport
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
CONFIG += c++11
# The following define makes your compiler emit warnings if you use
# any Qt feature that has been marked deprecated (the exact warnings
# depend on your compiler). Please consult the documentation of the
# deprecated API in order to know how to port your code away from it.
DEFINES += QT_DEPRECATED_WARNINGS
# You can also make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
# You can also select to disable deprecated APIs only up to a certain version of Qt.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
SOURCES += \
main.cpp \
mainwindow.cpp
HEADERS += \
mainwindow.h
FORMS += \
mainwindow.ui
# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QDebug>
#include <QtSerialPort/QSerialPort>
#include <QtSerialPort/QSerialPortInfo>
#include <QFileDialog>
#include <QFile>
#include <QTime>
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private slots:
void on_openButton_clicked();
void on_selectButton_clicked();
void on_sendButton_clicked();
void on_eraseButton_clicked();
void on_stopButton_clicked();
void initView();
void initSerial();
void readSerailData();
void sendFileData();
void cookSerialRawData(QByteArray raw);
void receiverSerialResponse(QString cmd);
void serialCmdSender(QString cmd);
void serialDataSender(QString data);
void on_refreshButton_clicked();
void on_testButton_clicked();
private:
Ui::MainWindow *ui;
QSerialPort *serial;
QStringList gFileDir;
qint64 gFileIndex;
qint64 gFileSize;
int16_t gFileName;
};
#endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
initView();
initSerial();
}
MainWindow::~MainWindow()
{
delete ui;
}
// 初始化界面
void MainWindow::initView()
{
// 初始化进度条
ui->progressBar->setValue(0);
ui->progressBar->setOrientation(Qt::Horizontal);
ui->progressBar->setAlignment(Qt::AlignVCenter);
ui->progressBar->setInvertedAppearance(false);
gFileIndex = 0;
}
// 初始化串口
void MainWindow::initSerial()
{
foreach(const QSerialPortInfo &info, QSerialPortInfo::availablePorts())
{
QSerialPort serial;
serial.setPort(info);
if(serial.open(QIODevice::ReadWrite))
{
ui->PortBox->addItem(serial.portName());
serial.close();
}
}
ui->BaudBox->setCurrentIndex(0);
ui->PortBox->setCurrentIndex(1);
ui->sendButton->setEnabled(false);
ui->selectButton->setEnabled(false);
ui->eraseButton->setEnabled(false);
ui->testButton->setEnabled(false);
ui->stopButton->setEnabled(false);
}
// 刷新串口
void MainWindow::on_refreshButton_clicked()
{
ui->PortBox->clear();
foreach(const QSerialPortInfo &info, QSerialPortInfo::availablePorts())
{
QSerialPort serial;
serial.setPort(info);
if(serial.open(QIODevice::ReadWrite))
{
ui->PortBox->addItem(serial.portName());
serial.close();
}
}
}
// 处理串口数据
void MainWindow::cookSerialRawData(QByteArray raw)
{
/* *
*
* 筛选出“{x,yyyyyy}”格式的数据
* x--通道
* y--数值
*
* */
static QString _tempData;
static QString _readData;
//异常类:无头且变量为空,已丢失头部,数据不可靠,直接返回
if ((!raw.contains("{"))&(_tempData.isNull()))
{
return;
}
//第一种:有头无尾,先清空原有内容,再附加
if ((raw.contains("{"))&(!raw.contains("}")))
{
_tempData.clear();
_tempData.append(raw);
}
//第二种:无头无尾且变量已有内容,数据中段部分,继续附加即可
if ((!raw.contains("{"))&(!raw.contains("}"))&(!_tempData.isNull()))
{
_tempData.append(raw);
}
//第三种:无头有尾且变量已有内容,已完整读取,附加后输出数据,并清空变量
if ((!raw.contains("{"))&(raw.contains("}"))&(!_tempData.isNull()))
{
_tempData.append(raw);
_readData = _tempData;
_tempData.clear();
}
//第四种:有头有尾(一段完整的内容),先清空原有内容,再附加,然后输出,最后清空变量
if ((raw.contains("{"))&(raw.contains("}")))
{
_tempData.clear();
_tempData.append(raw);
_readData = _tempData;
_tempData.clear();
}
QString cook;
QStringList list= _readData.split("{");
for(int i=0; i < list.length();i++)
{
if(!list.at(i).isEmpty())
{
cook = list.at(i);
if(!cook.isEmpty())
{
cook.insert(0, '{');
qDebug() << "cook:" << cook;
if(cook.contains("{",Qt::CaseSensitive) && cook.contains("}",Qt::CaseSensitive))
{
receiverSerialResponse(cook.split('{').at(1).split('}').at(0)); // 取{}中间的值
}
}
}
}
_readData.clear();
}
// 串口中断接收数据
void MainWindow::readSerailData()
{
static QByteArray sumData;
QByteArray tempData = serial->readAll();
if(!tempData.isEmpty())
{
sumData.append(tempData);
ui->textEdit->append(sumData);
// 接收的字符串中包含了字符'{'和字符'}'
if(sumData.contains("{") && sumData.contains("}"))
{
cookSerialRawData(sumData); // 数据解析
sumData.clear();
}
}
tempData.clear();
}
void MainWindow::sendFileData()
{
ui->textEdit->append("name:" + QString().number(gFileName) +" total:" +
QString().number((gFileSize/1024)+1) +
" current:" +
QString().number(gFileIndex));
ui->progressBar->setValue(gFileIndex);
if (gFileIndex == ((gFileSize/1024)+1))
{
// 发送完了就发送一个stop命令
serialCmdSender("stop");
gFileIndex = 0;
}
else
{
QFile file(QString(gFileDir.join("/")));
if (!file.open(QIODevice::ReadOnly | QIODevice::Unbuffered))
{
qDebug() << "Open file failed.";
} else {
file.seek(gFileIndex*1024);
serial->write(file.read(1024), 1024);
gFileIndex++;
qDebug() << gFileIndex;
file.close();
}
}
}
// 接收串口指令
void MainWindow::receiverSerialResponse(QString cmd)
{
// qDebug() << "CMD:" << cmd;
ui->rcvTB->clear();
ui->rcvTB->append("<" + cmd);
if(QString::localeAwareCompare(cmd, "start_ok") == 0)
{
qDebug() << "CMD:" << "start_ok";
// 发送文件
sendFileData();
}
else if(QString::localeAwareCompare(cmd, "cmd1_ok") == 0)
{
qDebug() << "CMD:" << "cmd1_ok";
}
else if(QString::localeAwareCompare(cmd, "cmd2_ok") == 0)
{
qDebug() << "CMD:" << "cmd2_ok";
}
else if(QString::localeAwareCompare(cmd, "stop_ok") == 0)
{
qDebug() << "CMD:" << "stop_ok";
}
else if(QString::localeAwareCompare(cmd, "write_ok") == 0)
{
qDebug() << "CMD:" << "write_ok";
//发送文件
sendFileData();
}
}
// 打开串口
void MainWindow::on_openButton_clicked()
{
if(ui->openButton->text()==QStringLiteral("打开串口"))
{
serial = new QSerialPort;
serial->setPortName(ui->PortBox->currentText());
serial->open(QIODevice::ReadWrite);
serial->setBaudRate(ui->BaudBox->currentText().toInt());
switch(ui->BitNumBox->currentIndex())
{
case 8: serial->setDataBits(QSerialPort::Data8); break;
default: break;
}
switch(ui->ParityBox->currentIndex())
{
case 0: serial->setParity(QSerialPort::NoParity); break;
default: break;
}
switch(ui->StopBox->currentIndex())
{
case 1: serial->setStopBits(QSerialPort::OneStop); break;
case 2: serial->setStopBits(QSerialPort::TwoStop); break;
default: break;
}
serial->setFlowControl(QSerialPort::NoFlowControl);
ui->PortBox->setEnabled(false);
ui->BaudBox->setEnabled(false);
ui->BitNumBox->setEnabled(false);
ui->ParityBox->setEnabled(false);
ui->StopBox->setEnabled(false);
ui->openButton->setText(QStringLiteral("关闭串口"));
ui->sendButton->setEnabled(true);
ui->selectButton->setEnabled(true);
ui->eraseButton->setEnabled(true);
ui->testButton->setEnabled(true);
ui->stopButton->setEnabled(true);
// 信号连接连接串口数据处理槽函数
QObject::connect(serial, &QSerialPort::readyRead, this, &MainWindow::readSerailData);
}
else
{
serial->clear();
serial->close();
serial->deleteLater();
ui->PortBox->setEnabled(true);
ui->BaudBox->setEnabled(true);
ui->BitNumBox->setEnabled(true);
ui->ParityBox->setEnabled(true);
ui->StopBox->setEnabled(true);
ui->openButton->setText(QStringLiteral("打开串口"));
ui->sendButton->setEnabled(false);
ui->selectButton->setEnabled(false);
ui->eraseButton->setEnabled(false);
ui->testButton->setEnabled(false);
ui->stopButton->setEnabled(false);
}
}
// 选择文件
void MainWindow::on_selectButton_clicked()
{
QFileDialog *fileDialog = new QFileDialog(this);
fileDialog->setWindowTitle("choice file");
fileDialog->setDirectory(".");
//fileDialog->setNameFilter(tr("mp3(*)")); // 文件类型过滤
fileDialog->setFileMode(QFileDialog::ExistingFiles);
fileDialog->setViewMode(QFileDialog::Detail);
QStringList fileNames;
if(fileDialog->exec())
{
fileNames = fileDialog->selectedFiles();
}
ui->textEdit->append(QString(fileNames.join("/")));
gFileDir = fileNames;
}
// 发送文件头
void MainWindow::on_sendButton_clicked()
{
QString fileName;
QString filefullName;
int i;
QFile file(QString(gFileDir.join("/")));
if (!file.open(QIODevice::ReadOnly | QIODevice::Unbuffered))
{
qDebug() << "Open file failed.";
}
else
{
// 获得文件名
filefullName = gFileDir.at(0);
for (i = filefullName.length()-1; i>0; i--)
{
if(filefullName[i] == "/")
break;
}
fileName = filefullName.mid(i+1).split('.').at(0);
gFileSize = file.size();
qDebug() << "fileName" << fileName;
qDebug() << "fileName" << fileName.toInt(nullptr, 16);
qDebug() << "Size:" << gFileSize;
qDebug() << "index:" << gFileIndex;
}
file.close();
ui->progressBar->setMaximum((gFileSize/1024)+1);
ui->progressBar->setMinimum(0);
ui->progressBar->setRange(0, (gFileSize/1024)+1);
// 文件名为:数字.文件类型
char fileIndex = fileName.toInt(nullptr, 10);
gFileName = fileName.toInt(nullptr, 10);
// 发送一个开始信号,带着文件类型代码
char sendCmd[] = {'s', 't', 'a', 'r', 't', fileIndex};
serialCmdSender(sendCmd);
ui->progressBar->reset();
}
// 全片擦除命令
void MainWindow::on_eraseButton_clicked()
{
QString cmd = "cmd2";
serialCmdSender(cmd);
}
// 停止发送
void MainWindow::on_stopButton_clicked()
{
QString cmd = "stop";
serialCmdSender(cmd);
}
// 测试命令
void MainWindow::on_testButton_clicked()
{
QString cmd = "cmd1";
serialCmdSender(cmd);
}
// 串口命令发送
void MainWindow::serialCmdSender(QString cmd)
{
serial->write("{" + cmd.toUtf8() + "}"); // "{cmd}"format
ui->sendCmdTB->clear();
ui->sendCmdTB->append(">" + cmd);
qDebug() << cmd;
}
// 串口数据发送
void MainWindow::serialDataSender(QString data)
{
serial->write(data.toUtf8());
ui->sendCmdTB->clear();
ui->sendCmdTB->append(">" + data);
qDebug() << data;
}
更多推荐
所有评论(0)