记录一下自己的灵感
文件传输小工具
1、通过 Ymodem 协议向单片机发送任意文件,单片机可以选择将文件保存到 SPI Flash、Nand Flash 等存储介质。软件上可以设置写入地址,写入地址通过 Ymodem 协议的起始帧传输到单片机。单片机驱动程序中,需要提供初始化、写入和轮询等 API 接口。小工具支持连续传输多个文件。
2、支持通过 UART 发送文件。
3、支持以太网传输文件,需要支持 TCP 和 UDP。
4、使用 USB 传输文件,这个还不确定。
串口波形小工具
1、此小工具在 PC 端运行。
2、在当前串口波形小工具的基础上添加多道波形显示和串口实时速度显示。
3、添加一个用户按钮,用于向单片机发送类似于“启动/结束”命令。
4、增加以太网通信功能,用户可以通过 TCP/UDP 协议上传波形数据。
5、窗口支持拉伸处理。
6、增加蓝牙通信功能,用户可以通过蓝牙向电脑发送波形数据,需要支持 SPP 和 BLE 两种蓝牙。
SPP/BLE 蓝牙波形小工具
在手机端,仿照串口波形小工具做一个波形显示小工具,方便蓝牙开发。需要能实时显示蓝牙传输速度,并且能保存波形。需要兼容的平台有 Android、IOS 以及微信小程序。
TCP/UDP 波形小工具
在手机端,仿照串口波形小工具做一个基于互联网的波形显示小工具,方便 ESP8266 等 WIFI 模块开发。需要兼容的平台有 Android、IOS 以及微信小程序。
蓝牙转串口小工具
windows 自带的蓝牙转串口功能太差,打开后会导致串口搜索变得卡慢。蓝牙转串口小工具可以接收 SPP 或 BLE 蓝牙数据,并生成一个虚拟串口,供给其它软件使用。这样一来,之前的所有软件都将支持蓝牙传输。
TCP/UDP 转串口小工具
Windows 里 TCP/UDP 转串口工具太少,而且大都不太好用,高速传输状态下往往造成卡顿。TCP/UDP 转串口小工具和蓝牙转串口小工具类似,可以接收下位机通过 TCP/UDP 协议上传的数据,并生成一个虚拟串口,共给其它软件使用。
串口屏上位机
使用 EMWIN 屏做为一个串口屏,要求能想淘宝那些串口屏一样,有个上位机,方便后续开发。
BIN 文件拼接小工具
有些设备上,为了方便后续的维护升级,会使用 Bootloarder + App 模式。这会带来一个问题,出厂的时候需要写入 Bootloarder 和 App 两个程序,即需要烧录两次才行。BIN 文件拼接小工具可以用来将 Bootloarder 和 App 两个 BIN 文件拼接到一起,出场时只需要烧录一个 BIN 文件即可。
要求:
1、可以拼接 2~5 个 BIN 文件。
2、每个 BIN 文件可以单独设定起始地址,每个 BIN 文件之间的空隙使用 0xFF 填充。
3、生成的文件包含 BIN 和 HEX 两种类型。
4、支持输入 HEX 文件。
简易串口助手
制作一个简易串口助手,方便教材中贴图。
1、用户可以独立配置串口号、波特率、数据位、停止位、奇偶校验和流控制。
2、可以选择文本发送和Hex发送。
3、可以选择接收区显示文本或Hex。
4、发送时可选要不要追加回车换行。
5、实时检测串口有无脱落,脱落后 5s 未检测到重新接入就关闭串口。
中文字库软件
GBK/GB2312 中文字库软件。
1、可以选择扫描方式。扫描方式有“从上到下,从左往右”、“从左往右、从上到下”。某一类或某一行扫描到尽头不满 8 位对齐时,要用 0 填充。
2、可以选择不同的字体以及字体大小,要能支持系统中的所有字体。
3、可以选择指定汉字生成字模。
4、可以选择生成 ASCII 码字库,点阵数据按照 ” ” 到 “~” 存储,此时可以选择输出 C 语言数组或字库文件。
5、可以选择生成一整个汉字字库,汉字的点阵数据按照 0x8140~0xFEFE 的顺序存储,不存在的码位直接跳过。字库文件后缀为 .fon。
6、可以将一张图片生成点阵数据。
单片机图片转换小工具
在单片机的 GUI 设计中,通常会涉及到 BMP、JPG/JPEG、PNG 和 GIF 的解码。BMP 解码有个很头疼的问题,BMP 的文件头不是 4 字节对齐的,导致数据域开头不是 4 字节对齐,使用 GD32 的 IPA 或 STM32 的 DMA2D 硬件解码时,往往会导致程序异常卡死。JPG/JPEG 解码还好,有现成的库,解码速度也还行。PNG 解码很费内存和 Flash,GIF 显示的话貌似没有哪个很好的库。
单片机图片显示小工具要效仿 BMP 图片的特点,生成的文件以原始像素点格式存储,不压缩,最大程度降低 MCU 解码的难度。然而这势必会造成生成的图片占据内存偏大,没办法,这是不可避免的。
1、生成的文件后缀为 .gom(grapics on mcu)的 GOM 文件,小工具要能解码并显示 GOM 文件。
2、文件头包含的信息如下所示,文件头必须是 4 字节对齐。文件头后边紧跟着像素点数据,像素点数据按照一帧一帧依次存储。像素点数据一律按从左往右,从上往下的扫描方式存储,没有行对齐。
typedef struct
{
unsigned int type; //'G'、'O'、'M'、'\0',地址 0 存储着 'G',以此类推,16 进制为 0x004D4F47
unsigned int version; //版本号,用于适配不同的驱动,'V'、'1'、'0'、'0',地址 0 存储着 'V',表示 V1.0.0 版本
unsigned int width; //图像宽度
unsigned int height; //图像高度
unsigned int format; //像素点格式,0-ARGB8888、1-RGB888、2-RGB565
unsigned int frameSize; //一帧图像像素点数据占内存大小(字节)
unsigned int frameTotal; //一帧图像占据总的内存大小,一帧图像占据的数据量必须保证 4 字节对齐,不足补零
unsigned int frameNum; //共有多少帧图像
unsigned int frameSpace; //帧间隙,ms,即前后两帧图像的时间间隙。最高位为有无帧间隔表,一般为零
unsigned int loop; //循环次数,0 表示无限循环,1 表示只显示一遍,以此类推
unsigned int xPelsPerMeter; //水平分辨率,每米像素数,无特殊要求可为 0
unsigned int yPelsPerMeter; //垂直分辨率,每米像素数,无特殊要求可为 0
unsigned int offSet; //第一帧图像距离文件起始地址偏移量
unsigned int fileSize; //文件总大小
unsigned int reserve[2]; //保留项,使得 GOM 文件头保持 64 字节对齐
}StructGOMFileHead;
frameSpace 为最高位为零时,表示所有的图像以固定间隔刷新,如果设定为 10ms,那么前后两帧图像的时间间隙为 10ms。如果 frameSpace 最高位为一,则表示帧间隙表存在,此时 frameSpace 低 16 位表示帧间隙表距离文件起始地址的偏移量。帧间隙表以无符号 16 位数记录所有帧间隙,表示显示完图像后需要延时多少时间再显示下一帧,对于非循环显示,最后一帧的延时时间可以为零。帧间隙表要保持 4 字节对齐,不足补零。
3、小工具支持保留原始像素点格式以及转换成特定的像素点格式。有些图片就比如 PNG,当需要拿 PNG 图片做为背景图片的时候,因为单片机一般以 RGB565 为背景像素点格式,所以最好是将 PNG 转换成 RGB565 的 GOM 文件。
4、支持 BMP 图片转成 GOM 文件,要求不能有行对齐,且按照从左往右,从上到下的扫描顺序存储像素点数据。
5、支持 PNG 图片转成 GOM 文件。
6、支持 JPG/JPEG 转成 GOM 文件。
7、支持 GIF 转成 GOM 文件。
8、支持批量转换。
9、支持镜像、翻转、裁剪等基本编辑操作。
单片机串口稳定性测试小工具
单片机串口驱动负责与上位机的交互通信,它的稳定性至关重要。
然而有些时候,我们写的串口驱动中,加了一些其它的操作,例如加了队列、加了中断、加了 DMA、加了 定时器等等,这些操作势必会降低系统的稳定性。短时间内,就比如一个小时内,串口还能正常工作;但是时间一长,就比如连续工作几天后,串口突然就不工作了。
单片机串口稳定性测试小工具用来实时测量串口的稳定性,并自动生成报告,要求如下:
1、以串口回环的方式测试。小工具向单片机发送一批数据,单片机收到数据后,将数据原封不动的传回来。测试数据需要是随机数。
2、支持常用的串口波特率。
3、可以指定依次发的数据量以及发送间隔。
4、实时显示测量时长,并自动记录错误发生时的时间(距离启动测量经过的时长),以日志的形式更新到界面显示。
5、实时显示串口的传输速度,按每分钟的平均速度。
串口测速小工具
这个小工具专门用来测试单片机在特定波特率下串口发送的极限速度,要求如下:
1、可以实时显示测量时长。
2、显示串口瞬时速度(1s 内的平均速度)。
3、串口一分钟内的平均速度。
4、接收到的总字节数。
5、用仪表盘的形式,指针指向当前实时速度,满偏为当前波特率下的极限速度。(在 USB CDC 模拟的串口下,串口速度与波特率无关,超过了极限速度直接显示满偏即可)
汉字内码查询小工具
方便查询汉字内码,要求如下:
1、支持通过汉字查询内码以及通过内码查询汉字。
2、支持 GBK、UTF-8、Unicode 等常见的编码格式。
3、支持 2 进制、8 进制、10 进制和 16 进制输入输出。
4、支持 ASCII 码内码查询与反查询。
串口/网络摄像头小工具
为方便开发板摄像头开发,也为了方便开发板截屏,需要在电脑端做一个显示图像的小工具,要求如下:
1、支持串口、TCP、UDP 协议。串口协议要能支持 2000000+ 超高波特率,支持可配置数据位、停止位、奇偶校验和流控制等参数。TCP/UDP 要能支持服务器和客户端两种模式。
2、支持 JPEG 图片、BMP 图片和纯像素点数据。传输纯像素点数据时,需要指定传输的像素点格式、图片长度和宽度。需要支持常见的像素点格式。支持将 OLED 的点阵数据显示到电脑屏幕。
3、能够保存图片,即将某一帧截下来,保存到电脑。
4、能够保存录像,即将某段时间的图像录制下来,并生成 GIF 图片或 MP4 等视频格式。
C 语言数组转 BIN 文件小工具
有些时候,我们需要将 C 语言中的常量数组转成 BIN 文件。例如常量数组太大了,单片机存不下,或者太占 Flash 空间,这时候我们可以选择将常量数组转换成 BIN 文件,保存到文件系统中。需要数据的时候,直接从文件系统中读取即可。
SD 卡读写小工具
这个小工具可以将文件直接写入到 SD 卡指定地址,这样一来,单片机使用 SD 卡时,就不需要额外再配置一个文件系统,直接读取特定地址的数据即可,要求如下:
1、支持擦除 SD 卡,即将 SD 卡所有存储单元全部初始化成 0xFF。
2、支持将 SD 卡还原成 FAT32 等文件系统,即 SD 卡修复。
3、支持将电脑中的某个文件直接写入到 SD 卡的特定地址。
4、支持批量写入文件,并最终生成一个报告,报告中记录了每个文件的文件名、存储地址、文件大小等信息。批量写入文件时,需要保证每个文件的起始地址都要保证 4 字节对齐。
5、支持读取 SD 卡某段地址的数据,显示并保存到电脑中。
颜色转换小工具
在电脑端或移动端,使用的往往是 RGB888 格式,而在单片机中,使用的更多是 RGB565,这就涉及到像素点格式转换。要求如下:
1、程序运行在 Windows 端。
2、支持 RGB888、RGB565 以及其它常见像素点的相互转换。
3、支持 10 进制、16 进制格式输出。
4、支持捕获电脑屏幕某一点的颜色值。
OLED 仿真组件
编写一个 OLED 仿真组件,使得 OLED 的开发可以从单片机平台转移到 PC 端,方便调试和加快编程开发。基于 LCD 的仿真组件已经有了,具体可参考 单片机 GUI 设计(十五)- PC 仿真 GUI,现在还缺 OLED 版本的。要求如下:
1、使用 C/C++ 编写组件,最终要留出 C 语言接口。
2、组件以源码的形式提供,用户需要使用时直接添加新文件,并调用源码的 API 函数,API 函数形参如下。
3、显示风格参照 PCtoLCD2002。
4、用户可自由配置 OLED 长宽。
5、要提供基本的画点、读点、画线函数,以及 12*6 和 16*8 ASCII 码显示函数和字符串打印函数。不需要 OLED 刷新显示函数。
6、组件中要内置一块 OLED 显存,然后创建一个线程,定时将显存中的数据更新到屏幕显示。
7、提供 3~5 个用户按钮。
标准电阻查询小工具
在 DC-DC 电源或可调 LDO 电源电路设计的时候,常常会涉及到计算反馈电阻。然而计算出来的反馈电阻常常要查询是否在标准电阻表中,不在的话换个参数继续算,非常不便。所以需要一个小工具方便查询标准电阻阻值,具体要求如下。
1、输入一个电阻值,查询与其相近的标准电阻值。
2、输入一个电容值,查询与其相近的标准电容值。
3、输入一个电感值,查询与其相近的标准电感值。
4、输入一个非标电阻值,以及误差范围,求解如何用两个标准电阻串联得到。要求罗列出所有适配的可能,以供参考。
5、输入一个非标电阻值,以及误差范围,求解如何用两个标准电阻并联得到。要求罗列出所有适配的可能,以供参考。
6、输入一个解析式,例如“y=1.25*(1+(R1/R2))”,以及 y 的目标值与误差范围,求解 R1 与 R2 所有的可能,R1 和 R2 必须是标准电阻值。小工具应该要具有判断解析式是否有误,并且要支持求解至多 5 个电阻值,即可能会需要求解 R1~R5 所有的可能。注:这里的“y=”可以不用。
7、标准电阻、电容以及电感值可参考“常用电阻、电容、电感表”。对于电阻值,默认求出来的标准电阻只在 E-24 标准中,用户可以勾选使能输出 E-96 系列的电阻值,此时优先输出 E-24 标准电阻,然后再输出 E-96 标准电阻。
+输出误差范围,例如“12.0=1.25*(1+(R1/R2))”,R1 可以取值 130kΩ,R2 可以取值 15kΩ,在 ±1% 的误差下,解析式取值范围为 11.87~12.30,标准值为 12.08。
曲线生成小工具
曲线生成小工具用来生成或拟合曲线,可以用在血压测量、信号发生器等应用场景,大致界面如下所示。

