目录
1. 下载cJSON源码
2. 建立STM32工程
3. 移植源文件
4. 修改源文件
修改cJSON源码中的malloc和free函数
修改test.c源文件
修改串口接收
?修改main.c文件
5. 测试
1. 下载cJSON源码
只需要下载源码,只会使用 cJSON.c cJSON.h test.c 三个源文件
cJSON源码下载
JSON中文说明
2. 建立STM32工程
cJSON对RAM大小要求较高,实际要求要看数据量大小。这里选择我地硬件电路
?工程设置非常简单,
- 主要设置一个串口 USART1 使能能串口接收中断 开启DMA传输,用于通讯
- 设置一个led灯表示系统运行
- 修改堆栈大小(非常重要,这里设置为0x1000)
?
生成工程?MycJSON_demo
3. 移植源文件
打开上面的工程文件,编译确认没有问题
?查看堆栈大小
堆栈大小和工程编译都没有问题后进行代码移植,
- 移植cJSON源码包中的cJSON.c cJSON.h test.c
- 移植内存管理函数malloc.c malloc.h
解压cJSON源码包,将cJSON.c cJSON.h test.c 三个文件复制到工程文件中并添加到工程中
?移植内存管理源文件,可以借鉴正点原子的内存管理,我是在正点原子基础上修改部分代码进行适配的
主要关注内存管理大小,内存申请函数 内存释放函数 ,内存使用率函数
?
?
?
源码如下:
#include "malloc.h"
//内存池(32字节对齐)
__align(32) uint8_t mem1base[MEM1_MAX_SIZE]; //内部SRAM内存池
__align(32) uint8_t mem2base[MEM2_MAX_SIZE] __attribute__((at(0XC01F4000))); //外部SDRAM内存池,前面2M给LTDC用了(1280*800*2)
__align(32) uint8_t mem3base[MEM3_MAX_SIZE] __attribute__((at(0X10000000))); //内部CCM内存池
//内存管理表
uint32_t mem1mapbase[MEM1_ALLOC_TABLE_SIZE]; //内部SRAM内存池MAP
uint32_t mem2mapbase[MEM2_ALLOC_TABLE_SIZE] __attribute__((at(0XC01F4000+MEM2_MAX_SIZE))); //外部SRAM内存池MAP
uint32_t mem3mapbase[MEM3_ALLOC_TABLE_SIZE] __attribute__((at(0X10000000+MEM3_MAX_SIZE))); //内部CCM内存池MAP
//内存管理参数
const uint32_t memtblsize[SRAMBANK]={MEM1_ALLOC_TABLE_SIZE,MEM2_ALLOC_TABLE_SIZE,MEM3_ALLOC_TABLE_SIZE}; //内存表大小
const uint32_t memblksize[SRAMBANK]={MEM1_BLOCK_SIZE,MEM2_BLOCK_SIZE,MEM3_BLOCK_SIZE}; //内存分块大小
const uint32_t memsize[SRAMBANK]={MEM1_MAX_SIZE,MEM2_MAX_SIZE,MEM3_MAX_SIZE}; //内存总大小
//内存管理控制器
struct _m_mallco_dev mallco_dev=
{
my_mem_init, //内存初始化
my_mem_perused, //内存使用率
mem1base,mem2base,mem3base, //内存池
mem1mapbase,mem2mapbase,mem3mapbase,//内存管理状态表
0,0,0, //内存管理未就绪
};
//复制内存
//*des:目的地址
//*src:源地址
//n:需要复制的内存长度(字节为单位)
void mymemcpy(void *des,void *src,uint32_t n)
{
uint8_t *xdes=des;
uint8_t *xsrc=src;
while(n--)*xdes++=*xsrc++;
}
//设置内存
//*s:内存首地址
//c :要设置的值
//count:需要设置的内存大小(字节为单位)
void mymemset(void *s,uint8_t c,uint32_t count)
{
uint8_t *xs = s;
while(count--)*xs++=c;
}
//内存管理初始化
//memx:所属内存块
void my_mem_init(uint8_t memx)
{
mymemset(mallco_dev.memmap[memx],0,memtblsize[memx]*4); //内存状态表数据清零
mallco_dev.memrdy[memx]=1; //内存管理初始化OK
}
//获取内存使用率
//memx:所属内存块
//返回值:使用率(扩大了10倍,0~1000,代表0.0%~100.0%)
uint16_t my_mem_perused(uint8_t memx)
{
uint32_t used=0;
uint32_t i;
for(i=0;i<memtblsize[memx];i++)
{
if(mallco_dev.memmap[memx][i])used++;
}
return (used*1000)/(memtblsize[memx]);
}
//获取内存使用率
//memx:所属内存块
//返回值:使用率(扩大了10倍,0~1000,代表0.0%~100.0%)
uint16_t MycJSONmem_perused(void)
{
uint32_t used=0;
uint32_t i;
for(i=0;i<memtblsize[SRAMIN];i++)
{
if(mallco_dev.memmap[SRAMIN][i])used++;
}
return (used*1000)/(memtblsize[SRAMIN]);
}
//内存分配(内部调用)
//memx:所属内存块
//size:要分配的内存大小(字节)
//返回值:0XFFFFFFFF,代表错误;其他,内存偏移地址
uint32_t my_mem_malloc(uint8_t memx,uint32_t size)
{
signed long offset=0;
uint32_t nmemb; //需要的内存块数
uint32_t cmemb=0;//连续空内存块数
uint32_t i;
if(!mallco_dev.memrdy[memx])mallco_dev.init(memx);//未初始化,先执行初始化
if(size==0)return 0XFFFFFFFF;//不需要分配
nmemb=size/memblksize[memx]; //获取需要分配的连续内存块数
if(size%memblksize[memx])nmemb++;
for(offset=memtblsize[memx]-1;offset>=0;offset--)//搜索整个内存控制区
{
if(!mallco_dev.memmap[memx][offset])cmemb++;//连续空内存块数增加
else cmemb=0; //连续内存块清零
if(cmemb==nmemb) //找到了连续nmemb个空内存块
{
for(i=0;i<nmemb;i++) //标注内存块非空
{
mallco_dev.memmap[memx][offset+i]=nmemb;
}
return (offset*memblksize[memx]);//返回偏移地址
}
}
return 0XFFFFFFFF;//未找到符合分配条件的内存块
}
//释放内存(内部调用)
//memx:所属内存块
//offset:内存地址偏移
//返回值:0,释放成功;1,释放失败;
uint8_t my_mem_free(uint8_t memx,uint32_t offset)
{
int i;
if(!mallco_dev.memrdy[memx])//未初始化,先执行初始化
{
mallco_dev.init(memx);
return 1;//未初始化
}
if(offset<memsize[memx])//偏移在内存池内.
{
int index=offset/memblksize[memx]; //偏移所在内存块号码
int nmemb=mallco_dev.memmap[memx][index]; //内存块数量
for(i=0;i<nmemb;i++) //内存块清零
{
mallco_dev.memmap[memx][index+i]=0;
}
return 0;
}else return 2;//偏移超区了.
}
//释放内存(外部调用)
//memx:所属内存块
//ptr:内存首地址
void myfree(uint8_t memx,void *ptr)
{
uint32_t offset;
if(ptr==NULL)return;//地址为0.
offset=(uint32_t)ptr-(uint32_t)mallco_dev.membase[memx];
my_mem_free(memx,offset); //释放内存
}
void MycJSONfree(void *ptr)
{
uint32_t offset;
if(ptr==NULL)return;//地址为0.
offset=(uint32_t)ptr-(uint32_t)mallco_dev.membase[SRAMIN];
my_mem_free(SRAMIN,offset); //释放内存
}
//分配内存(外部调用)
//memx:所属内存块
//size:内存大小(字节)
//返回值:分配到的内存首地址.
void *mymalloc(uint8_t memx,uint32_t size)
{
uint32_t offset;
offset=my_mem_malloc(memx,size);
if(offset==0XFFFFFFFF)return NULL;
else return (void*)((uint32_t)mallco_dev.membase[memx]+offset);
}
void *MycJSONmalloc(uint32_t size)
{
uint32_t offset;
offset=my_mem_malloc(SRAMIN,size);
if(offset==0XFFFFFFFF)return NULL;
else return (void*)((uint32_t)mallco_dev.membase[SRAMIN]+offset);
}
//重新分配内存(外部调用)
//memx:所属内存块
//*ptr:旧内存首地址
//size:要分配的内存大小(字节)
//返回值:新分配到的内存首地址.
void *myrealloc(uint8_t memx,void *ptr,uint32_t size)
{
uint32_t offset;
offset=my_mem_malloc(memx,size);
if(offset==0XFFFFFFFF)return NULL;
else
{
mymemcpy((void*)((uint32_t)mallco_dev.membase[memx]+offset),ptr,size); //拷贝旧内存内容到新内存
myfree(memx,ptr); //释放旧内存
return (void*)((uint32_t)mallco_dev.membase[memx]+offset); //返回新内存首地址
}
}
#ifndef __MALLOC_H
#define __MALLOC_H
#include "stm32f4xx_hal.h"
#ifndef NULL
#define NULL 0
#endif
//定义三个内存池
#define SRAMIN 0 //内部内存池
#define SRAMEX 1 //外部内存池(SDRAM)
#define SRAMCCM 2 //CCM内存池(此部分SRAM仅仅CPU可以访问!!!)
#define SRAMBANK 3 //定义支持的SRAM块数.
//mem1内存参数设定.mem1完全处于内部SRAM里面.
#define MEM1_BLOCK_SIZE 64 //内存块大小为64字节
#define MEM1_MAX_SIZE 16*1024 //最大管理内存 16K
#define MEM1_ALLOC_TABLE_SIZE MEM1_MAX_SIZE/MEM1_BLOCK_SIZE //内存表大小
//mem2内存参数设定.mem2的内存池处于外部SDRAM里面
#define MEM2_BLOCK_SIZE 64 //内存块大小为64字节
#define MEM2_MAX_SIZE 28912 *1024 //最大管理内存28912K
#define MEM2_ALLOC_TABLE_SIZE MEM2_MAX_SIZE/MEM2_BLOCK_SIZE //内存表大小
//mem3内存参数设定.mem3处于CCM,用于管理CCM(特别注意,这部分SRAM,仅CPU可以访问!!)
#define MEM3_BLOCK_SIZE 64 //内存块大小为64字节
#define MEM3_MAX_SIZE 60 *1024 //最大管理内存60K
#define MEM3_ALLOC_TABLE_SIZE MEM3_MAX_SIZE/MEM3_BLOCK_SIZE //内存表大小
//内存管理控制器
struct _m_mallco_dev
{
void (*init)(uint8_t); //初始化
uint16_t (*perused)(uint8_t); //内存使用率
uint8_t *membase[SRAMBANK]; //内存池 管理SRAMBANK个区域的内存
uint32_t *memmap[SRAMBANK]; //内存管理状态表
uint8_t memrdy[SRAMBANK]; //内存管理是否就绪
};
extern struct _m_mallco_dev mallco_dev; //在mallco.c里面定义
void mymemset(void *s,uint8_t c,uint32_t count); //设置内存
void mymemcpy(void *des,void *src,uint32_t n);//复制内存
void my_mem_init(uint8_t memx); //内存管理初始化函数(外/内部调用)
uint32_t my_mem_malloc(uint8_t memx,uint32_t size); //内存分配(内部调用)
uint8_t my_mem_free(uint8_t memx,uint32_t offset); //内存释放(内部调用)
uint16_t my_mem_perused(uint8_t memx) ; //获得内存使用率(外/内部调用)
//用户调用函数
void myfree(uint8_t memx,void *ptr); //内存释放(外部调用)
void *mymalloc(uint8_t memx,uint32_t size); //内存分配(外部调用)
void *myrealloc(uint8_t memx,void *ptr,uint32_t size);//重新分配内存(外部调用)
void MycJSONfree(void *ptr); //cJSON调用释放内存
void *MycJSONmalloc(uint32_t size); //cJSON调用申请内存
uint16_t MycJSONmem_perused(void); //cJSON获取内存使用率
#endif
设置头文件包含
?编译后提示错误,我们需要再修改源文件
4. 修改源文件
修改cJSON源码中的malloc和free函数
修改为
?再将
?修改为
修改test.c源文件
将test.c中所有free替换为MycJSONfree
?
?注释掉test.c中的main函数
修改串口接收
- 增加printf定义
- 定义串口接收数据结构
- 增加空闲中断函数
在usart.c中/* USER CODE BEGIN 1 */和/* USER CODE END 1 */中间增加代码,效果如下
USART_RECEIVE_T u2buff;
HAL_StatusTypeDef USART_Receive_DMA_EN(UART_HandleTypeDef *huart)
{
HAL_StatusTypeDef status;
/* 清除串口空闲中断标志 */
__HAL_UART_CLEAR_IDLEFLAG(huart);
/* 配置非阻塞模式下串口 DMA接收 */
status = HAL_UART_Receive_DMA(huart,u2buff.RX_pData,RX_LEN);
/* 使能串口空闲中断 */
__HAL_UART_ENABLE_IT(huart, UART_IT_IDLE);
return status;
}
void UsartReceive_IDLE(UART_HandleTypeDef *huart)
{
uint16_t temp;
if((__HAL_UART_GET_FLAG(huart,UART_FLAG_IDLE) != RESET))
{
__HAL_UART_CLEAR_IDLEFLAG(huart);
HAL_UART_DMAStop(huart);
temp = (*huart).hdmarx->Instance->NDTR;
if(huart->Instance==USART1)
{
u2buff.RX_Size = RX_LEN - temp;
u2buff.RX_flag = 1;
HAL_UART_Receive_DMA(huart,(uint8_t*)u2buff.RX_pData,RX_LEN);
}
}
}
//实现printf函数重定向到串口1,即支持printf信息到USART1
#ifdef __GNUC__
/* With GCC/RAISONANCE, small printf (option LD Linker->Libraries->Small printf
set to 'Yes') calls __io_putchar() */
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif /* __GNUC__ */
/**
* @brief Retargets the C library printf function to the USART.
* @param None
* @retval None
*/
PUTCHAR_PROTOTYPE
{
/* Place your implementation of fputc here */
/* e.g. write a character to the EVAL_COM1 and Loop until the end of transmission */
//myMutex01Handle
HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xFFFF);
return ch;
}
/* USER CODE END 1 */
在usart.h中增加代码效果如下
?代码为
#define RX_LEN 1024
typedef struct
{
uint8_t RX_flag:1; //IDLE receive flag
uint8_t TX_flag:1; //IDLE send flag
uint16_t RX_Size; //receive length
uint8_t RX_pData[RX_LEN]; //DMA receive buffer
}USART_RECEIVE_T;
extern USART_RECEIVE_T u2buff;
HAL_StatusTypeDef USART_Receive_DMA_EN(UART_HandleTypeDef *huart);
void UsartReceive_IDLE(UART_HandleTypeDef *huart);
?在stm32f4xx_it.c文件中增加串口空闲函数
?代码为
#include "usart.h"
UsartReceive_IDLE(&huart1);
?修改main.c文件
- 添加doit函数申明
- 增加串口DMA使能函数
- 增加串口数据处理逻辑
- 点亮运行灯
代码为
extern void doit(char *text);
USART_Receive_DMA_EN(&huart1);
if(u2buff.RX_flag)
{
doit((char*)u2buff.RX_pData);
u2buff.RX_flag = 0;
}
HAL_GPIO_TogglePin(LED_RUN_GPIO_Port,LED_RUN_Pin);
HAL_Delay(50);
?编译运行下载
5. 测试
连接串口后,发送如下json数据可以看到串口打印接收到得到数据
{"test":{"id":"1","name":"jack","url":"www.zjrobot.com"}}
内存使用率的测试
- 修改test.c文件中的create_objects函数
- main.c文件中增加create_objects函数调用
在test.c文件中create_objects函数中修改成如下内容进行内存使用率测试
?
?
void create_objects()
{
static uint32_t cnt_mem=1;
cJSON *root,*fmt,*img,*thm,*fld;char *out;int i; /* declare a few. */
/* Our "days of the week" array: */
const char *strings[7]={"Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"};
/* Our matrix: */
int numbers[3][3]={{0,-1,0},{1,0,0},{0,0,1}};
/* Our "gallery" item: */
int ids[4]={116,943,234,38793};
/* Our array of "records": */
struct record fields[2]={
{"zip",37.7668,-1.223959e+2,"","SAN FRANCISCO","CA","94107","US"},
{"zip",37.371991,-1.22026e+2,"","SUNNYVALE","CA","94085","US"}};
/* Here we construct some JSON standards, from the JSON site. */
printf("\r\n ***start*** usage %.1f%% cnt %d \r\n",MycJSONmem_perused()/10.0f,cnt_mem);
/* Our "Video" datatype: */
root=cJSON_CreateObject();
cJSON_AddItemToObject(root, "name", cJSON_CreateString("Jack (\"Bee\") Nimble"));
cJSON_AddItemToObject(root, "format", fmt=cJSON_CreateObject());
cJSON_AddStringToObject(fmt,"type", "rect");
cJSON_AddNumberToObject(fmt,"width", 1920);
cJSON_AddNumberToObject(fmt,"height", 1080);
cJSON_AddFalseToObject (fmt,"interlace");
cJSON_AddNumberToObject(fmt,"frame rate", 24);
out=cJSON_Print(root); cJSON_Delete(root); printf("%s\n",out); MycJSONfree(out); /* Print to text, Delete the cJSON, print it, release the string. */
/* Our "days of the week" array: */
root=cJSON_CreateStringArray(strings,7);
out=cJSON_Print(root); cJSON_Delete(root); printf("%s\n",out); MycJSONfree(out);
/* Our matrix: */
root=cJSON_CreateArray();
for (i=0;i<3;i++) cJSON_AddItemToArray(root,cJSON_CreateIntArray(numbers[i],3));
/* cJSON_ReplaceItemInArray(root,1,cJSON_CreateString("Replacement")); */
out=cJSON_Print(root); cJSON_Delete(root); printf("%s\n",out); MycJSONfree(out);
/* Our "gallery" item: */
root=cJSON_CreateObject();
cJSON_AddItemToObject(root, "Image", img=cJSON_CreateObject());
cJSON_AddNumberToObject(img,"Width",800);
cJSON_AddNumberToObject(img,"Height",600);
cJSON_AddStringToObject(img,"Title","View from 15th Floor");
cJSON_AddItemToObject(img, "Thumbnail", thm=cJSON_CreateObject());
cJSON_AddStringToObject(thm, "Url", "http:/*www.example.com/image/481989943");
cJSON_AddNumberToObject(thm,"Height",125);
cJSON_AddStringToObject(thm,"Width","100");
cJSON_AddItemToObject(img,"IDs", cJSON_CreateIntArray(ids,4));
out=cJSON_Print(root); cJSON_Delete(root); printf("%s\n",out); MycJSONfree(out);
/* Our array of "records": */
root=cJSON_CreateArray();
for (i=0;i<2;i++)
{
cJSON_AddItemToArray(root,fld=cJSON_CreateObject());
cJSON_AddStringToObject(fld, "precision", fields[i].precision);
cJSON_AddNumberToObject(fld, "Latitude", fields[i].lat);
cJSON_AddNumberToObject(fld, "Longitude", fields[i].lon);
cJSON_AddStringToObject(fld, "Address", fields[i].address);
cJSON_AddStringToObject(fld, "City", fields[i].city);
cJSON_AddStringToObject(fld, "State", fields[i].state);
cJSON_AddStringToObject(fld, "Zip", fields[i].zip);
cJSON_AddStringToObject(fld, "Country", fields[i].country);
}
/* cJSON_ReplaceItemInObject(cJSON_GetArrayItem(root,1),"City",cJSON_CreateIntArray(ids,4)); */
printf("\r\n ***mem*** usage %.1f%% cnt %d \r\n",MycJSONmem_perused()/10.0f,cnt_mem);
out=cJSON_Print(root); cJSON_Delete(root); printf("%s\n",out); MycJSONfree(out);
printf("\r\n ***end*** usage %.1f%% cnt %d \r\n",MycJSONmem_perused()/10.0f,cnt_mem);
cnt_mem++;
}
在main.c中增加create_objects函数调用和申明
?编译下载后,串口输出测试了超过20000次,没有出现内存泄漏等问题
?源码下载
https://download.csdn.net/download/xingzhewanfu/21495545?spm=1001.2014.3001.5501???????
|