上位机Qt应用程序与MCU板子之间的串口数据传输算法,举例1字节、2字节、4字节正负数。再加qDebug的重定向显示打印数据。

news2024/11/27 13:40:08

串口之间的数据传输算法

  • 前言
  • 【1】Qt界面设计图
  • 【2】串口char型举例
    • 串口收发正数举例
    • 串口收发负数举例
  • 【3】串口short 型举例
      • 大端序和小端序
    • 串口收发正数举例
    • 串口收发负数举例
  • 【4】串口int型举例
    • 串口收发正数举例
    • 串口收发负数举例
    • 串口收发正负数(简洁版推荐)
  • 【5】浮点数思路
  • 【6】qDebug重定向捕捉打印


前言

在C++里,可通过以下方法获取变量的范围,防止操作中出现差错。

#include "limits"               //获取变量的范围
qDebug()<<"unsigned char 的范围(0x00-0xFF):"<<std::numeric_limits<unsigned char>::min()<<"~"<<std::numeric_limits<unsigned char>::max();

【1】Qt界面设计图

处理将char和shorft和int分别转到槽,方面模拟串口之间数据解析

在这里插入图片描述


【2】串口char型举例

无符号1字节举例

 unsigned char value;
 
 unsigned char 的范围(0x00-0xFF)0 ~ 255
 
value 十进制 =  "255"  
value 十六进制 =  "0xff"  
value 二进制 =  "11111111"

说明:unsigned char型可以存储0-255的十进制数,你可以创建字节数组存储更多的整数,从MCU串口发送给Qt上位机串口。


unsigned char sendSerialdatas[5] = {0}; //MCU

要知道目前的串口数据只能通过转为1字节字符类型进行收发,这也是最便捷的操作。

串口收发正数举例

不安全型,可读性差,实现快

MCU

    // 第一种正数为例
    unsigned char value = 255;
    unsigned char sendSerialdatas[5] = {0};
    // MCU将255这个数值传给UI怎么做,如下:
    // 写法1 数据打包
    sendSerialdatas[0] = value; //直接将一个字节,八位的数赋值给一个字节,你可以选择十进制形式或者十六进制。
    sendSerialdatas[1] = 254;
    sendSerialdatas[2] = 253;
    sendSerialdatas[3] = 2;
    sendSerialdatas[4] = 1;

	// 发送到Qt端
	sendToLinuxQt(arrayname,len);

Qt

// Qt UI直接解析MCU数据即可,因为1字节已经是串口传输的最小数据了。
// 假定读取串口数据存入字节组
QByteArray data = 获取串口所有数据;

quint8 recvSerialdatas[32] = {0};
for (int i =0; i < data.size(); i++) {
   recvSerialdatas[i] = (unsigned char)data.at(i); //在qt中最好强制转化以下,根据你上传的数据类型一一对应。
}

打印数据如下

在这里插入图片描述


安全型,可读性好,实现慢点

最大只能存储255大小,&运算
1&1=1;
1&0=0;
0&1=0;
0&0=0; 类似乘法


& 0xFF解释:就是不管value是什么数据类型,我只要最低的八位。 如赋值:1111 1111 1111 1110 ,显然是16位,那我就只要1111 1110,因为字节数组最大只能到0xFF,且0xFF就是低八位。所以不要直接将一个大于字节数组范围的值赋值给字节数组。


具体

1111 1111 1111 1110
& 
000 0000 1111 1111
——————————
0000 0000 1111 1110 --》》保留1111 1110

MCU

// 写法2  安全的操作,隐含会将十进制转化为二进制在内存存储。
sendSerialdatas[0] = value & 0xFF;   
sendSerialdatas[1] = 254 & 0xFF;
sendSerialdatas[2] = 253 & 0xFF;
sendSerialdatas[3] = 2 & 0xFF;
sendSerialdatas[4] = 1 & 0xFF;

Qt

// Qt UI直接解析MCU数据即可,因为1字节已经是串口传输的最小数据了。
// 假定读取串口数据存入字节组
QByteArray data = 获取串口所有数据;

// UI直接解析即可,因为1字节已经是串口传输的最小数据了。
quint8 recvSerialdatas1[32] = {0};
for (int i = 0; i < data1.length(); i++) {
    recvSerialdatas1[i] = (unsigned char)data1.at(i);
    ui->plainTextEdit_2->appendPlainText(QString("recvSerialdatas[%1] = %2").arg(i).arg(recvSerialdatas1[i] & 0xFF)); //解析并显示,&0xFF代表接收的数必须是1字节范围内。
}

打印数据如下

在这里插入图片描述


串口收发负数举例

在C语言中,char类型的范围是从-128到127(signed char)或0到255(unsigned char),取决于它是有符号还是无符号的。


在负数中,你应该了解,什么是原码和补码。

`
假设我们使用8位二进制表示法,以下是一个正数的原码、反码和补码的举例:
原码:00110110
反码:00110110
补码:00110110
其中原码、反码和补码都表示十进制数54。符号位是0(最高位)
`
`
假设使用8位二进制表示法,以-3为例:

原码:10000011
反码:11111100
补码:11111101

符号位1(最高位)
`

所以,你领悟到了什么。。。
判断是否是正数和负数的条件:符号位。


MCU和UI一起举例