要求如下:
1、“横坐标”初始化时默认为 1024。
2、可以输入解析式,并显示波形。此时自变量范围从 0 到 “横坐标”定义的值。
3、可以输入 “csv” 表格文件或“xlsx”表格文件,表格文件中包含了曲线数据。读入表格文件后,程序需要统计表格文件中的数据量,并更新到“横坐标”,并将横坐标设定为不可编辑,对应按钮为失能状态。
4、波形显示区的曲线默认为一条直线。用户可以在输入波形或默认直线的基础上,通过拖拽,塑造出想要的波形。
5、小程序的输出有两个。一个是方程,是由多项式拟合而成的解析式,阶数可自由配置。另一个是数据文件,输出波形数据到 “csv”表格文件。点击“输出”后,小程序还应将拟合的曲线以不同颜色输出到波形显示区,用于对比查看拟合效果。
+小程序可以按照三角函数拟合输出方程。
+小程序可以按照高斯拟合输出方程。
+曲线平滑处理,而不是直接用直线绘制曲线。
PID 调试小工具
PID 调试过程中,常常需要修改比例(KP)、积分(KI)和微分(KD)这三个参数,需要反复调试才能最终确定这三个参数。按照以往方式,在代码中修改,然后再烧写到单片机。不仅麻烦,还浪费了单片机宝贵的烧写次数,费时费力。因此需要一个小工具来方便调节参数。电脑通过串口向目标设备发送比例(KP)、积分(KI)和微分(KD)这三个参数以及目标值,目标设备通过串口上报实时测量值。小工具大致界面如下所示。

