[MM32硬件] 【灵动微电子MM32F0121测评】10、 json格式处理及EEPROM读写

[复制链接]
 楼主| sujingliang 发表于 2025-6-17 10:20 | 显示全部楼层 |阅读模式
本帖最后由 sujingliang 于 2025-6-17 10:22 编辑

本文实现用cJSON处理json格式处理,EEPROM驱动及读写,通过串口发送json格式数据,写入EEPROM

板载EEPROM为24C02,容量2k bit

处理json需要用cJSON,可以从:https://gitee.com/tianbeibei/cJSON下载。

同样我没有下载,在自己机器上找到了一份cJSON.c,cJSON.h复制到utilities\cjson下备用,并将cJSON.c加入工程

一、EEPROM驱动
官方例程中关于EEPROM的驱动例程十分丰富,可参考以下路径的例程:
LibSamples_MM32F0120_V1.13.4\Samples\LibSamples\I2C

I2C配置
#define EEPROM_I2C_ADDRESS      0xA0
  1. void eeprom_I2C_Configure(void)
  2. {
  3.     GPIO_InitTypeDef GPIO_InitStruct;
  4.     I2C_InitTypeDef  I2C_InitStruct;

  5.     RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE);

  6.     I2C_DeInit(I2C1);

  7.     I2C_StructInit(&I2C_InitStruct);
  8.     I2C_InitStruct.I2C_Mode       = I2C_MODE_MASTER;
  9.     I2C_InitStruct.I2C_OwnAddress = I2C_OWN_ADDRESS;
  10.     I2C_InitStruct.I2C_ClockSpeed = 100000;
  11.     I2C_Init(I2C1, &I2C_InitStruct);

  12.     I2C_TargetAddressConfig(I2C1, EEPROM_I2C_ADDRESS);

  13.     RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOB, ENABLE);

  14.     GPIO_PinAFConfig(GPIOB, GPIO_PinSource10, GPIO_AF_1);
  15.     GPIO_PinAFConfig(GPIOB, GPIO_PinSource11, GPIO_AF_1);

  16.     GPIO_StructInit(&GPIO_InitStruct);
  17.     GPIO_InitStruct.GPIO_Pin   = GPIO_Pin_10 | GPIO_Pin_11;
  18.     GPIO_InitStruct.GPIO_Speed = GPIO_Speed_High;
  19.     GPIO_InitStruct.GPIO_Mode  = GPIO_Mode_AF_OD;
  20.     GPIO_Init(GPIOB, &GPIO_InitStruct);

  21.     I2C_Cmd(I2C1, ENABLE);
  22. }
I2C写数据
  1. void I2C_TxData_Polling(uint8_t *Buffer, uint8_t Length)
  2. {
  3.     uint8_t i = 0;

  4.     for (i = 0; i < Length; i++)
  5.     {
  6.         I2C_SendData(I2C1, Buffer[i]);

  7.         while (RESET == I2C_GetFlagStatus(I2C1, I2C_STATUS_FLAG_TFE))
  8.         {
  9.         }
  10.     }
  11. }
I2C读数据
  1. void I2C_RxData_Polling(uint8_t *Buffer, uint16_t Length)
  2. {
  3.     uint8_t i = 0;

  4.     for (i = 0; i < Length; i++)
  5.     {
  6.         I2C_ReadCmd(I2C1);

  7.         while (RESET == I2C_GetFlagStatus(I2C1, I2C_STATUS_FLAG_RFNE))
  8.         {
  9.         }

  10.         Buffer[i] = I2C_ReceiveData(I2C1);
  11.     }
  12. }


EEPROM写页函数
  1. void EEPROM_WritePage(uint8_t Address, uint8_t *Buffer, uint8_t Length)
  2. {
  3.     I2C_TxData_Polling((uint8_t *)&Address, 0x01);

  4.     I2C_TxData_Polling((uint8_t *)Buffer, Length);

  5.     while (RESET == I2C_GetFlagStatus(I2C1, I2C_STATUS_FLAG_TFE))
  6.     {
  7.     }

  8.     I2C_GenerateSTOP(I2C1);

  9.     while (RESET == I2C_GetFlagStatus(I2C1, I2C_STATUS_FLAG_TFE))
  10.     {
  11.     }
  12. }