//实现串口数据的收发 1字节数组为例
void MainWindow::on_pb_char_clicked()
{
    // MCU上传负数
    char value = -128;
    char sendSerialdatas[5] = {0}; //MCU

    // 写法2 MCU数据打包
    sendSerialdatas[0] = value;
    sendSerialdatas[1] = -127;
    sendSerialdatas[2] = 126;
    sendSerialdatas[3] = 2;
    sendSerialdatas[4] = -1;

    // MCU数据作为串口1
    for (int i = 0; i < 5; ++i) {
        ui->plainTextEdit_1->appendPlainText("转为十进制显示 = "+ QString::number(sendSerialdatas[i]));
    }

    // 假定UI读取串口数据存入字节组
    QByteArray data1;
    for (int i = 0; i < 5; ++i) {
        data1.append(sendSerialdatas[i]);
    }

    // UI直接解析即可,因为1字节已经是串口传输的最小数据了。
    char recvSerialdatas1[32] = {0};
    for (int i = 0; i < data1.length(); i++) {
        recvSerialdatas1[i] = (char)data1.at(i);
        ui->plainTextEdit_2->appendPlainText(QString("recvSerialdatas[%1] = ").arg(i)+ QString::number(recvSerialdatas1[i],10));
    }
}

运行效果

在这里插入图片描述


【3】串口short 型举例

无符号2字节举例

unsigned short 的范围(0x00-0xFFFF)0 ~ 65535
value 十进制 =  "65535"  
value 十六进制 =  "0xffff"  
value 二进制 =  "1111111111111111"

要知道字节数组只能存储1字节,那么2字节肯定分两份存储。
谁存储高八位,谁存储低八位,这就说到大端序和小端序了。

大端序和小端序

大端序和小端序是指数据在内存中存储的方式

以 0x12345678 为例,它在内存中的存储方式如下:

大端序高位字节(0x12)在前,低位字节(0x78)在后。
小端序低位字节(0x78)在前,高位字节(0x12)在后。


举个例子,假设有一个 32 位整型变量 x,存储了数值 0x12345678。
如果机器是大端序的,则 x 在内存中的存储方式如下:

+----+----+----+----+
| 12 | 34 | 56 | 78 |
+----+----+----+----+
buf[0] buf[1] buf[2] buf[3]

而如果机器是小端序的,则 x 在内存中的存储方式如下:


+----+----+----+----+
| 78 | 56 | 34 | 12 |
+----+----+----+----+
buf[0] buf[1] buf[2] buf[3]

串口收发正数举例

正数举例
方法1错误 方法2正确

// 实现串口数据的收发 2字节数组为例
void MainWindow::on_pb_shorft_clicked()
{
    ui->plainTextEdit_1->appendPlainText(QString("方法1直接将2字节十进制数存入字节数组通过串口发给UI(会有警告,只能获取2字节中的低字节的八位)"));
    // MCU上传UI正负数
    // 第一种正数为例(错误示范)
    unsigned short value;
    qDebug()<<"unsigned short 的范围(0x00-0xFFFF):"<<std::numeric_limits<unsigned short>::min()<<"~"<<std::numeric_limits<unsigned short>::max();
    value = 65535;
    qDebug()<<"value 十进制 = "<<QString::number(value,10)<<" value 十六进制 = "<<"0x"+QString::number(value,16)<<" value 二进制 = "<<QString::number(value,2);
    unsigned char sendSerialdatas[6] = {0}; //MCU
    // MCU将65535这个数值传给UI怎么做,如下:
    // 写法1 错误示范
    sendSerialdatas[0] = value;  //直接将2字节数赋值给一个字节
    sendSerialdatas[1] = 65534; //FFFE ->>1111 1111 (低八位)1111 1110 = 255
    sendSerialdatas[2] = 65533; //FFFD-> = 254
    sendSerialdatas[3] = 257;   //0001  (低八位)0000 0001 = 1
    sendSerialdatas[4] = 256;   //0001  (低八位)0000 0000  = 0

    // MCU数据作为串口1
    ui->plainTextEdit_1->appendPlainText(QString(" sendSerialdatas[0]  = %1 ->十六进制=%2->二进制=%3").arg(value).arg(QString::number(value,16)).arg(QString::number(value,2)));
    ui->plainTextEdit_1->appendPlainText(QString(" sendSerialdatas[1]  = %1 ->十六进制=%2->二进制=%3").arg(65534).arg(QString::number(65534,16)).arg(QString::number(65534,2)));
    ui->plainTextEdit_1->appendPlainText(QString(" sendSerialdatas[2]  = %1 ->十六进制=%2->二进制=%3").arg(65533).arg(QString::number(65533,16)).arg(QString::number(65533,2)));
    ui->plainTextEdit_1->appendPlainText(QString(" sendSerialdatas[3]  = %1 ->十六进制=%2->二进制=%3").arg(257).arg(QString::number(257,16)).arg(QString::number(257,2)));
    ui->plainTextEdit_1->appendPlainText(QString(" sendSerialdatas[3]  = %1 ->十六进制=%2->二进制=%3").arg(256).arg(QString::number(256,16)).arg(QString::number(256,2)));

    ui->plainTextEdit_1->appendPlainText(QString("方法2,将2字节(16位2进制)十进制数 & 0xFFFF,并且分成两部分(高位1字节和低位1字节)保存字节数组存储的是1个字节范围内"));

    // 假定读取UI串口数据存入字节组
    QByteArray data;
    for (int i = 0; i < 6; ++i) {
        data.append(sendSerialdatas[i]);
    }

    // UI直接解析错误,无法获取原数据
    ui->plainTextEdit_2->appendPlainText(QString("方法1 UI通过获取2字节数组,显示2字节的十进制在界面(数值解析错误)"));
    quint16  quint16recv = 0;
    for (int i = 0; i < data.size(); i++) {
        quint16recv = (unsigned char)data.at(i);
        ui->plainTextEdit_2->appendPlainText(QString("quint16recv[%1]= %2").arg(i).arg(quint16recv)); //解析并显示
    }
    ui->plainTextEdit_2->appendPlainText(QString("方法2,将2字节字节数组通过 移位和 &0xFFFF 还原成无符号短整型"));

//======================================================
    // 写法2  安全的
    value = 65535;
    sendSerialdatas[0] = (value>>8) & 0xFFFF;   //存储高八位(255),转为二进制后的前八位,总共16位
    sendSerialdatas[1] = value;     //存储低8位(255) 你也可以写成 (value & 0xFFFF)
    
    value = 65534;
    sendSerialdatas[2] = (value>>8) & 0xFFFF;
    sendSerialdatas[3] = value;
    
    value = 256;
    sendSerialdatas[4] = (value>>8) & 0xFFFF;
    sendSerialdatas[5] = value;

    // MCU数据作为串口1
    ui->plainTextEdit_1->appendPlainText(QString("sendSerialdatas[0] = (65535>>8) & 0xFFFF"));
    ui->plainTextEdit_1->appendPlainText(QString("sendSerialdatas[1] = 65535;"));
    ui->plainTextEdit_1->appendPlainText(QString("sendSerialdatas[2] = (65534>>8) & 0xFFFF;"));
    ui->plainTextEdit_1->appendPlainText(QString("sendSerialdatas[3] = 65534"));
    ui->plainTextEdit_1->appendPlainText(QString("sendSerialdatas[4] = (256>>8) & 0xFFFF;"));
    ui->plainTextEdit_1->appendPlainText(QString("sendSerialdatas[5] = 256;"));


    // 假定读取UI串口数据存入字节组
    QByteArray data1;
    for (int i = 0; i < 6; ++i) {
        data1.append(sendSerialdatas[i]);
    }

    // UI直接解析即可,1字节数数组无法存储2字节数字,需要分成两部分。( (recvSerialdatas1[i]<<8) | recvSerialdatas1[i]) & 0xFFFF)
    //255<<8 = 1111 1111 0000 0000 | 1111 1111 = 1111 1111 1111 1111 = 65535
    quint8 recvSerialdatas1[32] = {0};
    for (int i = 0; i < data1.length(); i++) {
        recvSerialdatas1[i] = (unsigned char)data1.at(i);
    }
    
    // UI还原MCU数据
    quint16 UIshorft = 0;
    UIshorft = ((sendSerialdatas[0]<<8) | sendSerialdatas[1]) &0xFFFF;
    ui->plainTextEdit_2->appendPlainText(QString("UIshorft = ((sendSerialdatas[0]<<8) | sendSerialdatas[1]) &0xFFFF = %2").arg(UIshorft));
    UIshorft = ((sendSerialdatas[2]<<8) | sendSerialdatas[3]) &0xFFFF;
    ui->plainTextEdit_2->appendPlainText(QString("UIshorft = ((sendSerialdatas[2]<<8) | sendSerialdatas[3]) &0xFFFF = %2").arg(UIshorft));
    UIshorft = ((sendSerialdatas[4]<<8) | sendSerialdatas[5]) &0xFFFF;
    ui->plainTextEdit_2->appendPlainText(QString("UIshorft = ((sendSerialdatas[4]<<8) | sendSerialdatas[5]) &0xFFFF = %2").arg(UIshorft));
}

