一 CJSON简介
项目遇到了一个需求,就是需要通过U盘或者其他外接设备,与板子(嵌入式芯片使用的是LPC1857)进行交互。交互的形式有两种:
-
一种是预存在外接U盘中的配置信息CFG,接上U盘之后,嵌入式芯片需要自动读取配置文件中的内容并改写自身的运行参数(
外接设备配置信息—->主控芯片
)。
-
一种是嵌入式芯片收集自身的状态信息DATA,接上U盘之后,将收集到的数据打包发送给U盘,便于维护人员对数据进行分析(
外接设备<—-主控芯片状态信息
)
现在已经准备好了一个带
USB HOST(嵌入式芯片作为USB主机处理外接设备的功能)
以及
简单的文件系统
(文件系统可以参考
【文件系统】FatFs文件系统在嵌入式芯片LPC18XX上的移植
中的FatFs,这个工程只是做一个简单的DEMO,所以没有使用FatFs)。在此基础上,准备移植一个CJSON库,作为封装上面CFG/DATA信息的数据格式。
CJSON是一个使用C语言编写的JSON数据解析器,具有超轻便,可移植,单文件的特点,使用MIT开源协议。优点就是简单轻便,易于移植,该数据格式还可以灵活适用于网络数据的传输
。
二 CJSON说明
-
数据结构
CJSON的数据结构如下所示:
-
基础操作
CJSON的基础操作包括创建、封装、解析、修改、删除。
关于他们的具体用法可以参考这篇文章写的非常详细:
C/C++ 使用cjson库 操作Json格式文件(创建、插入、解析、修改、删除)
(特别注意的是:上述
操作完成之后一定要释放它们返回的指针内存,否则会造成内存泄漏
)
三 CJSON移植
-
下载
-
CJSON库下载:
https://sourceforge.net/projects/cjson/
以上两个地址均可以下载CJSON库。
下载下来之后,拉取其中的cJSON.c/cJSON.h/test.c三个文件,其中前面两个cJSON.c/cJSON.h是库文件必要,最后一个test.c是示例非必要。
-
移植
keil中新建目录CJSON,并包含头文件路径:
以上,便移植完成了,就两个文件,简单吧。
四 CJSON应用
下面就是第一章里面提到的两种场景的具体应用:
-
外接设备配置信息(cfg.json)—->主控芯片
预存cfg.json文件的内容如下:
这边设计的逻辑就是,在外接U盘加载完毕之后,通过串口Shell命令行输入命令jsi进行文件读取,寻找名为cfg.json的文件,如果找不到,返回“找不到文件”。如果找到,则读取文件内容,并用CJSON进行数据解析,将解析得到的配置数据返回显示(
主要涉及CJSON格式的解析操作
)。
void shellAnalyseCommand(char * command, short cmdLen) //命令行解析函数
{
//...
//命令1:jsi 读取U盘中的cfd.json文件并解析
if (((key[0] == 'J') || (key[0] == 'j'))
&& ((key[1] == 'S') || (key[1] == 's'))
&& ((key[2] == 'I') || (key[2] == 'i')))
{
shellPrint("数据读取开始...\r\n");
FAT_FILE_HANDLE hSrc;
hSrc = fatFileOpen("cfg.json", FAT_FILE_OPEN_READ);
unsigned long len_s = 0;
if (hSrc == FAT_FILE_HANDLE_NULL)
{
shellPrint("找不到文件 \r\n");
return;
}
shellPrint("正在打开文件 %d \r\n", hSrc);
len_s = fatFileGetLen(hSrc);
char *out_s = (char *)malloc(len_s+1);
fatFileRead(hSrc,(unsigned char*)out_s,len_s);
fatFileClose(hSrc);
cJSON *cfg = cJSON_Parse(out_s);
cJSON *item = NULL;
cJSON *item_typ = NULL;
cJSON *item_wid = NULL;
if (!cfg)
{
shellPrint("Error before: [%s]\n", cJSON_GetErrorPtr());
free(out_s);
return;
}
free(out_s);
item = cJSON_GetObjectItem(cfg, "format");
item_typ = cJSON_GetObjectItem(item, "type");
item_wid = cJSON_GetObjectItem(item, "width");
shellPrint("数据读取完成...\r\n");
shellPrint("type:%s\r\nwidth:%d\r\n",item_typ->valuestring, item_wid->valueint);
return;
}
//...
}
测试通过命令行jsi可以正确读取:
-
外接设备<—-主控芯片状态信息(data.json)
这边设计的逻辑就是,在外接U盘加载完毕之后,通过串口Shell命令行输入命令jso进行数据封装,将内部的状态信息打包成CJSON格式的文件,再写入到U盘中(
主要涉及CJSON格式的封装操作
)。
void shellAnalyseCommand(char * command, short cmdLen) //命令行解析函数
{
//...
//命令2:jso 往U盘中写固定内容data.json文件
if (((key[0] == 'J') || (key[0] == 'j'))
&& ((key[1] == 'S') || (key[1] == 's'))
&& ((key[2] == 'O') || (key[2] == 'o')))
{
shellPrint("数据装载开始...\r\n");
cJSON *root = NULL;
cJSON *fmt = NULL;
char *out = NULL;
size_t len = 0;
root = cJSON_CreateObject();
cJSON_AddItemToObject(root, "name", cJSON_CreateString("Jack"));
cJSON_AddItemToObject(root, "format", fmt = cJSON_CreateObject());
cJSON_AddStringToObject(fmt, "type", "rect");
cJSON_AddNumberToObject(fmt, "width", 100);
cJSON_AddFalseToObject (fmt, "interlace");
out = cJSON_Print(root);
len = strlen(out) + 5;
shellPrint("数据装载完成...\r\n");
FAT_FILE_HANDLE hDst;
hDst = fatFileOpen("data.json", FAT_FILE_OPEN_WRITE);
if (hDst == FAT_FILE_HANDLE_NULL)
{
fatMakeFile("data.json");
hDst = fatFileOpen("data.json", FAT_FILE_OPEN_WRITE);
shellPrint("没有该文件则新建 %d \r\n", hDst);
}
else
{
fatFileClear(hDst);
shellPrint("打开JSON文件 %d \r\n", hDst);
}
shellPrint("写json文件开始...\r\n");
fatFileAppend(hDst, (unsigned char *)out, len);
fatFileClose(hDst);
shellPrint("写json文件完成...\r\n");
free(out);
cJSON_Delete(root);
return;
}
//...
}
测试通过命令行jsi可以正确读取:
同样的,实际查看U盘中也有该文件:
五 总结
形象地概括来说,
CJSON文件库相当于一件衣服,将赤裸的数据套上CJSON大衣(封装)之后出去见人,会更好看,更易识别。但是同样的,读取外部CJSON数据就是脱衣服的过程(解析),抽丝剥茧才能看到最纯粹的数据。在这个循环中,CJSON作为壳,要注意用完就释放,毕竟这件衣服是租来的,用完要还的
。
综上,通过USB HOST + 串口SHELL命令行 + 文件系统 + CJSON文件库,便可以实现U盘或者其他外接设备,与嵌入式芯片的JSON格式数据交互。