Rất nhiều cá sấu nhỏ.
1. Về giao tiếp nối tiếp:
Qt có lớp giao tiếp nối tiếp riêng, đó là QSerialPort, nhưng trong quá trình sử dụng, vì chúng ta cần sử dụng lớp giao tiếp nối tiếp tùy chỉnh hơn để giảm bớt khó khăn trong quá trình phát triển nên chúng tôi sẽ cung cấp một lớp giao tiếp nối tiếp, đó là lớp SerialPortHelper.
Trước hết, chúng ta cần biết cổng nối tiếp là gì. Giao tiếp cổng nối tiếp là một giao thức giao tiếp giữa máy và hệ thống. Bạn có thể coi nó như một bộ nhớ dùng chung, nơi bạn có thể ghi nội dung nếu cần, sau đó đọc. dữ liệu từ nó khi cần thiết. Tuy nhiên, cần lưu ý rằng trong gói Qt, bạn không cần biết dữ liệu trong cổng nối tiếp là của bạn hay do bạn gửi, vì tất cả là của bạn.
Truyền thông nối tiếp là gì?
Bây giờ chúng ta đã hiểu chung về giao tiếp nối tiếp là gì, chúng ta hãy xem sổ tay hướng dẫn giao tiếp cho giao tiếp nối tiếp trông như thế nào.
Từ những điều trên chúng ta có thể thấy rằng thông báo giao tiếp cổng nối tiếp có lẽ là một chuỗi ký tự thập lục phân theo quy định cụ thể, sau đó chỉ cần ghi các thông báo này vào cổng nối tiếp. Ví dụ: ba số đầu tiên 41 54 64 được mã hóa cứng và 36 là tín hiệu liên lạc của giao thức này, được sử dụng để phân biệt các mã thông báo khác nhau. Ví dụ: mã thông báo của lệnh trên là 36 và thông báo. mã của cái còn lại là 36. thông tin.
Sau đó, nội dung được đánh dấu màu tiếp theo là dữ liệu thực tế được chứa trong mã thông báo này, vì vậy tôi sẽ không đi sâu vào chi tiết về điều này.
Tuy nhiên, cần lưu ý rằng dữ liệu được phần cứng trả về hoặc dữ liệu gửi đến phần cứng không nhất thiết phải từ trái sang phải như con người. Một số lệnh, đặc biệt là những lệnh dài, yêu cầu phải từ phải sang trái, người ta thường nói. Thứ tự endian nhỏ, byte thấp trước, byte cao cuối cùng, ví dụ:
Cho ví dụ về các hàm ở cả hai đầu (thực ra hai hàm này đều có sẵn trong Qt, ở đây chúng tôi chỉ trình bày ý nghĩa của chúng):
//Chuyển đổi dữ liệu cuối nhỏ nhận được thành số nguyên không dấu QString SerialPortHelper::getLittleEnd(const QByteArray& data) { if (data.size() > 8) return "" qulonglong result = 0; < data.size(); ++i) { qulonglong tmp = (uchar)data[i]; kết quả += tmp <<= i * 8; } return QString::number(result); } //Chuyển đổi dữ liệu thứ tự byte lớn sang số dấu phẩy động QString SerialPortHelper::getBigEndFlt(const QByteArray& data) { const int fltLen = 4; ! = fltLen) trả về "null"; kết quả float = 0; uchar fltArr[fltLen] cho (int i = 0; i < fltLen; ++i) { fltArr[fltLen - i - 1] = data.at(i); } return QString::number(*(float*)fltArr, 'f', 9);
Được rồi, tiếp theo còn có bốn chữ số nữa. Hai chữ số đầu và hai chữ số cuối cần được tách ra.
Hai chữ số đầu tiên là mã kiểm tra CRC. Điều này yêu cầu kiểm tra cơ bản các số trước đó. Tôi không biết chi tiết. Đây là một hàm để tham khảo:
void SerialPortHelper::CRC16_2(const QByteArray& ba, uchar* crcBuf) { int pos, i; uchar* buf = (uchar*)ba.data(); int len = ba.size(); unsigned int crc = 0xFFFF; for (pos = 0; pos < len; pos++) { crc ^= (unsigned int)buf[pos]; // XOR byte thành byte có chữ ký nhỏ nhất của crc for (i = 8; i != 0; i--) // Lặp qua từng bit { if ((crc & 0x0001) != 0) // Nếu LSB được đặt { crc >>= 1; // Dịch phải và XOR 0xA001 crc ^= 0xA001; } else // Nếu không thì LSB không được đặt { crc >>= 1; // Chỉ cần dịch sang phải } } } //高低字节转换 crc = ((crc & 0x00ff) << 8) | ((crc & 0xff00) >> 8); //qDebug() << QString().sprintf("CRC:%04x", crc); crcBuf[0] = crc >> 8; crcBuf[1] = crc; }
Hai chữ số cuối cùng là cố định và hai tổ hợp cố định được sử dụng để phân chia các đoạn ký tự khác nhau.
Quá trình sử dụng giao tiếp nối tiếp
Chúng tôi thường sử dụng lớp cổng nối tiếp và quy trình chính như sau:
1. Lấy các tham số cơ bản: Chúng ta cần lấy một số tham số cơ bản của cổng nối tiếp này, bao gồm:
QString portName = "NULL"; int baudRate = 921600; QSerialPort::DataBits dataBits = QSerialPort::Data8; QSerialPort::StopBits stopBits = QSerialPort::OneStop; QSerialPort::Parity chẵn lẻ = QSerialPort::NoParity;
Chúng ta cần đặt các thuộc tính này khi bắt đầu cuộc gọi cổng này để nhận được thông báo cổng COM chính xác và gửi tin nhắn. Dưới đây là ví dụ về việc bắt đầu cổng nối tiếp:
serialPort = new QSerialPort(); serialPort->setPortName(param.portName); if (serialPort->open(QIODevice::ReadWrite)) { serialPort->setBaudRate(param.baudRate); serialPort->setDataBits(param.dataBits); serialPort->setStopBits(param.stopBits); serialPort->setParity(param.parity); //Không có lệnh nào được sử dụng để kết nối đến cổng kết nối connect(serialPort, &QSerialPort::readyRead, this, &CarControlModel::dataReceive); qDebug() << "connect:" << param.portName; phát ra checkConnableRetSig(true); setStartTime(); //phát ra comConnStatus(true); return true;
Nhìn thấy? Trên thực tế, nó rất đơn giản. Đối tượng này thực sự thực hiện một việc, thông báo cho bạn rằng dữ liệu hiện đang đến. Lưu ý rằng ReadyRead này không gửi cho bạn dữ liệu đã nhận mà thông báo cho bạn rằng cổng nối tiếp đã nhận được tin nhắn và rất có thể tin nhắn này không phải từng cái một mà có thể được phân đoạn theo từng phân đoạn hoặc tin nhắn có thể không đầy đủ. Đưa ra một ví dụ.
void CarControlModel::dataReceive() { if (serialPort != nullptr && serialPort->isOpen()) { QByteArray buffer = serialPort->readAll(); analysisData(buffer); } } void CarControlModel::analysisData(const QByteArray& dataArr) { // Phương thức phân tích dữ liệu qDebug() << "recv data: " << dataArr.toHex(' '); dataBuff.append(dataArr); QList dataList; for (;;) { int index = dataBuff.indexOf(QByteArray(gEnd, gEndLen)); if (index == -1) break; dataList.append(dataBuff.mid(0, index + gEndLen)); dataBuff = dataBuff.right(dataBuff.size() - index - gEndLen); } if (dataList.size() == 0) return; 。。。。
Nói cách khác, sau khi nhận được lệnh cổng nối tiếp, bạn có thể cần thực hiện phân tích để tách tin nhắn nhận được và xử lý nó.
Gửi hướng dẫn đến cổng nối tiếp:
Việc gửi hướng dẫn tương đối đơn giản, chẳng hạn như:
qint64 CarControlModel::startCentralCMD() { QByteArray allArr, funDataArr; funDataArr.clear(); funDataArr.append(0x32); funDataArr.append(0x01); //0x01 bắt đầu mã đĩa mã trung tâm(allArr, funDataArr); allArr); // Truyền vào vùng dữ liệu mã hàm, kết hợp nó thành một lệnh hoàn chỉnh và lưu trữ nó trong mảng void CarControlModel::code(QByteArray& allArr, const QByteArray& funDataArr) { allArr.clear(); ; allArr.append(gAddress, gAddressLen); allArr.append(funDataArr); crcBuf[gCrcLen]; CRC16_2(allArr, crcBuf); allArr.append((char*)crcBuf, gCrcLen); allArr.append(gEnd, gEndLen); //Cổng nối tiếp gửi tin nhắn ra qint64 CarControlModel::writeData(const QByteArray& data ) { qDebug() << "gửi dữ liệu: " << data.toHex(' '); qint64 ret = -1; if (serialPort != nullptr && serialPort->isOpen()) { ret = serialPort->write(data); { //emit sendWarningSig("Gửi không thành công: " + data.toHex(' ')); qDebug() << QString("Không thể gửi: " + data.toHex(' ')); } } else { sendWarningSig("Cổng nối tiếp chính của bộ điều khiển công nghiệp không được kết nối" } return ret;
Cuối cùng, bài viết này về [phát triển Qt] hiểu giao tiếp cổng nối tiếp trong một lần. Nếu bạn muốn biết thêm về [phát triển Qt] hiểu giao tiếp cổng nối tiếp trong một lần, vui lòng tìm kiếm các bài viết CFSDN hoặc tiếp tục duyệt các bài viết liên quan. , Tôi hy vọng bạn sẽ ủng hộ blog của tôi trong tương lai! .
Tôi là một lập trình viên xuất sắc, rất giỏi!