运行效果
方法1

在这里插入图片描述

方法2

在这里插入图片描述


串口收发负数举例

在C语言中,可以使用如下方式将一个十六位的short类型值存储到两个unsigned char变量中,同时区分正负数:

short value =  -32768;
unsigned char highByte = (unsigned char)(value >> 8); //80 ->1000 0000
unsigned char lowByte = (unsigned char)value; //0 ->0000 0000 
if (value < 0) {
    highByte |= 0x80; // 如果值为负,则设置符号位   
}

在这里插入图片描述

这里使用了移位运算符 >> 把高位和低位分别赋值给两个unsigned char类型的变量。如果原来的short类型值是负数,则通过将高字节的最高位(即符号位)设为1来标识它是个负数。

要从两个unsigned char变量中解析出一个short类型的值,可以使用如下代码:

unsigned char highByte =  80;
unsigned char lowByte = 0;
short value = ((short)highByte << 8) | lowByte; //
if (highByte & 0x80) {
    value = -(short)((~value) + 1); // 如果设置了符号位,则将其恢复
}

在这里插入图片描述
符号位是1负数
~value

在这里插入图片描述

-(short)((~value) + 1)

这里使用了移位运算符 << 将高字节左移8位,并使用按位或运算符|将低字节合并到一起。如果高字节的最高位被设置为1,则表示这是个负数,因此需要恢复符号位并转换为相应的负数值。