要求如下:
1、向目标设备发送配置数据时,以字符串形式发送命令,格式为 “[[KP,KI,KD,Target]]”,KP、KI、KD 分别为比例、积分、微分三个系数,target 则为目标值,例如 “[[0.5,0.0001,0.0,123.0]]” 表示将 KP、KI、KD 调整为 0.5、0.0001、0.0,目标值设定为 123.0。传输过程中引号 “” 无需传输,字符串总长度不得超过 256 字节。
2、目标设备上报测量结果时,同样以字符串形式传输,格式为 “{{value}}”,value 即为测量结果。”{{321.0}}” 表明当前测量结果为 321.0。传输过程中引号 “” 无需传输,字符串总长度不得超过 256 字节。
3、串口默认配置为:8 位数据位、1 位停止位、无奇偶校验、无流控制。需要支持常用的波特率,1200、2400、4800、38400、115200 等,波特率下拉框需要支持可编辑。
4、需要做串口脱落检测,串口脱落后自动关闭串口,并更新界面显示。
5、软件启动和点击串口下拉框时自动扫描电脑所有可用串口。
6、点击“配置”按钮后,接收区和右侧波形区清空显示,然后目标值以红色横线形式显示到波形显示区。波形显示区纵坐标范围随着测量结果范围的变化动态调整,但目标值需要始终保持在波形显示区中间位置。横坐标也要随着测量点数的增加而作修改,需要能实时显示所有的测量值。如果有历史测量数据,那么还应该保留显示最近 2s 数据,用于做分析对比。
+能将测量值保存到 “cvs”表格文件,方便数据留存分析。
+启动/停止测量按钮。
+窗体可拉伸,可以通过鼠标的滑动和滚动伸缩平移波形显示区。
目标设备 API 接口函数如下所示。
#include "stdio.h"
/*********************************************************************************************************
* 函数名称: StringToDouble
* 函数功能: 字符串转浮点数
* 输入参数: void
* 输出参数: void
* 返 回 值: void
* 创建日期: 2023年07月13日
* 注 意: 除数字、正负号、小数点外,不得带有其它符号
*********************************************************************************************************/
static double StringToDouble(char* string)
{
double sum, devision;
int negative, dotFlag;
unsigned int i;
sum = 0;
negative = 0;
i = 0;
devision = 1;
dotFlag = 0;
while(('+' == string[i]) || ('-' == string[i]) || ('.' == string[i]) || ((string[i] >= '0') && (string[i] <= '9')))
{
//正号
if ('+' == string[i])
{
negative = 0;
}
//负号
else if ('-' == string[i])
{
negative = 1;
}
//小数点
else if ('.' == string[i])
{
dotFlag = 1;
}
//数字
else if ((string[i] >= '0') && (string[i] <= '9'))
{
sum = (sum * 10.0) + (string[i] - '0');
if (0 != dotFlag)
{
devision = devision * 10;
}
}
//循环变量加一
i++;
}
//负号处理
if (0 != negative)
{
sum = -sum;
}
//小数点处理
sum = sum / devision;
//返回转换结果
return sum;
}
/*********************************************************************************************************
* 函数名称: PIDGetParam
* 函数功能: 获取 PID 参数
* 输入参数: uData:串口接收到的数据
* kp:用于输出比例参数
* ki:用于输出积分参数
* kd:用于输出微分参数
* target:用于输出目标值
* 输出参数: void
* 返 回 值: 0-获取失败,1-获取成功
* 创建日期: 2023年07月31日
* 注 意:
*********************************************************************************************************/
int PIDGetParam(char uData, double* kp, double* ki, double* kd, double* target)
{
static char s_arrPIDCmd[256] = {0};
static unsigned char s_iPIDCmdCnt = 0;
static char s_arrCmd[2] = {0};
static unsigned char s_iStartFlag = 0;
unsigned int i;
//数据往前挪动,用于判断是否出现了起始或终止符
s_arrCmd[0] = s_arrCmd[1];
s_arrCmd[1] = uData;
//检测到起始符号
if (('[' == s_arrCmd[0]) && ('[' == s_arrCmd[1]))
{
s_iStartFlag = 1;
s_arrPIDCmd[0] = '[';
s_arrPIDCmd[1] = '[';
s_iPIDCmdCnt = 2;
}
//检测到结束符号
else if ((']' == s_arrCmd[0]) && (']' == s_arrCmd[1]))
{
s_iStartFlag = 0;
s_iPIDCmdCnt = 0;
//获取 KP
*kp = StringToDouble(s_arrPIDCmd + 2);
//获取 KI
i = 2;
while(',' != s_arrPIDCmd[i]){ i++; }
i++;
*ki = StringToDouble(s_arrPIDCmd + i);
//获取 KD
while (',' != s_arrPIDCmd[i]) { i++; }
i++;
*kd = StringToDouble(s_arrPIDCmd + i);
//获取目标值
while (',' != s_arrPIDCmd[i]) { i++; }
i++;
*target = StringToDouble(s_arrPIDCmd + i);
//返回 1,表明获取参数成功
return 1;
}
//保存字符串
else if (0 != s_iStartFlag)
{
s_arrPIDCmd[s_iPIDCmdCnt++] = uData;
}
return 0;
}
/*********************************************************************************************************
* 函数名称: PIDSendResult
* 函数功能: PID 上报测量结果
* 输入参数: result:测量结果
* 输出参数: void
* 返 回 值: void
* 创建日期: 2023年07月31日
* 注 意:
*********************************************************************************************************/
void PIDSendResult(double result)
{
printf("{{%.3f}}\r\n", result);
}