EEPROM读数据函数
  1. void EEPROM_ReadData(uint8_t Address, uint8_t *Buffer, uint8_t Length)
  2. {
  3.     I2C_TxData_Polling((uint8_t *)&Address, 0x01);

  4.     I2C_RxData_Polling((uint8_t *)Buffer, Length);

  5.     I2C_GenerateSTOP(I2C1);

  6.     while (RESET == I2C_GetFlagStatus(I2C1, I2C_STATUS_FLAG_TFE))
  7.     {
  8.     }
  9. }
EEPROM写数据函数
  1. void EEPROM_WriteData(uint8_t Address, uint8_t *Buffer, uint8_t Length)
  2. {
  3.     uint8_t Start = 0;
  4.     uint8_t StartCount = 0, PageNumber = 0, FinalCount = 0;

  5.     if ((Address % EEPROM_PAGE_SIZE) == 0)
  6.     {
  7.         StartCount = 0;
  8.         PageNumber = Length / EEPROM_PAGE_SIZE;
  9.         FinalCount = Length % EEPROM_PAGE_SIZE;
  10.     }
  11.     else
  12.     {
  13.         Start = Address % EEPROM_PAGE_SIZE;

  14.         if (((Start + Length) / EEPROM_PAGE_SIZE) == 0)
  15.         {
  16.             StartCount = Length;
  17.             PageNumber = 0;
  18.             FinalCount = 0;
  19.         }
  20.         else
  21.         {
  22.             StartCount = EEPROM_PAGE_SIZE - Start;
  23.             PageNumber = (Length - StartCount) / EEPROM_PAGE_SIZE;
  24.             FinalCount = (Length - StartCount) % EEPROM_PAGE_SIZE;
  25.         }
  26.     }

  27.     if (StartCount)
  28.     {
  29.         EEPROM_WritePage(Address, Buffer, StartCount);

  30.         Address += StartCount;
  31.         Buffer  += StartCount;

  32.         PLATFORM_DelayMS(50);
  33.     }

  34.     while (PageNumber--)
  35.     {
  36.         EEPROM_WritePage(Address, Buffer, EEPROM_PAGE_SIZE);

  37.         Address += EEPROM_PAGE_SIZE;
  38.         Buffer  += EEPROM_PAGE_SIZE;

  39.         PLATFORM_DelayMS(50);
  40.     }

  41.     if (FinalCount)
  42.     {
  43.         EEPROM_WritePage(Address, Buffer, FinalCount);
  44.     }
  45. }


二、json数据处理
数据格式定义:
+--------+--------+-------------------+--------+

| 长度H  | 长度L |  JSON原始数据 | 校验位|
| (1B)    | (1B)   |   (N字节)          | (1B)   |
+--------+--------+-------------------+--------+
有效的数据示例:{"device":"MM32F0120","version":1.12,"enable":1}
save_config_to_eeprom()生成初始的json数据保存到EEPROM中
  1. //JSON数据格式化存储
  2. void save_config_to_eeprom(void) {
  3.     // 1. 创建JSON对象
  4.     cJSON *root = cJSON_CreateObject();
  5.     cJSON_AddStringToObject(root, "device", "MM32F0120");
  6.     cJSON_AddNumberToObject(root, "version", 1.12);
  7.     cJSON_AddBoolToObject(root, "enable", true);
  8.    
  9.     // 2. 序列化为字符串
  10.     char *json_str = cJSON_PrintUnformatted(root);
  11.     uint16_t json_len = strlen(json_str);
  12.    
  13.     // 3. 添加长度头(2字节)和数据校验(1字节)
  14.     uint8_t *buffer = malloc(json_len + 3);
  15.     buffer[0] = json_len >> 8;    // 长度高字节
  16.     buffer[1] = json_len & 0xFF;  // 长度低字节
  17.    
  18.     // 计算简单校验和
  19.     uint8_t checksum = 0;
  20.     for(int i=0; i<json_len; i++) {
  21.         buffer[i+2] = json_str[i];
  22.         checksum ^= json_str[i];  // XOR校验
  23.     }
  24.     buffer[json_len+2] = checksum;
  25.    
  26.     // 4. 写入EEPROM
  27.     EEPROM_WriteData(JSON_START_ADDR, buffer, json_len+3);
  28.    
  29.     // 5. 释放资源
  30.     free(buffer);
  31.     free(json_str);
  32.     cJSON_Delete(root);
  33. }