//实现串口数据的收发 2字节数组为例
void MainWindow::on_pb_shorft_clicked()
{
    //MCU上传UI负数
    //第一种负数为例
    short value;
    qDebug()<<"short 的范围(0x00-0xFFFF):"<<std::numeric_limits<short>::min()<<"~"<<std::numeric_limits<short>::max();
    value = -32768;
    qDebug()<<"value = -32768; \n十进制 = "<<QString::number(value,10)<<"\n十六进制 = "<<"0x"+QString::number(value,16)<<"\n二进制 = "<<QString::number(value,2);
    value = 32767;
    qDebug()<<" value = 32767; \n十进制 = "<<QString::number(value,10)<<"\n十六进制 = "<<"0x"+QString::number(value,16)<<"\n二进制 = "<<QString::number(value,2);
    unsigned char sendSerialdatas[6] = {0}; //MCU
    ui->plainTextEdit_1->appendPlainText(QString("方法2,将2字节(16位2进制)十进制数 ,并且分成两部分(高位1字节和低位1字节)保存字节数组存储的是1个字节范围内"));

    ui->plainTextEdit_2->appendPlainText(QString("方法2,将2字节字节数组还原成有符号短整型"));
    //写法2  安全的操作,隐含会将十进制转化为二进制在内存存储。
    value = -32768;
    sendSerialdatas[0] = ((short)value>>8) ;
    sendSerialdatas[1] = (short)value;
    value = 32767;
    sendSerialdatas[2] = ((short)value>>8);
    sendSerialdatas[3] = (short)value;
    value = -1;
    sendSerialdatas[4] = ((short)value>>8);
    sendSerialdatas[5] = (short)value;

     ui->plainTextEdit_1->appendPlainText(""
                                          "short value = -32768;\n"
                                          "sendSerialdatas[0] = (value>>8) ;\n"
                                         "sendSerialdatas[1] = value;\n"
                                          "value = 32767;\n"
                                          "sendSerialdatas[2] = (value>>8);\n"
                                          "sendSerialdatas[3] = value;\n"
                                          "value = -1;\n"
                                          "sendSerialdatas[4] = (value>>8);\n"
                                          "sendSerialdatas[5] = value;\n"
                                                  );
    //MCU数据作为串口1
    ui->plainTextEdit_1->appendPlainText(" sendSerialdatas[0] = " + QString::number(sendSerialdatas[0],16));
    ui->plainTextEdit_1->appendPlainText(" sendSerialdatas[1] = " + QString::number(sendSerialdatas[1],16));
    ui->plainTextEdit_1->appendPlainText(" sendSerialdatas[2] = " + QString::number(sendSerialdatas[2],16));
    ui->plainTextEdit_1->appendPlainText(" sendSerialdatas[3] = "+ QString::number(sendSerialdatas[3],16));
    ui->plainTextEdit_1->appendPlainText(" sendSerialdatas[4] = " + QString::number(sendSerialdatas[4],16));
    ui->plainTextEdit_1->appendPlainText(" sendSerialdatas[5] = "+ QString::number(sendSerialdatas[5],16));


    //假定读取串口数据存入字节组
    QByteArray data1;
    for (int i = 0; i < 6; ++i) {
        data1.append(sendSerialdatas[i]);
    }

    quint8 recvSerialdatas1[32] = {0};
    for (int i = 0; i < data1.length(); i++) {
        recvSerialdatas1[i] = (unsigned char)data1.at(i);
    }

    short fvalue = ((short)sendSerialdatas[0] << 8) | sendSerialdatas[1];
    qDebug ()<< " fvalue = "<<fvalue;
    if ( (sendSerialdatas[0] & 0x80) == 1) { //负数
        fvalue = -(short)((~fvalue) + 1);
    }
    ui->plainTextEdit_2->appendPlainText("-(short)((~fvalue) + 1); = "+ QString::number(fvalue,'f',0));

    //这里不用经过上面这步也可以解析成功
    qint16 UI = 0;
    UI = (((short)sendSerialdatas[0]<<8) | sendSerialdatas[1]) &0xFFFF;
    ui->plainTextEdit_2->appendPlainText(QString("UI = ((sendSerialdatas[0]<<8) | sendSerialdatas[1]) &0xFFFF = %2").arg(UI));
    UI = (((short)sendSerialdatas[2]<<8) | sendSerialdatas[3]) &0xFFFF;
    ui->plainTextEdit_2->appendPlainText(QString("UI = ((sendSerialdatas[2]<<8) | sendSerialdatas[3]) &0xFFFF = %2").arg(UI));
    UI = (((short)sendSerialdatas[4]<<8) | sendSerialdatas[5]) &0xFFFF;
    ui->plainTextEdit_2->appendPlainText(QString("UI = ((sendSerialdatas[4]<<8) | sendSerialdatas[5]) &0xFFFF = %2").arg(UI));
}

效果
在这里插入图片描述
在这里插入图片描述


【4】串口int型举例

串口收发正数举例

