项目最后优化(十五)

目录

一. wifi连接太慢

二. 开机 source qt-path和qt应用程序

方法 1:修改 /etc/rc.local

三. 数据库中设置ID为主键和索引

四. 芯片的唯一序列号作为ID

五. 添加自动和手动控制

四. 触摸选择光标

五. status判断

六. 两边到底怎么决定通信的

七. Linux 系统裁剪

7.1 boot0 裁剪

7.2 uboot 裁剪

7.3 内核裁剪

八. 降低D1-H启动时间

8.1 boot0 启动优化:

8.2 uboot 启动优化

8.2.1 完全去掉 uboot

8.2.2 避免 burnkey 的影响

8.2.3 提高 CPU 以及 flash 读取频率

8.2.4 关闭串口输出

8.2.5 修改 kernel 加载位置

8.2.6 修改 kernel 加载大小

8.2.7 关闭 kernel 校验

8.3 kernel 启动优化

8.3.1 initcall 优化

8.4 rootfs 启动优化


一. wifi连接太慢

void Serial::wifi_Connect(int *flag,const QString  &command)
{
    // 创建 QProcess 对象
        QProcess *process = new QProcess(this); // 'this' 是 'Serial' 类的实例,继承自 QObject

        // 设置进程的可执行文件
       // process->setProgram(command);

        // 启动进程
        process->start(command);

        // 等待进程启动
        if (!process->waitForStarted()) {
            qWarning() << "Failed to start process";
            process->deleteLater(); // 使用 deleteLater 释放内存
            return;
        }

        // 等待进程完成
        if (!process->waitForFinished()) {
            qWarning() << "Process did not finish correctly";
            process->deleteLater(); // 使用 deleteLater 释放内存
            return;
        }

        // 获取输出结果
        QString output = process->readAllStandardOutput();
        QString error = process->readAllStandardError();

        // 打印输出和错误信息
        qDebug() << "Process finished with exit code:" << process->exitCode();
        qDebug() << "Exit status:" << process->exitStatus();
        qDebug() << "Output:" << output;
        qDebug() << "Error:" << error;

        if (output.isEmpty()) {
            *flag = 0; // 标记输出为空,失败
        } else {
            *flag = 1; // 标记成功
        }

        // 释放内存
        process->deleteLater(); // 使用 deleteLater 释放内存
}执行太慢了,怎么办

wifi_Connect()函数执行太慢,可能是由于QProcess::waitForStarted()QProcess::waitForFinished() 阻塞了 UI 线程,导致程序等待进程执行完成,为了解决这个问题,可以尝试异步方式运行外部进程,而不是同步等待进程完成。这可以避免UI卡顿,并提升程序响应速度

你可以利用 QProcess 的信号和槽机制来处理进程完成后的操作,而不是在主线程中等待它完成

void Serial::wifi_Connect(int *flag_t, const QString &command) {
//    if (flag == nullptr) {
//        qCritical() << "[Error] Flag pointer is null! Cannot proceed with WiFi connection.";
//        return;
//    }

    *flag_t = this->flag;
      qDebug() << "this->flag;" << this->flag;
    // 如果进程还在运行,等待其结束
    if (process && process->state() != QProcess::NotRunning) {
        qWarning() << "[Warning] Previous process is still running! Waiting for it to finish. Command: " << command;
        process->waitForFinished();  // 等待之前的进程结束
    }

    // 创建一个新的 QProcess 对象
    process = QSharedPointer<QProcess>::create(this);
    qDebug() << "[Info] Created a new QProcess for command: " << command;
    // 启动外部命令
    process->start(command);

    // 检查进程是否成功启动
    if (!process->waitForStarted()) {
        qCritical() << "[Error] Failed to start process for command: " << command;
      //  *flag = 0;
        return; // 退出函数,防止进一步操作
    } else {
        qDebug() << "[Info] Process started successfully. Command: " << command;
    }

    // 连接信号和槽
    connect(process.data(), SIGNAL(readyReadStandardOutput()), this, SLOT(handleStandardOutput()));
    connect(process.data(), SIGNAL(readyReadStandardError()), this, SLOT(handleStandardError()));
   // connect(process.data(), SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(onProcessFinished(int, QProcess::ExitStatus)));

    // 等待进程完成
    if (!process->waitForFinished()) {
        qWarning() << "Process did not finish correctly";
        process->deleteLater(); // 使用 deleteLater 释放内存
        return;
    }

}