load_config_from_eeprom()从EEPROM读取JSON数据
  1. //从EEPROM读取JSON数据

  2. cJSON* load_config_from_eeprom(void) {
  3.     // 1. 读取长度头
  4.     uint8_t len_buf[2];
  5.     EEPROM_ReadData(JSON_START_ADDR, len_buf, 2);
  6.     uint16_t json_len = (len_buf[0] << 8) | len_buf[1];
  7.    
  8.     // 2. 读取JSON数据和校验位
  9.     uint8_t *buffer = malloc(json_len + 1);
  10.     EEPROM_ReadData(JSON_START_ADDR+2, buffer, json_len+1);
  11.     printf("%s\r\n",buffer);
  12.                
  13.     // 3. 校验数据
  14.     uint8_t checksum = 0;
  15.     for(int i=0; i<json_len; i++) {
  16.         checksum ^= buffer[i];
  17.     }
  18.    
  19.     if(checksum != buffer[json_len]) {
  20.         free(buffer);
  21.         return NULL;  // 校验失败
  22.     }
  23.    
  24.     // 4. 解析JSON
  25.     buffer[json_len] = '\0';  // 添加字符串结束符
  26.     cJSON *root = cJSON_Parse((char*)buffer);
  27.    
  28.     free(buffer);
  29.     return root;
  30. }
通过以上实现了向EEPROM写JSON数据和读取数据,可以通过以下程序验证:

  1. void eeprom_Sample(void)
  2. {
  3. eeprom_I2C_Configure()
  4. save_config_to_eeprom();
  5. PLATFORM_DelayMS(500);

  6. // 读取配置
  7.     cJSON *config = load_config_from_eeprom();
  8.     if(config) {
  9.         cJSON *device = cJSON_GetObjectItem(config, "device");
  10.         cJSON *version = cJSON_GetObjectItem(config, "version");
  11.         printf("Device: %s, Ver: %.2f\n", device->valuestring, version->valuedouble);
  12.         cJSON_Delete(config);
  13.     } else {
  14.         printf("Config load failed!\n");
  15.     }
  16. while(1)
  17. {
  18. }
  19. }


三、接收串口发过来的json数据保存到EEPROM

这里使用了UART不定长数据接收,可参考:https://bbs.21ic.com/icview-3461324-1-1.html

UART输入判断
  1. static void Process_Uart_Input(void)
  2. {
  3.         uint8_t frame_data[MAX_JSON_LEN];
  4.         uint8_t i,frame_len;
  5.         if(USART_RxStruct.Buffer[0]=='{'&&USART_RxStruct.Buffer[USART_RxStruct.CurrentCount-3]=='}')
  6.         {
  7.                 frame_len=USART_RxStruct.CurrentCount-2;
  8.                 for(i = 0; i < frame_len; i++) {
  9.         frame_data[i] = USART_RxStruct.Buffer[i];
  10.     }
  11.                 frame_data[frame_len]='\0';
  12.     if(is_json_valid(frame_data, frame_len)) {
  13.                         save_json_to_eeprom(frame_data,frame_len);
  14.                 }else
  15.                 {
  16.                         printf("json format is invalid\r\n");
  17.                 }
  18.         }
  19.         USART_RxData_Interrupt(255);
  20.         USART_ITConfig(USART1, USART_IT_IDLE, ENABLE);
  21. }
对UART传过来的数据进行JSON格式验证
  1. // 验证JSON格式
  2. uint8_t is_json_valid(const uint8_t *data, uint16_t len) {
  3.     // 确保以NULL结尾
  4.     char *json_str = malloc(len + 1);
  5.     if(!json_str) return 0;
  6.    
  7.     memcpy(json_str, data, len);
  8.     json_str[len] = '\0';
  9.                 printf("json_str:%s\r\n",json_str);
  10.     // 解析JSON
  11.     cJSON *root = cJSON_Parse(json_str);
  12.     uint8_t result = 0;
  13.    
  14.     if(root) {
  15.         // 验证基本结构
  16.         cJSON *device = cJSON_GetObjectItem(root, "device");
  17.         cJSON *version = cJSON_GetObjectItem(root, "version");
  18.         cJSON *enable = cJSON_GetObjectItem(root, "enable");
  19.         printf("device=%s,version=%s,enable=%s\r\n",device->valuestring,version->valuestring,enable->valuestring);
  20.         if(cJSON_IsString(device) && cJSON_IsNumber(version) &&
  21.            (cJSON_IsNumber(enable) || cJSON_IsBool(enable))) {
  22.             result = 1;
  23.         }
  24.         
  25.         cJSON_Delete(root);
  26.     }
  27.    
  28.     free(json_str);
  29.     return result;
  30. }