//实现串口数据的收发 4字节数组为例
void MainWindow::on_pb_int_clicked()
{
    ui->plainTextEdit_1->appendPlainText(QString("方法1直接将4字节十进制数存入字节数组通过串口发给UI(会有警告,只能获取4字节中的最低字节的八位)"));
    //MCU上传UI正负数
    //第一种正数为例
   unsigned int value;
    value = 4294967295;
    qDebug()<<"unsigned int value 的范围(0x00-0xFFFFFFFF):"<<std::numeric_limits<unsigned int>::min()<<"~"<<std::numeric_limits<unsigned int>::max();
    qDebug()<<"value 十进制 = "<<QString::number(value)<<" value 十六进制 = "<<"0x"+QString::number(value,16)<<" value 二进制 = "<<QString::number(value,2);
    unsigned char sendSerialdatas[32] = {0}; //MCU
    //MCU将65535这个数值传给UI怎么做,如下:
    //写法1
    sendSerialdatas[0] = value;            //直接将4字节数赋值给一个字节,11111111111111111111111111111111
    sendSerialdatas[1] = 4294967294; //FFFE ->>1111 1111 (低八位)1111 1111 = 255
    sendSerialdatas[2] = 4294967293; //FFFD-> = 254
    sendSerialdatas[3] = 65537;    //0001  (低八位)0000 0001 = 1
    sendSerialdatas[4] = 65536;     //0001  (低八位)0000 0000  = 0

    //MCU数据作为串口1
    ui->plainTextEdit_1->appendPlainText(QString(" sendSerialdatas[0]  = %1 ->十六进制=%2->二进制=%3").arg(value).arg(QString::number(value,16)).arg(QString::number(value,2)));
    ui->plainTextEdit_1->appendPlainText(QString(" sendSerialdatas[1]  = %1 ->十六进制=%2->二进制=%3").arg(4294967294).arg(QString::number(4294967294,16)).arg(QString::number(4294967294,2)));
    ui->plainTextEdit_1->appendPlainText(QString(" sendSerialdatas[2]  = %1 ->十六进制=%2->二进制=%3").arg(4294967293).arg(QString::number(4294967293,16)).arg(QString::number(4294967293,2)));
    ui->plainTextEdit_1->appendPlainText(QString(" sendSerialdatas[3]  = %1 ->十六进制=%2->二进制=%3").arg(65537).arg(QString::number(65537,16)).arg(QString::number(65537,2)));
    ui->plainTextEdit_1->appendPlainText(QString(" sendSerialdatas[3]  = %1 ->十六进制=%2->二进制=%3").arg(65536).arg(QString::number(65536,16)).arg(QString::number(65536,2)));

    ui->plainTextEdit_1->appendPlainText(QString("方法2,将4字节(32位2进制)十进制数 & 0xFFFFFFFF,并且分成两部分(高位1字节和低位1字节)保存字节数组存储的是1个字节范围内"));

    //假定读取串口数据存入字节组
    QByteArray data;
    for (int i = 0; i < 6; ++i) {
        data.append(sendSerialdatas[i]);
    }

    ui->plainTextEdit_2->appendPlainText(QString("方法1 UI通过获取4字节数组,显示4字节的十进制在界面(数值解析错误)"));
    quint16  quint16recv = 0;
    for (int i = 0; i < data.size(); i++) {
        quint16recv = (unsigned char)data.at(i);
        ui->plainTextEdit_2->appendPlainText(QString("quint16recv[%1]= %2").arg(i).arg(quint16recv)); //解析并显示
    }
    ui->plainTextEdit_2->appendPlainText(QString("方法2,将4字节字节数组通过 移位和 &0xFFFFFFFF 还原成无符号短整型"));


    //写法2  安全的操作,隐含会将十进制转化为二进制在内存存储。
    value = 4294967295;
    sendSerialdatas[0] = (value>>24) & 0xFFFFFFFF;       //存储32位中的最高八位
    sendSerialdatas[1] = (value>>16) & 0xFFFFFFFF;       //存储32位中的中高八位
    sendSerialdatas[2] = (value>>8) & 0xFFFFFFFF;         //存储32位中的中低八位
    sendSerialdatas[3] = value & 0xFFFFFFFF;                 //存储32位中的最低八位
    value = 4294967294;
    sendSerialdatas[4] = (value>>24) & 0xFFFFFFFF;       //存储32位中的最高八位
    sendSerialdatas[5] = (value>>16) & 0xFFFFFFFF;          //存储32位中的中高八位
    sendSerialdatas[6] = (value>>8) & 0xFFFFFFFF;             //存储32位中的中低八位
    sendSerialdatas[7] = value & 0xFFFFFFFF;                           //存储32位中的最低八位
    value = 65536;
    sendSerialdatas[8] = (value>>24) & 0xFFFFFFFF;       //存储32位中的最高八位
    sendSerialdatas[9] = (value>>16) & 0xFFFFFFFF;          //存储32位中的中高八位
    sendSerialdatas[10] = (value>>8) & 0xFFFFFFFF;            //存储32位中的中低八位
    sendSerialdatas[11] = value & 0xFFFFFFFF;                          //存储32位中的最低八位

    //MCU数据作为串口1
    ui->plainTextEdit_1->appendPlainText(QString("value = 4294967295;\r\n"
                                                "sendSerialdatas[0] = (value>>24) & 0xFFFFFFFF;\r\n"
                                                "sendSerialdatas[1] = (value>>16) & 0xFFFFFFFF;\r\n"
                                                "sendSerialdatas[2] = (value>>8) & 0xFFFFFFFF;\r\n"
                                                "sendSerialdatas[3] = value & 0xFFFFFFFF;"));
    ui->plainTextEdit_1->appendPlainText(QString("value = 4294967294;\r\n"
                                                "sendSerialdatas[0] = (value>>24) & 0xFFFFFFFF;\r\n"
                                                "sendSerialdatas[1] = (value>>16) & 0xFFFFFFFF;\r\n"
                                                "sendSerialdatas[2] = (value>>8) & 0xFFFFFFFF;\r\n"
                                                "sendSerialdatas[3] = value & 0xFFFFFFFF;"));
    ui->plainTextEdit_1->appendPlainText(QString("value = 65536;\r\n"
                                                "sendSerialdatas[0] = (value>>24) & 0xFFFFFFFF;\r\n"
                                                "sendSerialdatas[1] = (value>>16) & 0xFFFFFFFF;\r\n"
                                                "sendSerialdatas[2] = (value>>8) & 0xFFFFFFFF;\r\n"
                                                "sendSerialdatas[3] = value & 0xFFFFFFFF;"));


    //假定读取串口数据存入字节组
    QByteArray data1;
    for (int i = 0; i < 6; ++i) {
        data1.append(sendSerialdatas[i]);
    }

    quint8 recvSerialdatas1[32] = {0};
    for (int i = 0; i < data1.length(); i++) {
        recvSerialdatas1[i] = (unsigned char)data1.at(i);
    }

    quint32 UIint = 0;
    UIint = ((sendSerialdatas[0]<<24) | sendSerialdatas[1]<<16 | sendSerialdatas[2]<<8 | sendSerialdatas[3]) & 0xFFFFFFFF;
    ui->plainTextEdit_2->appendPlainText(QString("UIint = ((sendSerialdatas[0]<<24) | sendSerialdatas[1]<<16 | sendSerialdatas[2]<<8 | sendSerialdatas[3]) &0xFFFFFFFF = %2").arg(UIint));
    UIint = ((sendSerialdatas[4]<<24) | sendSerialdatas[5]<<16 | sendSerialdatas[6]<<8 | sendSerialdatas[7]) & 0xFFFFFFFF;
    ui->plainTextEdit_2->appendPlainText(QString("UIint = ((sendSerialdatas[4]<<24) | sendSerialdatas[5]<<16 | sendSerialdatas[6]<<8 | sendSerialdatas[7]) & 0xFFFFFFFF = %2").arg(UIint));
    UIint = ((sendSerialdatas[8]<<24) | sendSerialdatas[9]<<16 | sendSerialdatas[10]<<8 | sendSerialdatas[11]) & 0xFFFFFFFF;
    ui->plainTextEdit_2->appendPlainText(QString("UIint = ((sendSerialdatas[8]<<24) | sendSerialdatas[9]<<16 | sendSerialdatas[10]<<8 | sendSerialdatas[11]) & 0xFFFFFFFF = %2").arg(UIint));
}

