目录
一. 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
命令,让它在启动时自动执行。
步骤:
-
打开
/etc/rc.local
文件进行编辑:复制代码
sudo nano /etc/rc.local
-
在
exit 0
之前添加你的source
命令:复制代码
source /path/to/qt-path
注意将
/path/to/qt-path
替换为实际的路径。 -
保存文件并退出编辑器。
-
确保
/etc/rc.local
是可执行的:复制代码
sudo chmod +x /etc/rc.local
-
重启系统,确保命令在开机时自动执行。
-
重新启用启动脚本:
如果之前已经启用过脚本,不需要再次运行这条命令。如果还未启用,请执行:
复制代码
/etc/init.d/qtpath_init enable
-
测试启动脚本: 可以通过手动启动脚本来测试 Qt 应用程序是否能够正确运行:
复制代码
/etc/init.d/qtpath_init start
如果一切正常,你应该会看到 Qt 环境加载的消息以及应用程序的执行。如果
apply
程序有输出或图形界面,这个时候也应该启动。
三. 数据库中设置ID为主键和索引
主键在MySQL中用于唯一标识表中的每一行数据,确保每行数据的唯一性,加速了数据检索。
主键:表中的一个或多个列的组合,其值唯一地标识表中的每一行
MYSQL会自动会主键列创建唯一索引,加速基于主键的查询操作
索引:是对表中一个或多个列的值进行排序的数据结构,
所以我们应该怎么设置主键和索引呢
设置主键:
唯一性:主键列应包含唯一的值,用于唯一标识表中的每一行
非空:主键列不能有NULL值
选择主键列:
自然主键:使用表中已经存在的唯一列,例如身份证号,用户名等
代理主键:使用一个新的列作为主键,以简化设计并避免数据重复
设置索引
查询优化:为频繁用于查询,排序或连接的列设置索引
选择索引列
查询条件:对经常用于WHERE子句的列设置索引
排序和连接:对经常用于ORDER BY
、GROUP BY
和 JOIN
操作的列设置索引。
复合索引:对涉及多个列的查询条件,可以使用复合索引来优化性能。例如,在 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。