对UART接收的JSON打包,添加长度头和校验
  1. // 封装JSON数据(添加长度头和校验)
  2. uint16_t package_json(const uint8_t *json, uint16_t json_len, uint8_t *output) {
  3.     if(json_len > MAX_JSON_LEN) return 0;
  4.    
  5.     // 添加长度头(小端格式)
  6.     output[0] =  (json_len >> 8) & 0xFF; // 高字节
  7.     output[1] =                json_len & 0xFF;        // 低字节
  8.    
  9.     // 复制JSON数据
  10.     memcpy(&output[2], json, json_len);
  11.    
  12.     // 计算并添加校验位(异或校验)
  13.     uint8_t checksum = 0;
  14.     for(uint16_t i = 0; i < json_len; i++) {
  15.         checksum ^= json[i];
  16.     }
  17.     output[2 + json_len] = checksum;
  18.    
  19.     return json_len + 3; // 总长度 = 2(头) + json_len + 1(校验)
  20. }
保存JSON到EEPROM
  1. // 保存JSON到EEPROM
  2. void save_json_to_eeprom(const uint8_t *json, uint16_t len) {
  3.     // 封装数据
  4.     uint8_t packaged_data[MAX_JSON_LEN + 3];
  5.     uint16_t packaged_len = package_json(json, len, packaged_data);
  6.     printf("packaged_data=%s",packaged_data);
  7.     if(packaged_len > 0) {
  8.         // 写入EEPROM
  9.         EEPROM_WriteData(JSON_START_ADDR, packaged_data, packaged_len);
  10.         printf("JSON saved successfully\n");
  11.     }
  12. }
写个验证程序
  1. void eeprom_Sample(void)
  2. {
  3.                 USART_RxStruct.CompleteFlag = 0;
  4.                 USART_TxStruct.CompleteFlag = 1;
  5.     USART_Configure(115200);
  6.                 eeprom_I2C_Configure();
  7.         
  8.                 // 保存配置
  9.     //save_config_to_eeprom();
  10.     PLATFORM_DelayMS(500);
  11.     // 读取配置
  12.     cJSON *config = load_config_from_eeprom();
  13.     if(config) {
  14.         cJSON *device = cJSON_GetObjectItem(config, "device");
  15.         cJSON *version = cJSON_GetObjectItem(config, "version");
  16.         printf("Device: %s, Ver: %.2f\n", device->valuestring, version->valuedouble);
  17.         cJSON_Delete(config);
  18.     } else {
  19.         printf("Config load failed!\n");
  20.     }
  21.                
  22.                 while(1)
  23.                 {
  24.                         
  25.                                 if (1== USART_RxStruct.CompleteFlag&&USART_RxStruct.CurrentCount>0)
  26.                                 {
  27.                                         USART_RxStruct.CompleteFlag=0;
  28.                                         Process_Uart_Input();
  29.                                 }
  30.                                 PLATFORM_LED_Toggle(LED1);
  31.                                 PLATFORM_LED_Toggle(LED2);
  32.         PLATFORM_DelayMS(100);
  33.                 }
  34. }
tutieshi_640x594_8s.gif
1、上电,显示之前保存的json文件内容,其中"version":3.12
2、通过串口发送{"device":"MM32F0120","version":8.88,"enable":true},改变"version"为8.88
3、再次RESET,显示修改后的"version"为8.88








AdaMaYun 发表于 2025-7-31 17:57 | 显示全部楼层
json格式处理及EEPROM读写很不错
您需要登录后才可以回帖 登录 | 注册

本版积分规则

84

主题

146

帖子

3

粉丝
快速回复 在线客服 返回列表 返回顶部

84

主题

146

帖子

3

粉丝
快速回复 在线客服 返回列表 返回顶部