效果
在这里插入图片描述
在这里插入图片描述


串口收发负数举例

C语言使用unsigned char存储32位int类型的方法:

// 假设要存储的32位int为num
unsigned char bytes[4]; // 数组长度为4,每个元素是一个字节

bytes[0] = (num >> 24) & 0xFF; // 取出num的高8位存入bytes的第1个元素
bytes[1] = (num >> 16) & 0xFF; // 取出num的次高8位存入bytes的第2个元素
bytes[2] = (num >> 8) & 0xFF;  // 取出num的次低8位存入bytes的第3个元素
bytes[3] = num & 0xFF; 		   // 取出num的低8位存入bytes的第4个元素

以上代码将一个32位的int数按照大端序(高位字节在前,低位字节在后)存储到了一个unsigned char数组中。

解析正负数的方法如下:

// 假设已经从unsigned char数组中读取了4个字节存入bytes数组
int num;

if (bytes[0] & 0x80) { // 如果最高位为1,则表示这是一个负数
    num = ((int)bytes[0] << 24) | ((int)bytes[1] << 16) | ((int)bytes[2] << 8) | (int)bytes[3];
} else {
    num = ((int)(bytes[0] & 0x7F) << 24) | ((int)bytes[1] << 16) | ((int)bytes[2] << 8) | (int)bytes[3];
}

以上代码假设最高位为符号位,如果最高位为1,则表示这是一个负数,需要将bytes数组的所有元素取反后加1才能得到真正的值。

举例源码:

#include <stdio.h>

void print_bytes(unsigned char *bytes, int len) {
    for (int i = 0; i < len; i++) {
        printf("%02X ", bytes[i]);
    }
    printf("\n");
}

int main() {
    int num = -123456789;
    unsigned char bytes[4];

    bytes[0] = (num >> 24) & 0xFF;
    bytes[1] = (num >> 16) & 0xFF;
    bytes[2] = (num >> 8) & 0xFF;
    bytes[3] = num & 0xFF;

    printf("原始数值: %d\n", num);
    printf("字节序列: ");
    print_bytes(bytes, 4);

    if (bytes[0] & 0x80) {
        num = ((int)bytes[0] << 24) | ((int)bytes[1] << 16) | ((int)bytes[2] << 8) | (int)bytes[3];
    } else {
        num = ((int)(bytes[0] & 0x7F) << 24) | ((int)bytes[1] << 16) | ((int)bytes[2] << 8) | (int)bytes[3];
    }

    printf("解析结果: %d\n", num);

    return 0;
}

串口收发正负数(简洁版推荐)

1字节、2字节、4字节皆可使用

//实现串口数据的收发 4字节数组为例
void MainWindow::on_pb_int_clicked()
{
    qDebug()<<"int 的范围(0x00-0xFFFF):"<<std::numeric_limits<int>::min()<<"~"<<std::numeric_limits<int>::max();
    int value = -2147483648;
    qDebug()<<"value = -2147483648; \n十进制 = "<<QString::number(value,10)<<"\n十六进制 = "<<"0x"+QString::number(value,16)<<"\n二进制 = "<<QString::number(value,2);
    value = 2147483647;
    qDebug()<<" value = 2147483647; \n十进制 = "<<QString::number(value,10)<<"\n十六进制 = "<<"0x"+QString::number(value,16)<<"\n二进制 = "<<QString::number(value,2);

    int num = -2147483648;
    unsigned char bytes[8];

    //MCU
    bytes[0] = (num >> 24) & 0xFF;
    bytes[1] = (num >> 16) & 0xFF;
    bytes[2] = (num >> 8) & 0xFF;
    bytes[3] = num & 0xFF;

    value = 2147483647;
    bytes[4] = (value >> 24) & 0xFF;
    bytes[5] = (value >> 16) & 0xFF;
    bytes[6] = (value >> 8) & 0xFF;
    bytes[7] = value & 0xFF;

    qDebug("\n\n原始数值: %d %d\n", num,value);
    qDebug("字节序列: ");

    for (int i = 0; i < 8; i++) {
            qDebug("%02X ", bytes[i]);
        }

    //UI
    int v1,v2;
    if ( bytes[0] & 0x80 ) {  // 如果最高位为1,则表示这是一个负数
        v1 = ((int)bytes[0] << 24) | ((int)bytes[1] << 16) | ((int)bytes[2] << 8) | (int)bytes[3];
        qDebug()<<"负数";
    } else { //正数
        v1 = ((int)(bytes[0] & 0x7F) << 24) | ((int)bytes[1] << 16) | ((int)bytes[2] << 8) | (int)bytes[3];
        qDebug()<<"正数";
    }
    qDebug()<<"=======================================";
    if ( bytes[4] & 0x80 ) {  // 如果最高位为1,则表示这是一个负数
        v2 = ((int)bytes[4] << 24) | ((int)bytes[5] << 16) | ((int)bytes[6] << 8) | (int)bytes[7];
        qDebug()<<"负数";
    } else { //正数
        v2 = ((int)(bytes[4] & 0x7F) << 24) | ((int)bytes[5] << 16) | ((int)bytes[7] << 8) | (int)bytes[7];
        qDebug()<<"正数";
    }

    qDebug("解析结果: v1 = %d v2 = %d\n", v1,v2);
}