void Serial::onProcessFinished(int exitCode, QProcess::ExitStatus exitStatus) {
    qDebug() << "[Info] Process finished. Exit code:" << exitCode << ", Exit status:" << exitStatus;

    if (exitCode == 0) {
        qDebug() << "[Success] Process completed successfully.";
    } else {
        qWarning() << "[Failure] Process failed. Exit code: " << exitCode << ", Exit status: " << exitStatus;
    }

    // 检查 process 是否有效
    if (!process) {
        qCritical() << "[Error] Process object is null. It might have been destroyed elsewhere!";
        return;
    }

    // 在释放之前,检查进程状态
    if (process->state() != QProcess::NotRunning) {
        qWarning() << "[Warning] Process is still running! Waiting for it to finish.";
        if (!process->waitForFinished(5000)) {  // 等待进程结束(最多 5 秒)
            qCritical() << "[Critical] Process did not finish in time!";
        }
    }

    // 断开所有信号连接,确保不会访问被销毁的对象
    disconnect(process.data(), SIGNAL(readyReadStandardOutput()), this, SLOT(handleStandardOutput()));
    disconnect(process.data(), SIGNAL(readyReadStandardError()), this, SLOT(handleStandardError()));
    disconnect(process.data(), SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(onProcessFinished(int, QProcess::ExitStatus)));

    // 使用延时机制释放 QProcess 对象,避免在销毁时出现竞争条件
    QTimer::singleShot(10, this, SLOT(resetProcess()));
}

void Serial::resetProcess() {
    if (process && process->state() == QProcess::NotRunning) {
        qDebug() << "[Info] Process has been reset.";
        process.reset();
    } else {
        qWarning() << "[Warning] Process is still running or already null!";
    }
}


void Serial::handleStandardOutput() {
    if (process) {
        QString output = process->readAllStandardOutput();
        qDebug() << "[Info] Standard Output received from process:" << output;


        if (output.contains("Wifi connect ap : Success!")) {
            qDebug() << "[Success] WiFi connected to the specified network.";
            this->flag = 1;  // WiFi连接成功
            qDebug() <<"wifi_yesorno:"<<this->flag;
        }
//        else {
//            qWarning() << "[Warning] WiFi did not connect. Full output log: " << output;

//            this->flag = 0;  // WiFi连接失败
//        }

    } else {
        qCritical() << "[Error] Process object is null! Cannot read standard output.";
    }
}

void Serial::handleStandardError() {
    if (process) {
        QString error = process->readAllStandardError();
        qCritical() << "[Error] Standard Error received from process: " << error;
    } else {
        qCritical() << "[Error] Process object is null! Cannot read standard error.";
    }
}

二. 开机 source qt-path和qt应用程序

方法 1:修改 /etc/rc.local

/etc/rc.local 是一个用于在系统启动时执行自定义命令的文件。你可以在这个文件中添加 source 命令,让它在启动时自动执行。

步骤:

  1. 打开 /etc/rc.local 文件进行编辑:

    复制代码

    sudo nano /etc/rc.local

  2. exit 0 之前添加你的 source 命令:

    复制代码

    source /path/to/qt-path

    注意将 /path/to/qt-path 替换为实际的路径。

  3. 保存文件并退出编辑器。

  4. 确保 /etc/rc.local 是可执行的:

    复制代码

    sudo chmod +x /etc/rc.local

  5. 重启系统,确保命令在开机时自动执行。

  • 重新启用启动脚本

    如果之前已经启用过脚本,不需要再次运行这条命令。如果还未启用,请执行:

    复制代码

    /etc/init.d/qtpath_init enable

  • 测试启动脚本: 可以通过手动启动脚本来测试 Qt 应用程序是否能够正确运行:

    复制代码

    /etc/init.d/qtpath_init start

    如果一切正常,你应该会看到 Qt 环境加载的消息以及应用程序的执行。如果 apply 程序有输出或图形界面,这个时候也应该启动。

三. 数据库中设置ID为主键和索引

主键在MySQL中用于唯一标识表中的每一行数据,确保每行数据的唯一性,加速了数据检索。

主键:表中的一个或多个列的组合,其值唯一地标识表中的每一行

MYSQL会自动会主键列创建唯一索引,加速基于主键的查询操作

索引:是对表中一个或多个列的值进行排序的数据结构,

所以我们应该怎么设置主键和索引呢

设置主键:

唯一性:主键列应包含唯一的值,用于唯一标识表中的每一行

非空:主键列不能有NULL值

选择主键列:

自然主键:使用表中已经存在的唯一列,例如身份证号,用户名等

代理主键:使用一个新的列作为主键,以简化设计并避免数据重复

设置索引

查询优化:为频繁用于查询,排序或连接的列设置索引

选择索引列

查询条件:对经常用于WHERE子句的列设置索引

排序和连接:对经常用于ORDER BYGROUP BYJOIN 操作的列设置索引。

复合索引:对涉及多个列的查询条件,可以使用复合索引来优化性能。例如,在 SELECT * FROM orders WHERE customer_id = ? AND order_date = ? 的情况下,可以创建一个 (customer_id, order_date) 的复合索引。