运行结果
-2147483648
在这里插入图片描述
2147483647
在这里插入图片描述

int 的范围(0x00-0xFFFF): -2147483648 ~ 2147483647

原始数值: -2147483648 		2147483647

字节序列: 
80 
00 
00 
00 

7F 
FF 
FF 
FF 
负数
=======================================
正数
解析结果: v1 = -2147483648 v2 = 2147483647

【5】浮点数思路

转化为整数在转化为浮点数即可
未验证,没有效果展示

可以使用联合体将32位的float类型强制转换为unsigned char数组来存储。具体实现可以使用移位运算符将每个字节读取出来,并根据符号位判断正负数。

以下是一个示例源码:

union float_converter {
    float f;
    unsigned char bytes[4];
};

void store_float(float f, unsigned char* buffer) {
    union float_converter converter;
    converter.f = f;

    int sign_bit = (converter.bytes[3] >> 7) & 0x01;
    if (sign_bit == 1) {
        // 负数
        for (int i = 0; i < 4; i++) {
            buffer[i] = ~converter.bytes[i];
        }
    } else {
        // 正数
        for (int i = 0; i < 4; i++) {
            buffer[i] = converter.bytes[i];
        }
    }
}

float parse_float(unsigned char* buffer) {
    union float_converter converter;

    int sign_bit = (buffer[3] >> 7) & 0x01;
    if (sign_bit == 1) {
        // 负数
        for (int i = 0; i < 4; i++) {
            converter.bytes[i] = ~buffer[i];
        }
        converter.bytes[3] += 1;
    } else {
        // 正数
        for (int i = 0; i < 4; i++) {
            converter.bytes[i] = buffer[i];
        }
    }

    return converter.f;
}
使用示例:

float f = -123.45f;
unsigned char buffer[4];
store_float(f, buffer);
float parsed_f = parse_float(buffer);

【6】qDebug重定向捕捉打印

 //安装消息器第一步
    qInstallMessageHandler(myMessageOutput);
class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();
    //消息静态函数
    static void myMessageOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg);

private slots:
    void on_pb_char_clicked();
    void on_pb_shorft_clicked();
    void on_pb_int_clicked();
private:
    Ui::MainWindow *ui;
};

实现,在代码里时使用qDebug的地方会在msg里


void MainWindow::myMessageOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg)
  {
        Q_UNUSED(context);
        // 获取对主窗口的引用
       QMainWindow *mainWindow = qobject_cast<QMainWindow *>(qApp->topLevelWidgets().at(0));

       // 获取对文本编辑器的引用
       QPlainTextEdit *plainTextEdit = mainWindow->findChild<QPlainTextEdit *>("plainTextEdit_3");

       // 将该消息附加到文本编辑器中
       plainTextEdit->appendPlainText(msg);

      QByteArray localMsg = msg.toLocal8Bit();
      switch (type) {
      case QtDebugMsg:

          break;
      case QtInfoMsg:
          break;
      case QtWarningMsg:
          break;
      case QtCriticalMsg:
          break;
      case QtFatalMsg:
          break;
      }
  }

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/629047.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

大数据分析的Python实战指南:数据处理、可视化与机器学习【上进小菜猪大数据】

上进小菜猪&#xff0c;沈工大软件工程专业&#xff0c;爱好敲代码&#xff0c;持续输出干货。 引言&#xff1a; 大数据分析是当今互联网时代的核心技术之一。通过有效地处理和分析大量的数据&#xff0c;企业可以从中获得有价值的洞察&#xff0c;以做出更明智的决策。本文将…

Mujoco210 Ubuntu 22.04配置安装(一)

目录 .1 下载 1.1 解压 1.2 许可问题 1.3 环境配置 1.4 测试mujoco .2 安装mujoco-py 2.1 conda激活虚拟环境\或新创建一个环境 2.2 下载mujoco-py ​编辑 2.3 配置环境变量 2.4 测试mujoco-py 2.5 测试时的一些报错处理 2.5.0 command /usr/bin/gcc failed with…

Linux操作系统——第三章 基础IO

目录 接口介绍 open 文件描述符fd 0 & 1 & 2 文件描述符的分配规则 重定向 FILE 理解文件系统 inode ​编辑 理解硬链接 软链接 动态库和静态库 静态库与动态库 生成静态库 库搜索路径 生成动态库 使用动态库 运行动态库 使用外部库 接口介绍 o…

(顶刊复现)配电网两阶段鲁棒故障恢复(matlab实现)

参考文献&#xff1a; X. Chen, W. Wu and B. Zhang, "Robust Restoration Method for Active Distribution Networks," in IEEE Transactions on Power Systems, vol. 31, no. 5, pp. 4005-4015, Sept. 2016, doi: 10.1109/TPWRS.2015.2503426. 1.研究背景 1.1摘…

2023 Navicat for Redis 与 Navicat Premium 16.2 现已正式发布 | 释放 Redis 全部潜能

&#x1f337;&#x1f341; 博主 libin9iOak带您 Go to New World.✨&#x1f341; &#x1f984; 个人主页——libin9iOak的博客&#x1f390; &#x1f433; 《面试题大全》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33…

【C++】 Lambda表达式详解

▒ 目录 ▒ &#x1f6eb; 问题描述环境 1️⃣ 什么是Lambda表达式Lambda 表达式的各个部分 2️⃣ 优缺点优点缺点 3️⃣ 使用场景在线C工具STL算法库STL容器中需要传递比较函数&#xff08;示例失败了&#xff09;多线程示例 4️⃣ Lambda表达式与函数指针的比较5️⃣ 捕获列表…

KISS复盘法

KISS复盘法 KISS复盘法是一种科学的项目复盘方法&#xff0c;能够把过往经验转化为实践能力&#xff0c;以促进下一次活动更好地展开&#xff0c;从而不断提升个人和团队的能力&#xff01; 模型介绍 【复盘】原是围棋术语&#xff0c;本意是对弈者在下完一盘棋之后&#xff0…

距离保护原理

距离保护是反映故障点至保护安装处的距离&#xff0c;并根据距离的远近确定动作时间的一种保护。故障点距保护安装处越近&#xff0c;保护的动作时间就越短&#xff0c;反之就越长&#xff0c;从而保证动作的选择性。测量故障点至保护安装处的距离&#xff0c;实际上就是用阻抗…

Spring Boot banner详解

Spring Boot 3.x系列文章 Spring Boot 2.7.8 中文参考指南(一)Spring Boot 2.7.8 中文参考指南(二)-WebSpring Boot 源码阅读初始化环境搭建Spring Boot 框架整体启动流程详解Spring Boot 系统初始化器详解Spring Boot 监听器详解Spring Boot banner详解 自定义banner Spring …

快速排序算法的编码和优化

快速排序的基本思路是&#xff1a; 先通过第一趟排序&#xff0c;将数组原地划分为两部分&#xff0c;其中一部分的所有数据都小于另一部分的所有数据。原数组被划分为2份通过递归的处理&#xff0c; 再对原数组分割的两部分分别划分为两部分&#xff0c;同样是使得其中一部分…

springboot+java高校教材征订管理系统

教材管理系统从功能、数据流程、可行性、运行环境等方面进行需求分析。对教材管理系统的数据库、功能进行了详细设计。分析了主要界面设计和相关组件设计&#xff0c;对教材管理系统的具体实现进行了介绍。 采用Java技术&#xff0c;从数据库中获取数据、向数据库中写入数据&am…

Linux 4.10 将带来深远影响的三项小改变

Linux的演进永不停歇。Linus Torvalds一直在努力工作&#xff0c;希望能够在新的内核版本当中(4.11)融入更多变化。不过在目前的Linux 4.10中&#xff0c;我们同样发现了三组能够有效提升性能并实现多种前所未有功能集的变更。 Linux的演进永不停歇。Linus Torvalds一直在努力…

如何快速写出一个完整的测试用例

一、前言 测试工作中最为基础核心的内容就是设计测试用例&#xff0c;我们一般会认为数量越少、发现缺陷越多的用例就是好的用例。那么&#xff0c;怎样才能设计出好的测试用例呢&#xff1f;本次专题就向大家介绍如何编写一个完整且靠谱的测试用例。 二、测试用例的重要性 …

使用IPSW文件将iOS系统从Beta恢复到稳定正式版教程

起因 作为一名iOS开发者&#xff0c;为了拥抱新系统&#xff08;手贱&#xff09;&#xff0c;将开发机升级到了最新的iOS 17 Beta版本&#xff0c;从而导致使用现有的Xcode无法成功配对该版本系统。故准备想方设法回滚到原先的iOS 16.5稳定版 回滚方式 若要将iOS设备回退至…

【MySQL高级篇笔记-数据库其它调优策略(中) 】

此笔记为尚硅谷MySQL高级篇部分内容 目录 一、数据库调优的措施 1、调优的目标 2、如何定位调优问题 3、调优的维度和步骤 二、优化MySQL服务器 1、优化服务器硬件 2、优化MySQL的参数 三、优化数据库结构 1、拆分表&#xff1a;冷热数据分离 2、增加中间表 3、增加…

CKA 01_docker部署Kubernetes 部署docker 使用kubeadm引导集群 安装Pod网络

文章目录 1. 虚拟机步骤2. Docker 部署 Kubernetes2.1 部署 docker2.1.1 环境要求2.1.2 安装 docker 引擎2.1.3 worker 节点对 master 节点免密2.1.4 设定 docker 开机自启2.1.5 打开桥接&#xff0c;查看桥接流量2.1.6 设定 systemd 方式管理 cgroup2.1.7 docker部署完成2.1.8…

光伏储能直流系统MATLAB仿真(PV光伏阵列+Boost DCDC变换器+负载+双向DCDC变换器+锂离子电池系统)

PV光伏阵列Boost DCDC变换器负载双向DCDC变换器锂离子电池系统 资源地址&#xff1a; 光伏储能直流系统MATLAB仿真&#xff08;PV光伏阵列BoostDCDC变换器负载双向DCDC变换器锂离子电池系统&#xff09;-Matlab文档类资源-CSDN文库 主要模块&#xff1a; PV光伏阵列、Boost…

这 3个Python 函数你知道吗?

动动发财的小手&#xff0c;点个赞吧&#xff01; 作为21世纪最流行的语言之一&#xff0c;Python当然有很多有趣的功能值得深入探索和研究。今天将介绍其中的三个&#xff0c;每个都从理论上和通过实际示例进行介绍。 我想要介绍这些函数的主要原因是它们可以帮助您避免编写循…

CKA 09_Kubernetes工作负载与调度 资源调度 三类QoS request 资源需求 limit 资源限额

文章目录 1. 资源调度1.1 准备工作1.2 为什么需要 request 和 limit1.3 内存限制1.3.1 Brustable1.3.2 Guaranteed1.3.3 BestEffort1.3.4 当容器申请的资源超出 limit 和 request 1.4 CPU限制 1. 资源调度 1.1 准备工作 Kubernetes 采用 request 和 limit 两种限制类型来对资源…

中国电子学会2023年05月份青少年软件编程Python等级考试试卷一级真题(含答案)

2023-05 Python一级真题 分数&#xff1a;100 题数&#xff1a;37 测试时长&#xff1a;60min 一、单选题(共25题&#xff0c;共50分) 1. 可以对Python代码进行多行注释的是&#xff1f;&#xff08;C &#xff09; A.# B." " C. D. 2. 下列可以作为Py…