四. 芯片的唯一序列号作为ID

可以在cat  /sys/class/sunxi_info/sys_info 获取到 sunxi_serial 唯一ID,用于MAC生成,加密等需求

root@TinaLinux:/sys/devices/platform#  cat /sys/class/sunxi_info/sys_info
sunxi_platform    : sun20iw1
sunxi_secure      : normal
sunxi_serial      : 14521d4b0142623100005c0000000000(这是芯片的唯一序列号))
sunxi_chiptype    : 00000000
sunxi_batchno     : 0x18590003

这个就是芯片的唯一序列号

14521d4b0142623100005c0000000000

控制方面,我就按下控制才会发送就行。SOC按下,发送到MYSQL中,不按下还是之前状态,上位机按下,改变状态,我是一直判断status是否有0出现,出现了就读取, 

五. 添加自动和手动控制

温度湿度调节,就纯自动,设定一个目标温湿度就行,并且到达目标温度后设定时间。(风机,压缩机)

风机也自动调节CO2浓度,设定时间

那水位呢,水位的作用是?

光照,自动设定一个光强,到达后设定时间

喷淋,水加热,自动就是设定一个时间。

位置调节也自动和手动,自动就是要求到达哪个位置然后自动去到那个位置。

四. 触摸选择光标

五. status判断

因为qtcontrol只是改变字段的值,所以不用加status;但是qtdata和camera是一直发新的值,但是我们读取只读取某个值,所以我们加一个status,发送的时候是给这个字段发0,当有接收的时候,改变这个字段为1;所以我们要保证发送和接收同步,但我觉得不稳定,所以还是用时间戳吧。因为如果接收端没有把status变为1,发送端就发新数据了,那么接收端还是接收的之前的数据。所以我们加个时间戳,获取最新的数据,并且同时判断status是否为0.如果为1,则是之气那关机的时候发的值,上位机这边不会去接收。

还是都加时间戳,判断现在的时间和最新的时间相差大不大,不大就接收。status是用来判断是否接收成功过。

如果嵌入式端按下按键,直接控制,并且改变字段,(不同于上位机先改变字段再控制),上位机接收到可以知道嵌入式端的状态;上位机按下按键,改变字段,读取之后再显示,嵌入式端获取上位机改变的值做出控制。加一个status_1,嵌入式按下会让它为1,上位机读取会让它为0,.....

六. 两边到底怎么决定通信的

设备 发送数据,上位机接收,查找选中的ID,和最新的时间,并且现在的时间和最新时间差不超过1,也就是上位机打开的时候,设备没有打开,它就不会传递数据;上位机打开,设备改了数据库,上位机就接收数据。

设备按钮控制,上位机接收,也最新时间,不超过1。

上位机控制按下,设备接收,设备找到自己的ID,把读到的数据库时间和现在时间比较,如果小于等于1,就是上位机刚刚改变的值,就控制;所以,设备按下,直接改变A为1,也判断时间是否真正确如果正确也改变A为1,并且得是现在的数据才可以。

这样的话上位机控制,就不能改变字段了,得添加字段,添加对应ID,时间为现在时间,设备读取到时间差小于等于1,所以读取;所以读取完这一次后,后面再判断就不对了,所以只读取一次没有问题。

七. Linux 系统裁剪

Tina 固件中通常包含 boot0、uboot、kernel、rootfs 等镜像。

7.1 boot0 裁剪

由于 boot0 很小,因此略过。

7.2 uboot 裁剪

修改 uboot 配置文件,删减不需要的配置·

删除不需要的uboot命令

7.3 内核裁剪

删除不需要的功能,如符号表,打印,调试等功能

删除不需要的驱动

修改内核源代码

内核压缩

八. 降低D1-H启动时间

TinaLinux D1 系统当前的启动流程如下: brom --> boot0 --> opensbi --> uboot --> rootfs --> app

brom:是芯片内置的启动固件,存储在 ROM 中,当设备通电时,BROM 是执行的第一个程序,它主要负责加载下一阶段的启动程序(通常是 boot0)

boot0:这是一个简单的初级引导程序,位于 SoC 的外部存储(如 SPI Flash 或 SD 卡)中。boot0 的任务是初始化一些基本的硬件,并加载 OpenSBI(或 U-Boot 等更高级的启动程序)

OpenSBI (RISC-V specific):OpenSBI是RISC-V 处理器架构特有的一部分,提供了底层的supervisor功能和一些标准服务,它用于支持 RISC-V 平台上运行的操作系统和 U-Boot。OpenSBI 通常回初始化更复杂的硬件组件,并引导 U-Boot

U-Boot:U-Boot 是一个功能强大的引导加载程序,负责加载 Linux 内核以及传递启动参数。它可以从各种存储设备引导操作系统,在这个阶段,U-Boot 会加载 rootfs 并将控制权交给 Linux 内核。

RootFS:rootfs是系统的根文件系统,包含了操作系统所需的所有用户态工具,库和应用程序。当Linux 内核启动后,它会挂载 rootfs,提供系统运行所需的基本环境

App:应用程序层面是在操作系统启动并准备好后运行的用户应用程序。它们可以是系统服务、用户空间应用或其他软件,运行在 rootfs 提供的环境中

8.1 boot0 启动优化:

boot0 运行在 SRAM,主要功能是对 DRAM 进行初始化,并将 uboot加载至 DRAM

boot0 可优化的地方不多,可以做的是:

• 关闭串口输出。

• 减少检测按键和检测串口的等待时间。

• 加载 uboot 的时候,不要先加载后搬运,直接加载到 uboot 的运行地址。

可将 sys_config.fex 中的 [platform] 下 debug_mode 设置为 0 来关闭 boot0 串口输出,修改 完后,重新打包固件才能生效。

[platform]

debug_mode = 0

8.2 uboot 启动优化

uboot 主要功能是引导内核,量产升级,电源管理,开机音乐/logo、fastboot 刷机等。

8.2.1 完全去掉 uboot

uboot 的包含很多重要功能,通常会保留。某些情况可以去掉,直接从 boot0 加载内核并启动, 可节省一些时间。

8.2.2 避免 burnkey 的影响

对于启用了 burnkey 支持,且还没使用 DragonSN 工具将 key 烧录进去的板子,每次启动到 uboot 都会尝试跟 PC 端工具交互产生如下 log,带来延时。 [1.334]usb burn from boot ... [1.400]usb prepare ok usb sof ok [1.662]usb probe ok [1.664]usb setup ok ... [4.698]do_burn_from_boot usb : have no handshake 如果产品不需要 burnkey,可将 uboot-board.dts 中的 [target] 下 burn_key 设置为 0。 或者使用 DragonSN 工具,烧录一次 key,并设置烧录标志,以使后续启动可跳过检测。

8.2.3 提高 CPU 以及 flash 读取频率

可设置uboot-board.dts 中的 [target] 下 boot_clock 来修改 uboot 运行时 CPU 频率

8.2.4 关闭串口输出

可将 uboot-board.dts 中的 [platform] 下 debug_mode 设置为 0 来关闭 uboot 串口输出, 修改过后,重新编译 uboot 才生效

8.2.5 修改 kernel 加载位置

如果 uboot 将内核加载到 DRAM 的地址与内核中 load address 不匹配,就需要将内核移动到正确位置

8.2.6 修改 kernel 加载大小

8.2.7 关闭 kernel 校验

uboot 加载了内核以后,默认会对内核进行校验,可以在串口输出中看到: Verifying Checksum ... OK 如果不想校验可以去掉,目前的情况是可以减少几十毫秒 (不同平台,不同内核大小,时间不同) 的启动时间。 具体修改 env 配置文件(路径见上文),新增一行"verify=no"。

8.3 kernel 启动优化

8.3.1 initcall 优化

在 cmdline 中 设置initcall_debug=1,即可打印跟踪所有内核初始化过程中调用 initcall 的顺序以及耗时

具体修改 env 配置文件(路径见上文),新增一行"initcall_debug=1",并在"setargs_*"后加入" initcall_debug=${initcall_debug}",如下所示。 setargs_nand=setenv bootargs console=${console} console=tty0 root=${nand_root} init=${init} loglevel=${loglevel} partitions=${partitions} initcall_debug=${initcall_debug} 加入后,内核启动时就会有类似如下的打印,对于耗时较多的 initcall,可进行深入优化。 [ 0.021772] initcall sunxi_pinctrl_init+0x0/0x44 returned 0 after 9765 usecs [ 0.067694] initcall param_sysfs_init+0x0/0x198 returned 0 after 29296 usecs [ 0.070240] initcall genhd_device_init+0x0/0x88 returned 0 after 9765 usecs [ 0.080405] initcall init_scsi+0x0/0x90 returned 0 after 9765 usecs [ 0.090384] initcall mmc_init+0x0/0x84 returned 0 after 9765 usecs

8.4 rootfs 启动优化

rootfs 启动优化主要是优化 rootfs 的挂载到 init 进程执行

对于 D1 来说,一些典型优化项的效果如下:

• 关闭 boot0 打印,优化 ~ 100ms

• 关闭 uboot 打印,优化 ~ 150ms

• 关闭 uboot 的 burn_key 选项,优化 ~ 3s。

• 关闭 kernel 打印,优化 > 2s

• 修改 rootfs 压缩类型,从 squashfs xz 换成 squashfs gzip,优化 ~ 150ms。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值