提交全部资料

This commit is contained in:
2026-03-31 15:40:09 +08:00
parent 46f0e8d6c2
commit 4d2204bf84
235 changed files with 101676 additions and 0 deletions

View File

@@ -0,0 +1,315 @@
/*
FreeRTOS V9.0.0 - Copyright (C) 2016 Real Time Engineers Ltd.
All rights reserved
VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION.
This file is part of the FreeRTOS distribution.
FreeRTOS is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License (version 2) as published by the
Free Software Foundation >>>> AND MODIFIED BY <<<< the FreeRTOS exception.
***************************************************************************
>>! NOTE: The modification to the GPL is included to allow you to !<<
>>! distribute a combined work that includes FreeRTOS without being !<<
>>! obliged to provide the source code for proprietary components !<<
>>! outside of the FreeRTOS kernel. !<<
***************************************************************************
FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. Full license text is available on the following
link: http://www.freertos.org/a00114.html
***************************************************************************
* *
* FreeRTOS provides completely free yet professionally developed, *
* robust, strictly quality controlled, supported, and cross *
* platform software that is more than just the market leader, it *
* is the industry's de facto standard. *
* *
* Help yourself get started quickly while simultaneously helping *
* to support the FreeRTOS project by purchasing a FreeRTOS *
* tutorial book, reference manual, or both: *
* http://www.FreeRTOS.org/Documentation *
* *
***************************************************************************
http://www.FreeRTOS.org/FAQHelp.html - Having a problem? Start by reading
the FAQ page "My application does not run, what could be wrong?". Have you
defined configASSERT()?
http://www.FreeRTOS.org/support - In return for receiving this top quality
embedded software for free we request you assist our global community by
participating in the support forum.
http://www.FreeRTOS.org/training - Investing in training allows your team to
be as productive as possible as early as possible. Now you can receive
FreeRTOS training directly from Richard Barry, CEO of Real Time Engineers
Ltd, and the world's leading authority on the world's leading RTOS.
http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products,
including FreeRTOS+Trace - an indispensable productivity tool, a DOS
compatible FAT file system, and our tiny thread aware UDP/IP stack.
http://www.FreeRTOS.org/labs - Where new FreeRTOS products go to incubate.
Come and try FreeRTOS+TCP, our new open source TCP/IP stack for FreeRTOS.
http://www.OpenRTOS.com - Real Time Engineers ltd. license FreeRTOS to High
Integrity Systems ltd. to sell under the OpenRTOS brand. Low cost OpenRTOS
licenses offer ticketed support, indemnification and commercial middleware.
http://www.SafeRTOS.com - High Integrity Systems also provide a safety
engineered and independently SIL3 certified version for use in safety and
mission critical applications that require provable dependability.
*/
#ifndef FREERTOS_CONFIG_H
#define FREERTOS_CONFIG_H
#include "stm32f10x.h"
#include "bsp_usart.h"
//针对不同的编译器调用不同的stdint.h文件
#if defined(__ICCARM__) || defined(__CC_ARM) || defined(__GNUC__)
#include <stdint.h>
extern uint32_t SystemCoreClock;
#endif
//断言
#define vAssertCalled(char,int) printf("Error:%s,%d\r\n",char,int)
#define configASSERT(x) if((x)==0) vAssertCalled(__FILE__,__LINE__)
/************************************************************************
* FreeRTOS基础配置配置选项
*********************************************************************/
/* 置1RTOS使用抢占式调度器置0RTOS使用协作式调度器时间片
*
* 注:在多任务管理机制上,操作系统可以分为抢占式和协作式两种。
* 协作式操作系统是任务主动释放CPU后切换到下一个任务。
* 任务切换的时机完全取决于正在运行的任务。
*/
#define configUSE_PREEMPTION 1
//1使能时间片调度(默认式使能的)
#define configUSE_TIME_SLICING 1
/* 某些运行FreeRTOS的硬件有两种方法选择下一个要执行的任务
* 通用方法和特定于硬件的方法(以下简称“特殊方法”)。
*
* 通用方法:
* 1.configUSE_PORT_OPTIMISED_TASK_SELECTION 为 0 或者硬件不支持这种特殊方法。
* 2.可以用于所有FreeRTOS支持的硬件
* 3.完全用C实现效率略低于特殊方法。
* 4.不强制要求限制最大可用优先级数目
* 特殊方法:
* 1.必须将configUSE_PORT_OPTIMISED_TASK_SELECTION设置为1。
* 2.依赖一个或多个特定架构的汇编指令(一般是类似计算前导零[CLZ]指令)。
* 3.比通用方法更高效
* 4.一般强制限定最大可用优先级数目为32
* 一般是硬件计算前导零指令如果所使用的MCU没有这些硬件指令的话此宏应该设置为0
*/
#define configUSE_PORT_OPTIMISED_TASK_SELECTION 1
/* 置1使能低功耗tickless模式置0保持系统节拍tick中断一直运行
* 假设开启低功耗的话可能会导致下载出现问题,因为程序在睡眠中,可用以下办法解决
*
* 下载方法:
* 1.将开发版正常连接好
* 2.按住复位按键,点击下载瞬间松开复位按键
*
* 1.通过跳线帽将 BOOT 0 接高电平(3.3V)
* 2.重新上电,下载
*
* 1.使用FlyMcu擦除一下芯片然后进行下载
* STMISP -> 清除芯片(z)
*/
#define configUSE_TICKLESS_IDLE 0
/*
* 写入实际的CPU内核时钟频率也就是CPU指令执行频率通常称为Fclk
* Fclk为供给CPU内核的时钟信号我们所说的cpu主频为 XX MHz
* 就是指的这个时钟信号相应的1/Fclk即为cpu时钟周期
*/
#define configCPU_CLOCK_HZ (SystemCoreClock)
//RTOS系统节拍中断的频率。即一秒中断的次数每次中断RTOS都会进行任务调度
#define configTICK_RATE_HZ (( TickType_t )1000)
//可使用的最大优先级
#define configMAX_PRIORITIES (32)
//空闲任务使用的堆栈大小
#define configMINIMAL_STACK_SIZE ((unsigned short)128)
//任务名字字符串长度
#define configMAX_TASK_NAME_LEN (16)
//系统节拍计数器变量数据类型1表示为16位无符号整形0表示为32位无符号整形
#define configUSE_16_BIT_TICKS 0
//空闲任务放弃CPU使用权给其他同优先级的用户任务
#define configIDLE_SHOULD_YIELD 1
//启用队列
#define configUSE_QUEUE_SETS 0
//开启任务通知功能,默认开启
#define configUSE_TASK_NOTIFICATIONS 1
//使用互斥信号量
#define configUSE_MUTEXES 1
//使用递归互斥信号量
#define configUSE_RECURSIVE_MUTEXES 0
//为1时使用计数信号量
#define configUSE_COUNTING_SEMAPHORES 0
/* 设置可以注册的信号量和消息队列个数 */
#define configQUEUE_REGISTRY_SIZE 10
#define configUSE_APPLICATION_TASK_TAG 0
/*****************************************************************
FreeRTOS与内存申请有关配置选项
*****************************************************************/
//支持动态内存申请
#define configSUPPORT_DYNAMIC_ALLOCATION 1
//支持静态内存
#define configSUPPORT_STATIC_ALLOCATION 0
//系统所有总的堆大小
#define configTOTAL_HEAP_SIZE ((size_t)(10*1024))
/***************************************************************
FreeRTOS与钩子函数有关的配置选项
**************************************************************/
/* 置1使用空闲钩子Idle Hook类似于回调函数置0忽略空闲钩子
*
* 空闲任务钩子是一个函数,这个函数由用户来实现,
* FreeRTOS规定了函数的名字和参数void vApplicationIdleHook(void )
* 这个函数在每个空闲任务周期都会被调用
* 对于已经删除的RTOS任务空闲任务可以释放分配给它们的堆栈内存。
* 因此必须保证空闲任务可以被CPU执行
* 使用空闲钩子函数设置CPU进入省电模式是很常见的
* 不可以调用会引起空闲任务阻塞的API函数
*/
#define configUSE_IDLE_HOOK 0
/* 置1使用时间片钩子Tick Hook置0忽略时间片钩子
*
*
* 时间片钩子是一个函数,这个函数由用户来实现,
* FreeRTOS规定了函数的名字和参数void vApplicationTickHook(void )
* 时间片中断可以周期性的调用
* 函数必须非常短小,不能大量使用堆栈,
* 不能调用以”FromISR" 或 "FROM_ISR”结尾的API函数
*/
/*xTaskIncrementTick函数是在xPortSysTickHandler中断函数中被调用的。因此vApplicationTickHook()函数执行的时间必须很短才行*/
#define configUSE_TICK_HOOK 0
//使用内存申请失败钩子函数
#define configUSE_MALLOC_FAILED_HOOK 0
/*
* 大于0时启用堆栈溢出检测功能如果使用此功能
* 用户必须提供一个栈溢出钩子函数,如果使用的话
* 此值可以为1或者2因为有两种栈溢出检测方法 */
#define configCHECK_FOR_STACK_OVERFLOW 0
/********************************************************************
FreeRTOS与运行时间和任务状态收集有关的配置选项
**********************************************************************/
//启用运行时间统计功能
#define configGENERATE_RUN_TIME_STATS 0
//启用可视化跟踪调试
#define configUSE_TRACE_FACILITY 0
/* 与宏configUSE_TRACE_FACILITY同时为1时会编译下面3个函数
* prvWriteNameToBuffer()
* vTaskList(),
* vTaskGetRunTimeStats()
*/
#define configUSE_STATS_FORMATTING_FUNCTIONS 1
/********************************************************************
FreeRTOS与协程有关的配置选项
*********************************************************************/
//启用协程启用协程以后必须添加文件croutine.c
#define configUSE_CO_ROUTINES 0
//协程的有效优先级数目
#define configMAX_CO_ROUTINE_PRIORITIES ( 2 )
/***********************************************************************
FreeRTOS与软件定时器有关的配置选项
**********************************************************************/
//启用软件定时器
#define configUSE_TIMERS 1
//软件定时器优先级
#define configTIMER_TASK_PRIORITY (configMAX_PRIORITIES-1)
//软件定时器队列长度
#define configTIMER_QUEUE_LENGTH 10
//软件定时器任务堆栈大小
#define configTIMER_TASK_STACK_DEPTH (configMINIMAL_STACK_SIZE*1)
/************************************************************
FreeRTOS可选函数配置选项
************************************************************/
#define INCLUDE_xTaskGetSchedulerState 1
#define INCLUDE_vTaskPrioritySet 1
#define INCLUDE_uxTaskPriorityGet 1
#define INCLUDE_vTaskDelete 1
#define INCLUDE_vTaskCleanUpResources 1
#define INCLUDE_vTaskSuspend 1
#define INCLUDE_vTaskDelayUntil 1
#define INCLUDE_vTaskDelay 1
#define INCLUDE_eTaskGetState 1
#define INCLUDE_xTimerPendFunctionCall 0
//#define INCLUDE_xTaskGetCurrentTaskHandle 1
//#define INCLUDE_uxTaskGetStackHighWaterMark 0
//#define INCLUDE_xTaskGetIdleTaskHandle 0
/******************************************************************
FreeRTOS与中断有关的配置选项
******************************************************************/
#ifdef __NVIC_PRIO_BITS
#define configPRIO_BITS __NVIC_PRIO_BITS
#else
#define configPRIO_BITS 4
#endif
//中断最低优先级
#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY 15
//系统可管理的最高中断优先级
#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 5
#define configKERNEL_INTERRUPT_PRIORITY ( configLIBRARY_LOWEST_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) ) /* 240 */
#define configMAX_SYSCALL_INTERRUPT_PRIORITY ( configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )
/****************************************************************
FreeRTOS与中断服务函数有关的配置选项
****************************************************************/
#define xPortPendSVHandler PendSV_Handler
#define vPortSVCHandler SVC_Handler
/* 以下为使用Percepio Tracealyzer需要的东西不需要时将 configUSE_TRACE_FACILITY 定义为 0 */
#if ( configUSE_TRACE_FACILITY == 1 )
#include "trcRecorder.h"
#define INCLUDE_xTaskGetCurrentTaskHandle 1 // 启用一个可选函数(该函数被 Trace源码使用默认该值为0 表示不用)
#endif
#endif /* FREERTOS_CONFIG_H */

View File

@@ -0,0 +1,107 @@
/*
* This file is part of the EasyFlash Library.
*
* Copyright (c) 2014-2019, Armink, <armink.ztl@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* 'Software'), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* Function: It is an head file for this library. You can see all be called functions.
* Created on: 2014-09-10
*/
#ifndef EASYFLASH_H_
#define EASYFLASH_H_
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#include <ef_cfg.h>
#include <ef_def.h>
#ifdef __cplusplus
extern "C" {
#endif
/* easyflash.c */
EfErrCode easyflash_init(void);
#ifdef EF_USING_ENV
/* only supported on ef_env.c */
size_t ef_get_env_blob(const char *key, void *value_buf, size_t buf_len, size_t *saved_value_len);
bool ef_get_env_obj(const char *key, env_node_obj_t env);
size_t ef_read_env_value(env_node_obj_t env, uint8_t *value_buf, size_t buf_len);
EfErrCode ef_set_env_blob(const char *key, const void *value_buf, size_t buf_len);
/* ef_env.c, ef_env_legacy_wl.c and ef_env_legacy.c */
EfErrCode ef_load_env(void);
void ef_print_env(void);
char *ef_get_env(const char *key);
EfErrCode ef_set_env(const char *key, const char *value);
EfErrCode ef_del_env(const char *key);
EfErrCode ef_save_env(void);
EfErrCode ef_env_set_default(void);
size_t ef_get_env_write_bytes(void);
EfErrCode ef_set_and_save_env(const char *key, const char *value);
EfErrCode ef_del_and_save_env(const char *key);
#endif
#ifdef EF_USING_IAP
/* ef_iap.c */
EfErrCode ef_erase_bak_app(size_t app_size);
EfErrCode ef_erase_user_app(uint32_t user_app_addr, size_t user_app_size);
EfErrCode ef_erase_spec_user_app(uint32_t user_app_addr, size_t app_size,
EfErrCode (*app_erase)(uint32_t addr, size_t size));
EfErrCode ef_erase_bl(uint32_t bl_addr, size_t bl_size);
EfErrCode ef_write_data_to_bak(uint8_t *data, size_t size, size_t *cur_size,
size_t total_size);
EfErrCode ef_copy_app_from_bak(uint32_t user_app_addr, size_t app_size);
EfErrCode ef_copy_spec_app_from_bak(uint32_t user_app_addr, size_t app_size,
EfErrCode (*app_write)(uint32_t addr, const uint32_t *buf, size_t size));
EfErrCode ef_copy_bl_from_bak(uint32_t bl_addr, size_t bl_size);
uint32_t ef_get_bak_app_start_addr(void);
#endif
#ifdef EF_USING_LOG
/* ef_log.c */
EfErrCode ef_log_read(size_t index, uint32_t *log, size_t size);
EfErrCode ef_log_write(const uint32_t *log, size_t size);
EfErrCode ef_log_clean(void);
size_t ef_log_get_used_size(void);
#endif
/* ef_utils.c */
uint32_t ef_calc_crc32(uint32_t crc, const void *buf, size_t size);
/* ef_port.c */
EfErrCode ef_port_read(uint32_t addr, uint32_t *buf, size_t size);
EfErrCode ef_port_erase(uint32_t addr, size_t size);
EfErrCode ef_port_write(uint32_t addr, const uint32_t *buf, size_t size);
void ef_port_env_lock(void);
void ef_port_env_unlock(void);
void ef_log_debug(const char *file, const long line, const char *format, ...);
void ef_log_info(const char *format, ...);
void ef_print(const char *format, ...);
#ifdef __cplusplus
}
#endif
#endif /* EASYFLASH_H_ */

View File

@@ -0,0 +1,89 @@
/*
* This file is part of the EasyFlash Library.
*
* Copyright (c) 2015-2019, Armink, <armink.ztl@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* 'Software'), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* Function: It is the configure head file for this library.
* Created on: 2015-07-14
*/
#ifndef EF_CFG_H_
#define EF_CFG_H_
#include <stm32f10x_conf.h>
/* using ENV function, default is NG (Next Generation) mode start from V4.0 */
#define EF_USING_ENV
/* using IAP function */
//#define EF_USING_IAP
/* using save log function */
// #define EF_USING_LOG
/* page size for stm32 flash */
#if defined(STM32F10X_LD) || defined(STM32F10X_LD_VL) || defined (STM32F10X_MD) || defined (STM32F10X_MD_VL)
#define PAGE_SIZE 1024
#else
#define PAGE_SIZE 2048
#endif
/* the minimum size of flash erasure */
#define EF_ERASE_MIN_SIZE PAGE_SIZE /* it is one page for STM3210x */
/* the flash write granularity, unit: bit
* only support 1(nor flash)/ 8(stm32f4)/ 32(stm32f1)/ 64(stm32l4) */
#define EF_WRITE_GRAN 32
/*
*
* This all Backup Area Flash storage index. All used flash area configure is under here.
* |----------------------------| Storage Size
* | Environment variables area | ENV area size @see ENV_AREA_SIZE
* |----------------------------|
* | Saved log area | Log area size @see LOG_AREA_SIZE
* |----------------------------|
* |(IAP)Downloaded application | IAP already downloaded application, unfixed size
* |----------------------------|
*
* @note all area sizes must be aligned with EF_ERASE_MIN_SIZE
*
* The EasyFlash add the NG (Next Generation) mode start from V4.0. All old mode before V4.0, called LEGACY mode.
*
* - NG (Next Generation) mode is default mode from V4.0. It's easy to settings, only defined the ENV_AREA_SIZE.
* - The LEGACY mode has been DEPRECATED. It is NOT RECOMMENDED to continue using.
* Beacuse it will use ram to buffer the ENV and spend more flash erase times.
* If you want use it please using the V3.X version.
*/
/* backup area start address */
#define EF_START_ADDR (FLASH_BASE + 55 * 1024) /* from the chip position: 100KB */
/* ENV area size. It's at least one empty sector for GC. So it's definination must more then or equal 2 flash sector size. */
#define ENV_AREA_SIZE (2 * EF_ERASE_MIN_SIZE) /* 8K */
/* saved log area size */
//#define LOG_AREA_SIZE (5 * EF_ERASE_MIN_SIZE) /* 20K */
/* print debug information of flash */
#define PRINT_DEBUG
#endif /* EF_CFG_H_ */

View File

@@ -0,0 +1,124 @@
/*
* This file is part of the EasyFlash Library.
*
* Copyright (c) 2019-2020, Armink, <armink.ztl@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* 'Software'), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* Function: It is the definitions head file for this library.
* Created on: 2019-11-20
*/
#ifndef EF_DEF_H_
#define EF_DEF_H_
#ifdef __cplusplus
extern "C" {
#endif
/* EasyFlash software version number */
#define EF_SW_VERSION "4.1.99"
#define EF_SW_VERSION_NUM 0x40199
/*
* ENV version number defined by user.
* Please change it when your firmware add a new ENV to default_env_set.
*/
#ifndef EF_ENV_VER_NUM
#define EF_ENV_VER_NUM 0
#endif
/* the ENV max name length must less then it */
#ifndef EF_ENV_NAME_MAX
#define EF_ENV_NAME_MAX 32
#endif
/* EasyFlash debug print function. Must be implement by user. */
#ifdef PRINT_DEBUG
#define EF_DEBUG(...) ef_log_debug(__FILE__, __LINE__, __VA_ARGS__)
#else
#define EF_DEBUG(...)
#endif
/* EasyFlash routine print function. Must be implement by user. */
#define EF_INFO(...) ef_log_info(__VA_ARGS__)
/* EasyFlash assert for developer. */
#define EF_ASSERT(EXPR) \
if (!(EXPR)) \
{ \
EF_DEBUG("(%s) has assert failed at %s.\n", #EXPR, __FUNCTION__); \
while (1); \
}
typedef struct _ef_env {
char *key;
void *value;
size_t value_len;
} ef_env, *ef_env_t;
/* EasyFlash error code */
typedef enum {
EF_NO_ERR,
EF_ERASE_ERR,
EF_READ_ERR,
EF_WRITE_ERR,
EF_ENV_NAME_ERR,
EF_ENV_NAME_EXIST,
EF_ENV_FULL,
EF_ENV_INIT_FAILED,
} EfErrCode;
/* the flash sector current status */
typedef enum {
EF_SECTOR_EMPTY,
EF_SECTOR_USING,
EF_SECTOR_FULL,
} EfSecrorStatus;
enum env_status {
ENV_UNUSED,
ENV_PRE_WRITE,
ENV_WRITE,
ENV_PRE_DELETE,
ENV_DELETED,
ENV_ERR_HDR,
ENV_STATUS_NUM,
};
typedef enum env_status env_status_t;
struct env_node_obj {
env_status_t status; /**< ENV node status, @see node_status_t */
bool crc_is_ok; /**< ENV node CRC32 check is OK */
uint8_t name_len; /**< name length */
uint32_t magic; /**< magic word(`K`, `V`, `4`, `0`) */
uint32_t len; /**< ENV node total length (header + name + value), must align by EF_WRITE_GRAN */
uint32_t value_len; /**< value length */
char name[EF_ENV_NAME_MAX]; /**< name */
struct {
uint32_t start; /**< ENV node start address */
uint32_t value; /**< value start address */
} addr;
};
typedef struct env_node_obj *env_node_obj_t;
#ifdef __cplusplus
}
#endif
#endif /* EF_DEF_H_ */

View File

@@ -0,0 +1,152 @@
# EasyFlash Types 插件plugin
---
## 1、介绍
目前 EasyFlash 会将环境变量以字符串形式存储于 Flash 中,在这种模式下,对于非字符串类型的环境变量在使用时,就必须得增加额外的字符串转换代码。设计 Types 插件就是为了方便用户在使用 EasyFlash 时,以更加简单的方式去操作各种类型的环境变量。
主要支持的类型包括C 的 **基本类型****数组类型** 以及 **结构体类型** 。对于结构体类型, Types 插件内部采用 [struct2json](https://github.com/armink/struct2json) 库进行转换,所以项目中需要依赖 [struct2json](https://github.com/armink/struct2json) 库。
## 2、使用
### 2.1 源码导入
导入之前需要确认自己的项目中已包含 EasyFlash 核心源码,即包括 "\easyflash\inc"、"\easyflash\port" 及 "\easyflash\src" 下相关文件(导入方法可以参考这个移植文档:[点击打开](https://github.com/armink/EasyFlash/blob/master/docs/zh/port.md))。再将 Types 插件源码导入到项目中,最好连同 "plugins\types" 文件夹一起拷贝至项目中已有的 easyflash 文件夹下。然后需要添加 `easyflash\plugins\types\struct2json\inc``easyflash\plugins\types` 两个文件夹路径到项目头文件路径中即可。
### 2.2 初始化
```C
void ef_types_init(S2jHook *hook)
```
这个方法为 Types 插件的初始化方法,主要初始化 struct2json 库所需的内存管理方法。默认使用的 malloc 及 free 作为内存管理方法,如果使用默认内存管理方式,则无需初始化。例如如果使用 RT-Thread 操作系统自带的内存管理方法,则可以参考下面的初始化代码:
```C
S2jHook s2jHook = {
.free_fn = rt_free,
.malloc_fn = (void *(*)(size_t))rt_malloc,
};
ef_types_init(&s2jHook);
```
### 2.3 操作环境变量
#### 2.3.1 基本类型
对于基本类型的环境变量操作方法与 EasyFlash 原有的 API 一致,只是修改了入参及出参的类型,所有可用的 API 如下:
```C
bool ef_get_bool(const char *key);
char ef_get_char(const char *key);
short ef_get_short(const char *key);
int ef_get_int(const char *key);
long ef_get_long(const char *key);
float ef_get_float(const char *key);
double ef_get_double(const char *key);
EfErrCode ef_set_bool(const char *key, bool value);
EfErrCode ef_set_char(const char *key, char value);
EfErrCode ef_set_short(const char *key, short value);
EfErrCode ef_set_int(const char *key, int value);
EfErrCode ef_set_long(const char *key, long value);
EfErrCode ef_set_float(const char *key, float value);
EfErrCode ef_set_double(const char *key, double value);
```
#### 2.3.2 数组类型
与基本类型的操作方法大体一致,不同点在于:获取到的环境变量需通过指针类型的入参进行返回。所有可用的 API 如下:
```C
void ef_get_bool_array(const char *key, bool *value);
void ef_get_char_array(const char *key, char *value);
void ef_get_short_array(const char *key, short *value);
void ef_get_int_array(const char *key, int *value);
void ef_get_long_array(const char *key, long *value);
void ef_get_float_array(const char *key, float *value);
void ef_get_double_array(const char *key, double *value);
void ef_get_string_array(const char *key, char **value);
EfErrCode ef_set_bool_array(const char *key, bool *value, size_t len);
EfErrCode ef_set_char_array(const char *key, char *value, size_t len);
EfErrCode ef_set_short_array(const char *key, short *value, size_t len);
EfErrCode ef_set_int_array(const char *key, int *value, size_t len);
EfErrCode ef_set_long_array(const char *key, long *value, size_t len);
EfErrCode ef_set_float_array(const char *key, float *value, size_t len);
EfErrCode ef_set_double_array(const char *key, double *value, size_t len);
EfErrCode ef_set_string_array(const char *key, char **value, size_t len);
```
#### 2.3.3 结构体类型
对于结构体类型,这里首先需要使用 struct2json 库来编写与该结构体对应的 JSON 互转方法,再将编写好的互转方法作为入参进行使用。结构体类型环境变量操作的 API 如下:
```C
void *ef_get_struct(const char *key, ef_types_get_cb get_cb);
EfErrCode ef_set_struct(const char *key, void *value, ef_types_set_cb set_cb);
```
具体使用流程及结构体与 JSON 之间的互转方法可以参考下面的 Demo
```C
/* 定义结构体 */
typedef struct {
char name[16];
} Hometown;
typedef struct {
uint8_t id;
double weight;
uint8_t score[8];
char name[16];
Hometown hometown;
} Student;
/* 定义结构体转 JSON 的方法 */
static cJSON *stu_set_cb(void* struct_obj) {
Student *struct_student = (Student *)struct_obj;
/* 创建 Student JSON 对象 */
s2j_create_json_obj(json_student);
/* 序列化数据到 Student JSON 对象 */
s2j_json_set_basic_element(json_student, struct_student, int, id);
s2j_json_set_basic_element(json_student, struct_student, double, weight);
s2j_json_set_array_element(json_student, struct_student, int, score, 8);
s2j_json_set_basic_element(json_student, struct_student, string, name);
/* 序列化数据到 Student.Hometown JSON 对象 */
s2j_json_set_struct_element(json_hometown, json_student, struct_hometown, struct_student, Hometown, hometown);
s2j_json_set_basic_element(json_hometown, struct_hometown, string, name);
return json_student;
}
/* 定义 JSON 转结构体的方法 */
static void *stu_get_cb(cJSON* json_obj) {
/* 创建 Student 结构体对象(提示: s2j_ 开头的方法是 struct2json 库提供的) */
s2j_create_struct_obj(struct_student, Student);
/* 反序列化数据到 Student 结构体对象 */
s2j_struct_get_basic_element(struct_student, json_obj, int, id);
s2j_struct_get_array_element(struct_student, json_obj, int, score);
s2j_struct_get_basic_element(struct_student, json_obj, string, name);
s2j_struct_get_basic_element(struct_student, json_obj, double, weight);
/* 反序列化数据到 Student.Hometown 结构体对象 */
s2j_struct_get_struct_element(struct_hometown, struct_student, json_hometown, json_obj, Hometown, hometown);
s2j_struct_get_basic_element(struct_hometown, json_hometown, string, name);
return struct_student;
}
/* 设置结构体类型环境变量 */
Student orignal_student = {
.id = 24,
.weight = 71.2,
.score = {1, 2, 3, 4, 5, 6, 7, 8},
.name = "张三",
.hometown.name = "北京",
};
ef_set_struct("张三学生", &orignal_student, stu_set_cb);
/* 获取结构体类型环境变量 */
Student *student;
ef_get_struct("张三学生", student, stu_get_cb);
/* 打印获取到的结构体内容 */
printf("姓名:%s 籍贯:%s \n", student->name, student->hometown.name);
/* 释放获取结构体类型环境变量过程中开辟的动态内存 */
s2jHook.free_fn(student);
```

View File

@@ -0,0 +1,395 @@
/*
* This file is part of the EasyFlash Library.
*
* Copyright (c) 2015-2016, Armink, <armink.ztl@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* 'Software'), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* Function: Types plugin source code for this library.
* Created on: 2015-12-16
*/
#include "ef_types.h"
#include <stdlib.h>
#include <stdio.h>
/**
* array support types
*/
typedef enum {
EF_ARRAY_TYPES_BOOL,
EF_ARRAY_TYPES_CHAR,
EF_ARRAY_TYPES_SHORT,
EF_ARRAY_TYPES_INT,
EF_ARRAY_TYPES_LONG,
EF_ARRAY_TYPES_FLOAT,
EF_ARRAY_TYPES_DOUBLE,
EF_ARRAY_TYPES_STRING,
} ef_array_types;
/**
* EasyFlash types plugin initialize.
*
* @param hook Memory management hook function.
* If hook is null or not call this function, then use free and malloc of C library.
*/
void ef_types_init(S2jHook *hook) {
s2j_init(hook);
}
bool ef_get_bool(const char *key) {
char *value = ef_get_env(key);
if(value) {
return atoi(value) == 0 ? false : true;
} else {
EF_INFO("Couldn't find this ENV(%s)!\n", key);
return false;
}
}
char ef_get_char(const char *key) {
return ef_get_long(key);
}
short ef_get_short(const char *key) {
return ef_get_long(key);
}
int ef_get_int(const char *key) {
return ef_get_long(key);
}
long ef_get_long(const char *key) {
char *value = ef_get_env(key);
if(value) {
return atol(value);
} else {
EF_INFO("Couldn't find this ENV(%s)!\n", key);
return NULL;
}
}
float ef_get_float(const char *key) {
return ef_get_double(key);
}
double ef_get_double(const char *key) {
char *value = ef_get_env(key);
if(value) {
return atof(value);
} else {
EF_INFO("Couldn't find this ENV(%s)!\n", key);
return NULL;
}
}
/**
* get array ENV value
*
* @param key ENV name
* @param value returned ENV value
* @param types ENV array's type
*/
static void ef_get_array(const char *key, void *value, ef_array_types types) {
char *char_value = ef_get_env(key);
cJSON *array;
size_t size, i;
EF_ASSERT(value);
if (char_value) {
array = cJSON_Parse(char_value);
if (array) {
size = cJSON_GetArraySize(array);
for (i = 0; i < size; i++) {
switch (types) {
case EF_ARRAY_TYPES_BOOL: {
*((bool *) value + i) = cJSON_GetArrayItem(array, i)->valueint;
break;
}
case EF_ARRAY_TYPES_CHAR: {
*((char *) value + i) = cJSON_GetArrayItem(array, i)->valueint;
break;
}
case EF_ARRAY_TYPES_SHORT: {
*((short *) value + i) = cJSON_GetArrayItem(array, i)->valueint;
break;
}
case EF_ARRAY_TYPES_INT: {
*((int *) value + i) = cJSON_GetArrayItem(array, i)->valueint;
break;
}
case EF_ARRAY_TYPES_LONG: {
*((long *) value + i) = cJSON_GetArrayItem(array, i)->valueint;
break;
}
case EF_ARRAY_TYPES_FLOAT: {
*((float *) value + i) = cJSON_GetArrayItem(array, i)->valuedouble;
break;
}
case EF_ARRAY_TYPES_DOUBLE: {
*((double *) value + i) = cJSON_GetArrayItem(array, i)->valuedouble;
break;
}
case EF_ARRAY_TYPES_STRING: {
*((char **) value + i) = cJSON_GetArrayItem(array, i)->valuestring;
break;
}
}
}
} else {
EF_INFO("This ENV(%s) value type has error!\n", key);
}
cJSON_Delete(array);
} else {
EF_INFO("Couldn't find this ENV(%s)!\n", key);
}
}
void ef_get_bool_array(const char *key, bool *value) {
ef_get_array(key, value, EF_ARRAY_TYPES_BOOL);
}
void ef_get_char_array(const char *key, char *value) {
ef_get_array(key, value, EF_ARRAY_TYPES_CHAR);
}
void ef_get_short_array(const char *key, short *value) {
ef_get_array(key, value, EF_ARRAY_TYPES_SHORT);
}
void ef_get_int_array(const char *key, int *value) {
ef_get_array(key, value, EF_ARRAY_TYPES_INT);
}
void ef_get_long_array(const char *key, long *value) {
ef_get_array(key, value, EF_ARRAY_TYPES_LONG);
}
void ef_get_float_array(const char *key, float *value) {
ef_get_array(key, value, EF_ARRAY_TYPES_FLOAT);
}
void ef_get_double_array(const char *key, double *value) {
ef_get_array(key, value, EF_ARRAY_TYPES_DOUBLE);
}
void ef_get_string_array(const char *key, char **value) {
ef_get_array(key, value, EF_ARRAY_TYPES_STRING);
}
/**
* get structure ENV value
*
* @param key ENV name
* @param get_cb get structure callback function.
* You can use json to structure function which in the struct2json lib(https://github.com/armink/struct2json).
*
* @return value returned structure ENV value pointer. @note The returned value will malloc new ram.
* You must free the value then used finish.
*/
void *ef_get_struct(const char *key, ef_types_get_cb get_cb) {
char *char_value = ef_get_env(key);
cJSON *json_value = cJSON_Parse(char_value);
void *value = NULL;
if (json_value) {
value = get_cb(json_value);
cJSON_Delete(json_value);
}
return value;
}
EfErrCode ef_set_bool(const char *key, bool value) {
char char_value[2] = { 0 };
if (!value) {
strcpy(char_value, "0");
} else {
strcpy(char_value, "1");
}
return ef_set_env(key, char_value);
}
EfErrCode ef_set_char(const char *key, char value) {
return ef_set_long(key, value);
}
EfErrCode ef_set_short(const char *key, short value) {
return ef_set_long(key, value);
}
EfErrCode ef_set_int(const char *key, int value) {
return ef_set_long(key, value);
}
EfErrCode ef_set_long(const char *key, long value) {
char char_value[21] = { 0 };
snprintf(char_value, 20, "%ld", value);
return ef_set_env(key, char_value);
}
EfErrCode ef_set_float(const char *key, float value) {
return ef_set_double(key, value);
}
EfErrCode ef_set_double(const char *key, double value) {
char char_value[21] = { 0 };
snprintf(char_value, 20, "%lf", value);
return ef_set_env(key, char_value);
}
/**
* set array ENV value
*
* @param key ENV name
* @param value ENV value
* @param len array length
* @param types ENV array's type
*
* @return ENV set result
*/
static EfErrCode ef_set_array(const char *key, void *value, size_t len, ef_array_types types) {
char *char_value = NULL;
cJSON *array = NULL, *array_item = NULL;
size_t i;
EfErrCode result = EF_NO_ERR;
EF_ASSERT(value);
array = cJSON_CreateArray();
if (array) {
for (i = 0; i < len; i++) {
switch (types) {
case EF_ARRAY_TYPES_BOOL: {
array_item = cJSON_CreateBool(*((bool *) value + i));
break;
}
case EF_ARRAY_TYPES_CHAR: {
array_item = cJSON_CreateNumber(*((char *) value + i));
break;
}
case EF_ARRAY_TYPES_SHORT: {
array_item = cJSON_CreateNumber(*((short *) value + i));
break;
}
case EF_ARRAY_TYPES_INT: {
array_item = cJSON_CreateNumber(*((int *) value + i));
break;
}
case EF_ARRAY_TYPES_LONG: {
array_item = cJSON_CreateNumber(*((long *) value + i));
break;
}
case EF_ARRAY_TYPES_FLOAT: {
array_item = cJSON_CreateNumber(*((float *) value + i));
break;
}
case EF_ARRAY_TYPES_DOUBLE: {
array_item = cJSON_CreateNumber(*((double *) value + i));
break;
}
case EF_ARRAY_TYPES_STRING: {
array_item = cJSON_CreateString(*((char **) value + i));
break;
}
default:
/* the types parameter has error */
EF_ASSERT(0);
}
if (array_item) {
cJSON_AddItemToArray(array, array_item);
} else {
result = EF_ENV_FULL;
EF_INFO("Memory full!\n", key);
break;
}
}
char_value = cJSON_PrintUnformatted(array);
if (char_value) {
result = ef_set_env(key, char_value);
s2jHook.free_fn(char_value);
} else {
result = EF_ENV_FULL;
EF_INFO("Memory full!\n", key);
}
cJSON_Delete(array);
} else {
result = EF_ENV_FULL;
EF_INFO("Memory full!\n", key);
}
return result;
}
EfErrCode ef_set_bool_array(const char *key, bool *value, size_t len) {
return ef_set_array(key, value, len, EF_ARRAY_TYPES_BOOL);
}
EfErrCode ef_set_char_array(const char *key, char *value, size_t len) {
return ef_set_array(key, value, len, EF_ARRAY_TYPES_CHAR);
}
EfErrCode ef_set_short_array(const char *key, short *value, size_t len) {
return ef_set_array(key, value, len, EF_ARRAY_TYPES_SHORT);
}
EfErrCode ef_set_int_array(const char *key, int *value, size_t len) {
return ef_set_array(key, value, len, EF_ARRAY_TYPES_INT);
}
EfErrCode ef_set_long_array(const char *key, long *value, size_t len) {
return ef_set_array(key, value, len, EF_ARRAY_TYPES_LONG);
}
EfErrCode ef_set_float_array(const char *key, float *value, size_t len) {
return ef_set_array(key, value, len, EF_ARRAY_TYPES_FLOAT);
}
EfErrCode ef_set_double_array(const char *key, double *value, size_t len) {
return ef_set_array(key, value, len, EF_ARRAY_TYPES_DOUBLE);
}
EfErrCode ef_set_string_array(const char *key, char **value, size_t len) {
return ef_set_array(key, value, len, EF_ARRAY_TYPES_STRING);
}
/**
* set structure ENV value
*
* @param key ENV name
* @param value structure ENV value pointer
* @param get_cb set structure callback function.
* You can use structure to json function which in the struct2json lib(https://github.com/armink/struct2json).
*/
EfErrCode ef_set_struct(const char *key, void *value, ef_types_set_cb set_cb) {
EfErrCode result = EF_NO_ERR;
cJSON *json_value = set_cb(value);
char *char_value = cJSON_PrintUnformatted(json_value);
result = ef_set_env(key, char_value);
cJSON_Delete(json_value);
s2jHook.free_fn(char_value);
return result;
}

View File

@@ -0,0 +1,76 @@
/*
* This file is part of the EasyFlash Library.
*
* Copyright (c) 2015-2016, Armink, <armink.ztl@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* 'Software'), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* Function: It is an head file for this plugin. You can see all be called functions.
* Created on: 2015-12-16
*/
#ifndef EF_TYPES_H_
#define EF_TYPES_H_
#include <easyflash.h>
#include <stdbool.h>
#include "struct2json\inc\s2j.h"
/* EasyFlash types plugin's software version number */
#define EF_TYPES_SW_VERSION "0.11.03"
typedef cJSON *(*ef_types_set_cb)(void* struct_obj);
typedef void *(*ef_types_get_cb)(cJSON* json_obj);
void ef_types_init(S2jHook *hook);
bool ef_get_bool(const char *key);
char ef_get_char(const char *key);
short ef_get_short(const char *key);
int ef_get_int(const char *key);
long ef_get_long(const char *key);
float ef_get_float(const char *key);
double ef_get_double(const char *key);
void ef_get_bool_array(const char *key, bool *value);
void ef_get_char_array(const char *key, char *value);
void ef_get_short_array(const char *key, short *value);
void ef_get_int_array(const char *key, int *value);
void ef_get_long_array(const char *key, long *value);
void ef_get_float_array(const char *key, float *value);
void ef_get_double_array(const char *key, double *value);
void ef_get_string_array(const char *key, char **value);
void *ef_get_struct(const char *key, ef_types_get_cb get_cb);
EfErrCode ef_set_bool(const char *key, bool value);
EfErrCode ef_set_char(const char *key, char value);
EfErrCode ef_set_short(const char *key, short value);
EfErrCode ef_set_int(const char *key, int value);
EfErrCode ef_set_long(const char *key, long value);
EfErrCode ef_set_float(const char *key, float value);
EfErrCode ef_set_double(const char *key, double value);
EfErrCode ef_set_bool_array(const char *key, bool *value, size_t len);
EfErrCode ef_set_char_array(const char *key, char *value, size_t len);
EfErrCode ef_set_short_array(const char *key, short *value, size_t len);
EfErrCode ef_set_int_array(const char *key, int *value, size_t len);
EfErrCode ef_set_long_array(const char *key, long *value, size_t len);
EfErrCode ef_set_float_array(const char *key, float *value, size_t len);
EfErrCode ef_set_double_array(const char *key, double *value, size_t len);
EfErrCode ef_set_string_array(const char *key, char **value, size_t len);
EfErrCode ef_set_struct(const char *key, void *value, ef_types_set_cb set_cb);
#endif /* EF_TYPES_H_ */

View File

@@ -0,0 +1,154 @@
/*
Copyright (c) 2009 Dave Gamble
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#ifndef cJSON__h
#define cJSON__h
#include <stddef.h>
#ifdef __cplusplus
extern "C"
{
#endif
/* cJSON Types: */
#define cJSON_False 0
#define cJSON_True 1
#define cJSON_NULL 2
#define cJSON_Number 3
#define cJSON_String 4
#define cJSON_Array 5
#define cJSON_Object 6
#define cJSON_IsReference 256
#define cJSON_StringIsConst 512
/* The cJSON structure: */
typedef struct cJSON {
struct cJSON *next,*prev; /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */
struct cJSON *child; /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */
int type; /* The type of the item, as above. */
char *valuestring; /* The item's string, if type==cJSON_String */
int valueint; /* The item's number, if type==cJSON_Number */
double valuedouble; /* The item's number, if type==cJSON_Number */
char *string; /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */
} cJSON;
typedef struct cJSON_Hooks {
void *(*malloc_fn)(size_t sz);
void (*free_fn)(void *ptr);
} cJSON_Hooks;
/* Supply malloc, realloc and free functions to cJSON */
extern void cJSON_InitHooks(cJSON_Hooks* hooks);
/* Supply a block of JSON, and this returns a cJSON object you can interrogate. Call cJSON_Delete when finished. */
extern cJSON *cJSON_Parse(const char *value);
/* Render a cJSON entity to text for transfer/storage. Free the char* when finished. */
extern char *cJSON_Print(cJSON *item);
/* Render a cJSON entity to text for transfer/storage without any formatting. Free the char* when finished. */
extern char *cJSON_PrintUnformatted(cJSON *item);
/* Render a cJSON entity to text using a buffered strategy. prebuffer is a guess at the final size. guessing well reduces reallocation. fmt=0 gives unformatted, =1 gives formatted */
extern char *cJSON_PrintBuffered(cJSON *item,int prebuffer,int fmt);
/* Delete a cJSON entity and all subentities. */
extern void cJSON_Delete(cJSON *c);
/* Returns the number of items in an array (or object). */
extern int cJSON_GetArraySize(cJSON *array);
/* Retrieve item number "item" from array "array". Returns NULL if unsuccessful. */
extern cJSON *cJSON_GetArrayItem(cJSON *array,int item);
/* Get item "string" from object. Case insensitive. */
extern cJSON *cJSON_GetObjectItem(cJSON *object,const char *string);
extern int cJSON_HasObjectItem(cJSON *object,const char *string);
/* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */
extern const char *cJSON_GetErrorPtr(void);
/* These calls create a cJSON item of the appropriate type. */
extern cJSON *cJSON_CreateNull(void);
extern cJSON *cJSON_CreateTrue(void);
extern cJSON *cJSON_CreateFalse(void);
extern cJSON *cJSON_CreateBool(int b);
extern cJSON *cJSON_CreateNumber(double num);
extern cJSON *cJSON_CreateString(const char *string);
extern cJSON *cJSON_CreateArray(void);
extern cJSON *cJSON_CreateObject(void);
/* These utilities create an Array of count items. */
extern cJSON *cJSON_CreateIntArray(const int *numbers,int count);
extern cJSON *cJSON_CreateFloatArray(const float *numbers,int count);
extern cJSON *cJSON_CreateDoubleArray(const double *numbers,int count);
extern cJSON *cJSON_CreateStringArray(const char **strings,int count);
/* Append item to the specified array/object. */
extern void cJSON_AddItemToArray(cJSON *array, cJSON *item);
extern void cJSON_AddItemToObject(cJSON *object,const char *string,cJSON *item);
extern void cJSON_AddItemToObjectCS(cJSON *object,const char *string,cJSON *item); /* Use this when string is definitely const (i.e. a literal, or as good as), and will definitely survive the cJSON object */
/* Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON. */
extern void cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item);
extern void cJSON_AddItemReferenceToObject(cJSON *object,const char *string,cJSON *item);
/* Remove/Detatch items from Arrays/Objects. */
extern cJSON *cJSON_DetachItemFromArray(cJSON *array,int which);
extern void cJSON_DeleteItemFromArray(cJSON *array,int which);
extern cJSON *cJSON_DetachItemFromObject(cJSON *object,const char *string);
extern void cJSON_DeleteItemFromObject(cJSON *object,const char *string);
/* Update array items. */
extern void cJSON_InsertItemInArray(cJSON *array,int which,cJSON *newitem); /* Shifts pre-existing items to the right. */
extern void cJSON_ReplaceItemInArray(cJSON *array,int which,cJSON *newitem);
extern void cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem);
/* Duplicate a cJSON item */
extern cJSON *cJSON_Duplicate(cJSON *item,int recurse);
/* Duplicate will create a new, identical cJSON item to the one you pass, in new memory that will
need to be released. With recurse!=0, it will duplicate any children connected to the item.
The item->next and ->prev pointers are always zero on return from Duplicate. */
/* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */
extern cJSON *cJSON_ParseWithOpts(const char *value,const char **return_parse_end,int require_null_terminated);
extern void cJSON_Minify(char *json);
/* Macros for creating things quickly. */
#define cJSON_AddNullToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateNull())
#define cJSON_AddTrueToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateTrue())
#define cJSON_AddFalseToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateFalse())
#define cJSON_AddBoolToObject(object,name,b) cJSON_AddItemToObject(object, name, cJSON_CreateBool(b))
#define cJSON_AddNumberToObject(object,name,n) cJSON_AddItemToObject(object, name, cJSON_CreateNumber(n))
#define cJSON_AddStringToObject(object,name,s) cJSON_AddItemToObject(object, name, cJSON_CreateString(s))
/* When assigning an integer value, it needs to be propagated to valuedouble too. */
#define cJSON_SetIntValue(object,val) ((object)?(object)->valueint=(object)->valuedouble=(val):(val))
#define cJSON_SetNumberValue(object,val) ((object)?(object)->valueint=(object)->valuedouble=(val):(val))
/* Macro for iterating over an array */
#define cJSON_ArrayForEach(pos, head) for(pos = (head)->child; pos != NULL; pos = pos->next)
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -0,0 +1,91 @@
/*
* This file is part of the struct2json Library.
*
* Copyright (c) 2015, Armink, <armink.ztl@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* 'Software'), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* Function: It is an head file for this library. You can see all be called functions.
* Created on: 2015-10-14
*/
#ifndef __S2J_H__
#define __S2J_H__
#include <cJSON.h>
#include <string.h>
#include "s2jdef.h"
#ifdef __cplusplus
extern "C" {
#endif
/* struct2json software version number */
#define S2J_SW_VERSION "1.0.2"
/* Create JSON object */
#define s2j_create_json_obj(json_obj) \
S2J_CREATE_JSON_OBJECT(json_obj)
/* Delete JSON object */
#define s2j_delete_json_obj(json_obj) \
S2J_DELETE_JSON_OBJECT(json_obj)
/* Set basic type element for JSON object */
#define s2j_json_set_basic_element(to_json, from_struct, type, element) \
S2J_JSON_SET_BASIC_ELEMENT(to_json, from_struct, type, element)
/* Set array type element for JSON object */
#define s2j_json_set_array_element(to_json, from_struct, type, element, size) \
S2J_JSON_SET_ARRAY_ELEMENT(to_json, from_struct, type, element, size)
/* Set child structure type element for JSON object */
#define s2j_json_set_struct_element(child_json, to_json, child_struct, from_struct, type, element) \
S2J_JSON_SET_STRUCT_ELEMENT(child_json, to_json, child_struct, from_struct, type, element)
/* Create structure object */
#define s2j_create_struct_obj(struct_obj, type) \
S2J_CREATE_STRUCT_OBJECT(struct_obj, type)
/* Delete structure object */
#define s2j_delete_struct_obj(struct_obj) \
S2J_DELETE_STRUCT_OBJECT(struct_obj)
/* Get basic type element for structure object */
#define s2j_struct_get_basic_element(to_struct, from_json, type, element) \
S2J_STRUCT_GET_BASIC_ELEMENT(to_struct, from_json, type, element)
/* Get array type element for structure object */
#define s2j_struct_get_array_element(to_struct, from_json, type, element) \
S2J_STRUCT_GET_ARRAY_ELEMENT(to_struct, from_json, type, element)
/* Get child structure type element for structure object */
#define s2j_struct_get_struct_element(child_struct, to_struct, child_json, from_json, type, element) \
S2J_STRUCT_GET_STRUCT_ELEMENT(child_struct, to_struct, child_json, from_json, type, element)
/* s2j.c */
extern S2jHook s2jHook;
void s2j_init(S2jHook *hook);
#ifdef __cplusplus
}
#endif
#endif /* __S2J_H__ */

View File

@@ -0,0 +1,150 @@
/*
* This file is part of the struct2json Library.
*
* Copyright (c) 2015, Armink, <armink.ztl@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* 'Software'), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* Function: It is an head file for this library.
* Created on: 2015-10-14
*/
#ifndef __S2JDEF_H__
#define __S2JDEF_H__
#include <cJSON.h>
#include <string.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct {
void *(*malloc_fn)(size_t sz);
void (*free_fn)(void *ptr);
} S2jHook, *S2jHook_t;
#define S2J_STRUCT_GET_int_ELEMENT(to_struct, from_json, _element) \
json_temp = cJSON_GetObjectItem(from_json, #_element); \
if (json_temp) (to_struct)->_element = json_temp->valueint;
#define S2J_STRUCT_GET_string_ELEMENT(to_struct, from_json, _element) \
json_temp = cJSON_GetObjectItem(from_json, #_element); \
if (json_temp) strcpy((to_struct)->_element, json_temp->valuestring);
#define S2J_STRUCT_GET_double_ELEMENT(to_struct, from_json, _element) \
json_temp = cJSON_GetObjectItem(from_json, #_element); \
if (json_temp) (to_struct)->_element = json_temp->valuedouble;
#define S2J_STRUCT_ARRAY_GET_int_ELEMENT(to_struct, from_json, _element, index) \
(to_struct)->_element[index] = from_json->valueint;
#define S2J_STRUCT_ARRAY_GET_string_ELEMENT(to_struct, from_json, _element, index) \
strcpy((to_struct)->_element[index], from_json->valuestring);
#define S2J_STRUCT_ARRAY_GET_double_ELEMENT(to_struct, from_json, _element, index) \
(to_struct)->_element[index] = from_json->valuedouble;
#define S2J_STRUCT_ARRAY_GET_ELEMENT(to_struct, from_json, type, _element, index) \
S2J_STRUCT_ARRAY_GET_##type##_ELEMENT(to_struct, from_json, _element, index)
#define S2J_JSON_SET_int_ELEMENT(to_json, from_struct, _element) \
cJSON_AddNumberToObject(to_json, #_element, (from_struct)->_element);
#define S2J_JSON_SET_double_ELEMENT(to_json, from_struct, _element) \
cJSON_AddNumberToObject(to_json, #_element, (from_struct)->_element);
#define S2J_JSON_SET_string_ELEMENT(to_json, from_struct, _element) \
cJSON_AddStringToObject(to_json, #_element, (from_struct)->_element);
#define S2J_JSON_ARRAY_SET_int_ELEMENT(to_json, from_struct, _element, index) \
cJSON_AddItemToArray(to_json, cJSON_CreateNumber((from_struct)->_element[index]));
#define S2J_JSON_ARRAY_SET_double_ELEMENT(to_json, from_struct, _element, index) \
cJSON_AddItemToArray(to_json, cJSON_CreateNumber((from_struct)->_element[index]));
#define S2J_JSON_ARRAY_SET_string_ELEMENT(to_json, from_struct, _element, index) \
cJSON_AddItemToArray(to_json, cJSON_CreateString((from_struct)->_element[index]));
#define S2J_JSON_ARRAY_SET_ELEMENT(to_json, from_struct, type, _element, index) \
S2J_JSON_ARRAY_SET_##type##_ELEMENT(to_json, from_struct, _element, index)
#define S2J_CREATE_JSON_OBJECT(json_obj) \
cJSON *json_obj = cJSON_CreateObject();
#define S2J_DELETE_JSON_OBJECT(json_obj) \
cJSON_Delete(json_obj);
#define S2J_JSON_SET_BASIC_ELEMENT(to_json, from_struct, type, _element) \
S2J_JSON_SET_##type##_ELEMENT(to_json, from_struct, _element)
#define S2J_JSON_SET_ARRAY_ELEMENT(to_json, from_struct, type, _element, size) \
{ \
cJSON *array; \
size_t index = 0; \
array = cJSON_CreateArray(); \
if (array) { \
while (index < size) { \
S2J_JSON_ARRAY_SET_ELEMENT(array, from_struct, type, _element, index++); \
} \
cJSON_AddItemToObject(to_json, #_element, array); \
} \
}
#define S2J_JSON_SET_STRUCT_ELEMENT(child_json, to_json, child_struct, from_struct, type, _element) \
type *child_struct = &((from_struct)->_element); \
cJSON *child_json = cJSON_CreateObject(); \
if (child_json) cJSON_AddItemToObject(to_json, #_element, child_json);
#define S2J_CREATE_STRUCT_OBJECT(struct_obj, type) \
cJSON *json_temp; \
type *struct_obj = s2jHook.malloc_fn(sizeof(type)); \
if (struct_obj) memset(struct_obj, 0, sizeof(type));
#define S2J_DELETE_STRUCT_OBJECT(struct_obj) \
s2jHook.free_fn(struct_obj);
#define S2J_STRUCT_GET_BASIC_ELEMENT(to_struct, from_json, type, _element) \
S2J_STRUCT_GET_##type##_ELEMENT(to_struct, from_json, _element)
#define S2J_STRUCT_GET_ARRAY_ELEMENT(to_struct, from_json, type, _element) \
{ \
cJSON *array, *array_element; \
size_t index = 0, size = 0; \
array = cJSON_GetObjectItem(from_json, #_element); \
if (array) { \
size = cJSON_GetArraySize(array); \
while (index < size) { \
array_element = cJSON_GetArrayItem(array, index); \
if (array_element) S2J_STRUCT_ARRAY_GET_ELEMENT(to_struct, array_element, type, _element, index++); \
} \
} \
}
#define S2J_STRUCT_GET_STRUCT_ELEMENT(child_struct, to_struct, child_json, from_json, type, _element) \
type *child_struct = &((to_struct)->_element); \
cJSON *child_json = cJSON_GetObjectItem(from_json, #_element);
#ifdef __cplusplus
}
#endif
#endif /* __S2JDEF_H__ */

View File

@@ -0,0 +1,61 @@
# C结构体与 JSON 快速互转库
---
## struct2json
[struct2json](https://github.com/armink/struct2json) 是一个开源的C结构体与 JSON 快速互转库,它可以快速实现 **结构体对象****JSON 对象** 之间序列化及反序列化要求。快速、简洁的 API 设计,大大降低直接使用 JSON 解析库来实现此类功能的代码复杂度。
## 起源
把面向对象设计应用到C语言中是当下很流行的设计思想。由于C语言中没有类所以一般使用结构体 `struct` 充当类那么结构体变量就是对象。有了对象之后很多时候需要考虑对象的序列化及反序列化问题。C语言不像很多高级语言拥有反射等机制使得对象序列化及反序列化被原生的支持。
对于C语言来说序列化为 JSON 字符串是个不错的选择,所以就得使用 [cJSON](https://github.com/kbranigan/cJSON) 这类 JSON 解析库但是使用后的代码冗余且逻辑性差所以萌生对cJSON库进行二次封装实现一个 struct 与 JSON 之间快速互转的库。 struct2json 就诞生于此。下面是 struct2json 主要使用场景:
- **持久化** :结构体对象序列化为 JSON 对象后可直接保存至文件、Flash实现对结构体对象的掉电存储
- **通信** 高级语言对JSON支持的很友好例如 Javascript、Groovy 就对 JSON 具有原生的支持,所以 JSON 也可作为C语言与其他语言软件之间的通信协议格式及对象传递格式
- **可视化** :序列化为 JSON 后的对象,可以更加直观的展示到控制台或者 UI 上,可用于产品调试、产品二次开发等场景;
## 如何使用
### 声明结构体
如下声明了两个结构体,结构体 `Hometown` 是结构体 `Student` 的子结构体
```C
/* 籍贯 */
typedef struct {
char name[16];
} Hometown;
/* 学生 */
typedef struct {
uint8_t id;
uint8_t score[8];
char name[10];
double weight;
Hometown hometown;
} Student;
```
### 将结构体对象序列化为 JSON 对象
|使用前([源文件](https://github.com/armink/struct2json/blob/master/docs/zh/assets/not_use_struct2json.c)|使用后([源文件](https://github.com/armink/struct2json/blob/master/docs/zh/assets/used_struct2json.c)|
|:-----:|:-----:|
|![结构体转JSON-使用前](https://git.oschina.net/Armink/struct2json/raw/master/docs/zh/images/not_use_struct2json.png)| ![结构体转JSON-使用后](https://git.oschina.net/Armink/struct2json/raw/master/docs/zh/images/used_struct2json.png)|
### 将 JSON 对象反序列化为结构体对象
|使用前([源文件](https://github.com/armink/struct2json/blob/master/docs/zh/assets/not_use_struct2json_for_json.c)|使用后([源文件](https://github.com/armink/struct2json/blob/master/docs/zh/assets/used_struct2json_for_json.c)|
|:-----:|:-----:|
|![JSON转结构体-使用前](https://git.oschina.net/Armink/struct2json/raw/master/docs/zh/images/not_use_struct2json_for_json.png)| ![JSON转结构体-使用后](https://git.oschina.net/Armink/struct2json/raw/master/docs/zh/images/used_struct2json_for_json.png)|
欢迎大家 **fork and pull request**([Github](https://github.com/armink/struct2json)|[OSChina](http://git.oschina.net/armink/struct2json)|[Coding](https://coding.net/u/armink/p/struct2json/git)) 。如果觉得这个开源项目很赞,可以点击[项目主页](https://github.com/armink/struct2json) 右上角的**Star**,同时把它推荐给更多有需要的朋友。
## 文档
具体内容参考[`\docs\zh\`](https://github.com/armink/struct2json/tree/master/docs/zh)下的文件。务必保证在 **阅读文档** 后再使用。
## 许可
MIT Copyright (c) armink.ztl@gmail.com

View File

@@ -0,0 +1,762 @@
/*
Copyright (c) 2009 Dave Gamble
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
/* cJSON */
/* JSON parser in C. */
#include <string.h>
#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <float.h>
#include <limits.h>
#include <ctype.h>
#include "cJSON.h"
static const char *ep;
const char *cJSON_GetErrorPtr(void) {return ep;}
static int cJSON_strcasecmp(const char *s1,const char *s2)
{
if (!s1) return (s1==s2)?0:1;if (!s2) return 1;
for(; tolower(*s1) == tolower(*s2); ++s1, ++s2) if(*s1 == 0) return 0;
return tolower(*(const unsigned char *)s1) - tolower(*(const unsigned char *)s2);
}
static void *(*cJSON_malloc)(size_t sz) = malloc;
static void (*cJSON_free)(void *ptr) = free;
static char* cJSON_strdup(const char* str)
{
size_t len;
char* copy;
len = strlen(str) + 1;
if ((copy = (char*)cJSON_malloc(len)) == NULL) return 0;
memcpy(copy,str,len);
return copy;
}
void cJSON_InitHooks(cJSON_Hooks* hooks)
{
if (!hooks) { /* Reset hooks */
cJSON_malloc = malloc;
cJSON_free = free;
return;
}
cJSON_malloc = (hooks->malloc_fn)?hooks->malloc_fn:malloc;
cJSON_free = (hooks->free_fn)?hooks->free_fn:free;
}
/* Internal constructor. */
static cJSON *cJSON_New_Item(void)
{
cJSON* node = (cJSON*)cJSON_malloc(sizeof(cJSON));
if (node) memset(node,0,sizeof(cJSON));
return node;
}
/* Delete a cJSON structure. */
void cJSON_Delete(cJSON *c)
{
cJSON *next;
while (c)
{
next=c->next;
if (!(c->type&cJSON_IsReference) && c->child) cJSON_Delete(c->child);
if (!(c->type&cJSON_IsReference) && c->valuestring) cJSON_free(c->valuestring);
if (!(c->type&cJSON_StringIsConst) && c->string) cJSON_free(c->string);
cJSON_free(c);
c=next;
}
}
/* Parse the input text to generate a number, and populate the result into item. */
static const char *parse_number(cJSON *item,const char *num)
{
double n=0,sign=1,scale=0;int subscale=0,signsubscale=1;
if (*num=='-') sign=-1,num++; /* Has sign? */
if (*num=='0') num++; /* is zero */
if (*num>='1' && *num<='9') do n=(n*10.0)+(*num++ -'0'); while (*num>='0' && *num<='9'); /* Number? */
if (*num=='.' && num[1]>='0' && num[1]<='9') {num++; do n=(n*10.0)+(*num++ -'0'),scale--; while (*num>='0' && *num<='9');} /* Fractional part? */
if (*num=='e' || *num=='E') /* Exponent? */
{ num++;if (*num=='+') num++; else if (*num=='-') signsubscale=-1,num++; /* With sign? */
while (*num>='0' && *num<='9') subscale=(subscale*10)+(*num++ - '0'); /* Number? */
}
n=sign*n*pow(10.0,(scale+subscale*signsubscale)); /* number = +/- number.fraction * 10^+/- exponent */
item->valuedouble=n;
item->valueint=(int)n;
item->type=cJSON_Number;
return num;
}
static int pow2gt (int x) { --x; x|=x>>1; x|=x>>2; x|=x>>4; x|=x>>8; x|=x>>16; return x+1; }
typedef struct {char *buffer; int length; int offset; } printbuffer;
static char* ensure(printbuffer *p,int needed)
{
char *newbuffer;int newsize;
if (!p || !p->buffer) return 0;
needed+=p->offset;
if (needed<=p->length) return p->buffer+p->offset;
newsize=pow2gt(needed);
newbuffer=(char*)cJSON_malloc(newsize);
if (!newbuffer) {cJSON_free(p->buffer);p->length=0,p->buffer=0;return 0;}
if (newbuffer) memcpy(newbuffer,p->buffer,p->length);
cJSON_free(p->buffer);
p->length=newsize;
p->buffer=newbuffer;
return newbuffer+p->offset;
}
static int update(printbuffer *p)
{
char *str;
if (!p || !p->buffer) return 0;
str=p->buffer+p->offset;
return p->offset+strlen(str);
}
/* Render the number nicely from the given item into a string. */
static char *print_number(cJSON *item,printbuffer *p)
{
char *str=0;
double d=item->valuedouble;
if (d==0)
{
if (p) str=ensure(p,2);
else str=(char*)cJSON_malloc(2); /* special case for 0. */
if (str) strcpy(str,"0");
}
else if (fabs(((double)item->valueint)-d)<=DBL_EPSILON && d<=INT_MAX && d>=INT_MIN)
{
if (p) str=ensure(p,21);
else str=(char*)cJSON_malloc(21); /* 2^64+1 can be represented in 21 chars. */
if (str) sprintf(str,"%d",item->valueint);
}
else
{
if (p) str=ensure(p,64);
else str=(char*)cJSON_malloc(64); /* This is a nice tradeoff. */
if (str)
{
if (fpclassify(d) != FP_ZERO && !isnormal(d)) sprintf(str,"null");
else if (fabs(floor(d)-d)<=DBL_EPSILON && fabs(d)<1.0e60) sprintf(str,"%.0f",d);
else if (fabs(d)<1.0e-6 || fabs(d)>1.0e9) sprintf(str,"%e",d);
else sprintf(str,"%f",d);
}
}
return str;
}
static unsigned parse_hex4(const char *str)
{
unsigned h=0;
if (*str>='0' && *str<='9') h+=(*str)-'0'; else if (*str>='A' && *str<='F') h+=10+(*str)-'A'; else if (*str>='a' && *str<='f') h+=10+(*str)-'a'; else return 0;
h=h<<4;str++;
if (*str>='0' && *str<='9') h+=(*str)-'0'; else if (*str>='A' && *str<='F') h+=10+(*str)-'A'; else if (*str>='a' && *str<='f') h+=10+(*str)-'a'; else return 0;
h=h<<4;str++;
if (*str>='0' && *str<='9') h+=(*str)-'0'; else if (*str>='A' && *str<='F') h+=10+(*str)-'A'; else if (*str>='a' && *str<='f') h+=10+(*str)-'a'; else return 0;
h=h<<4;str++;
if (*str>='0' && *str<='9') h+=(*str)-'0'; else if (*str>='A' && *str<='F') h+=10+(*str)-'A'; else if (*str>='a' && *str<='f') h+=10+(*str)-'a'; else return 0;
return h;
}
/* Parse the input text into an unescaped cstring, and populate item. */
static const unsigned char firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
static const char *parse_string(cJSON *item,const char *str)
{
const char *ptr=str+1;char *ptr2;char *out;int len=0;unsigned uc,uc2;
if (*str!='\"') {ep=str;return 0;} /* not a string! */
while (*ptr!='\"' && *ptr && ++len) if (*ptr++ == '\\') ptr++; /* Skip escaped quotes. */
out=(char*)cJSON_malloc(len+1); /* This is how long we need for the string, roughly. */
if (!out) return 0;
ptr=str+1;ptr2=out;
while (*ptr!='\"' && *ptr)
{
if (*ptr!='\\') *ptr2++=*ptr++;
else
{
ptr++;
switch (*ptr)
{
case 'b': *ptr2++='\b'; break;
case 'f': *ptr2++='\f'; break;
case 'n': *ptr2++='\n'; break;
case 'r': *ptr2++='\r'; break;
case 't': *ptr2++='\t'; break;
case 'u': /* transcode utf16 to utf8. */
uc=parse_hex4(ptr+1);ptr+=4; /* get the unicode char. */
if ((uc>=0xDC00 && uc<=0xDFFF) || uc==0) break; /* check for invalid. */
if (uc>=0xD800 && uc<=0xDBFF) /* UTF16 surrogate pairs. */
{
if (ptr[1]!='\\' || ptr[2]!='u') break; /* missing second-half of surrogate. */
uc2=parse_hex4(ptr+3);ptr+=6;
if (uc2<0xDC00 || uc2>0xDFFF) break; /* invalid second-half of surrogate. */
uc=0x10000 + (((uc&0x3FF)<<10) | (uc2&0x3FF));
}
len=4;if (uc<0x80) len=1;else if (uc<0x800) len=2;else if (uc<0x10000) len=3; ptr2+=len;
switch (len) {
case 4: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6;
case 3: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6;
case 2: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6;
case 1: *--ptr2 =(uc | firstByteMark[len]);
}
ptr2+=len;
break;
default: *ptr2++=*ptr; break;
}
ptr++;
}
}
*ptr2=0;
if (*ptr=='\"') ptr++;
item->valuestring=out;
item->type=cJSON_String;
return ptr;
}
/* Render the cstring provided to an escaped version that can be printed. */
static char *print_string_ptr(const char *str,printbuffer *p)
{
const char *ptr;char *ptr2,*out;int len=0,flag=0;unsigned char token;
for (ptr=str;*ptr;ptr++) flag|=((*ptr>0 && *ptr<32)||(*ptr=='\"')||(*ptr=='\\'))?1:0;
if (!flag)
{
len=ptr-str;
if (p) out=ensure(p,len+3);
else out=(char*)cJSON_malloc(len+3);
if (!out) return 0;
ptr2=out;*ptr2++='\"';
strcpy(ptr2,str);
ptr2[len]='\"';
ptr2[len+1]=0;
return out;
}
if (!str)
{
if (p) out=ensure(p,3);
else out=(char*)cJSON_malloc(3);
if (!out) return 0;
strcpy(out,"\"\"");
return out;
}
ptr=str;while (('\0' != (token=*ptr)) && ++len) {if (NULL != strchr("\"\\\b\f\n\r\t",token)) len++; else if (token<32) len+=5;ptr++;}
if (p) out=ensure(p,len+3);
else out=(char*)cJSON_malloc(len+3);
if (!out) return 0;
ptr2=out;ptr=str;
*ptr2++='\"';
while (*ptr)
{
if ((unsigned char)*ptr>31 && *ptr!='\"' && *ptr!='\\') *ptr2++=*ptr++;
else
{
*ptr2++='\\';
switch (token=*ptr++)
{
case '\\': *ptr2++='\\'; break;
case '\"': *ptr2++='\"'; break;
case '\b': *ptr2++='b'; break;
case '\f': *ptr2++='f'; break;
case '\n': *ptr2++='n'; break;
case '\r': *ptr2++='r'; break;
case '\t': *ptr2++='t'; break;
default: sprintf(ptr2,"u%04x",token);ptr2+=5; break; /* escape and print */
}
}
}
*ptr2++='\"';*ptr2++=0;
return out;
}
/* Invote print_string_ptr (which is useful) on an item. */
static char *print_string(cJSON *item,printbuffer *p) {return print_string_ptr(item->valuestring,p);}
/* Predeclare these prototypes. */
static const char *parse_value(cJSON *item,const char *value);
static char *print_value(cJSON *item,int depth,int fmt,printbuffer *p);
static const char *parse_array(cJSON *item,const char *value);
static char *print_array(cJSON *item,int depth,int fmt,printbuffer *p);
static const char *parse_object(cJSON *item,const char *value);
static char *print_object(cJSON *item,int depth,int fmt,printbuffer *p);
/* Utility to jump whitespace and cr/lf */
static const char *skip(const char *in) {while (in && *in && (unsigned char)*in<=32) in++; return in;}
/* Parse an object - create a new root, and populate. */
cJSON *cJSON_ParseWithOpts(const char *value,const char **return_parse_end,int require_null_terminated)
{
const char *end=0;
cJSON *c=cJSON_New_Item();
ep=0;
if (!c) return 0; /* memory fail */
end=parse_value(c,skip(value));
if (!end) {cJSON_Delete(c);return 0;} /* parse failure. ep is set. */
/* if we require null-terminated JSON without appended garbage, skip and then check for a null terminator */
if (require_null_terminated) {end=skip(end);if (*end) {cJSON_Delete(c);ep=end;return 0;}}
if (return_parse_end) *return_parse_end=end;
return c;
}
/* Default options for cJSON_Parse */
cJSON *cJSON_Parse(const char *value) {return cJSON_ParseWithOpts(value,0,0);}
/* Render a cJSON item/entity/structure to text. */
char *cJSON_Print(cJSON *item) {return print_value(item,0,1,0);}
char *cJSON_PrintUnformatted(cJSON *item) {return print_value(item,0,0,0);}
char *cJSON_PrintBuffered(cJSON *item,int prebuffer,int fmt)
{
printbuffer p;
p.buffer=(char*)cJSON_malloc(prebuffer);
p.length=prebuffer;
p.offset=0;
return print_value(item,0,fmt,&p);
//return p.buffer;
}
/* Parser core - when encountering text, process appropriately. */
static const char *parse_value(cJSON *item,const char *value)
{
if (!value) return 0; /* Fail on null. */
if (!strncmp(value,"null",4)) { item->type=cJSON_NULL; return value+4; }
if (!strncmp(value,"false",5)) { item->type=cJSON_False; return value+5; }
if (!strncmp(value,"true",4)) { item->type=cJSON_True; item->valueint=1; return value+4; }
if (*value=='\"') { return parse_string(item,value); }
if (*value=='-' || (*value>='0' && *value<='9')) { return parse_number(item,value); }
if (*value=='[') { return parse_array(item,value); }
if (*value=='{') { return parse_object(item,value); }
ep=value;return 0; /* failure. */
}
/* Render a value to text. */
static char *print_value(cJSON *item,int depth,int fmt,printbuffer *p)
{
char *out=0;
if (!item) return 0;
if (p)
{
switch ((item->type)&255)
{
case cJSON_NULL: {out=ensure(p,5); if (out) strcpy(out,"null"); break;}
case cJSON_False: {out=ensure(p,6); if (out) strcpy(out,"false"); break;}
case cJSON_True: {out=ensure(p,5); if (out) strcpy(out,"true"); break;}
case cJSON_Number: out=print_number(item,p);break;
case cJSON_String: out=print_string(item,p);break;
case cJSON_Array: out=print_array(item,depth,fmt,p);break;
case cJSON_Object: out=print_object(item,depth,fmt,p);break;
}
}
else
{
switch ((item->type)&255)
{
case cJSON_NULL: out=cJSON_strdup("null"); break;
case cJSON_False: out=cJSON_strdup("false");break;
case cJSON_True: out=cJSON_strdup("true"); break;
case cJSON_Number: out=print_number(item,0);break;
case cJSON_String: out=print_string(item,0);break;
case cJSON_Array: out=print_array(item,depth,fmt,0);break;
case cJSON_Object: out=print_object(item,depth,fmt,0);break;
}
}
return out;
}
/* Build an array from input text. */
static const char *parse_array(cJSON *item,const char *value)
{
cJSON *child;
if (*value!='[') {ep=value;return 0;} /* not an array! */
item->type=cJSON_Array;
value=skip(value+1);
if (*value==']') return value+1; /* empty array. */
item->child=child=cJSON_New_Item();
if (!item->child) return 0; /* memory fail */
value=skip(parse_value(child,skip(value))); /* skip any spacing, get the value. */
if (!value) return 0;
while (*value==',')
{
cJSON *new_item;
if ((new_item=cJSON_New_Item()) == NULL) return 0; /* memory fail */
child->next=new_item;new_item->prev=child;child=new_item;
value=skip(parse_value(child,skip(value+1)));
if (!value) return 0; /* memory fail */
}
if (*value==']') return value+1; /* end of array */
ep=value;return 0; /* malformed. */
}
/* Render an array to text */
static char *print_array(cJSON *item,int depth,int fmt,printbuffer *p)
{
char **entries;
char *out=0,*ptr,*ret;int len=5;
cJSON *child=item->child;
int numentries=0,i=0,fail=0;
size_t tmplen=0;
/* How many entries in the array? */
while (child) numentries++,child=child->next;
/* Explicitly handle numentries==0 */
if (!numentries)
{
if (p) out=ensure(p,3);
else out=(char*)cJSON_malloc(3);
if (out) strcpy(out,"[]");
return out;
}
if (p)
{
/* Compose the output array. */
i=p->offset;
ptr=ensure(p,1);if (!ptr) return 0; *ptr='['; p->offset++;
child=item->child;
while (child && !fail)
{
print_value(child,depth+1,fmt,p);
p->offset=update(p);
if (child->next) {len=fmt?2:1;ptr=ensure(p,len+1);if (!ptr) return 0;*ptr++=',';if(fmt)*ptr++=' ';*ptr=0;p->offset+=len;}
child=child->next;
}
ptr=ensure(p,2);if (!ptr) return 0; *ptr++=']';*ptr=0;
out=(p->buffer)+i;
}
else
{
/* Allocate an array to hold the values for each */
entries=(char**)cJSON_malloc(numentries*sizeof(char*));
if (!entries) return 0;
memset(entries,0,numentries*sizeof(char*));
/* Retrieve all the results: */
child=item->child;
while (child && !fail)
{
ret=print_value(child,depth+1,fmt,0);
entries[i++]=ret;
if (ret) len+=strlen(ret)+2+(fmt?1:0); else fail=1;
child=child->next;
}
/* If we didn't fail, try to malloc the output string */
if (!fail) out=(char*)cJSON_malloc(len);
/* If that fails, we fail. */
if (!out) fail=1;
/* Handle failure. */
if (fail)
{
for (i=0;i<numentries;i++) if (entries[i]) cJSON_free(entries[i]);
cJSON_free(entries);
return 0;
}
/* Compose the output array. */
*out='[';
ptr=out+1;*ptr=0;
for (i=0;i<numentries;i++)
{
tmplen=strlen(entries[i]);memcpy(ptr,entries[i],tmplen);ptr+=tmplen;
if (i!=numentries-1) {*ptr++=',';if(fmt)*ptr++=' ';*ptr=0;}
cJSON_free(entries[i]);
}
cJSON_free(entries);
*ptr++=']';*ptr++=0;
}
return out;
}
/* Build an object from the text. */
static const char *parse_object(cJSON *item,const char *value)
{
cJSON *child;
if (*value!='{') {ep=value;return 0;} /* not an object! */
item->type=cJSON_Object;
value=skip(value+1);
if (*value=='}') return value+1; /* empty array. */
item->child=child=cJSON_New_Item();
if (!item->child) return 0;
value=skip(parse_string(child,skip(value)));
if (!value) return 0;
child->string=child->valuestring;child->valuestring=0;
if (*value!=':') {ep=value;return 0;} /* fail! */
value=skip(parse_value(child,skip(value+1))); /* skip any spacing, get the value. */
if (!value) return 0;
while (*value==',')
{
cJSON *new_item;
if ((new_item=cJSON_New_Item()) == NULL) return 0; /* memory fail */
child->next=new_item;new_item->prev=child;child=new_item;
value=skip(parse_string(child,skip(value+1)));
if (!value) return 0;
child->string=child->valuestring;child->valuestring=0;
if (*value!=':') {ep=value;return 0;} /* fail! */
value=skip(parse_value(child,skip(value+1))); /* skip any spacing, get the value. */
if (!value) return 0;
}
if (*value=='}') return value+1; /* end of array */
ep=value;return 0; /* malformed. */
}
/* Render an object to text. */
static char *print_object(cJSON *item,int depth,int fmt,printbuffer *p)
{
char **entries=0,**names=0;
char *out=0,*ptr,*ret,*str;int len=7,i=0,j;
cJSON *child=item->child;
int numentries=0,fail=0;
size_t tmplen=0;
/* Count the number of entries. */
while (child) numentries++,child=child->next;
/* Explicitly handle empty object case */
if (!numentries)
{
if (p) out=ensure(p,fmt?depth+4:3);
else out=(char*)cJSON_malloc(fmt?depth+4:3);
if (!out) return 0;
ptr=out;*ptr++='{';
if (fmt) {*ptr++='\n';for (i=0;i<depth-1;i++) *ptr++='\t';}
*ptr++='}';*ptr++=0;
return out;
}
if (p)
{
/* Compose the output: */
i=p->offset;
len=fmt?2:1; ptr=ensure(p,len+1); if (!ptr) return 0;
*ptr++='{'; if (fmt) *ptr++='\n'; *ptr=0; p->offset+=len;
child=item->child;depth++;
while (child)
{
if (fmt)
{
ptr=ensure(p,depth); if (!ptr) return 0;
for (j=0;j<depth;j++) *ptr++='\t';
p->offset+=depth;
}
print_string_ptr(child->string,p);
p->offset=update(p);
len=fmt?2:1;
ptr=ensure(p,len); if (!ptr) return 0;
*ptr++=':';if (fmt) *ptr++='\t';
p->offset+=len;
print_value(child,depth,fmt,p);
p->offset=update(p);
len=(fmt?1:0)+(child->next?1:0);
ptr=ensure(p,len+1); if (!ptr) return 0;
if (child->next) *ptr++=',';
if (fmt) *ptr++='\n';*ptr=0;
p->offset+=len;
child=child->next;
}
ptr=ensure(p,fmt?(depth+1):2); if (!ptr) return 0;
if (fmt) for (i=0;i<depth-1;i++) *ptr++='\t';
*ptr++='}';*ptr=0;
out=(p->buffer)+i;
}
else
{
/* Allocate space for the names and the objects */
entries=(char**)cJSON_malloc(numentries*sizeof(char*));
if (!entries) return 0;
names=(char**)cJSON_malloc(numentries*sizeof(char*));
if (!names) {cJSON_free(entries);return 0;}
memset(entries,0,sizeof(char*)*numentries);
memset(names,0,sizeof(char*)*numentries);
/* Collect all the results into our arrays: */
child=item->child;depth++;if (fmt) len+=depth;
while (child && !fail)
{
names[i]=str=print_string_ptr(child->string,0);
entries[i++]=ret=print_value(child,depth,fmt,0);
if (str && ret) len+=strlen(ret)+strlen(str)+2+(fmt?2+depth:0); else fail=1;
child=child->next;
}
/* Try to allocate the output string */
if (!fail) out=(char*)cJSON_malloc(len);
if (!out) fail=1;
/* Handle failure */
if (fail)
{
for (i=0;i<numentries;i++) {if (names[i]) cJSON_free(names[i]);if (entries[i]) cJSON_free(entries[i]);}
cJSON_free(names);cJSON_free(entries);
return 0;
}
/* Compose the output: */
*out='{';ptr=out+1;if (fmt)*ptr++='\n';*ptr=0;
for (i=0;i<numentries;i++)
{
if (fmt) for (j=0;j<depth;j++) *ptr++='\t';
tmplen=strlen(names[i]);memcpy(ptr,names[i],tmplen);ptr+=tmplen;
*ptr++=':';if (fmt) *ptr++='\t';
strcpy(ptr,entries[i]);ptr+=strlen(entries[i]);
if (i!=numentries-1) *ptr++=',';
if (fmt) *ptr++='\n';*ptr=0;
cJSON_free(names[i]);cJSON_free(entries[i]);
}
cJSON_free(names);cJSON_free(entries);
if (fmt) for (i=0;i<depth-1;i++) *ptr++='\t';
*ptr++='}';*ptr++=0;
}
return out;
}
/* Get Array size/item / object item. */
int cJSON_GetArraySize(cJSON *array) {cJSON *c=array->child;int i=0;while(c)i++,c=c->next;return i;}
cJSON *cJSON_GetArrayItem(cJSON *array,int item) {cJSON *c=array->child; while (c && item>0) item--,c=c->next; return c;}
cJSON *cJSON_GetObjectItem(cJSON *object,const char *string) {cJSON *c=object->child; while (c && cJSON_strcasecmp(c->string,string)) c=c->next; return c;}
int cJSON_HasObjectItem(cJSON *object,const char *string) {
cJSON *c=object->child;
while (c )
{
if(cJSON_strcasecmp(c->string,string)==0){
return 1;
}
c=c->next;
}
return 0;
}
/* Utility for array list handling. */
static void suffix_object(cJSON *prev,cJSON *item) {prev->next=item;item->prev=prev;}
/* Utility for handling references. */
static cJSON *create_reference(cJSON *item) {cJSON *ref=cJSON_New_Item();if (!ref) return 0;memcpy(ref,item,sizeof(cJSON));ref->string=0;ref->type|=cJSON_IsReference;ref->next=ref->prev=0;return ref;}
/* Add item to array/object. */
void cJSON_AddItemToArray(cJSON *array, cJSON *item) {cJSON *c=array->child;if (!item) return; if (!c) {array->child=item;} else {while (c && c->next) c=c->next; suffix_object(c,item);}}
void cJSON_AddItemToObject(cJSON *object,const char *string,cJSON *item) {if (!item) return; if (item->string) cJSON_free(item->string);item->string=cJSON_strdup(string);cJSON_AddItemToArray(object,item);}
void cJSON_AddItemToObjectCS(cJSON *object,const char *string,cJSON *item) {if (!item) return; if (!(item->type&cJSON_StringIsConst) && item->string) cJSON_free(item->string);item->string=(char*)string;item->type|=cJSON_StringIsConst;cJSON_AddItemToArray(object,item);}
void cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item) {cJSON_AddItemToArray(array,create_reference(item));}
void cJSON_AddItemReferenceToObject(cJSON *object,const char *string,cJSON *item) {cJSON_AddItemToObject(object,string,create_reference(item));}
cJSON *cJSON_DetachItemFromArray(cJSON *array,int which) {cJSON *c=array->child;while (c && which>0) c=c->next,which--;if (!c) return 0;
if (c->prev) c->prev->next=c->next;if (c->next) c->next->prev=c->prev;if (c==array->child) array->child=c->next;c->prev=c->next=0;return c;}
void cJSON_DeleteItemFromArray(cJSON *array,int which) {cJSON_Delete(cJSON_DetachItemFromArray(array,which));}
cJSON *cJSON_DetachItemFromObject(cJSON *object,const char *string) {int i=0;cJSON *c=object->child;while (c && cJSON_strcasecmp(c->string,string)) i++,c=c->next;if (c) return cJSON_DetachItemFromArray(object,i);return 0;}
void cJSON_DeleteItemFromObject(cJSON *object,const char *string) {cJSON_Delete(cJSON_DetachItemFromObject(object,string));}
/* Replace array/object items with new ones. */
void cJSON_InsertItemInArray(cJSON *array,int which,cJSON *newitem) {cJSON *c=array->child;while (c && which>0) c=c->next,which--;if (!c) {cJSON_AddItemToArray(array,newitem);return;}
newitem->next=c;newitem->prev=c->prev;c->prev=newitem;if (c==array->child) array->child=newitem; else newitem->prev->next=newitem;}
void cJSON_ReplaceItemInArray(cJSON *array,int which,cJSON *newitem) {cJSON *c=array->child;while (c && which>0) c=c->next,which--;if (!c) return;
newitem->next=c->next;newitem->prev=c->prev;if (newitem->next) newitem->next->prev=newitem;
if (c==array->child) array->child=newitem; else newitem->prev->next=newitem;c->next=c->prev=0;cJSON_Delete(c);}
void cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem){int i=0;cJSON *c=object->child;while(c && cJSON_strcasecmp(c->string,string))i++,c=c->next;if(c){newitem->string=cJSON_strdup(string);cJSON_ReplaceItemInArray(object,i,newitem);}}
/* Create basic types: */
cJSON *cJSON_CreateNull(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_NULL;return item;}
cJSON *cJSON_CreateTrue(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_True;return item;}
cJSON *cJSON_CreateFalse(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_False;return item;}
cJSON *cJSON_CreateBool(int b) {cJSON *item=cJSON_New_Item();if(item)item->type=b?cJSON_True:cJSON_False;return item;}
cJSON *cJSON_CreateNumber(double num) {cJSON *item=cJSON_New_Item();if(item){item->type=cJSON_Number;item->valuedouble=num;item->valueint=(int)num;}return item;}
cJSON *cJSON_CreateString(const char *string) {cJSON *item=cJSON_New_Item();if(item){item->type=cJSON_String;item->valuestring=cJSON_strdup(string);}return item;}
cJSON *cJSON_CreateArray(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_Array;return item;}
cJSON *cJSON_CreateObject(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_Object;return item;}
/* Create Arrays: */
cJSON *cJSON_CreateIntArray(const int *numbers,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && i<count;i++){n=cJSON_CreateNumber(numbers[i]);if(!i)a->child=n;else suffix_object(p,n);p=n;}return a;}
cJSON *cJSON_CreateFloatArray(const float *numbers,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && i<count;i++){n=cJSON_CreateNumber(numbers[i]);if(!i)a->child=n;else suffix_object(p,n);p=n;}return a;}
cJSON *cJSON_CreateDoubleArray(const double *numbers,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && i<count;i++){n=cJSON_CreateNumber(numbers[i]);if(!i)a->child=n;else suffix_object(p,n);p=n;}return a;}
cJSON *cJSON_CreateStringArray(const char **strings,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && i<count;i++){n=cJSON_CreateString(strings[i]);if(!i)a->child=n;else suffix_object(p,n);p=n;}return a;}
/* Duplication */
cJSON *cJSON_Duplicate(cJSON *item,int recurse)
{
cJSON *newitem,*cptr,*nptr=0,*newchild;
/* Bail on bad ptr */
if (!item) return 0;
/* Create new item */
newitem=cJSON_New_Item();
if (!newitem) return 0;
/* Copy over all vars */
newitem->type=item->type&(~cJSON_IsReference),newitem->valueint=item->valueint,newitem->valuedouble=item->valuedouble;
if (item->valuestring) {newitem->valuestring=cJSON_strdup(item->valuestring); if (!newitem->valuestring) {cJSON_Delete(newitem);return 0;}}
if (item->string) {newitem->string=cJSON_strdup(item->string); if (!newitem->string) {cJSON_Delete(newitem);return 0;}}
/* If non-recursive, then we're done! */
if (!recurse) return newitem;
/* Walk the ->next chain for the child. */
cptr=item->child;
while (cptr)
{
newchild=cJSON_Duplicate(cptr,1); /* Duplicate (with recurse) each item in the ->next chain */
if (!newchild) {cJSON_Delete(newitem);return 0;}
if (nptr) {nptr->next=newchild,newchild->prev=nptr;nptr=newchild;} /* If newitem->child already set, then crosswire ->prev and ->next and move on */
else {newitem->child=newchild;nptr=newchild;} /* Set newitem->child and move to it */
cptr=cptr->next;
}
return newitem;
}
void cJSON_Minify(char *json)
{
char *into=json;
while (*json)
{
if (*json==' ') json++;
else if (*json=='\t') json++; /* Whitespace characters. */
else if (*json=='\r') json++;
else if (*json=='\n') json++;
else if (*json=='/' && json[1]=='/') while (*json && *json!='\n') json++; /* double-slash comments, to end of line. */
else if (*json=='/' && json[1]=='*') {while (*json && !(*json=='*' && json[1]=='/')) json++;json+=2;} /* multiline comments. */
else if (*json=='\"'){*into++=*json++;while (*json && *json!='\"'){if (*json=='\\') *into++=*json++;*into++=*json++;}*into++=*json++;} /* string literals, which are \" sensitive. */
else *into++=*json++; /* All other characters. */
}
*into=0; /* and null-terminate. */
}

View File

@@ -0,0 +1,52 @@
/*
* This file is part of the struct2json Library.
*
* Copyright (c) 2015, Armink, <armink.ztl@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* 'Software'), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* Function: Initialize interface for this library.
* Created on: 2015-10-14
*/
#include <s2j.h>
#include <stdlib.h>
S2jHook s2jHook = {
.malloc_fn = malloc,
.free_fn = free,
};
/**
* struct2json library initialize
* @note It will initialize cJSON library hooks.
*/
void s2j_init(S2jHook *hook) {
/* initialize cJSON library */
cJSON_InitHooks((cJSON_Hooks *)hook);
/* initialize hooks */
if (hook) {
s2jHook.malloc_fn = (hook->malloc_fn) ? hook->malloc_fn : malloc;
s2jHook.free_fn = (hook->free_fn) ? hook->free_fn : free;
} else {
hook->malloc_fn = malloc;
hook->free_fn = free;
}
}

View File

@@ -0,0 +1,228 @@
/*
* This file is part of the EasyFlash Library.
*
* Copyright (c) 2015, Armink, <armink.ztl@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* 'Software'), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* Function: Portable interface for stm32f10x platform.
* Created on: 2015-01-16
*/
#include <easyflash.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
/* default environment variables set for user */
static const ef_env default_env_set[] = {
{"boot_times","0"},
};
static char log_buf[128];
/**
* Flash port for hardware initialize.
*
* @param default_env default ENV set for user
* @param default_env_size default ENV size
*
* @return result
*/
EfErrCode ef_port_init(ef_env const **default_env, size_t *default_env_size) {
EfErrCode result = EF_NO_ERR;
*default_env = default_env_set;
*default_env_size = sizeof(default_env_set) / sizeof(default_env_set[0]);
return result;
}
/**
* Read data from flash.
* @note This operation's units is word.
*
* @param addr flash address
* @param buf buffer to store read data
* @param size read bytes size
*
* @return result
*/
EfErrCode ef_port_read(uint32_t addr, uint32_t *buf, size_t size) {
EfErrCode result = EF_NO_ERR;
uint8_t *buf_8 = (uint8_t *)buf;
size_t i;
/*copy from flash to ram */
for (i = 0; i < size; i++, addr ++, buf_8++) {
*buf_8 = *(uint8_t *) addr;
}
return result;
}
/**
* Erase data on flash.
* @note This operation is irreversible.
* @note This operation's units is different which on many chips.
*
* @param addr flash address
* @param size erase bytes size
*
* @return result
*/
EfErrCode ef_port_erase(uint32_t addr, size_t size) {
EfErrCode result = EF_NO_ERR;
FLASH_Status flash_status;
size_t erase_pages, i;
/* make sure the start address is a multiple of FLASH_ERASE_MIN_SIZE */
EF_ASSERT(addr % EF_ERASE_MIN_SIZE == 0);
/* calculate pages */
erase_pages = size / PAGE_SIZE;
if (size % PAGE_SIZE != 0) {
erase_pages++;
}
/* start erase */
FLASH_Unlock();
FLASH_ClearFlag(FLASH_FLAG_BSY | FLASH_FLAG_EOP | FLASH_FLAG_PGERR | FLASH_FLAG_WRPRTERR);
for (i = 0; i < erase_pages; i++) {
flash_status = FLASH_ErasePage(addr + (PAGE_SIZE * i));
if (flash_status != FLASH_COMPLETE) {
result = EF_ERASE_ERR;
break;
}
}
FLASH_Lock();
return result;
}
/**
* Write data to flash.
* @note This operation's units is word.
* @note This operation must after erase. @see flash_erase.
*
* @param addr flash address
* @param buf the write data buffer
* @param size write bytes size
*
* @return result
*/
EfErrCode ef_port_write(uint32_t addr, const uint32_t *buf, size_t size) {
EfErrCode result = EF_NO_ERR;
size_t i;
uint32_t read_data;
FLASH_Unlock();
FLASH_ClearFlag(FLASH_FLAG_BSY | FLASH_FLAG_EOP | FLASH_FLAG_PGERR | FLASH_FLAG_WRPRTERR);
for (i = 0; i < size; i += 4, buf++, addr += 4) {
/* write data */
FLASH_ProgramWord(addr, *buf);
read_data = *(uint32_t *)addr;
/* check data */
if (read_data != *buf) {
result = EF_WRITE_ERR;
break;
}
}
FLASH_Lock();
return result;
}
/**
* lock the ENV ram cache
*/
void ef_port_env_lock(void) {
__disable_irq();
}
/**
* unlock the ENV ram cache
*/
void ef_port_env_unlock(void) {
__enable_irq();
}
/**
* This function is print flash debug info.
*
* @param file the file which has call this function
* @param line the line number which has call this function
* @param format output format
* @param ... args
*
*/
void ef_log_debug(const char *file, const long line, const char *format, ...) {
#ifdef PRINT_DEBUG
va_list args;
/* args point to the first variable parameter */
va_start(args, format);
ef_print("[Flash](%s:%ld) ", file, line);
/* must use vprintf to print */
vsprintf(log_buf, format, args);
ef_print("%s", log_buf);
printf("\r");
va_end(args);
#endif
}
/**
* This function is print flash routine info.
*
* @param format output format
* @param ... args
*/
void ef_log_info(const char *format, ...) {
va_list args;
/* args point to the first variable parameter */
va_start(args, format);
ef_print("[Flash]");
/* must use vprintf to print */
vsprintf(log_buf, format, args);
ef_print("%s", log_buf);
printf("\r");
va_end(args);
}
/**
* This function is print flash non-package info.
*
* @param format output format
* @param ... args
*/
void ef_print(const char *format, ...) {
va_list args;
/* args point to the first variable parameter */
va_start(args, format);
/* must use vprintf to print */
vsprintf(log_buf, format, args);
printf("%s", log_buf);
va_end(args);
}

View File

@@ -0,0 +1,109 @@
/*
* This file is part of the EasyFlash Library.
*
* Copyright (c) 2014-2019, Armink, <armink.ztl@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* 'Software'), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* Function: Initialize interface for this library.
* Created on: 2014-09-09
*/
/*
*
* This all Backup Area Flash storage index. All used flash area configure is under here.
* |----------------------------| Storage Size
* | Environment variables area | ENV area size @see ENV_AREA_SIZE
* |----------------------------|
* | Saved log area | Log area size @see LOG_AREA_SIZE
* |----------------------------|
* |(IAP)Downloaded application | IAP already downloaded application, unfixed size
* |----------------------------|
*
* @note all area sizes must be aligned with EF_ERASE_MIN_SIZE
*
* The EasyFlash add the NG (Next Generation) mode start from V4.0. All old mode before V4.0, called LEGACY mode.
*
* - NG (Next Generation) mode is default mode from V4.0. It's easy to settings, only defined the ENV_AREA_SIZE.
* - The LEGACY mode has been DEPRECATED. It is NOT RECOMMENDED to continue using.
* Beacuse it will use ram to buffer the ENV and spend more flash erase times.
* If you want use it please using the V3.X version.
*/
#include <easyflash.h>
#if !defined(EF_START_ADDR)
#error "Please configure backup area start address (in ef_cfg.h)"
#endif
#if !defined(EF_ERASE_MIN_SIZE)
#error "Please configure minimum size of flash erasure (in ef_cfg.h)"
#endif
/**
* EasyFlash system initialize.
*
* @return result
*/
EfErrCode easyflash_init(void) {
extern EfErrCode ef_port_init(ef_env const **default_env, size_t *default_env_size);
extern EfErrCode ef_env_init(ef_env const *default_env, size_t default_env_size);
// extern EfErrCode ef_iap_init(void);
extern EfErrCode ef_log_init(void);
size_t default_env_set_size = 0;
const ef_env *default_env_set;
EfErrCode result = EF_NO_ERR;
static bool init_ok = false;
if (init_ok) {
return EF_NO_ERR;
}
result = ef_port_init(&default_env_set, &default_env_set_size);
#ifdef EF_USING_ENV
if (result == EF_NO_ERR) {
result = ef_env_init(default_env_set, default_env_set_size);
}
#endif
#ifdef EF_USING_IAP
if (result == EF_NO_ERR) {
result = ef_iap_init();
}
#endif
#ifdef EF_USING_LOG
if (result == EF_NO_ERR) {
result = ef_log_init();
}
#endif
if (result == EF_NO_ERR) {
init_ok = true;
EF_INFO("EasyFlash V%s is initialize success.\n", EF_SW_VERSION);
} else {
EF_INFO("EasyFlash V%s is initialize fail.\n", EF_SW_VERSION);
}
// EF_INFO("You can get the latest version on https://github.com/armink/EasyFlash .\n");
return result;
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,922 @@
/*
* This file is part of the EasyFlash Library.
*
* Copyright (c) 2014-2018, Armink, <armink.ztl@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* 'Software'), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* Function: Environment variables operating interface. (normal mode)
* Created on: 2014-10-06
*/
#include <easyflash.h>
#include <string.h>
#include <stdlib.h>
#if defined(EF_USING_ENV) && defined(EF_ENV_USING_LEGACY_MODE)
#ifndef EF_ENV_USING_WL_MODE
#if defined(EF_USING_ENV) && (!defined(ENV_USER_SETTING_SIZE) || !defined(ENV_AREA_SIZE))
#error "Please configure user setting ENV size or ENV area size (in ef_cfg.h)"
#endif
/**
* ENV area has 2 sections
* 1. System section
* It storages ENV parameters. (Units: Word)
* 2. Data section
* It storages all ENV. Storage format is key=value\0.
* All ENV must be 4 bytes alignment. The remaining part must fill '\0'.
*
* @note Word = 4 Bytes in this file
* @note When using power fail safeguard mode, it has two ENV areas(Area0, Area1).
*/
/* flash ENV parameters index and size in system section */
enum {
/* data section ENV end address index in system section */
ENV_PARAM_INDEX_END_ADDR = 0,
#ifdef EF_ENV_USING_PFS_MODE
/* saved count for ENV area */
ENV_PARAM_INDEX_SAVED_COUNT,
#endif
#ifdef EF_ENV_AUTO_UPDATE
/* current version number for ENV */
ENV_PARAM_INDEX_VER_NUM,
#endif
/* data section CRC32 code index in system section */
ENV_PARAM_INDEX_DATA_CRC,
/* flash ENV parameters word size */
ENV_PARAM_WORD_SIZE,
/* flash ENV parameters byte size */
ENV_PARAM_BYTE_SIZE = ENV_PARAM_WORD_SIZE * 4,
};
/* default ENV set, must be initialized by user */
static ef_env const *default_env_set;
/* default ENV set size, must be initialized by user */
static size_t default_env_set_size = 0;
/* ENV ram cache */
static uint32_t env_cache[ENV_USER_SETTING_SIZE / 4] = { 0 };
/* ENV start address in flash */
static uint32_t env_start_addr = 0;
/* ENV ram cache has changed when ENV created, deleted and changed value. */
static bool env_cache_changed = false;
/* initialize OK flag */
static bool init_ok = false;
#ifdef EF_ENV_USING_PFS_MODE
/* current load ENV area address */
static uint32_t cur_load_area_addr = 0;
/* next save ENV area address */
static uint32_t next_save_area_addr = 0;
#endif
static uint32_t get_env_system_addr(void);
static uint32_t get_env_data_addr(void);
static uint32_t get_env_end_addr(void);
static void set_env_end_addr(uint32_t end_addr);
static EfErrCode write_env(const char *key, const char *value);
static char *find_env(const char *key);
static EfErrCode del_env(const char *key);
static size_t get_env_data_size(void);
static size_t get_env_user_used_size(void);
static EfErrCode create_env(const char *key, const char *value);
static uint32_t calc_env_crc(void);
static bool env_crc_is_ok(void);
#ifdef EF_ENV_AUTO_UPDATE
static EfErrCode env_auto_update(void);
#endif
/**
* Flash ENV initialize.
*
* @param default_env default ENV set for user
* @param default_env_size default ENV set size
*
* @note user_size must equal with total_size in normal mode
*
* @return result
*/
EfErrCode ef_env_init(ef_env const *default_env, size_t default_env_size) {
EfErrCode result = EF_NO_ERR;
EF_ASSERT(ENV_AREA_SIZE);
EF_ASSERT(ENV_USER_SETTING_SIZE);
EF_ASSERT(EF_ERASE_MIN_SIZE);
/* must be word alignment for ENV */
EF_ASSERT(ENV_USER_SETTING_SIZE % 4 == 0);
EF_ASSERT(ENV_AREA_SIZE % 4 == 0);
EF_ASSERT(default_env);
EF_ASSERT(default_env_size < ENV_USER_SETTING_SIZE);
#ifndef EF_ENV_USING_PFS_MODE
/* total_size must be aligned with erase_min_size */
if (ENV_USER_SETTING_SIZE % EF_ERASE_MIN_SIZE == 0) {
EF_ASSERT(ENV_USER_SETTING_SIZE == ENV_AREA_SIZE);
} else {
EF_ASSERT((ENV_USER_SETTING_SIZE / EF_ERASE_MIN_SIZE + 1)*EF_ERASE_MIN_SIZE == ENV_AREA_SIZE);
}
#else
/* total_size must be aligned with erase_min_size */
if (ENV_USER_SETTING_SIZE % EF_ERASE_MIN_SIZE == 0) {
/* it has double area when used power fail safeguard mode */
EF_ASSERT(2 * ENV_USER_SETTING_SIZE == ENV_AREA_SIZE);
} else {
/* it has double area when used power fail safeguard mode */
EF_ASSERT(2 * (ENV_USER_SETTING_SIZE / EF_ERASE_MIN_SIZE + 1)*EF_ERASE_MIN_SIZE == ENV_AREA_SIZE);
}
#endif
env_start_addr = EF_START_ADDR;
default_env_set = default_env;
default_env_set_size = default_env_size;
EF_DEBUG("ENV start address is 0x%08X, size is %d bytes.\n", EF_START_ADDR, ENV_AREA_SIZE);
result = ef_load_env();
#ifdef EF_ENV_AUTO_UPDATE
if (result == EF_NO_ERR) {
env_auto_update();
}
#endif
if (result == EF_NO_ERR) {
init_ok = true;
}
return result;
}
/**
* ENV set default.
*
* @return result
*/
EfErrCode ef_env_set_default(void) {
extern EfErrCode ef_env_ver_num_set_default(void);
EfErrCode result = EF_NO_ERR;
size_t i;
EF_ASSERT(default_env_set);
EF_ASSERT(default_env_set_size);
/* lock the ENV cache */
ef_port_env_lock();
/* set environment end address is at data section start address */
set_env_end_addr(get_env_data_addr());
#ifdef EF_ENV_USING_PFS_MODE
/* set saved count to default 0 */
env_cache[ENV_PARAM_INDEX_SAVED_COUNT] = 0;
#endif
#ifdef EF_ENV_AUTO_UPDATE
/* initialize version number */
env_cache[ENV_PARAM_INDEX_VER_NUM] = EF_ENV_VER_NUM;
#endif
/* create default ENV */
for (i = 0; i < default_env_set_size; i++) {
create_env(default_env_set[i].key, default_env_set[i].value);
}
/* unlock the ENV cache */
ef_port_env_unlock();
result = ef_save_env();
#ifdef EF_ENV_USING_PFS_MODE
/* reset other PFS area's data */
if (result == EF_NO_ERR) {
env_cache_changed = true;
result = ef_save_env();
}
#endif
return result;
}
/**
* Get ENV system section start address.
*
* @return system section start address
*/
static uint32_t get_env_system_addr(void) {
#ifndef EF_ENV_USING_PFS_MODE
return env_start_addr;
#else
return cur_load_area_addr;
#endif
}
/**
* Get ENV data section start address.
*
* @return data section start address
*/
static uint32_t get_env_data_addr(void) {
return get_env_system_addr() + ENV_PARAM_BYTE_SIZE;
}
/**
* Get ENV end address.
* It's the first word in ENV.
*
* @return ENV end address
*/
static uint32_t get_env_end_addr(void) {
/* it is the first word */
return env_cache[ENV_PARAM_INDEX_END_ADDR];
}
/**
* Set ENV end address.
* It's the first word in ENV.
*
* @param end_addr ENV end address
*/
static void set_env_end_addr(uint32_t end_addr) {
env_cache[ENV_PARAM_INDEX_END_ADDR] = end_addr;
}
/**
* Get current ENV data section size.
*
* @return size
*/
static size_t get_env_data_size(void) {
if (get_env_end_addr() > get_env_data_addr()) {
return get_env_end_addr() - get_env_data_addr();
} else {
return 0;
}
}
/**
* Get current user used ENV size.
*
* @return bytes
*/
static size_t get_env_user_used_size(void) {
if (get_env_end_addr() > get_env_system_addr()) {
return get_env_end_addr() - get_env_system_addr();
} else {
return 0;
}
}
/**
* Get current ENV already write bytes.
*
* @return write bytes
*/
size_t ef_get_env_write_bytes(void) {
#ifndef EF_ENV_USING_PFS_MODE
return get_env_user_used_size();
#else
return get_env_user_used_size() * 2;
#endif
}
/**
* Write an ENV at the end of cache.
*
* @param key ENV name
* @param value ENV value
*
* @return result
*/
static EfErrCode write_env(const char *key, const char *value) {
EfErrCode result = EF_NO_ERR;
size_t key_len = strlen(key), value_len = strlen(value), env_str_len;
char *env_cache_bak = (char *)env_cache;
/* calculate ENV storage length, contain '=' and '\0'. */
env_str_len = key_len + value_len + 2;
if (env_str_len % 4 != 0) {
env_str_len = (env_str_len / 4 + 1) * 4;
}
/* check capacity of ENV */
if (env_str_len + get_env_user_used_size() >= ENV_USER_SETTING_SIZE) {
return EF_ENV_FULL;
}
/* calculate current ENV ram cache end address */
env_cache_bak += get_env_user_used_size();
/* copy key name */
memcpy(env_cache_bak, key, key_len);
env_cache_bak += key_len;
/* copy equal sign */
*env_cache_bak = '=';
env_cache_bak++;
/* copy value */
memcpy(env_cache_bak, value, value_len);
env_cache_bak += value_len;
/* fill '\0' for string end sign */
*env_cache_bak = '\0';
env_cache_bak ++;
/* fill '\0' for word alignment */
memset(env_cache_bak, 0, env_str_len - (key_len + value_len + 2));
set_env_end_addr(get_env_end_addr() + env_str_len);
/* ENV ram cache has changed */
env_cache_changed = true;
return result;
}
/**
* Find ENV.
*
* @param key ENV name
*
* @return found ENV in ram cache
*/
static char *find_env(const char *key) {
char *env_start, *env_end, *env, *found_env = NULL;
size_t key_len = strlen(key), env_len;
if ((key == NULL) || *key == '\0') {
EF_INFO("Flash ENV name must be not empty!\n");
return NULL;
}
/* from data section start to data section end */
env_start = (char *) ((char *) env_cache + ENV_PARAM_BYTE_SIZE);
env_end = (char *) ((char *) env_cache + get_env_user_used_size());
/* ENV is null */
if (env_start == env_end) {
return NULL;
}
env = env_start;
while (env < env_end) {
/* the key length must be equal */
if (!strncmp(env, key, key_len) && (env[key_len] == '=')) {
found_env = env;
break;
} else {
/* calculate ENV length, contain '\0'. */
env_len = strlen(env) + 1;
/* next ENV and word alignment */
if (env_len % 4 == 0) {
env += env_len;
} else {
env += (env_len / 4 + 1) * 4;
}
}
}
return found_env;
}
/**
* If the ENV is not exist, create it.
* @see flash_write_env
*
* @param key ENV name
* @param value ENV value
*
* @return result
*/
static EfErrCode create_env(const char *key, const char *value) {
EfErrCode result = EF_NO_ERR;
EF_ASSERT(key);
EF_ASSERT(value);
if ((key == NULL) || *key == '\0') {
EF_INFO("Flash ENV name must be not empty!\n");
return EF_ENV_NAME_ERR;
}
if (strchr(key, '=')) {
EF_INFO("Flash ENV name can't contain '='.\n");
return EF_ENV_NAME_ERR;
}
/* find ENV */
if (find_env(key)) {
EF_INFO("The name of \"%s\" is already exist.\n", key);
return EF_ENV_NAME_EXIST;
}
/* write ENV at the end of cache */
result = write_env(key, value);
return result;
}
/**
* Delete an ENV in cache.
*
* @param key ENV name
*
* @return result
*/
static EfErrCode del_env(const char *key) {
EfErrCode result = EF_NO_ERR;
char *del_env = NULL;
size_t del_env_length, remain_env_length;
EF_ASSERT(key);
if ((key == NULL) || *key == '\0') {
EF_INFO("Flash ENV name must be not NULL!\n");
return EF_ENV_NAME_ERR;
}
if (strchr(key, '=')) {
EF_INFO("Flash ENV name or value can't contain '='.\n");
return EF_ENV_NAME_ERR;
}
/* find ENV */
del_env = find_env(key);
if (!del_env) {
EF_INFO("Not find \"%s\" in ENV.\n", key);
return EF_ENV_NAME_ERR;
}
del_env_length = strlen(del_env);
/* '\0' also must be as ENV length */
del_env_length ++;
/* the address must multiple of 4 */
if (del_env_length % 4 != 0) {
del_env_length = (del_env_length / 4 + 1) * 4;
}
/* calculate remain ENV length */
remain_env_length = get_env_data_size()
- (((uint32_t) del_env + del_env_length) - ((uint32_t) env_cache + ENV_PARAM_BYTE_SIZE));
/* remain ENV move forward */
memcpy(del_env, del_env + del_env_length, remain_env_length);
/* reset ENV end address */
set_env_end_addr(get_env_end_addr() - del_env_length);
/* ENV ram cache has changed */
env_cache_changed = true;
return result;
}
/**
* Set an ENV.If it value is NULL, delete it.
* If not find it in ENV table, then create it.
*
* @param key ENV name
* @param value ENV value
*
* @return result
*/
EfErrCode ef_set_env(const char *key, const char *value) {
EfErrCode result = EF_NO_ERR;
char *old_env, *old_value;
if (!init_ok) {
EF_INFO("ENV isn't initialize OK.\n");
return EF_ENV_INIT_FAILED;
}
/* lock the ENV cache */
ef_port_env_lock();
/* if ENV value is NULL, delete it */
if (value == NULL) {
result = del_env(key);
} else {
old_env = find_env(key);
/* If find this ENV, then compare the new value and old value. */
if (old_env) {
/* find the old value address */
old_env = strchr(old_env, '=');
old_value = old_env + 1;
/* If it is changed then delete it and recreate it */
if (strcmp(old_value, value)) {
result = del_env(key);
if (result == EF_NO_ERR) {
result = create_env(key, value);
}
}
} else {
result = create_env(key, value);
}
}
/* unlock the ENV cache */
ef_port_env_unlock();
return result;
}
/**
* Del an ENV.
*
* @param key ENV name
*
* @return result
*/
EfErrCode ef_del_env(const char *key) {
EfErrCode result = EF_NO_ERR;
if (!init_ok) {
EF_INFO("ENV isn't initialize OK.\n");
return EF_ENV_INIT_FAILED;
}
/* lock the ENV cache */
ef_port_env_lock();
result = del_env(key);
/* unlock the ENV cache */
ef_port_env_unlock();
return result;
}
/**
* Get an ENV value by key name.
*
* @param key ENV name
*
* @return value
*/
char *ef_get_env(const char *key) {
char *env = NULL, *value = NULL;
if (!init_ok) {
EF_INFO("ENV isn't initialize OK.\n");
return NULL;
}
/* find ENV */
env = find_env(key);
if (env == NULL) {
return NULL;
}
/* get value address */
value = strchr(env, '=');
if (value != NULL) {
/* the equal sign next character is value */
value++;
}
return value;
}
/**
* Print ENV.
*/
void ef_print_env(void) {
uint32_t *env_cache_data_addr = env_cache + ENV_PARAM_WORD_SIZE,
*env_cache_end_addr =
(uint32_t *) (env_cache + ENV_PARAM_WORD_SIZE + get_env_data_size() / 4);
uint8_t j;
char c;
if (!init_ok) {
EF_INFO("ENV isn't initialize OK.\n");
return;
}
for (; env_cache_data_addr < env_cache_end_addr; env_cache_data_addr += 1) {
for (j = 0; j < 4; j++) {
c = (*env_cache_data_addr) >> (8 * j);
ef_print("%c", c);
if (c == '\0') {
ef_print("\n");
break;
}
}
}
#ifndef EF_ENV_USING_PFS_MODE
ef_print("\nmode: normal\n");
ef_print("size: %ld/%ld bytes.\n", get_env_user_used_size(), ENV_USER_SETTING_SIZE);
#else
ef_print("\nmode: power fail safeguard\n");
ef_print("size: %ld/%ld bytes, write bytes %ld/%ld.\n", get_env_user_used_size(),
ENV_USER_SETTING_SIZE, ef_get_env_write_bytes(), ENV_AREA_SIZE);
ef_print("saved count: %ld\n", env_cache[ENV_PARAM_INDEX_SAVED_COUNT]);
#endif
#ifdef EF_ENV_AUTO_UPDATE
ef_print("ver num: %d\n", env_cache[ENV_PARAM_INDEX_VER_NUM]);
#endif
}
/**
* Load flash ENV to ram.
*
* @return result
*/
#ifndef EF_ENV_USING_PFS_MODE
EfErrCode ef_load_env(void) {
EfErrCode result = EF_NO_ERR;
uint32_t *env_cache_bak, env_end_addr;
/* read ENV end address from flash */
ef_port_read(get_env_system_addr() + ENV_PARAM_INDEX_END_ADDR * 4, &env_end_addr, 4);
/* if ENV is not initialize or flash has dirty data, set default for it */
if ((env_end_addr == 0xFFFFFFFF) || (env_end_addr < env_start_addr)
|| (env_end_addr > env_start_addr + ENV_USER_SETTING_SIZE)) {
result = ef_env_set_default();
} else {
/* set ENV end address */
set_env_end_addr(env_end_addr);
env_cache_bak = env_cache + ENV_PARAM_WORD_SIZE;
/* read all ENV from flash */
ef_port_read(get_env_data_addr(), env_cache_bak, get_env_data_size());
/* read ENV CRC code from flash */
ef_port_read(get_env_system_addr() + ENV_PARAM_INDEX_DATA_CRC * 4,
&env_cache[ENV_PARAM_INDEX_DATA_CRC] , 4);
/* if ENV CRC32 check is fault, set default for it */
if (!env_crc_is_ok()) {
EF_INFO("Warning: ENV CRC check failed. Set it to default.\n");
result = ef_env_set_default();
}
}
return result;
}
#else
EfErrCode ef_load_env(void) {
EfErrCode result = EF_NO_ERR;
uint32_t area0_start_address = env_start_addr, area1_start_address = env_start_addr
+ ENV_AREA_SIZE / 2;
uint32_t area0_end_addr, area1_end_addr, area0_crc, area1_crc, area0_saved_count, area1_saved_count;
bool area0_is_valid = true, area1_is_valid = true;
/* read ENV area end address from flash */
ef_port_read(area0_start_address + ENV_PARAM_INDEX_END_ADDR * 4, &area0_end_addr, 4);
ef_port_read(area1_start_address + ENV_PARAM_INDEX_END_ADDR * 4, &area1_end_addr, 4);
if ((area0_end_addr == 0xFFFFFFFF) || (area0_end_addr < area0_start_address)
|| (area0_end_addr > area0_start_address + ENV_USER_SETTING_SIZE)) {
area0_is_valid = false;
}
if ((area1_end_addr == 0xFFFFFFFF) || (area1_end_addr < area1_start_address)
|| (area1_end_addr > area1_start_address + ENV_USER_SETTING_SIZE)) {
area1_is_valid = false;
}
/* check area0 CRC when it is valid */
if (area0_is_valid) {
/* read ENV area0 crc32 code from flash */
ef_port_read(area0_start_address + ENV_PARAM_INDEX_DATA_CRC * 4, &area0_crc, 4);
/* read ENV from ENV area0 */
ef_port_read(area0_start_address, env_cache, area0_end_addr - area0_start_address);
/* current load ENV area address is area0 start address */
cur_load_area_addr = area0_start_address;
if (!env_crc_is_ok()) {
area0_is_valid = false;
}
}
/* check area1 CRC when it is valid */
if (area1_is_valid) {
/* read ENV area1 crc32 code from flash */
ef_port_read(area1_start_address + ENV_PARAM_INDEX_DATA_CRC * 4, &area1_crc, 4);
/* read ENV from ENV area1 */
ef_port_read(area1_start_address, env_cache, area1_end_addr - area1_start_address);
/* current load ENV area address is area1 start address */
cur_load_area_addr = area1_start_address;
if (!env_crc_is_ok()) {
area1_is_valid = false;
}
}
/* all ENV area CRC is OK then compare saved count */
if (area0_is_valid && area1_is_valid) {
/* read ENV area saved count from flash */
ef_port_read(area0_start_address + ENV_PARAM_INDEX_SAVED_COUNT * 4,
&area0_saved_count, 4);
ef_port_read(area1_start_address + ENV_PARAM_INDEX_SAVED_COUNT * 4,
&area1_saved_count, 4);
/* the bigger saved count area is valid */
if ((area0_saved_count > area1_saved_count) || ((area0_saved_count == 0) && (area1_saved_count == 0xFFFFFFFF))) {
area1_is_valid = false;
} else {
area0_is_valid = false;
}
}
if (area0_is_valid) {
/* current load ENV area address is area0 start address */
cur_load_area_addr = area0_start_address;
/* next save ENV area address is area1 start address */
next_save_area_addr = area1_start_address;
/* read all ENV from area0 */
ef_port_read(area0_start_address, env_cache, area0_end_addr - area0_start_address);
} else if (area1_is_valid) {
/* next save ENV area address is area0 start address */
next_save_area_addr = area0_start_address;
} else {
/* current load ENV area address is area1 start address */
cur_load_area_addr = area1_start_address;
/* next save ENV area address is area0 start address */
next_save_area_addr = area0_start_address;
/* set the ENV to default */
result = ef_env_set_default();
}
return result;
}
#endif
/**
* Save ENV to flash.
*/
EfErrCode ef_save_env(void) {
EfErrCode result = EF_NO_ERR;
uint32_t write_addr, write_size;
/* ENV ram cache has not changed don't need to save */
if (!env_cache_changed) {
return result;
}
#ifndef EF_ENV_USING_PFS_MODE
write_addr = get_env_system_addr();
write_size = get_env_user_used_size();
/* calculate and cache CRC32 code */
env_cache[ENV_PARAM_INDEX_DATA_CRC] = calc_env_crc();
#else
write_addr = next_save_area_addr;
write_size = get_env_user_used_size();
/* replace next_save_area_addr with cur_load_area_addr */
next_save_area_addr = cur_load_area_addr;
cur_load_area_addr = write_addr;
/* change the ENV end address to next save area address */
set_env_end_addr(write_addr + write_size);
/* ENV area saved count +1 */
env_cache[ENV_PARAM_INDEX_SAVED_COUNT]++;
/* calculate and cache CRC32 code */
env_cache[ENV_PARAM_INDEX_DATA_CRC] = calc_env_crc();
#endif
/* erase ENV */
result = ef_port_erase(write_addr, write_size);
switch (result) {
case EF_NO_ERR: {
EF_DEBUG("Erased ENV OK.\n");
break;
}
case EF_ERASE_ERR: {
EF_INFO("Error: Erased ENV fault! Start address is 0x%08X, size is %ld.\n", write_addr, write_size);
/* will return when erase fault */
return result;
}
}
/* write ENV to flash */
result = ef_port_write(write_addr, env_cache, write_size);
switch (result) {
case EF_NO_ERR: {
EF_DEBUG("Saved ENV OK.\n");
break;
}
case EF_WRITE_ERR: {
EF_INFO("Error: Saved ENV fault! Start address is 0x%08X, size is %ld.\n", write_addr, write_size);
break;
}
}
env_cache_changed = false;
return result;
}
/**
* Calculate the cached ENV CRC32 value.
*
* @return CRC32 value
*/
static uint32_t calc_env_crc(void) {
uint32_t crc32 = 0;
/* Calculate the ENV end address CRC32. The 4 is ENV end address bytes size. */
crc32 = ef_calc_crc32(crc32, &env_cache[ENV_PARAM_INDEX_END_ADDR], 4);
#ifdef EF_ENV_USING_PFS_MODE
/* Calculate the ENV area saved count CRC32. */
crc32 = ef_calc_crc32(crc32, &env_cache[ENV_PARAM_INDEX_SAVED_COUNT], 4);
#endif
/* Calculate the all ENV data CRC32. */
crc32 = ef_calc_crc32(crc32, &env_cache[ENV_PARAM_WORD_SIZE], get_env_data_size());
EF_DEBUG("Calculate ENV CRC32 number is 0x%08X.\n", crc32);
return crc32;
}
/**
* Check the ENV CRC32
*
* @return true is ok
*/
static bool env_crc_is_ok(void) {
if (calc_env_crc() == env_cache[ENV_PARAM_INDEX_DATA_CRC]) {
EF_DEBUG("Verify ENV CRC32 result is OK.\n");
return true;
} else {
return false;
}
}
/**
* Set and save an ENV. If set ENV is success then will save it.
*
* @param key ENV name
* @param value ENV value
*
* @return result
*/
EfErrCode ef_set_and_save_env(const char *key, const char *value) {
EfErrCode result = EF_NO_ERR;
result = ef_set_env(key, value);
if (result == EF_NO_ERR) {
result = ef_save_env();
}
return result;
}
/**
* Del and save an ENV. If del ENV is success then will save it.
*
* @param key ENV name
*
* @return result
*/
EfErrCode ef_del_and_save_env(const char *key) {
EfErrCode result = EF_NO_ERR;
result = ef_del_env(key);
if (result == EF_NO_ERR) {
result = ef_save_env();
}
return result;
}
#ifdef EF_ENV_AUTO_UPDATE
/**
* Auto update ENV to latest default when current EF_ENV_VER is changed.
*
* @return result
*/
static EfErrCode env_auto_update(void)
{
size_t i;
/* lock the ENV cache */
ef_port_env_lock();
/* read ENV version number from flash*/
ef_port_read(get_env_system_addr() + ENV_PARAM_INDEX_VER_NUM * 4,
&env_cache[ENV_PARAM_INDEX_VER_NUM] , 4);
/* check version number */
if (env_cache[ENV_PARAM_INDEX_VER_NUM] != EF_ENV_VER_NUM) {
env_cache_changed = true;
/* update version number */
env_cache[ENV_PARAM_INDEX_VER_NUM] = EF_ENV_VER_NUM;
/* add a new ENV when it's not found */
for (i = 0; i < default_env_set_size; i++) {
if (find_env(default_env_set[i].key) == NULL) {
create_env(default_env_set[i].key, default_env_set[i].value);
}
}
}
/* unlock the ENV cache */
ef_port_env_unlock();
return ef_save_env();
}
#endif /* EF_ENV_AUTO_UPDATE */
#endif /* EF_ENV_USING_WL_MODE */
#endif /* defined(EF_USING_ENV) && defined(EF_ENV_USING_LEGACY_MODE) */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,289 @@
/*
* This file is part of the EasyFlash Library.
*
* Copyright (c) 2015-2017, Armink, <armink.ztl@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* 'Software'), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* Function: IAP(In-Application Programming) operating interface.
* Created on: 2015-01-05
*/
#include <easyflash.h>
#ifdef EF_USING_IAP
/* IAP section backup application section start address in flash */
static uint32_t bak_app_start_addr = 0;
/**
* Flash IAP function initialize.
*
* @return result
*/
EfErrCode ef_iap_init(void) {
EfErrCode result = EF_NO_ERR;
bak_app_start_addr = EF_START_ADDR ;
#if defined(EF_USING_ENV)
bak_app_start_addr += ENV_AREA_SIZE;
#endif
#if defined(EF_USING_LOG)
bak_app_start_addr += LOG_AREA_SIZE;
#endif
return result;
}
/**
* Erase backup area application data.
*
* @param app_size application size
*
* @return result
*/
EfErrCode ef_erase_bak_app(size_t app_size) {
EfErrCode result = EF_NO_ERR;
result = ef_port_erase(ef_get_bak_app_start_addr(), app_size);
switch (result) {
case EF_NO_ERR: {
EF_INFO("Erased backup area application OK.\n");
break;
}
case EF_ERASE_ERR: {
EF_INFO("Warning: Erase backup area application fault!\n");
/* will return when erase fault */
return result;
}
}
return result;
}
/**
* Erase user old application by using specified erase function.
*
* @param user_app_addr application entry address
* @param app_size application size
* @param app_erase user specified application erase function
*
* @return result
*/
EfErrCode ef_erase_spec_user_app(uint32_t user_app_addr, size_t app_size,
EfErrCode (*app_erase)(uint32_t addr, size_t size)) {
EfErrCode result = EF_NO_ERR;
result = app_erase(user_app_addr, app_size);
switch (result) {
case EF_NO_ERR: {
EF_INFO("Erased user application OK.\n");
break;
}
case EF_ERASE_ERR: {
EF_INFO("Warning: Erase user application fault!\n");
/* will return when erase fault */
return result;
}
}
return result;
}
/**
* Erase user old application by using default `ef_port_erase` function.
*
* @param user_app_addr application entry address
* @param app_size application size
*
* @return result
*/
EfErrCode ef_erase_user_app(uint32_t user_app_addr, size_t app_size) {
return ef_erase_spec_user_app(user_app_addr, app_size, ef_port_erase);
}
/**
* Erase old bootloader
*
* @param bl_addr bootloader entry address
* @param bl_size bootloader size
*
* @return result
*/
EfErrCode ef_erase_bl(uint32_t bl_addr, size_t bl_size) {
EfErrCode result = EF_NO_ERR;
result = ef_port_erase(bl_addr, bl_size);
switch (result) {
case EF_NO_ERR: {
EF_INFO("Erased bootloader OK.\n");
break;
}
case EF_ERASE_ERR: {
EF_INFO("Warning: Erase bootloader fault!\n");
/* will return when erase fault */
return result;
}
}
return result;
}
/**
* Write data of application to backup area.
*
* @param data a part of application
* @param size data size
* @param cur_size current write application size
* @param total_size application total size
*
* @return result
*/
EfErrCode ef_write_data_to_bak(uint8_t *data, size_t size, size_t *cur_size,
size_t total_size) {
EfErrCode result = EF_NO_ERR;
/* make sure don't write excess data */
if (*cur_size + size > total_size) {
size = total_size - *cur_size;
}
result = ef_port_write(ef_get_bak_app_start_addr() + *cur_size, (uint32_t *) data, size);
switch (result) {
case EF_NO_ERR: {
*cur_size += size;
EF_DEBUG("Write data to backup area OK.\n");
break;
}
case EF_WRITE_ERR: {
EF_INFO("Warning: Write data to backup area fault!\n");
break;
}
}
return result;
}
/**
* Copy backup area application to application entry by using specified write function.
*
* @param user_app_addr application entry address
* @param app_size application size
* @param app_write user specified application write function
*
* @return result
*/
EfErrCode ef_copy_spec_app_from_bak(uint32_t user_app_addr, size_t app_size,
EfErrCode (*app_write)(uint32_t addr, const uint32_t *buf, size_t size)) {
size_t cur_size;
uint32_t app_cur_addr, bak_cur_addr;
EfErrCode result = EF_NO_ERR;
/* 32 words size buffer */
uint32_t buff[32];
/* cycle copy data */
for (cur_size = 0; cur_size < app_size; cur_size += sizeof(buff)) {
app_cur_addr = user_app_addr + cur_size;
bak_cur_addr = ef_get_bak_app_start_addr() + cur_size;
ef_port_read(bak_cur_addr, buff, sizeof(buff));
result = app_write(app_cur_addr, buff, sizeof(buff));
if (result != EF_NO_ERR) {
break;
}
}
switch (result) {
case EF_NO_ERR: {
EF_INFO("Write data to application entry OK.\n");
break;
}
case EF_WRITE_ERR: {
EF_INFO("Warning: Write data to application entry fault!\n");
break;
}
}
return result;
}
/**
* Copy backup area application to application entry by using default `ef_port_write` function.
*
* @param user_app_addr application entry address
* @param app_size application size
*
* @return result
*/
EfErrCode ef_copy_app_from_bak(uint32_t user_app_addr, size_t app_size) {
return ef_copy_spec_app_from_bak(user_app_addr, app_size, ef_port_write);
}
/**
* Copy backup area bootloader to bootloader entry.
*
* @param bl_addr bootloader entry address
* @param bl_size bootloader size
*
* @return result
*/
EfErrCode ef_copy_bl_from_bak(uint32_t bl_addr, size_t bl_size) {
size_t cur_size;
uint32_t bl_cur_addr, bak_cur_addr;
EfErrCode result = EF_NO_ERR;
/* 32 words buffer */
uint32_t buff[32];
/* cycle copy data by 32bytes buffer */
for (cur_size = 0; cur_size < bl_size; cur_size += sizeof(buff)) {
bl_cur_addr = bl_addr + cur_size;
bak_cur_addr = ef_get_bak_app_start_addr() + cur_size;
ef_port_read(bak_cur_addr, buff, sizeof(buff));
result = ef_port_write(bl_cur_addr, buff, sizeof(buff));
if (result != EF_NO_ERR) {
break;
}
}
switch (result) {
case EF_NO_ERR: {
EF_INFO("Write data to bootloader entry OK.\n");
break;
}
case EF_WRITE_ERR: {
EF_INFO("Warning: Write data to bootloader entry fault!\n");
break;
}
}
return result;
}
/**
* Get IAP section start address in flash.
*
* @return size
*/
uint32_t ef_get_bak_app_start_addr(void) {
return bak_app_start_addr;
}
#endif /* EF_USING_IAP */

View File

@@ -0,0 +1,731 @@
/*
* This file is part of the EasyFlash Library.
*
* Copyright (c) 2015-2019, Armink, <armink.ztl@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* 'Software'), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* Function: Save logs to flash.
* Created on: 2015-06-04
*/
#include <easyflash.h>
#ifdef EF_USING_LOG
#if defined(EF_USING_LOG) && !defined(LOG_AREA_SIZE)
#error "Please configure log area size (in ef_cfg.h)"
#endif
/* magic code on every sector header. 'EF' is 0xEF30EF30 */
#define LOG_SECTOR_MAGIC 0xEF30EF30
/* sector header size, includes the sector magic code and status magic code */
#define LOG_SECTOR_HEADER_SIZE 12
/* sector header word size,what is equivalent to the total number of sectors header index */
#define LOG_SECTOR_HEADER_WORD_SIZE 3
/**
* Sector status magic code
* The sector status is 8B after LOG_SECTOR_MAGIC at every sector header.
* ==============================================
* | header(12B) | status |
* ----------------------------------------------
* | 0xEF30EF30 0xFFFFFFFF 0xFFFFFFFF | empty |
* | 0xEF30EF30 0xFEFEFEFE 0xFFFFFFFF | using |
* | 0xEF30EF30 0xFEFEFEFE 0xFCFCFCFC | full |
* ==============================================
*
* State transition relationship: empty->using->full
* The FULL status will change to EMPTY after sector clean.
*/
#define SECTOR_STATUS_MAGIC_EMPUT 0xFFFFFFFF
#define SECTOR_STATUS_MAGIC_USING 0xFEFEFEFE
#define SECTOR_STATUS_MAGIC_FULL 0xFCFCFCFC
typedef enum {
SECTOR_STATUS_EMPUT,
SECTOR_STATUS_USING,
SECTOR_STATUS_FULL,
SECTOR_STATUS_HEADER_ERROR,
} SectorStatus;
typedef enum {
SECTOR_HEADER_MAGIC_INDEX,
SECTOR_HEADER_USING_INDEX,
SECTOR_HEADER_FULL_INDEX,
} SectorHeaderIndex;
/* the stored logs start address and end address. It's like a ring buffer implemented on flash. */
static uint32_t log_start_addr = 0, log_end_addr = 0;
/* saved log area address for flash */
static uint32_t log_area_start_addr = 0;
/* initialize OK flag */
static bool init_ok = false;
static void find_start_and_end_addr(void);
static uint32_t get_next_flash_sec_addr(uint32_t cur_addr);
/**
* The flash save log function initialize.
*
* @return result
*/
EfErrCode ef_log_init(void) {
EfErrCode result = EF_NO_ERR;
EF_ASSERT(LOG_AREA_SIZE);
EF_ASSERT(EF_ERASE_MIN_SIZE);
/* the log area size must be an integral multiple of erase minimum size. */
EF_ASSERT(LOG_AREA_SIZE % EF_ERASE_MIN_SIZE == 0);
/* the log area size must be more than twice of EF_ERASE_MIN_SIZE */
EF_ASSERT(LOG_AREA_SIZE / EF_ERASE_MIN_SIZE >= 2);
#ifdef EF_USING_ENV
log_area_start_addr = EF_START_ADDR + ENV_AREA_SIZE;
#else
log_area_start_addr = EF_START_ADDR;
#endif
/* find the log store start address and end address */
find_start_and_end_addr();
/* initialize OK */
init_ok = true;
return result;
}
/**
* Get flash sector current status.
*
* @param addr sector address, this function will auto calculate the sector header address by this address.
*
* @return the flash sector current status
*/
static SectorStatus get_sector_status(uint32_t addr) {
uint32_t header_buf[LOG_SECTOR_HEADER_WORD_SIZE] = {0}, header_addr = 0;
uint32_t sector_header_magic = 0;
uint32_t status_full_magic = 0, status_use_magic = 0;
/* calculate the sector header address */
header_addr = addr & (~(EF_ERASE_MIN_SIZE - 1));
if (ef_port_read(header_addr, header_buf, sizeof(header_buf)) == EF_NO_ERR) {
sector_header_magic = header_buf[SECTOR_HEADER_MAGIC_INDEX];
status_use_magic = header_buf[SECTOR_HEADER_USING_INDEX];
status_full_magic = header_buf[SECTOR_HEADER_FULL_INDEX];
} else {
EF_DEBUG("Error: Read sector header data error.\n");
return SECTOR_STATUS_HEADER_ERROR;
}
/* compare header magic code */
if(sector_header_magic == LOG_SECTOR_MAGIC){
if((status_use_magic == SECTOR_STATUS_MAGIC_EMPUT) && (status_full_magic == SECTOR_STATUS_MAGIC_EMPUT)) {
return SECTOR_STATUS_EMPUT;
} else if((status_use_magic == SECTOR_STATUS_MAGIC_USING) && (status_full_magic == SECTOR_STATUS_MAGIC_EMPUT)) {
return SECTOR_STATUS_USING;
} else if((status_use_magic == SECTOR_STATUS_MAGIC_USING) && (status_full_magic == SECTOR_STATUS_MAGIC_FULL)) {
return SECTOR_STATUS_FULL;
} else {
return SECTOR_STATUS_HEADER_ERROR;
}
} else {
return SECTOR_STATUS_HEADER_ERROR;
}
}
/**
* Write flash sector current status.
*
* @param addr sector address, this function will auto calculate the sector header address by this address.
* @param status sector cur status
*
* @return result
*/
static EfErrCode write_sector_status(uint32_t addr, SectorStatus status) {
uint32_t header, header_addr = 0;
/* calculate the sector header address */
header_addr = addr & (~(EF_ERASE_MIN_SIZE - 1));
/* calculate the sector staus magic */
switch (status) {
case SECTOR_STATUS_EMPUT: {
header = LOG_SECTOR_MAGIC;
return ef_port_write(header_addr, &header, sizeof(header));
}
case SECTOR_STATUS_USING: {
header = SECTOR_STATUS_MAGIC_USING;
return ef_port_write(header_addr + sizeof(header), &header, sizeof(header));
}
case SECTOR_STATUS_FULL: {
header = SECTOR_STATUS_MAGIC_FULL;
return ef_port_write(header_addr + sizeof(header) * 2, &header, sizeof(header));
}
default:
return EF_WRITE_ERR;
}
}
/**
* Find the current flash sector using end address by continuous 0xFF.
*
* @param addr sector address
*
* @return current flash sector using end address
*/
static uint32_t find_sec_using_end_addr(uint32_t addr) {
/* read section data buffer size */
#define READ_BUF_SIZE 32
uint32_t sector_start = addr, data_start = addr, continue_ff = 0, read_buf_size = 0, i;
uint8_t buf[READ_BUF_SIZE];
EF_ASSERT(READ_BUF_SIZE % 4 == 0);
/* calculate the sector start and data start address */
sector_start = addr & (~(EF_ERASE_MIN_SIZE - 1));
data_start = sector_start + LOG_SECTOR_HEADER_SIZE;
/* counts continuous 0xFF which is end of sector */
while (data_start < sector_start + EF_ERASE_MIN_SIZE) {
if (data_start + READ_BUF_SIZE < sector_start + EF_ERASE_MIN_SIZE) {
read_buf_size = READ_BUF_SIZE;
} else {
read_buf_size = sector_start + EF_ERASE_MIN_SIZE - data_start;
}
ef_port_read(data_start, (uint32_t *)buf, read_buf_size);
for (i = 0; i < read_buf_size; i++) {
if (buf[i] == 0xFF) {
continue_ff++;
} else {
continue_ff = 0;
}
}
data_start += read_buf_size;
}
/* calculate current flash sector using end address */
if (continue_ff >= EF_ERASE_MIN_SIZE - LOG_SECTOR_HEADER_SIZE) {
/* from 0 to sec_size all sector is 0xFF, so the sector is empty */
return sector_start + LOG_SECTOR_HEADER_SIZE;
} else if (continue_ff >= 4) {
/* form end_addr - 4 to sec_size length all area is 0xFF, so it's used part of the sector.
* the address must be word alignment. */
if (continue_ff % 4 != 0) {
continue_ff = (continue_ff / 4 + 1) * 4;
}
return sector_start + EF_ERASE_MIN_SIZE - continue_ff;
} else {
/* all sector not has continuous 0xFF, so the sector is full */
return sector_start + EF_ERASE_MIN_SIZE;
}
}
/**
* Find the log store start address and end address.
* It's like a ring buffer implemented on flash.
* The flash log area can be in two states depending on start address and end address:
* state 1 state 2
* |============| |============|
* log area start--> |############| <-- start address |############| <-- end address
* |############| | empty |
* |------------| |------------|
* |############| |############| <-- start address
* |############| |############|
* |------------| |------------|
* | . | | . |
* | . | | . |
* | . | | . |
* |------------| |------------|
* |############| <-- end address |############|
* | empty | |############|
* log area end --> |============| |============|
*
* LOG_AREA_SIZE = log area end - log area star
*
*/
static void find_start_and_end_addr(void) {
size_t cur_size = 0;
SectorStatus cur_sec_status, last_sec_status;
uint32_t cur_using_sec_addr = 0;
/* all status sector counts */
size_t empty_sec_counts = 0, using_sec_counts = 0, full_sector_counts = 0;
/* total sector number */
size_t total_sec_num = LOG_AREA_SIZE / EF_ERASE_MIN_SIZE;
/* see comment of find_start_and_end_addr function */
uint8_t cur_log_sec_state = 0;
/* get the first sector status */
cur_sec_status = get_sector_status(log_area_start_addr);
last_sec_status = cur_sec_status;
for (cur_size = EF_ERASE_MIN_SIZE; cur_size < LOG_AREA_SIZE; cur_size += EF_ERASE_MIN_SIZE) {
/* get current sector status */
cur_sec_status = get_sector_status(log_area_start_addr + cur_size);
/* compare last and current status */
switch (last_sec_status) {
case SECTOR_STATUS_EMPUT: {
switch (cur_sec_status) {
case SECTOR_STATUS_EMPUT:
break;
case SECTOR_STATUS_USING:
EF_DEBUG("Error: Log area error! Now will clean all log area.\n");
ef_log_clean();
return;
case SECTOR_STATUS_FULL:
EF_DEBUG("Error: Log area error! Now will clean all log area.\n");
ef_log_clean();
return;
}
empty_sec_counts++;
break;
}
case SECTOR_STATUS_USING: {
switch (cur_sec_status) {
case SECTOR_STATUS_EMPUT:
/* like state 1 */
cur_log_sec_state = 1;
log_start_addr = log_area_start_addr;
cur_using_sec_addr = log_area_start_addr + cur_size - EF_ERASE_MIN_SIZE;
break;
case SECTOR_STATUS_USING:
EF_DEBUG("Error: Log area error! Now will clean all log area.\n");
ef_log_clean();
return;
case SECTOR_STATUS_FULL:
/* like state 2 */
cur_log_sec_state = 2;
log_start_addr = log_area_start_addr + cur_size;
cur_using_sec_addr = log_area_start_addr + cur_size - EF_ERASE_MIN_SIZE;
break;
}
using_sec_counts++;
break;
}
case SECTOR_STATUS_FULL: {
switch (cur_sec_status) {
case SECTOR_STATUS_EMPUT:
/* like state 1 */
if (cur_log_sec_state == 2) {
EF_DEBUG("Error: Log area error! Now will clean all log area.\n");
ef_log_clean();
return;
} else {
cur_log_sec_state = 1;
log_start_addr = log_area_start_addr;
log_end_addr = log_area_start_addr + cur_size;
cur_using_sec_addr = log_area_start_addr + cur_size - EF_ERASE_MIN_SIZE;
}
break;
case SECTOR_STATUS_USING:
if(total_sec_num <= 2) {
/* like state 1 */
cur_log_sec_state = 1;
log_start_addr = log_area_start_addr;
cur_using_sec_addr = log_area_start_addr + cur_size;
} else {
/* like state 2 when the sector is the last one */
if (cur_size + EF_ERASE_MIN_SIZE >= LOG_AREA_SIZE) {
cur_log_sec_state = 2;
log_start_addr = get_next_flash_sec_addr(log_area_start_addr + cur_size);
cur_using_sec_addr = log_area_start_addr + cur_size;
}
}
break;
case SECTOR_STATUS_FULL:
break;
}
full_sector_counts++;
break;
}
case SECTOR_STATUS_HEADER_ERROR:
EF_DEBUG("Error: Log sector header error! Now will clean all log area.\n");
ef_log_clean();
return;
}
last_sec_status = cur_sec_status;
}
/* the last sector status counts */
if (cur_sec_status == SECTOR_STATUS_EMPUT) {
empty_sec_counts++;
} else if (cur_sec_status == SECTOR_STATUS_USING) {
using_sec_counts++;
} else if (cur_sec_status == SECTOR_STATUS_FULL) {
full_sector_counts++;
} else if (cur_sec_status == SECTOR_STATUS_HEADER_ERROR) {
EF_DEBUG("Error: Log sector header error! Now will clean all log area.\n");
ef_log_clean();
return;
}
if (using_sec_counts != 1) {
/* this state is almost impossible */
EF_DEBUG("Error: There must be only one sector status is USING! Now will clean all log area.\n");
ef_log_clean();
} else {
/* find the end address */
log_end_addr = find_sec_using_end_addr(cur_using_sec_addr);
}
}
/**
* Get log used flash total size.
*
* @return log used flash total size. @note NOT contain sector headers
*/
size_t ef_log_get_used_size(void) {
size_t header_total_num = 0, physical_size = 0;
/* must be call this function after initialize OK */
if (!init_ok) {
return 0;
}
if (log_start_addr < log_end_addr) {
physical_size = log_end_addr - log_start_addr;
} else {
physical_size = LOG_AREA_SIZE - (log_start_addr - log_end_addr);
}
header_total_num = physical_size / EF_ERASE_MIN_SIZE + 1;
return physical_size - header_total_num * LOG_SECTOR_HEADER_SIZE;
}
/**
* Sequential reading log data. It will ignore sector headers.
*
* @param addr address
* @param log log buffer
* @param size log size, not contain sector headers.
*
* @return result
*/
static EfErrCode log_seq_read(uint32_t addr, uint32_t *log, size_t size) {
EfErrCode result = EF_NO_ERR;
size_t read_size = 0, read_size_temp = 0;
while (size) {
/* move to sector data address */
if ((addr + read_size) % EF_ERASE_MIN_SIZE == 0) {
addr += LOG_SECTOR_HEADER_SIZE;
}
/* calculate current sector last data size */
read_size_temp = EF_ERASE_MIN_SIZE - (addr % EF_ERASE_MIN_SIZE);
if (size < read_size_temp) {
read_size_temp = size;
}
result = ef_port_read(addr + read_size, log + read_size / 4, read_size_temp);
if (result != EF_NO_ERR) {
return result;
}
read_size += read_size_temp;
size -= read_size_temp;
}
return result;
}
/**
* Calculate flash physical address by log index.
*
* @param index log index
*
* @return flash physical address
*/
static uint32_t log_index2addr(size_t index) {
size_t header_total_offset = 0;
/* total include sector number */
size_t sector_num = index / (EF_ERASE_MIN_SIZE - LOG_SECTOR_HEADER_SIZE) + 1;
header_total_offset = sector_num * LOG_SECTOR_HEADER_SIZE;
if (log_start_addr < log_end_addr) {
return log_start_addr + index + header_total_offset;
} else {
if (log_start_addr + index + header_total_offset < log_area_start_addr + LOG_AREA_SIZE) {
return log_start_addr + index + header_total_offset;
} else {
return log_start_addr + index + header_total_offset - LOG_AREA_SIZE;
}
}
}
/**
* Read log from flash.
*
* @param index index for saved log.
* Minimum index is 0.
* Maximum index is ef_log_get_used_size() - 1.
* @param log the log which will read from flash
* @param size read bytes size
*
* @return result
*/
EfErrCode ef_log_read(size_t index, uint32_t *log, size_t size) {
EfErrCode result = EF_NO_ERR;
size_t cur_using_size = ef_log_get_used_size();
size_t read_size_temp = 0;
size_t header_total_num = 0;
if (!size) {
return result;
}
EF_ASSERT(size % 4 == 0);
EF_ASSERT(index < cur_using_size);
if (index + size > cur_using_size) {
EF_DEBUG("Warning: Log read size out of bound. Cut read size.\n");
size = cur_using_size - index;
}
/* must be call this function after initialize OK */
if (!init_ok) {
return EF_ENV_INIT_FAILED;
}
if (log_start_addr < log_end_addr) {
log_seq_read(log_index2addr(index), log, size);
} else {
if (log_index2addr(index) + size <= log_area_start_addr + LOG_AREA_SIZE) {
/* Flash log area
* |--------------|
* log_area_start_addr --> |##############|
* |##############|
* |##############|
* |--------------|
* |##############|
* |##############|
* |##############| <-- log_end_addr
* |--------------|
* log_start_addr --> |##############|
* read start --> |**************| <-- read end
* |##############|
* |--------------|
*
* read from (log_start_addr + log_index2addr(index)) to (log_start_addr + index + log_index2addr(index))
*/
result = log_seq_read(log_index2addr(index), log, size);
} else if (log_index2addr(index) < log_area_start_addr + LOG_AREA_SIZE) {
/* Flash log area
* |--------------|
* log_area_start_addr --> |**************| <-- read end
* |##############|
* |##############|
* |--------------|
* |##############|
* |##############|
* |##############| <-- log_end_addr
* |--------------|
* log_start_addr --> |##############|
* read start --> |**************|
* |**************|
* |--------------|
* read will by 2 steps
* step1: read from (log_start_addr + log_index2addr(index)) to flash log area end address
* step2: read from flash log area start address to read size's end address
*/
read_size_temp = (log_area_start_addr + LOG_AREA_SIZE) - log_index2addr(index);
header_total_num = read_size_temp / EF_ERASE_MIN_SIZE;
/* Minus some ignored bytes */
read_size_temp -= header_total_num * LOG_SECTOR_HEADER_SIZE;
result = log_seq_read(log_index2addr(index), log, read_size_temp);
if (result == EF_NO_ERR) {
result = log_seq_read(log_area_start_addr, log + read_size_temp / 4, size - read_size_temp);
}
} else {
/* Flash log area
* |--------------|
* log_area_start_addr --> |##############|
* read start --> |**************|
* |**************| <-- read end
* |--------------|
* |##############|
* |##############|
* |##############| <-- log_end_addr
* |--------------|
* log_start_addr --> |##############|
* |##############|
* |##############|
* |--------------|
* read from (log_start_addr + log_index2addr(index) - LOG_AREA_SIZE) to read size's end address
*/
result = log_seq_read(log_index2addr(index) - LOG_AREA_SIZE, log, size);
}
}
return result;
}
/**
* Write log to flash.
*
* @param log the log which will be write to flash
* @param size write bytes size
*
* @return result
*/
EfErrCode ef_log_write(const uint32_t *log, size_t size) {
EfErrCode result = EF_NO_ERR;
size_t write_size = 0, writable_size = 0;
uint32_t write_addr = log_end_addr, erase_addr;
SectorStatus sector_status;
EF_ASSERT(size % 4 == 0);
/* must be call this function after initialize OK */
if (!init_ok) {
return EF_ENV_INIT_FAILED;
}
if ((sector_status = get_sector_status(write_addr)) == SECTOR_STATUS_HEADER_ERROR) {
return EF_WRITE_ERR;
}
/* write some log when current sector status is USING and EMPTY */
if ((sector_status == SECTOR_STATUS_USING) || (sector_status == SECTOR_STATUS_EMPUT)) {
/* write the already erased but not used area */
writable_size = EF_ERASE_MIN_SIZE - ((write_addr - log_area_start_addr) % EF_ERASE_MIN_SIZE);
if (size >= writable_size) {
result = ef_port_write(write_addr, log, writable_size);
if (result != EF_NO_ERR) {
goto exit;
}
/* change the current sector status to FULL */
result = write_sector_status(write_addr, SECTOR_STATUS_FULL);
if (result != EF_NO_ERR) {
goto exit;
}
write_size += writable_size;
} else {
result = ef_port_write(write_addr, log, size);
log_end_addr = write_addr + size;
goto exit;
}
}
/* erase and write remain log */
while (true) {
/* calculate next available sector address */
erase_addr = write_addr = get_next_flash_sec_addr(write_addr - 4);
/* move the flash log start address to next available sector address */
if (log_start_addr == erase_addr) {
log_start_addr = get_next_flash_sec_addr(log_start_addr);
}
/* erase sector */
result = ef_port_erase(erase_addr, EF_ERASE_MIN_SIZE);
if (result != EF_NO_ERR) {
goto exit;
}
/* change the sector status to EMPTY and USING when write begin sector start address */
result = write_sector_status(write_addr, SECTOR_STATUS_EMPUT);
result = write_sector_status(write_addr, SECTOR_STATUS_USING);
if (result == EF_NO_ERR) {
write_addr += LOG_SECTOR_HEADER_SIZE;
} else {
goto exit;
}
/* calculate current sector writable data size */
writable_size = EF_ERASE_MIN_SIZE - LOG_SECTOR_HEADER_SIZE;
if (size - write_size >= writable_size) {
result = ef_port_write(write_addr, log + write_size / 4, writable_size);
if (result != EF_NO_ERR) {
goto exit;
}
/* change the current sector status to FULL */
result = write_sector_status(write_addr, SECTOR_STATUS_FULL);
if (result != EF_NO_ERR) {
goto exit;
}
log_end_addr = write_addr + writable_size;
write_size += writable_size;
write_addr += writable_size;
} else {
result = ef_port_write(write_addr, log + write_size / 4, size - write_size);
if (result != EF_NO_ERR) {
goto exit;
}
log_end_addr = write_addr + (size - write_size);
break;
}
}
exit:
return result;
}
/**
* Get next flash sector address.The log total sector like ring buffer which implement by flash.
*
* @param cur_addr cur flash address
*
* @return next flash sector address
*/
static uint32_t get_next_flash_sec_addr(uint32_t cur_addr) {
size_t cur_sec_id = (cur_addr - log_area_start_addr) / EF_ERASE_MIN_SIZE;
size_t sec_total_num = LOG_AREA_SIZE / EF_ERASE_MIN_SIZE;
if (cur_sec_id + 1 >= sec_total_num) {
/* return to ring head */
return log_area_start_addr;
} else {
return log_area_start_addr + (cur_sec_id + 1) * EF_ERASE_MIN_SIZE;
}
}
/**
* Clean all log which in flash.
*
* @return result
*/
EfErrCode ef_log_clean(void) {
EfErrCode result = EF_NO_ERR;
uint32_t write_addr = log_area_start_addr;
/* clean address */
log_start_addr = log_area_start_addr;
log_end_addr = log_start_addr + LOG_SECTOR_HEADER_SIZE;
/* erase log flash area */
result = ef_port_erase(log_area_start_addr, LOG_AREA_SIZE);
if (result != EF_NO_ERR) {
goto exit;
}
/* setting first sector is EMPTY to USING */
write_sector_status(write_addr, SECTOR_STATUS_EMPUT);
write_sector_status(write_addr, SECTOR_STATUS_USING);
if (result != EF_NO_ERR) {
goto exit;
}
write_addr += EF_ERASE_MIN_SIZE;
/* add sector header */
while (true) {
write_sector_status(write_addr, SECTOR_STATUS_EMPUT);
if (result != EF_NO_ERR) {
goto exit;
}
write_addr += EF_ERASE_MIN_SIZE;
if (write_addr >= log_area_start_addr + LOG_AREA_SIZE) {
break;
}
}
exit:
return result;
}
#endif /* EF_USING_LOG */

View File

@@ -0,0 +1,99 @@
/*
* This file is part of the EasyFlash Library.
*
* Copyright (c) 2015-2017, Armink, <armink.ztl@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* 'Software'), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* Function: Some utils for this library.
* Created on: 2015-01-14
*/
#include <easyflash.h>
static const uint32_t crc32_table[] =
{
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
};
/**
* Calculate the CRC32 value of a memory buffer.
*
* @param crc accumulated CRC32 value, must be 0 on first call
* @param buf buffer to calculate CRC32 value for
* @param size bytes in buffer
*
* @return calculated CRC32 value
*/
uint32_t ef_calc_crc32(uint32_t crc, const void *buf, size_t size)
{
const uint8_t *p;
p = (const uint8_t *)buf;
crc = crc ^ ~0U;
while (size--) {
crc = crc32_table[(crc ^ *p++) & 0xFF] ^ (crc >> 8);
}
return crc ^ ~0U;
}

View File

@@ -0,0 +1,39 @@
/*
* @Date: 2025-06-26 09:38:45
* @LastEditors: 路怀帅
* @LastEditTime: 2025-07-05 11:31:57
* @FilePath: \RVMDKuv5e:\个人项目\零距电子\安灯遥控器\Andon_Remote_Control\Andon_RTOS\User\inc\bsp_bat.h
*/
#ifndef __BAT_H
#define __BAT_H
#include "main.h"
typedef struct
{
void (*Init)(void);
uint8_t (*Read_State)(void);
} BATClassStruct;
typedef enum
{
BAT_Charging = 0, // 电池充电中
BAT_Full, // 电池充满
BAT_Low, // 电池低电量
BAT_Hight // 电池高电量
} BAT_State_Enum;
extern BATClassStruct BATClass;
void Power_button_init(void);
uint8_t Check_Power_buttun(void);
typedef struct {
void (* Init)(void);
uint16_t (* GetValue)(uint8_t ADC_channelx);
} ADC1ClassStruct;
extern ADC1ClassStruct ADC1Class;
#endif

View File

@@ -0,0 +1,18 @@
#ifndef __BUTTON_H__
#define __BUTTON_H__
#include "stdint.h"
#include "string.h"
#ifdef __cplusplus
extern "C"
{
#endif
void user_button_init(void);
extern uint8_t isLEDTog;
extern uint8_t isSend;
#ifdef __cplusplus
}
#endif
#endif /* __FLEXIBLE_BUTTON_H__ */

View File

@@ -0,0 +1,10 @@
#ifndef __CHIPID_H
#define __CHIPID_H
#include "stm32f10x.h"
void Get_ChipID(void);
void WDOG_Init(void);
#endif /* __CHIPID_H */

View File

@@ -0,0 +1,69 @@
/*
* @Date: 2025-06-26 09:38:45
* @LastEditors: 路怀帅
* @LastEditTime: 2025-06-26 14:37:29
* @FilePath: \Andon_Remote_Control\MDK_PROJECT\Drive\Led.h
*/
#ifndef __LED_H
#define __LED_H
#include "main.h"
typedef struct
{
void (*Init)(void);
void (*Set)(volatile uint8_t led_x);
void (*GREEN_Toggle)(void);
void (*RED_Toggle)(void);
void (*BLUE_Toggle)(void);
void (*YELLOW_Toggle)(void);
} LEDClassStruct;
#define RED_ON GPIO_ResetBits(GPIOA, GPIO_Pin_6)
#define RED_OFF GPIO_SetBits(GPIOA, GPIO_Pin_6)
#define RED_TOG GPIOA->BSRR = ((GPIOA->ODR & GPIO_Pin_6) << 16) | (~GPIOA->ODR & GPIO_Pin_6)
#define GREEN_ON GPIO_ResetBits(GPIOA, GPIO_Pin_7)
#define GREEN_OFF GPIO_SetBits(GPIOA, GPIO_Pin_7)
#define GREEN_TOG GPIOA->BSRR = ((GPIOA->ODR & GPIO_Pin_7) << 16) | (~GPIOA->ODR & GPIO_Pin_7)
#define BLUE_ON GPIO_ResetBits(GPIOB, GPIO_Pin_0)
#define BLUE_OFF GPIO_SetBits(GPIOB, GPIO_Pin_0)
#define BLUE_TOG GPIOA->BSRR = ((GPIOB->ODR & GPIO_Pin_0) << 16) | (~GPIOB->ODR & GPIO_Pin_0)
#define ASR_Power_ON GPIO_SetBits(GPIOB, GPIO_Pin_8)
#define ASR_Power_OFF GPIO_ResetBits(GPIOB, GPIO_Pin_8)
#define BOARD_Power_ON GPIO_SetBits(GPIOA, GPIO_Pin_1)
#define BOARD_Power_OFF GPIO_ResetBits(GPIOA, GPIO_Pin_1)
#define LED_ALL_ON RED_ON;GREEN_ON;BLUE_ON
#define LED_ALL_OFF RED_OFF;GREEN_OFF;BLUE_OFF
typedef enum
{
None = 0,
CHANGE_Ing, // 充电中
CHANGE_Full, // 充满
UNLOCK_HighBat, // 解锁高电量
UNLOCK_LowBat, // 解锁低电量
SEND_Success, // 发送成功
SEND_Fail, // 发送失败
} LED_State_Enum;
typedef enum
{
BLACK = 0,
GREEN,
RED,
BLUE,
YELLOW,
WHITE
} LED_Color_Enum;
void ledBlinkTask(void);
extern LEDClassStruct LEDClass;
void ASR_Power_Init(void);
void BOARD_Power_Init(void);
#endif

View File

@@ -0,0 +1,27 @@
#ifndef __MOD_H
#define __MOD_H
#include "main.h"
typedef void (*ToggleFunc)(void);
typedef void (*DelayFunc)(uint32_t);
typedef struct
{
void (*Loop)(ToggleFunc toggle, DelayFunc delayFunc, uint8_t iterations, uint32_t delayTime);
} MODClassStruct;
typedef struct {
const char *e_val;
const char *t_val;
const char *r_val;
const char *c_val;
} LoraCommandParams;
extern MODClassStruct MODClass;
extern LoraCommandParams cmd_params[];
#endif

View File

@@ -0,0 +1,21 @@
#ifndef __USART_AS_H
#define __USART_AS_H
#include "main.h"
typedef struct {
void (* USART1_Init)(uint32_t baudRate);
void (* USART2_Init)(uint32_t baudRate);
void (* USART3_Init)(uint32_t baudRate);
void (* USART_SendString1)(USART_TypeDef *USARTx, uint8_t *str, uint16_t len);
void (* USART_SendString2)(USART_TypeDef* USARTx, uint8_t *str);
} USARTClassStruct;
void Usart_SendString( USART_TypeDef * pUSARTx, char *str);
extern USARTClassStruct USARTClass;
extern uint8_t Asr_Power_state;
extern uint8_t g_asrRxFlag;
extern uint8_t Asr_recbuff[];
#endif

View File

@@ -0,0 +1,38 @@
#ifndef __DELAY_H
#define __DELAY_H
#include "stm32f10x.h"
void delay_init(void);
void delay_ms(u16 nms);
void delay_us(u32 nus);
#endif

View File

@@ -0,0 +1,121 @@
/**
* @File: flexible_button.h
* @Author: MurphyZhao
* @Date: 2018-09-29
*
* Copyright (c) 2018-2019 MurphyZhao <d2014zjt@163.com>
* https://github.com/murphyzhao
* All rights reserved.
* License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Change logs:
* Date Author Notes
* 2018-09-29 MurphyZhao First add
* 2019-08-02 MurphyZhao 迁移代码到 murphyzhao 仓库
*
*/
#ifndef __FLEXIBLE_BUTTON_H__
#define __FLEXIBLE_BUTTON_H__
#include "stdint.h"
#include "string.h"
typedef void (*flex_button_response_callback)(void *);
typedef enum
{
FLEX_BTN_PRESS_DOWN = 0,
FLEX_BTN_PRESS_CLICK,
FLEX_BTN_PRESS_DOUBLE_CLICK,
FLEX_BTN_PRESS_SHORT_START,
FLEX_BTN_PRESS_SHORT_UP,
FLEX_BTN_PRESS_LONG_START,
FLEX_BTN_PRESS_LONG_UP,
FLEX_BTN_PRESS_LONG_HOLD,
FLEX_BTN_PRESS_LONG_HOLD_UP,
FLEX_BTN_PRESS_MAX,
FLEX_BTN_PRESS_NONE,
} flex_button_event_t;
/**
* flex_button_t
*
* @brief Button data structure
* Below are members that need to user init before scan.
*
* @member pressed_logic_level: Logic level when the button is pressed.
* Must be inited by 'flex_button_register' API
* before start button scan.
* @member debounce_tick: The time of button debounce.
* The value is number of button scan cycles.
* @member click_start_tick: The time of start click.
* The value is number of button scan cycles.
* @member short_press_start_tick: The time of short press start tick.
* The value is number of button scan cycles.
* @member long_press_start_tick: The time of long press start tick.
* The value is number of button scan cycles.
* @member long_hold_start_tick: The time of hold press start tick.
* The value is number of button scan cycles.
* @member usr_button_read: Read the logic level value of specified button.
* @member cb: Button event callback function.
* If use 'flex_button_event_read' api,
* you don't need to initialize the 'cb' member.
* @member next : Next button struct
*/
typedef struct flex_button
{
uint8_t pressed_logic_level : 1; /* need user to init */
/**
* @event
* The event of button in flex_button_evnt_t enum list.
* Automatically initialized to the default value FLEX_BTN_PRESS_NONE
* by 'flex_button_register' API.
*/
uint8_t event : 4;
/**
* @status
* Used to record the status of the button
* Automatically initialized to the default value 0.
*/
uint8_t status : 3;
uint16_t scan_cnt; /* default 0. Used to record the number of key scans */
uint16_t click_cnt; /* default 0. Used to record the number of key click */
uint16_t debounce_tick;
uint16_t click_start_tick;
uint16_t short_press_start_tick;
uint16_t long_press_start_tick;
uint16_t long_hold_start_tick;
uint8_t (*usr_button_read)(void);
flex_button_response_callback cb;
struct flex_button *next;
} flex_button_t;
#ifdef __cplusplus
extern "C"
{
#endif
int8_t flex_button_register(flex_button_t *button);
flex_button_event_t flex_button_event_read(flex_button_t *button);
void flex_button_scan(void);
#ifdef __cplusplus
}
#endif
#endif /* __FLEXIBLE_BUTTON_H__ */

View File

@@ -0,0 +1,328 @@
#include "main.h"
#include "FreeRTOS.h"
#include "semphr.h"
#include "timers.h"
#include "task.h"
#include "shell.h"
#include "shell_port.h"
static TaskHandle_t AppTaskCreate_Handle = NULL; // 创建任务句柄
static TaskHandle_t Main_Task_Handle = NULL; // Main任务句柄
static TaskHandle_t Key_Task_Handle = NULL; // Key任务句柄
static TaskHandle_t Wdog_Task_Handle = NULL; // Wdog任务句柄
static TaskHandle_t Bat_Task_Handle = NULL; // 电池任务句柄
static TimerHandle_t xTimeHandle = NULL; //软件定时器句柄
static void AppTaskCreate(void); // 用于创建任务
static void Main_Task(void *pvParameters); // Main_Task任务实现
static void Key_Task(void *pvParameters); // KEY_Task任务实现
static void Wdog_Task(void *pvParameters); // Wdog发送任务实现
static void Bat_Task(void *pvParameters); // 电池检测任务实现
static void timeCallBackTask(void); // 定时锁机任务实现
uint8_t lora_sendbuf[255]; //Lora数据构造数组
void BSP_Init(void);
int main(void)
{
BaseType_t xReturn = pdPASS;
BSP_Init();
xReturn = xTaskCreate((TaskFunction_t)AppTaskCreate,
(const char *)"AppTaskCreate",
(uint16_t)512,
(void *)NULL,
(UBaseType_t)1,
(TaskHandle_t *)&AppTaskCreate_Handle);
if (pdPASS == xReturn)
vTaskStartScheduler();
else
return -1;
while (1)
;
}
static void AppTaskCreate(void)
{
BaseType_t xReturn = pdPASS;
taskENTER_CRITICAL(); //临界保护
/*创建线程*/
xReturn = xTaskCreate((TaskFunction_t)Main_Task,
(const char *)"Main_Task",
(uint16_t)512,
(void *)NULL,
(UBaseType_t)2,
(TaskHandle_t *)&Main_Task_Handle);
if (pdPASS != xReturn)
printf("创建Main_Task任务失败!\r\n");
xReturn = xTaskCreate((TaskFunction_t)Key_Task,
(const char *)"Key_Task",
(uint16_t)64,
(void *)NULL,
(UBaseType_t)2,
(TaskHandle_t *)&Key_Task_Handle);
if (pdPASS != xReturn)
printf("创建Key_Task任务失败!\r\n");
xReturn = xTaskCreate((TaskFunction_t)Wdog_Task,
(const char *)"Wdog_Task",
(uint16_t)512,
(void *)NULL,
(UBaseType_t)2, Wdog_Task_Handle);
if (pdPASS != xReturn)
printf("创建Wdog_Task任务失败!\r\n");
xReturn = xTaskCreate((TaskFunction_t)Bat_Task,
(const char *)"Bat_Task",
(uint16_t)64,
(void *)NULL,
(UBaseType_t)2, Bat_Task_Handle);
if (pdPASS != xReturn)
printf("创建Bat_Task任务失败!\r\n");
/*注册超时模块软件定时器*/
xTimeHandle = xTimerCreate(
(const char *)"time_lock", // 定时器名字
(TickType_t)1000, // 定时器的周期
pdTRUE, // 定时器周期执行
(void *)1, // 给定时器分配的唯一ID
(TimerCallbackFunction_t)timeCallBackTask // 定时器回调函数
);
if (xTimeHandle)
{
xTimerStart(xTimeHandle, 0); // 开启定时器
}
vTaskDelete(AppTaskCreate_Handle);
taskEXIT_CRITICAL();
}
/*软件定时器回调*/
void timeCallBackTask(void)
{
if (rc_data()->Lock_state == UNLOCK)
{
rc_data()->Lock_time_tick++;
}
if (rc_data()->Lock_time_tick >= rc_data()->Kv_get.lock_time)
{
rc_data()->Asr_state = LOCK;
rc_data()->Lock_state = LOCK;
rc_data()->Lock_time_tick = 0;
ASR_Power_OFF;
LEDClass.Set(BLACK);
}
}
/*主线程函数*/
static void Main_Task(void *parameter)
{
while (1)
{
/*按键控制模式*/
if((rc_data()->Lock_state ==UNLOCK)&&(rc_data()->Asr_state == LOCK))
{
LEDClass.Set(BLACK);
ADC1Class.GetValue(0) > 170 ? LEDClass.Set(GREEN) : LEDClass.Set(YELLOW);
if(rc_data()->isLEDTog == ENABLE)
{
LEDClass.Set(BLACK);
MODClass.Loop(LEDClass.GREEN_Toggle,vTaskDelay,7,50);
rc_data()->isLEDTog = 0;
}
if(rc_data()->isSend == ENABLE )
{
rc_data()->isSend = DISABLE;
if ((rc_data()->Key_data[0] == 0 && rc_data()->Key_data[1] == 1) || (rc_data()->Key_data[0] != 0))
{
sprintf((char *)lora_sendbuf,
"AT+LRSEND=85,1,33,\"{\"E\":\"%d\",\"T\":\"%d\",\"R\":\"%d\",\"C\":\"%d\"}\"\r\n",
rc_data()->Key_data[0],rc_data()->Key_data[1],
//(rc_data()->Key_data[0] != 9 && rc_data()->Key_data[0] != 10) ? rc_data()->Key_data[0] : 0,
//(rc_data()->Key_data[0] != 9 && rc_data()->Key_data[0] != 10) ? rc_data()->Key_data[1] : 0,
rc_data()->Key_data[2],
rc_data()->Key_data[3]);
LEDClass.Set(BLACK);
USARTClass.USART_SendString1(USART3, lora_sendbuf, 54);
MODClass.Loop(LEDClass.GREEN_Toggle,vTaskDelay,4,400);
LEDClass.Set(BLACK);
rc_data()->Lock_state = LOCK;
}
else
{
LEDClass.Set(BLACK);
MODClass.Loop(LEDClass.RED_Toggle,vTaskDelay,4,400);
}
memset(rc_data()->Key_data,0x00,4);
}
}
/*语音控制模式*/
if((rc_data()->Lock_state==UNLOCK)&&rc_data()->Asr_state == UNLOCK)
{
LEDClass.Set(WHITE);
if (Asr_recbuff[0] == 0x55 && Asr_recbuff[1] == 0xAA)
{
rc_data()->Lock_time_tick =0;
switch (Asr_recbuff[2])
{
case 1 ... 12:
{
const LoraCommandParams *params = &cmd_params[Asr_recbuff[2] - 1];
sprintf((char *)lora_sendbuf,
"AT+LRSEND=85,1,33,\"{\"E\":\"%s\",\"T\":\"%s\",\"R\":\"%s\",\"C\":\"%s\"}\"\r\n",
params->e_val, params->t_val, params->r_val, params->c_val);
break;
}
default:
break;
}
USARTClass.USART_SendString1(USART3, lora_sendbuf, 54);
memset(Asr_recbuff, 0x00, 20);
LEDClass.Set(BLACK);
MODClass.Loop(LEDClass.GREEN_Toggle,vTaskDelay,4,400);
LEDClass.Set(BLACK);
rc_data()->Lock_state = LOCK;
rc_data()->Asr_state = LOCK;
vTaskDelay(2000);
ASR_Power_OFF;
}
}
if(rc_data()->Lock_state ==LOCK)
{
BATClass.Read_State();
}
vTaskDelay(20);
}
}
/*看门狗线程函数*/
static void Wdog_Task(void *parameter)
{
while (1)
{
IWDG_ReloadCounter();
vTaskDelay(30);
}
}
/*电池检测线程函数*/
static void Bat_Task(void *parameter)
{
while (1)
{
// printf("bat value:%d\r\n",ADC1Class.GetValue(0));
vTaskDelay(1000);
}
}
/*按键检测线程函数*/
static void Key_Task(void *parameter)
{
while (1)
{
flex_button_scan();
vTaskDelay(10);
}
}
/*用户密码提取kv函数*/
//static void read_password(void)
//{
// char *read_password;
// read_password = ef_get_env("ziit_password");
// assert_param(read_password);
// strcpy(shellPasswordDefaultUser, read_password);
// printf("The ziit password is %s \n\r", shellPasswordDefaultUser);
//}
/*锁定时间提取kv函数*/
static void read_lock_time(void)
{
char *keys;
keys = ef_get_env("key_s");
assert_param(keys); //检查参数合法性
rc_data()->Kv_get.lock_time = atol(keys); //str => int
printf("锁定时间:%d\r\n", rc_data()->Kv_get.lock_time);
}
/*
* 按键使能提取函数 - 使用16位变量存储10个按键状态
* 每个按键占用1位最低位(bit0)表示第1个按键bit9表示第10个按键
* 0表示失能1表示使能
*/
static void read_key_en_table(void)
{
char *key_en_table;
uint16_t key_en_bits = 0; // 16位存储按键状态
// 从环境变量获取键值
key_en_table = ef_get_env("key_en_table");
assert_param(key_en_table); // 检查参数合法性
// 将字符串转换为16位数值
key_en_bits = (uint16_t)strtoul(key_en_table, NULL, 0);
// 存储到数据结构中
rc_data()->Kv_get.key_en_table = key_en_bits;
}
/*BSP初始化函数*/
static void BSP_Init(void)
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
delay_init();
LEDClass.Init();
ASR_Power_Init();
USARTClass.USART1_Init(115200);
USARTClass.USART3_Init(9600);
USARTClass.USART2_Init(9600);
USARTClass.USART_SendString1(USART3, (uint8_t *)"start",5);
BATClass.Init();
ADC1Class.Init();
rc_data()->Lock_state = UNLOCK;
rc_data()->Asr_state = LOCK;
rc_data()->isSend = DISABLE;
rc_data()->isLEDTog = DISABLE;
ASR_Power_OFF;
delay_ms(700);
delay_ms(700);
BOARD_Power_Init();
LEDClass.Set(GREEN);
Power_button_init();
if (easyflash_init() == EF_NO_ERR)
{
// read_password();
read_lock_time();
read_key_en_table();
}
while (1)
{
if (Check_Power_buttun() == 0)
{
break;
}
}
WDOG_Init();
userShellInit();
user_button_init();
}

View File

@@ -0,0 +1,57 @@
#ifndef __MAIN_H
#define __MAIN_H
// ST lib
#include "stm32f10x.h"
// C lib
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
// system
#include "delay.h"
// drive
#include "flexible_button.h"
#include "bsp_led.h"
#include "bsp_button.h"
#include "bsp_usart.h"
#include "bsp_chipid.h"
#include "bsp_bat.h"
#include "bsp_mod.h"
#include "easyflash.h"
#define LOCK 0
#define UNLOCK 1
extern uint8_t Lora_recbuff[];
/*整机数据结构体*/
typedef struct rc_data
{
uint8_t Lock_state; //整机锁定状态
uint8_t Asr_state; //语音锁定状态
uint8_t Key_data[4]; //当前键值
uint8_t Lock_time_tick; //无操作锁定计时
uint8_t isLEDTog; //闪烁信号
uint8_t isSend; //发送使能
union
{
struct
{
char lock_time; // 数据库读取锁定时间
uint8_t password[8]; // 数据库读取密码
uint16_t key_en_table; // 按键使能表 10个按键
} Kv_get;
};
} rc_data_t;
rc_data_t *rc_data(void);
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,557 @@
/**
* @file shell.h
* @author Letter (NevermindZZT@gmail.com)
* @brief letter shell
* @version 3.0.0
* @date 2019-12-30
*
* @copyright (c) 2020 Letter
*
*/
#ifndef __SHELL_H__
#define __SHELL_H__
#include "shell_cfg.h"
#define SHELL_VERSION "3.2.4" /**< 版本号 */
/**
* @brief shell 断言
*
* @param expr 表达式
* @param action 断言失败操作
*/
#define SHELL_ASSERT(expr, action) \
if (!(expr)) { \
action; \
}
#if SHELL_USING_LOCK == 1
#define SHELL_LOCK(shell) shell->lock(shell)
#define SHELL_UNLOCK(shell) shell->unlock(shell)
#else
#define SHELL_LOCK(shell)
#define SHELL_UNLOCK(shell)
#endif /** SHELL_USING_LOCK == 1 */
/**
* @brief shell 命令权限
*
* @param permission 权限级别
*/
#define SHELL_CMD_PERMISSION(permission) \
(permission & 0x000000FF)
/**
* @brief shell 命令类型
*
* @param type 类型
*/
#define SHELL_CMD_TYPE(type) \
((type & 0x0000000F) << 8)
/**
* @brief 使能命令在未校验密码的情况下使用
*/
#define SHELL_CMD_ENABLE_UNCHECKED \
(1 << 12)
/**
* @brief 禁用返回值打印
*/
#define SHELL_CMD_DISABLE_RETURN \
(1 << 13)
/**
* @brief 只读属性(仅对变量生效)
*/
#define SHELL_CMD_READ_ONLY \
(1 << 14)
/**
* @brief 命令参数数量
*/
#define SHELL_CMD_PARAM_NUM(num) \
((num & 0x0000000F)) << 16
#ifndef SHELL_SECTION
#if defined(__CC_ARM) || defined(__CLANG_ARM)
#define SHELL_SECTION(x) __attribute__((section(x), aligned(1)))
#elif defined (__IAR_SYSTEMS_ICC__)
#define SHELL_SECTION(x) @ x
#elif defined(__GNUC__)
#define SHELL_SECTION(x) __attribute__((section(x), aligned(1)))
#else
#define SHELL_SECTION(x)
#endif
#endif
#ifndef SHELL_USED
#if defined(__CC_ARM) || defined(__CLANG_ARM)
#define SHELL_USED __attribute__((used))
#elif defined (__IAR_SYSTEMS_ICC__)
#define SHELL_USED __root
#elif defined(__GNUC__)
#define SHELL_USED __attribute__((used))
#else
#define SHELL_USED
#endif
#endif
/**
* @brief shell float型参数转换
*/
#define SHELL_PARAM_FLOAT(x) (*(float *)(&x))
/**
* @brief shell 代理函数名
*/
#define SHELL_AGENCY_FUNC_NAME(_func) agency##_func
/**
* @brief shell代理函数定义
*
* @param _func 被代理的函数
* @param ... 代理参数
*/
#define SHELL_AGENCY_FUNC(_func, ...) \
void SHELL_AGENCY_FUNC_NAME(_func)(int p1, int p2, int p3, int p4, int p5, int p6, int p7) \
{ _func(__VA_ARGS__); }
#if SHELL_USING_CMD_EXPORT == 1
/**
* @brief shell 命令定义
*
* @param _attr 命令属性
* @param _name 命令名
* @param _func 命令函数
* @param _desc 命令描述
* @param ... 其他参数
*/
#define SHELL_EXPORT_CMD(_attr, _name, _func, _desc, ...) \
const char shellCmd##_name[] = #_name; \
const char shellDesc##_name[] = #_desc; \
SHELL_USED const ShellCommand \
shellCommand##_name SHELL_SECTION("shellCommand") = \
{ \
.attr.value = _attr, \
.data.cmd.name = shellCmd##_name, \
.data.cmd.function = (int (*)())_func, \
.data.cmd.desc = shellDesc##_name, \
##__VA_ARGS__ \
}
#if SHELL_USING_FUNC_SIGNATURE == 1
/**
* @brief shell 命令定义
*
* @param _attr 命令属性
* @param _name 命令名
* @param _func 命令函数
* @param _desc 命令描述
* @param _sign 命令签名
*/
#define SHELL_EXPORT_CMD_SIGN(_attr, _name, _func, _desc, _sign) \
const char shellCmd##_name[] = #_name; \
const char shellDesc##_name[] = #_desc; \
const char shellSign##_name[] = #_sign; \
SHELL_USED const ShellCommand \
shellCommand##_name SHELL_SECTION("shellCommand") = \
{ \
.attr.value = _attr, \
.data.cmd.name = shellCmd##_name, \
.data.cmd.function = (int (*)())_func, \
.data.cmd.desc = shellDesc##_name, \
.data.cmd.signature = shellSign##_name \
}
#endif /** SHELL_USING_FUNC_SIGNATURE == 1 */
/**
* @brief shell 代理命令定义
*
* @param _attr 命令属性
* @param _name 命令名
* @param _func 命令函数
* @param _desc 命令描述
* @param ... 代理参数
*/
#define SHELL_EXPORT_CMD_AGENCY(_attr, _name, _func, _desc, ...) \
SHELL_AGENCY_FUNC(_func, ##__VA_ARGS__) \
SHELL_EXPORT_CMD(_attr, _name, SHELL_AGENCY_FUNC_NAME(_func), _desc)
/**
* @brief shell 变量定义
*
* @param _attr 变量属性
* @param _name 变量名
* @param _value 变量值
* @param _desc 变量描述
*/
#define SHELL_EXPORT_VAR(_attr, _name, _value, _desc) \
const char shellCmd##_name[] = #_name; \
const char shellDesc##_name[] = #_desc; \
SHELL_USED const ShellCommand \
shellVar##_name SHELL_SECTION("shellCommand") = \
{ \
.attr.value = _attr, \
.data.var.name = shellCmd##_name, \
.data.var.value = (void *)_value, \
.data.var.desc = shellDesc##_name \
}
/**
* @brief shell 用户定义
*
* @param _attr 用户属性
* @param _name 用户名
* @param _password 用户密码
* @param _desc 用户描述
*/
#define SHELL_EXPORT_USER(_attr, _name, _password, _desc) \
const char shellCmd##_name[] = #_name; \
const char shellPassword##_name[] = #_password; \
const char shellDesc##_name[] = #_desc; \
SHELL_USED const ShellCommand \
shellUser##_name SHELL_SECTION("shellCommand") = \
{ \
.attr.value = _attr|SHELL_CMD_TYPE(SHELL_TYPE_USER), \
.data.user.name = shellCmd##_name, \
.data.user.password = shellPassword##_name, \
.data.user.desc = shellDesc##_name \
}
/**
* @brief shell 按键定义
*
* @param _attr 按键属性
* @param _value 按键键值
* @param _func 按键函数
* @param _desc 按键描述
*/
#define SHELL_EXPORT_KEY(_attr, _value, _func, _desc) \
const char shellDesc##_value[] = #_desc; \
SHELL_USED const ShellCommand \
shellKey##_value SHELL_SECTION("shellCommand") = \
{ \
.attr.value = _attr|SHELL_CMD_TYPE(SHELL_TYPE_KEY), \
.data.key.value = _value, \
.data.key.function = (void (*)(Shell *))_func, \
.data.key.desc = shellDesc##_value \
}
/**
* @brief shell 代理按键定义
*
* @param _attr 按键属性
* @param _value 按键键值
* @param _func 按键函数
* @param _desc 按键描述
* @param ... 代理参数
*/
#define SHELL_EXPORT_KEY_AGENCY(_attr, _value, _func, _desc, ...) \
SHELL_AGENCY_FUNC(_func, ##__VA_ARGS__) \
SHELL_EXPORT_KEY(_attr, _value, SHELL_AGENCY_FUNC_NAME(_func), _desc)
#if SHELL_USING_FUNC_SIGNATURE == 1
/**
* @brief shell 参数解析器定义
*
* @param _attr 参数解析器属性
* @param _type 参数解析器类型
* @param _parser 参数解析器函数
* @param _cleaner 参数清理器
*/
#define SHELL_EXPORT_PARAM_PARSER(_attr, _type, _parser, _cleaner) \
const char shellDesc##_parser[] = #_type; \
SHELL_USED const ShellCommand \
shellCommand##_parser SHELL_SECTION("shellCommand") = \
{ \
.attr.value = _attr|SHELL_CMD_TYPE(SHELL_TYPE_PARAM_PARSER), \
.data.paramParser.type = shellDesc##_parser, \
.data.paramParser.parser = (int (*)(char *, void **))_parser, \
.data.paramParser.cleaner = (int (*)(void *))_cleaner \
}
#endif
#else
/**
* @brief shell 命令item定义
*
* @param _attr 命令属性
* @param _name 命令名
* @param _func 命令函数
* @param _desc 命令描述
*/
#define SHELL_CMD_ITEM(_attr, _name, _func, _desc) \
{ \
.attr.value = _attr, \
.data.cmd.name = #_name, \
.data.cmd.function = (int (*)())_func, \
.data.cmd.desc = #_desc \
}
/**
* @brief shell 变量item定义
*
* @param _attr 变量属性
* @param _name 变量名
* @param _value 变量值
* @param _desc 变量描述
*/
#define SHELL_VAR_ITEM(_attr, _name, _value, _desc) \
{ \
.attr.value = _attr, \
.data.var.name = #_name, \
.data.var.value = (void *)_value, \
.data.var.desc = #_desc \
}
/**
* @brief shell 用户item定义
*
* @param _attr 用户属性
* @param _name 用户名
* @param _password 用户密码
* @param _desc 用户描述
*/
#define SHELL_USER_ITEM(_attr, _name, _password, _desc) \
{ \
.attr.value = _attr|SHELL_CMD_TYPE(SHELL_TYPE_USER), \
.data.user.name = #_name, \
.data.user.password = #_password, \
.data.user.desc = #_desc \
}
/**
* @brief shell 按键item定义
*
* @param _attr 按键属性
* @param _value 按键键值
* @param _func 按键函数
* @param _desc 按键描述
*/
#define SHELL_KEY_ITEM(_attr, _value, _func, _desc) \
{ \
.attr.value = _attr|SHELL_CMD_TYPE(SHELL_TYPE_KEY), \
.data.key.value = _value, \
.data.key.function = (void (*)(Shell *))_func, \
.data.key.desc = #_desc \
}
#if SHELL_USING_FUNC_SIGNATURE == 1
/**
* @brief shell 参数解析器item定义
*
* @param _attr 参数解析器属性
* @param _type 参数解析器类型
* @param _parser 参数解析器函数
* @param _cleaner 参数清理器
*/
#define SHELL_PARAM_PARSER_ITEM(_attr, _type, _parser, _cleaner) \
{ \
.attr.value = _attr|SHELL_CMD_TYPE(SHELL_TYPE_PARAM_PARSER), \
.data.paramParser.type = #_type, \
.data.paramParser.parser = (int (*)(char *, void **))_parser, \
.data.paramParser.cleaner = (int (*)(void *))_cleaner \
}
#endif /** SHELL_USING_FUNC_SIGNATURE == 1 */
#define SHELL_EXPORT_CMD(_attr, _name, _func, _desc)
#if SHELL_USING_FUNC_SIGNATURE == 1
#define SHELL_EXPORT_CMD_SIGN(_attr, _name, _func, _desc, _sign)
#endif /** SHELL_USING_FUNC_SIGNATURE == 1 */
#define SHELL_EXPORT_CMD_AGENCY(_attr, _name, _func, _desc, ...)
#define SHELL_EXPORT_VAR(_attr, _name, _value, _desc)
#define SHELL_EXPORT_USER(_attr, _name, _password, _desc)
#define SHELL_EXPORT_KEY(_attr, _value, _func, _desc)
#define SHELL_EXPORT_KEY_AGENCY(_attr, _name, _func, _desc, ...)
#if SHELL_USING_FUNC_SIGNATURE == 1
#define SHELL_EXPORT_PARAM_PARSER(_attr, _type, _parser, _cleaner)
#endif /** SHELL_USING_FUNC_SIGNATURE == 1 */
#endif /** SHELL_USING_CMD_EXPORT == 1 */
/**
* @brief shell command类型
*/
typedef enum
{
SHELL_TYPE_CMD_MAIN = 0, /**< main形式命令 */
SHELL_TYPE_CMD_FUNC, /**< C函数形式命令 */
SHELL_TYPE_VAR_INT, /**< int型变量 */
SHELL_TYPE_VAR_SHORT, /**< short型变量 */
SHELL_TYPE_VAR_CHAR, /**< char型变量 */
SHELL_TYPE_VAR_STRING, /**< string型变量 */
SHELL_TYPE_VAR_POINT, /**< 指针型变量 */
SHELL_TYPE_VAR_NODE, /**< 节点变量 */
SHELL_TYPE_USER, /**< 用户 */
SHELL_TYPE_KEY, /**< 按键 */
#if SHELL_USING_FUNC_SIGNATURE == 1
SHELL_TYPE_PARAM_PARSER, /**< 参数解析器 */
#endif
} ShellCommandType;
/**
* @brief Shell定义
*/
typedef struct shell_def
{
struct
{
const struct shell_command *user; /**< 当前用户 */
int activeTime; /**< shell激活时间 */
char *path; /**< 当前shell路径 */
#if SHELL_USING_COMPANION == 1
struct shell_companion_object *companions; /**< 伴生对象 */
#endif
#if SHELL_KEEP_RETURN_VALUE == 1
int retVal; /**< 返回值 */
#endif
} info;
struct
{
unsigned short length; /**< 输入数据长度 */
unsigned short cursor; /**< 当前光标位置 */
char *buffer; /**< 输入缓冲 */
char *param[SHELL_PARAMETER_MAX_NUMBER]; /**< 参数 */
unsigned short bufferSize; /**< 输入缓冲大小 */
unsigned short paramCount; /**< 参数数量 */
int keyValue; /**< 输入按键键值 */
} parser;
#if SHELL_HISTORY_MAX_NUMBER > 0
struct
{
char *item[SHELL_HISTORY_MAX_NUMBER]; /**< 历史记录 */
unsigned short number; /**< 历史记录数 */
unsigned short record; /**< 当前记录位置 */
signed short offset; /**< 当前历史记录偏移 */
} history;
#endif /** SHELL_HISTORY_MAX_NUMBER > 0 */
struct
{
void *base; /**< 命令表基址 */
unsigned short count; /**< 命令数量 */
} commandList;
struct
{
unsigned char isChecked : 1; /**< 密码校验通过 */
unsigned char isActive : 1; /**< 当前活动Shell */
unsigned char tabFlag : 1; /**< tab标志 */
} status;
signed short (*read)(char *, unsigned short); /**< shell读函数 */
signed short (*write)(char *, unsigned short); /**< shell写函数 */
#if SHELL_USING_LOCK == 1
int (*lock)(struct shell_def *); /**< shell 加锁 */
int (*unlock)(struct shell_def *); /**< shell 解锁 */
#endif
} Shell;
/**
* @brief shell command定义
*/
typedef struct shell_command
{
union
{
struct
{
unsigned char permission : 8; /**< command权限 */
ShellCommandType type : 4; /**< command类型 */
unsigned char enableUnchecked : 1; /**< 在未校验密码的情况下可用 */
unsigned char disableReturn : 1; /**< 禁用返回值输出 */
unsigned char readOnly : 1; /**< 只读 */
unsigned char reserve : 1; /**< 保留 */
unsigned char paramNum : 4; /**< 参数数量 */
} attrs;
int value;
} attr; /**< 属性 */
union
{
struct
{
const char *name; /**< 命令名 */
int (*function)(); /**< 命令执行函数 */
const char *desc; /**< 命令描述 */
#if SHELL_USING_FUNC_SIGNATURE == 1
const char *signature; /**< 函数签名 */
#endif
} cmd; /**< 命令定义 */
struct
{
const char *name; /**< 变量名 */
void *value; /**< 变量值 */
const char *desc; /**< 变量描述 */
} var; /**< 变量定义 */
struct
{
const char *name; /**< 用户名 */
const char *password; /**< 用户密码 */
const char *desc; /**< 用户描述 */
} user; /**< 用户定义 */
struct
{
int value; /**< 按键键值 */
void (*function)(Shell *); /**< 按键执行函数 */
const char *desc; /**< 按键描述 */
} key; /**< 按键定义 */
#if SHELL_USING_FUNC_SIGNATURE == 1
struct
{
const char *type; /**< 参数类型 */
int (*parser)(char *, void **); /**< 解析函数 */
int (*cleaner)(void *); /**< 清理器 */
} paramParser; /**< 参数解析器 */
#endif
} data;
} ShellCommand;
/**
* @brief shell节点变量属性
*/
typedef struct
{
void *var; /**< 变量引用 */
int (*get)(); /**< 变量get方法 */
int (*set)(); /**< 变量set方法 */
} ShellNodeVarAttr;
#define shellSetPath(_shell, _path) (_shell)->info.path = _path
#define shellGetPath(_shell) ((_shell)->info.path)
#define shellDeInit(shell) shellRemove(shell)
void shellInit(Shell *shell, char *buffer, unsigned short size);
void shellRemove(Shell *shell);
unsigned short shellWriteString(Shell *shell, const char *string);
void shellPrint(Shell *shell, const char *fmt, ...);
void shellScan(Shell *shell, char *fmt, ...);
Shell* shellGetCurrent(void);
void shellHandler(Shell *shell, char data);
void shellWriteEndLine(Shell *shell, char *buffer, int len);
void shellTask(void *param);
int shellRun(Shell *shell, const char *cmd);
#if SHELL_USING_COMPANION == 1
/**
* @brief shell伴生对象定义
*/
typedef struct shell_companion_object
{
int id; /**< 伴生对象ID */
void *obj; /**< 伴生对象 */
struct shell_companion_object *next; /**< 下一个伴生对象 */
} ShellCompanionObj;
signed char shellCompanionAdd(Shell *shell, int id, void *object);
signed char shellCompanionDel(Shell *shell, int id);
void *shellCompanionGet(Shell *shell, int id);
#endif
#endif

View File

@@ -0,0 +1,182 @@
/**
* @file shell_cfg.h
* @author Letter (nevermindzzt@gmail.com)
* @brief shell config
* @version 3.0.0
* @date 2019-12-31
*
* @copyright (c) 2019 Letter
*
*/
#ifndef __SHELL_CFG_H__
#define __SHELL_CFG_H__
#include "stm32f10x.h"
#include "FreeRTOS.h"
#include "task.h"
#include "portable.h"
#define SHELL_GET_TICK() (xTaskGetTickCount() * portTICK_PERIOD_MS)
/**
* @brief 是否使用默认shell任务while循环使能宏`SHELL_USING_TASK`后此宏有意义
* 使能此宏,则`shellTask()`函数会一直循环读取输入一般使用操作系统建立shell
* 任务时使能此宏,关闭此宏的情况下,一般适用于无操作系统,在主循环中调用`shellTask()`
*/
#define SHELL_TASK_WHILE 1
/**
* @brief 是否使用命令导出方式
* 使能此宏后,可以使用`SHELL_EXPORT_CMD()`等导出命令
* 定义shell命令关闭此宏的情况下需要使用命令表的方式
*/
#define SHELL_USING_CMD_EXPORT 1
/**
* @brief 是否使用shell伴生对象
* 一些扩展的组件(文件系统支持,日志工具等)需要使用伴生对象
*/
#define SHELL_USING_COMPANION 0
/**
* @brief 支持shell尾行模式
*/
#define SHELL_SUPPORT_END_LINE 1
/**
* @brief 是否在输出命令列表中列出用户
*/
#define SHELL_HELP_LIST_USER 1
/**
* @brief 是否在输出命令列表中列出变量
*/
#define SHELL_HELP_LIST_VAR 0
/**
* @brief 是否在输出命令列表中列出按键
*/
#define SHELL_HELP_LIST_KEY 0
/**
* @brief 是否在输出命令列表中展示命令权限
*/
#define SHELL_HELP_SHOW_PERMISSION 0
/**
* @brief 使用LF作为命令行回车触发
* 可以和SHELL_ENTER_CR同时开启
*/
#define SHELL_ENTER_LF 0
/**
* @brief 使用CR作为命令行回车触发
* 可以和SHELL_ENTER_LF同时开启
*/
#define SHELL_ENTER_CR 1
/**
* @brief 使用CRLF作为命令行回车触发
* 不可以和SHELL_ENTER_LF或SHELL_ENTER_CR同时开启
*/
#define SHELL_ENTER_CRLF 0
/**
* @brief 使用执行未导出函数的功能
* 启用后,可以通过`exec [addr] [args]`直接执行对应地址的函数
* @attention 如果地址错误,可能会直接引起程序崩溃
*/
#define SHELL_EXEC_UNDEF_FUNC 0
/**
* @brief shell命令参数最大数量
* 包含命令名在内超过16个参数并且使用了参数自动转换的情况下需要修改源码
*/
#define SHELL_PARAMETER_MAX_NUMBER 8
/**
* @brief 历史命令记录数量
*/
#define SHELL_HISTORY_MAX_NUMBER 5
/**
* @brief 双击间隔(ms)
* 使能宏`SHELL_LONG_HELP`后此宏生效定义双击tab补全help的时间间隔
*/
#define SHELL_DOUBLE_CLICK_TIME 200
/**
* @brief 管理的最大shell数量
*/
#define SHELL_MAX_NUMBER 10
/**
* @brief shell格式化输出的缓冲大小
* 为0时不使用shell格式化输出
*/
#define SHELL_PRINT_BUFFER 128
/**
* @brief shell格式化输入的缓冲大小
* 为0时不使用shell格式化输入
* @note shell格式化输入会阻塞shellTask, 仅适用于在有操作系统的情况下使用
*/
#define SHELL_SCAN_BUFFER 128
/**
* @brief 获取系统时间(ms)
* 定义此宏为获取系统Tick如`HAL_GetTick()`
* @note 此宏不定义时无法使用双击tab补全命令help无法使用shell超时锁定
*/
#define SHELL_GET_TICK() (xTaskGetTickCount() * portTICK_PERIOD_MS)
/**
* @brief 使用锁
* @note 使用shell锁时需要对加锁和解锁进行实现
*/
#define SHELL_USING_LOCK 1
/**
* @brief shell内存分配
* shell本身不需要此接口若使用shell伴生对象需要进行定义
*/
#define SHELL_MALLOC(size) pvPortMalloc(size)
/**
* @brief shell内存释放
* shell本身不需要此接口若使用shell伴生对象需要进行定义
*/
#define SHELL_FREE(obj) vPortFree(obj)
/**
* @brief 是否显示shell信息
*/
#define SHELL_SHOW_INFO 1
/**
* @brief 是否在登录后清除命令行
*/
#define SHELL_CLS_WHEN_LOGIN 1
/**
* @brief shell默认用户
*/
#define SHELL_DEFAULT_USER "ziit"
/**
* @brief shell默认用户密码
* 若默认用户不需要密码,设为""
*/
#define SHELL_DEFAULT_USER_PASSWORD "ZIIT1234"
/**
* @brief shell自动锁定超时
* shell当前用户密码有效的时候生效超时后会自动重新锁定shell
* 设置为0时关闭自动锁定功能时间单位为`SHELL_GET_TICK()`单位
* @note 使用超时锁定必须保证`SHELL_GET_TICK()`有效
*/
#define SHELL_LOCK_TIMEOUT 5 * 60 * 1000
#endif

View File

@@ -0,0 +1,103 @@
/**
* @file shell_cmd_list.c
* @author Letter (NevermindZZT@gmail.com)
* @brief shell cmd list
* @version 3.0.0
* @date 2020-01-17
*
* @copyright (c) 2020 Letter
*
*/
#include "shell.h"
#if SHELL_USING_CMD_EXPORT != 1
extern int shellSetVar(char *name, int value);
extern void shellUp(Shell *shell);
extern void shellDown(Shell *shell);
extern void shellRight(Shell *shell);
extern void shellLeft(Shell *shell);
extern void shellTab(Shell *shell);
extern void shellBackspace(Shell *shell);
extern void shellDelete(Shell *shell);
extern void shellEnter(Shell *shell);
extern void shellHelp(int argc, char *argv[]);
extern void shellUsers(void);
extern void shellCmds(void);
extern void shellVars(void);
extern void shellKeys(void);
extern void shellClear(void);
#if SHELL_EXEC_UNDEF_FUNC == 1
extern int shellExecute(int argc, char *argv[]);
#endif
SHELL_AGENCY_FUNC(shellRun, shellGetCurrent(), (const char *)p1);
/**
* @brief shell命令表
*
*/
const ShellCommand shellCommandList[] =
{
{.attr.value=SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_USER),
.data.user.name = SHELL_DEFAULT_USER,
.data.user.password = SHELL_DEFAULT_USER_PASSWORD,
.data.user.desc = "default user"},
SHELL_CMD_ITEM(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_FUNC),
setVar, shellSetVar, set var),
SHELL_KEY_ITEM(SHELL_CMD_PERMISSION(0), 0x1B5B4100, shellUp, up),
SHELL_KEY_ITEM(SHELL_CMD_PERMISSION(0), 0x1B5B4200, shellDown, down),
SHELL_KEY_ITEM(SHELL_CMD_PERMISSION(0)|SHELL_CMD_ENABLE_UNCHECKED,
0x1B5B4300, shellRight, right),
SHELL_KEY_ITEM(SHELL_CMD_PERMISSION(0)|SHELL_CMD_ENABLE_UNCHECKED,
0x1B5B4400, shellLeft, left),
SHELL_KEY_ITEM(SHELL_CMD_PERMISSION(0), 0x09000000, shellTab, tab),
SHELL_KEY_ITEM(SHELL_CMD_PERMISSION(0)|SHELL_CMD_ENABLE_UNCHECKED,
0x08000000, shellBackspace, backspace),
SHELL_KEY_ITEM(SHELL_CMD_PERMISSION(0)|SHELL_CMD_ENABLE_UNCHECKED,
0x7F000000, shellDelete, delete),
SHELL_KEY_ITEM(SHELL_CMD_PERMISSION(0)|SHELL_CMD_ENABLE_UNCHECKED,
0x1B5B337E, shellDelete, delete),
#if SHELL_ENTER_LF == 1
SHELL_KEY_ITEM(SHELL_CMD_PERMISSION(0)|SHELL_CMD_ENABLE_UNCHECKED,
0x0A000000, shellEnter, enter),
#endif
#if SHELL_ENTER_CR == 1
SHELL_KEY_ITEM(SHELL_CMD_PERMISSION(0)|SHELL_CMD_ENABLE_UNCHECKED,
0x0D000000, shellEnter, enter),
#endif
#if SHELL_ENTER_CRLF == 1
SHELL_KEY_ITEM(SHELL_CMD_PERMISSION(0)|SHELL_CMD_ENABLE_UNCHECKED,
0x0D0A0000, shellEnter, enter),
#endif
SHELL_CMD_ITEM(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_MAIN)|SHELL_CMD_DISABLE_RETURN,
help, shellHelp, show command info\r\nhelp [cmd]),
SHELL_CMD_ITEM(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_FUNC)|SHELL_CMD_DISABLE_RETURN,
users, shellUsers, list all user),
SHELL_CMD_ITEM(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_FUNC)|SHELL_CMD_DISABLE_RETURN,
cmds, shellCmds, list all cmd),
SHELL_CMD_ITEM(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_FUNC)|SHELL_CMD_DISABLE_RETURN,
vars, shellVars, list all var),
SHELL_CMD_ITEM(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_FUNC)|SHELL_CMD_DISABLE_RETURN,
keys, shellKeys, list all key),
SHELL_CMD_ITEM(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_FUNC)|SHELL_CMD_DISABLE_RETURN,
clear, shellClear, clear console),
SHELL_CMD_ITEM(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_FUNC)|SHELL_CMD_DISABLE_RETURN,
sh, SHELL_AGENCY_FUNC_NAME(shellRun), run command directly),
#if SHELL_EXEC_UNDEF_FUNC == 1
SHELL_CMD_ITEM(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_MAIN)|SHELL_CMD_DISABLE_RETURN,
exec, shellExecute, execute function undefined),
#endif
};
/**
* @brief shell命令表大小
*
*/
const unsigned short shellCommandCount
= sizeof(shellCommandList) / sizeof(ShellCommand);
#endif

View File

@@ -0,0 +1,87 @@
/**
* @file shell_companion.c
* @author Letter (nevermindzzt@gmail.com)
* @brief shell companion object support
* @version 3.0.3
* @date 2020-07-22
*
* @copyright (c) 2020 Letter
*
*/
#include "shell.h"
#if SHELL_USING_COMPANION == 1
/**
* @brief shell添加伴生对象
*
* @param shell shell对象
* @param id 伴生对象ID
* @param object 伴生对象
* @return signed char 0 添加成功 -1 添加失败
*/
signed char shellCompanionAdd(Shell *shell, int id, void *object)
{
ShellCompanionObj *companions = shell->info.companions;
ShellCompanionObj *node = SHELL_MALLOC(sizeof(ShellCompanionObj));
SHELL_ASSERT(node, return -1);
node->id = id;
node->obj = object;
node->next = companions;
shell->info.companions = node;
return 0;
}
/**
* @brief shell删除伴生对象
*
* @param shell shell对象
* @param id 伴生对象ID
* @return signed char 0 删除成功 -1 无匹配对象
*/
signed char shellCompanionDel(Shell *shell, int id)
{
ShellCompanionObj *companions = shell->info.companions;
ShellCompanionObj *front = companions;
while (companions)
{
if (companions->id == id)
{
if (companions == shell->info.companions && !(companions->next))
{
shell->info.companions = (void *)0;
}
else
{
front->next = companions->next;
}
SHELL_FREE(companions);
return 0;
}
front = companions;
companions = companions->next;
}
return -1;
}
/**
* @brief shell获取伴生对象
*
* @param shell shell对象
* @param id 伴生对象ID
* @return void* 伴生对象无匹配对象时返回NULL
*/
void *shellCompanionGet(Shell *shell, int id)
{
SHELL_ASSERT(shell, return (void *)0);
ShellCompanionObj *companions = shell->info.companions;
while (companions)
{
if (companions->id == id)
{
return companions->obj;
}
companions = companions->next;
}
return (void *)0;
}
#endif /** SHELL_USING_COMPANION == 1 */

View File

@@ -0,0 +1,848 @@
/**
* @file shell_ext.c
* @author Letter (NevermindZZT@gmail.com)
* @brief shell extensions
* @version 3.0.0
* @date 2019-12-31
*
* @copyright (c) 2019 Letter
*
*/
#include "shell_cfg.h"
#include "shell.h"
#include "shell_ext.h"
#include "string.h"
extern ShellCommand* shellSeekCommand(Shell *shell,
const char *cmd,
ShellCommand *base,
unsigned short compareLength);
extern int shellGetVarValue(Shell *shell, ShellCommand *command);
#if SHELL_SUPPORT_ARRAY_PARAM == 1
extern int shellSplit(char *string, unsigned short strLen, char *array[], char splitKey, short maxNum);
static int shellExtParseArray(Shell *shell, char *string, char *type, size_t *result);
static int shellExtCleanerArray(Shell *shell, char *type, void *param);
#endif /** SHELL_SUPPORT_ARRAY_PARAM == 1 */
#if SHELL_USING_FUNC_SIGNATURE == 1
/**
* @brief 获取下一个参数类型
*
* @param signature 函数签名
* @param index 参数遍历在签名中的起始索引
* @param type 获取到的参数类型
*
* @return int 下一个参数在签名中的索引
*/
static int shellGetNextParamType(const char *signature, int index, char *type)
{
const char *p = signature + index;
#if SHELL_SUPPORT_ARRAY_PARAM == 1
if (*p == '[')
{
*type++ = *p++;
index++;
}
#endif /** SHELL_SUPPORT_ARRAY_PARAM == 1 */
if (*p == 'L')
{
while (*p != ';' && *p != 0)
{
*type++ = *p++;
index++;
}
*type++ = *p++;
index++;
}
else if (*p != 0)
{
*type++ = *p;
index++;
}
*type = '\0';
return index;
}
/**
* @brief 获取期待的参数个数
*
* @param signature 函数签名
*
* @return int 参数个数
*/
static int shellGetParamNumExcept(const char *signature)
{
int num = 0;
const char *p = signature;
while (*p)
{
#if SHELL_SUPPORT_ARRAY_PARAM == 1
if (*p == '[')
{
p++;
}
#endif /** SHELL_SUPPORT_ARRAY_PARAM == 1 */
if (*p == 'L')
{
while (*p != ';' && *p != 0)
{
p++;
}
p++;
}
else
{
p++;
}
num++;
}
return num;
}
#endif
/**
* @brief 判断数字进制
*
* @param string 参数字符串
* @return ShellNumType 进制
*/
static ShellNumType shellExtNumType(char *string)
{
char *p = string;
ShellNumType type = NUM_TYPE_DEC;
if ((*p == '0') && ((*(p + 1) == 'x') || (*(p + 1) == 'X')))
{
type = NUM_TYPE_HEX;
}
else if ((*p == '0') && ((*(p + 1) == 'b') || (*(p + 1) == 'B')))
{
type = NUM_TYPE_BIN;
}
else if (*p == '0')
{
type = NUM_TYPE_OCT;
}
while (*p++)
{
if (*p == '.' && *(p + 1) != 0)
{
type = NUM_TYPE_FLOAT;
break;
}
}
return type;
}
/**
* @brief 字符转数字
*
* @param code 字符
* @return char 数字
*/
static char shellExtToNum(char code)
{
if ((code >= '0') && (code <= '9'))
{
return code -'0';
}
else if ((code >= 'a') && (code <= 'f'))
{
return code - 'a' + 10;
}
else if ((code >= 'A') && (code <= 'F'))
{
return code - 'A' + 10;
}
else
{
return 0;
}
}
/**
* @brief 解析字符参数
*
* @param string 字符串参数
* @return char 解析出的字符
*/
static char shellExtParseChar(char *string)
{
char *p = (*string == '\'') ? (string + 1) : string;
char value = 0;
if (*p == '\\')
{
switch (*(p + 1))
{
case 'b':
value = '\b';
break;
case 'r':
value = '\r';
break;
case 'n':
value = '\n';
break;
case 't':
value = '\t';
break;
case '0':
value = 0;
break;
default:
value = *(p + 1);
break;
}
}
else
{
value = *p;
}
return value;
}
/**
* @brief 解析字符串参数
*
* @param string 字符串参数
* @return char* 解析出的字符串
*/
static char* shellExtParseString(char *string)
{
char *p = string;
unsigned short index = 0;
if (*string == '\"')
{
p = ++string;
}
while (*p)
{
if (*p == '\\')
{
*(string + index) = shellExtParseChar(p);
p++;
}
else if (*p == '\"')
{
*(string + index) = 0;
}
else
{
*(string + index) = *p;
}
p++;
index ++;
}
*(string + index) = 0;
return string;
}
/**
* @brief 解析数字参数
*
* @param string 字符串参数
* @return size_t 解析出的数字
*/
static size_t shellExtParseNumber(char *string)
{
ShellNumType type = NUM_TYPE_DEC;
char radix = 10;
char *p = string;
char offset = 0;
signed char sign = 1;
size_t valueInt = 0;
float valueFloat = 0.0;
size_t devide = 0;
if (*string == '-')
{
sign = -1;
}
type = shellExtNumType(string + ((sign == -1) ? 1 : 0));
switch ((char)type)
{
case NUM_TYPE_HEX:
radix = 16;
offset = 2;
break;
case NUM_TYPE_OCT:
radix = 8;
offset = 1;
break;
case NUM_TYPE_BIN:
radix = 2;
offset = 2;
break;
default:
break;
}
p = string + offset + ((sign == -1) ? 1 : 0);
while (*p)
{
if (*p == '.')
{
devide = 1;
p++;
continue;
}
valueInt = valueInt * radix + shellExtToNum(*p);
devide *= 10;
p++;
}
if (type == NUM_TYPE_FLOAT && devide != 0)
{
valueFloat = (float)valueInt / devide * sign;
return *(size_t *)(&valueFloat);
}
else
{
return valueInt * sign;
}
}
/**
* @brief 解析变量参数
*
* @param shell shell对象
* @param var 变量
* @param result 解析结果
*
* @return int 0 解析成功 --1 解析失败
*/
static int shellExtParseVar(Shell *shell, char *var, size_t *result)
{
ShellCommand *command = shellSeekCommand(shell,
var + 1,
shell->commandList.base,
0);
if (command)
{
*result = shellGetVarValue(shell, command);
return 0;
}
else
{
return -1;
}
}
/**
* @brief 解析参数
*
* @param shell shell对象
* @param string 参数
* @param type 参数类型
* @param result 解析结果
*
* @return int 0 解析成功 --1 解析失败
*/
int shellExtParsePara(Shell *shell, char *string, char *type, size_t *result)
{
if (type == NULL || (*string == '$' && *(string + 1)))
{
if (*string == '\'' && *(string + 1))
{
*result = (size_t)shellExtParseChar(string);
return 0;
}
else if (*string == '-' || (*string >= '0' && *string <= '9'))
{
*result = shellExtParseNumber(string);
return 0;
}
else if (*string == '$' && *(string + 1))
{
return shellExtParseVar(shell, string, result);
}
else if (*string)
{
*result = (size_t)shellExtParseString(string);
return 0;
}
}
#if SHELL_USING_FUNC_SIGNATURE == 1
else
{
if (*string == '$' && *(string + 1))
{
return shellExtParseVar(shell, string, result);
}
#if SHELL_SUPPORT_ARRAY_PARAM == 1
else if (type[0] == '[')
{
return shellExtParseArray(shell, string, type, result);
}
#endif /** SHELL_SUPPORT_ARRAY_PARAM == 1 */
else if (strcmp("c", type) == 0)
{
*result = (size_t)shellExtParseChar(string);
return 0;
}
else if (strcmp("q", type) == 0
|| strcmp("h", type) == 0
|| strcmp("i", type) == 0
|| strcmp("f", type) == 0
|| strcmp("p", type) == 0)
{
*result = shellExtParseNumber(string);
return 0;
}
else if (strcmp("s", type) == 0)
{
*result = (size_t)shellExtParseString(string);
return 0;
}
else
{
ShellCommand *command = shellSeekCommand(shell,
type,
shell->commandList.base,
0);
if (command != NULL)
{
void *param;
if (command->data.paramParser.parser(shellExtParseString(string), &param) == 0)
{
*result = (size_t)param;
return 0;
}
else
{
shellWriteString(shell, "Parse param for type: ");
shellWriteString(shell, type);
shellWriteString(shell, " failed\r\n");
return -1;
}
}
else
{
shellWriteString(shell, "Can't find the param parser for type: ");
shellWriteString(shell, type);
shellWriteString(shell, "\r\n");
return -1;
}
}
}
#endif /** SHELL_USING_FUNC_SIGNATURE == 1 */
return -1;
}
#if SHELL_USING_FUNC_SIGNATURE == 1
/**
* @brief 清理参数
*
* @param shell shell
* @param type 参数类型
* @param param 参数
*
* @return int 0 清理成功 -1 清理失败
*/
int shellExtCleanerPara(Shell *shell, char *type, size_t param)
{
if (type == NULL)
{
return 0;
}
else
{
#if SHELL_SUPPORT_ARRAY_PARAM == 1
if (type[0] == '[') {
return shellExtCleanerArray(shell, type, (void *) param);
}
else
#endif /** SHELL_SUPPORT_ARRAY_PARAM == 1 */
if (strcmp("c", type) == 0
|| strcmp("q", type) == 0
|| strcmp("h", type) == 0
|| strcmp("i", type) == 0
|| strcmp("f", type) == 0
|| strcmp("p", type) == 0
|| strcmp("s", type) == 0)
{
return 0;
}
else
{
ShellCommand *command = shellSeekCommand(shell,
type,
shell->commandList.base,
0);
if (command != NULL && command->data.paramParser.cleaner != NULL)
{
return command->data.paramParser.cleaner((void *)param);
}
}
}
return -1;
}
#endif /** SHELL_USING_FUNC_SIGNATURE == 1 */
#if SHELL_SUPPORT_ARRAY_PARAM == 1
/**
* @brief 估算数组长度
*
* @param string 数组参数
*
* @return int 估算的数组长度
*/
static int shellEstimateArrayLength(char *string)
{
int length = 0;
char *p = string;
while (*p)
{
if (*p == ',')
{
length++;
}
p++;
}
return length + 1;
}
/**
* @brief 分割数组参数
*
* @param string 数组参数
* @param array 分割后的字符串数组
*
* @return int 数组长度
*/
static int shellSplitArray(char *string, char ***array)
{
int strLen = strlen(string);
if (string[strLen - 1] == ']')
{
string[--strLen] = 0;
}
if (string[0] == '[')
{
--strLen;
string++;
}
int size = shellEstimateArrayLength(string);
*array = SHELL_MALLOC(size * sizeof(char *));
return shellSplit(string, strLen, *array, ',', size);
}
/**
* @brief 解析数组参数
*
* @param shell shell 对象
* @param string 数组参数
* @param type 参数类型
* @param result 解析结果
*
* @return int 0 解析成功 -1 解析失败
*/
static int shellExtParseArray(Shell *shell, char *string, char *type, size_t *result)
{
char **params;
int size = shellSplitArray(string, &params);
int elementBytes = sizeof(void *);
if (strcmp(type + 1, "q") == 0)
{
elementBytes = sizeof(char);
}
else if (strcmp(type + 1, "h") == 0)
{
elementBytes = sizeof(short);
}
else if (strcmp(type + 1, "i") == 0)
{
elementBytes = sizeof(int);
}
ShellArrayHeader *header = SHELL_MALLOC(elementBytes * size + sizeof(ShellArrayHeader));
*result = (size_t) ((size_t) header + sizeof(ShellArrayHeader));
header->size = size;
header->elementBytes = elementBytes;
for (short i = 0; i < size; i++)
{
size_t value;
if (shellExtParsePara(shell, params[i], type + 1, &value) != 0)
{
SHELL_FREE(header);
SHELL_FREE(params);
return -1;
}
memcpy((void *) ((size_t) *result + elementBytes * i), &value, elementBytes);
}
SHELL_FREE(params);
return 0;
}
/**
* @brief 清理数组参数
*
* @param shell shell 对象
* @param type 参数类型
* @param param 参数
*
* @return int 0 清理成功 -1 清理失败
*/
static int shellExtCleanerArray(Shell *shell, char *type, void *param)
{
ShellArrayHeader *header = (ShellArrayHeader *) ((size_t) param - sizeof(ShellArrayHeader));
for (short i = 0; i < header->size; i++)
{
if (shellExtCleanerPara(shell, type + 1, *(size_t *) ((size_t) param + header->elementBytes * i)) != 0)
{
return -1;
}
}
SHELL_FREE(header);
return 0;
}
/**
* @brief 获取数组大小
*
* @param param 数组
*
* @return int 数组大小
*/
int shellGetArrayParamSize(void *param)
{
ShellArrayHeader *header = (ShellArrayHeader *) ((size_t) param - sizeof(ShellArrayHeader));
return header->size;
}
#endif /** SHELL_SUPPORT_ARRAY_PARAM == 1 */
/**
* @brief 执行命令
*
* @param shell shell对象
* @param command 命令
* @param argc 参数个数
* @param argv 参数
* @return int 返回值
*/
int shellExtRun(Shell *shell, ShellCommand *command, int argc, char *argv[])
{
int ret = 0;
size_t params[SHELL_PARAMETER_MAX_NUMBER] = {0};
int paramNum = command->attr.attrs.paramNum > (argc - 1) ?
command->attr.attrs.paramNum : (argc - 1);
#if SHELL_USING_FUNC_SIGNATURE == 1
char type[16];
int index = 0;
if (command->data.cmd.signature != NULL)
{
int except = shellGetParamNumExcept(command->data.cmd.signature);
if (except != argc - 1)
{
shellWriteString(shell, "Parameters number incorrect\r\n");
return -1;
}
}
#endif
for (int i = 0; i < argc - 1; i++)
{
#if SHELL_USING_FUNC_SIGNATURE == 1
if (command->data.cmd.signature != NULL) {
index = shellGetNextParamType(command->data.cmd.signature, index, type);
if (shellExtParsePara(shell, argv[i + 1], type, &params[i]) != 0)
{
return -1;
}
}
else
#endif /** SHELL_USING_FUNC_SIGNATURE == 1 */
{
if (shellExtParsePara(shell, argv[i + 1], NULL, &params[i]) != 0)
{
return -1;
}
}
}
switch (paramNum)
{
#if SHELL_PARAMETER_MAX_NUMBER >= 1
case 0:
ret = command->data.cmd.function();
break;
#endif /** SHELL_PARAMETER_MAX_NUMBER >= 1 */
#if SHELL_PARAMETER_MAX_NUMBER >= 2
case 1:
{
int (*func)(size_t) = command->data.cmd.function;
ret = func(params[0]);
break;
}
#endif /** SHELL_PARAMETER_MAX_NUMBER >= 2 */
#if SHELL_PARAMETER_MAX_NUMBER >= 3
case 2:
{
int (*func)(size_t, size_t) = command->data.cmd.function;
ret = func(params[0], params[1]);
break;
}
#endif /** SHELL_PARAMETER_MAX_NUMBER >= 3 */
#if SHELL_PARAMETER_MAX_NUMBER >= 4
case 3:
{
int (*func)(size_t, size_t, size_t) = command->data.cmd.function;
ret = func(params[0], params[1], params[2]);
break;
}
#endif /** SHELL_PARAMETER_MAX_NUMBER >= 4 */
#if SHELL_PARAMETER_MAX_NUMBER >= 5
case 4:
{
int (*func)(size_t, size_t, size_t, size_t) = command->data.cmd.function;
ret = func(params[0], params[1], params[2], params[3]);
break;
}
#endif /** SHELL_PARAMETER_MAX_NUMBER >= 5 */
#if SHELL_PARAMETER_MAX_NUMBER >= 6
case 5:
{
int (*func)(size_t, size_t, size_t, size_t, size_t) = command->data.cmd.function;
ret = func(params[0], params[1], params[2], params[3], params[4]);
break;
}
#endif /** SHELL_PARAMETER_MAX_NUMBER >= 6 */
#if SHELL_PARAMETER_MAX_NUMBER >= 7
case 6:
{
int (*func)(size_t, size_t, size_t, size_t, size_t, size_t) = command->data.cmd.function;
ret = func(params[0], params[1], params[2], params[3], params[4], params[5]);
break;
}
#endif /** SHELL_PARAMETER_MAX_NUMBER >= 7 */
#if SHELL_PARAMETER_MAX_NUMBER >= 8
case 7:
{
int (*func)(size_t, size_t, size_t, size_t, size_t, size_t, size_t) = command->data.cmd.function;
ret = func(params[0], params[1], params[2], params[3], params[4], params[5], params[6]);
break;
}
#endif /** SHELL_PARAMETER_MAX_NUMBER >= 8 */
#if SHELL_PARAMETER_MAX_NUMBER >= 9
case 8:
{
int (*func)(size_t, size_t, size_t, size_t, size_t, size_t, size_t, size_t)
= command->data.cmd.function;
ret = func(params[0], params[1], params[2], params[3], params[4], params[5], params[6], params[7]);
break;
}
#endif /** SHELL_PARAMETER_MAX_NUMBER >= 9 */
#if SHELL_PARAMETER_MAX_NUMBER >= 10
case 9:
{
int (*func)(size_t, size_t, size_t, size_t, size_t, size_t, size_t, size_t,
size_t) = command->data.cmd.function;
ret = func(params[0], params[1], params[2], params[3], params[4], params[5], params[6], params[7],
params[8]);
break;
}
#endif /** SHELL_PARAMETER_MAX_NUMBER >= 10 */
#if SHELL_PARAMETER_MAX_NUMBER >= 11
case 10:
{
int (*func)(size_t, size_t, size_t, size_t, size_t, size_t, size_t, size_t,
size_t, size_t) = command->data.cmd.function;
ret = func(params[0], params[1], params[2], params[3], params[4], params[5], params[6], params[7],
params[8], params[9]);
break;
}
#endif /** SHELL_PARAMETER_MAX_NUMBER >= 11 */
#if SHELL_PARAMETER_MAX_NUMBER >= 12
case 11:
{
int (*func)(size_t, size_t, size_t, size_t, size_t, size_t, size_t, size_t,
size_t, size_t, size_t) = command->data.cmd.function;
ret = func(params[0], params[1], params[2], params[3], params[4], params[5], params[6], params[7],
params[8], params[9], params[10]);
break;
}
#endif /** SHELL_PARAMETER_MAX_NUMBER >= 12 */
#if SHELL_PARAMETER_MAX_NUMBER >= 13
case 12:
{
int (*func)(size_t, size_t, size_t, size_t, size_t, size_t, size_t, size_t,
size_t, size_t, size_t, size_t) = command->data.cmd.function;
ret = func(params[0], params[1], params[2], params[3], params[4], params[5], params[6], params[7],
params[8], params[9], params[10], params[11]);
break;
}
#endif /** SHELL_PARAMETER_MAX_NUMBER >= 13 */
#if SHELL_PARAMETER_MAX_NUMBER >= 14
case 13:
{
int (*func)(size_t, size_t, size_t, size_t, size_t, size_t, size_t, size_t,
size_t, size_t, size_t, size_t, size_t) = command->data.cmd.function;
ret = func(params[0], params[1], params[2], params[3], params[4], params[5], params[6], params[7],
params[8], params[9], params[10], params[11], params[12]);
break;
}
#endif /** SHELL_PARAMETER_MAX_NUMBER >= 14 */
#if SHELL_PARAMETER_MAX_NUMBER >= 15
case 14:
{
int (*func)(size_t, size_t, size_t, size_t, size_t, size_t, size_t, size_t,
size_t, size_t, size_t, size_t, size_t, size_t)
= command->data.cmd.function;
ret = func(params[0], params[1], params[2], params[3], params[4], params[5], params[6], params[7],
params[8], params[9], params[10], params[11], params[12], params[13]);
break;
}
#endif /** SHELL_PARAMETER_MAX_NUMBER >= 15 */
#if SHELL_PARAMETER_MAX_NUMBER >= 16
case 15:
{
int (*func)(size_t, size_t, size_t, size_t, size_t, size_t, size_t, size_t,
size_t, size_t, size_t, size_t, size_t, size_t, size_t)
= command->data.cmd.function;
ret = func(params[0], params[1], params[2], params[3], params[4], params[5], params[6], params[7],
params[8], params[9], params[10], params[11], params[12], params[13], params[14]);
break;
}
#endif /** SHELL_PARAMETER_MAX_NUMBER >= 16 */
default:
ret = -1;
break;
}
#if SHELL_USING_FUNC_SIGNATURE == 1
if (command->data.cmd.signature != NULL) {
index = 0;
for (int i = 0; i < argc - 1; i++)
{
index = shellGetNextParamType(command->data.cmd.signature, index, type);
shellExtCleanerPara(shell, type, params[i]);
}
}
#endif /** SHELL_USING_FUNC_SIGNATURE == 1 */
return ret;
}

View File

@@ -0,0 +1,47 @@
/**
* @file shell_ext.h
* @author Letter (NevermindZZT@gmail.com)
* @brief shell extensions
* @version 3.0.0
* @date 2019-12-31
*
* @copyright (c) 2019 Letter
*
*/
#ifndef __SHELL_EXT_H__
#define __SHELL_EXT_H__
#include "shell.h"
/**
* @brief 数字类型
*
*/
typedef enum
{
NUM_TYPE_DEC, /**< 十进制整型 */
NUM_TYPE_BIN, /**< 二进制整型 */
NUM_TYPE_OCT, /**< 八进制整型 */
NUM_TYPE_HEX, /**< 十六进制整型 */
NUM_TYPE_FLOAT /**< 浮点型 */
} ShellNumType;
#if SHELL_SUPPORT_ARRAY_PARAM == 1
typedef struct
{
unsigned short size;
unsigned char elementBytes;
} ShellArrayHeader;
#endif /** SHELL_SUPPORT_ARRAY_PARAM == 1 */
int shellExtParsePara(Shell *shell, char *string, char *type, size_t *result);
#if SHELL_USING_FUNC_SIGNATURE == 1
int shellExtCleanerPara(Shell *shell, char *type, size_t param);
#endif /** SHELL_USING_FUNC_SIGNATURE == 1 */
#if SHELL_SUPPORT_ARRAY_PARAM == 1
int shellGetArrayParamSize(void *param);
#endif /** SHELL_SUPPORT_ARRAY_PARAM == 1 */
int shellExtRun(Shell *shell, ShellCommand *command, int argc, char *argv[]);
#endif

View File

@@ -0,0 +1,121 @@
/**
* @file shell_port.c
* @author Letter (NevermindZZT@gmail.com)
* @brief
* @version 0.1
* @date 2019-02-22
*
* @copyright (c) 2019 Letter
*
*/
#include "FreeRTOS.h"
#include "task.h"
#include "shell.h"
#include "queue.h"
#include "semphr.h"
#include "stm32f10x.h"
#include "bsp_usart.h"
#include "string.h"
Shell shell;
char shellBuffer[512];
static SemaphoreHandle_t shellMutex;
/**
* @brief 用户shell写
*
* @param data 数据
* @param len 数据长度
*
* @return short 实际写入的数据长度
*/
short userShellWrite(char *data, unsigned short len)
{
Usart_SendString(USART1,data);
return len;
}
/**
* @brief 用户shell读
*
* @param data 数据
* @param len 数据长度
*
* @return short 实际读取到
*/
/* 自定义接口函数 */
extern uint8_t g_debugRxBuf[];
extern uint8_t g_debugRxFlag;
extern uint16_t g_debugRxLen;
void userShellRun(void)
{
if(g_debugRxFlag)
{
for(uint8_t i=0;i<=g_debugRxLen;i++)
{
shellHandler(&shell,g_debugRxBuf[i]);
}
g_debugRxLen = 0;
g_debugRxFlag = 0;
memset(g_debugRxBuf,0x00,255);
}
}
short userShellRead(char *data, unsigned short len)
{
userShellRun();
vTaskDelay(20);
return 0;
}
/**
* @brief 用户shell上锁
*
* @param shell shell
*
* @return int 0
*/
int userShellLock(Shell *shell)
{
xSemaphoreTake(shellMutex, portMAX_DELAY);
return 0;
}
/**
* @brief 用户shell解锁
*
* @param shell shell
*
* @return int 0
*/
int userShellUnlock(Shell *shell)
{
xSemaphoreGive(shellMutex);
return 0;
}
/**
* @brief 用户shell初始化
*
*/
void userShellInit(void)
{
shellMutex = xSemaphoreCreateMutex();
shell.write = userShellWrite;
shell.read = userShellRead;
shell.lock = userShellLock;
shell.unlock = userShellUnlock;
shellInit(&shell, shellBuffer, 512);
if (xTaskCreate(shellTask, "shell", 256, &shell, 5, NULL) != pdPASS)
{
printf("shell task creat failed");
}
}

View File

@@ -0,0 +1,21 @@
/**
* @file shell_port.h
* @author Letter (NevermindZZT@gmail.com)
* @brief
* @version 0.1
* @date 2019-02-22
*
* @copyright (c) 2019 Letter
*
*/
#ifndef __SHELL_PORT_H__
#define __SHELL_PORT_H__
#include "shell.h"
extern Shell shell;
void userShellInit(void);
void userShellRun(void);
#endif

View File

@@ -0,0 +1,265 @@
/*
* @Date: 2025-06-26 16:58:50
* @LastEditors: 路怀帅
* @LastEditTime: 2025-07-04 17:07:40
* @FilePath: \RVMDKuv5e:\个人项目\零距电子\安灯遥控器\Andon_Remote_Control\Andon_RTOS\User\src\bsp_bat.c
*/
#include "main.h"
#include "FreeRTOS.h"
#include "semphr.h"
#include "task.h"
static void BAT_Init(void);
static uint8_t BAT_State_Read(void);
BATClassStruct BATClass = {
.Init = BAT_Init,
.Read_State = BAT_State_Read};
static void BAT_Init()
{
/*电池检测IO初始化*/
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_Init(GPIOB, &GPIO_InitStructure);
}
int cont = 0;
static uint8_t BAT_State_Read()
{
cont++;
uint8_t isCharging, batteryLevel;
batteryLevel = GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_2);
isCharging = GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_4);
if ((isCharging == 0) && (batteryLevel == 1))
{
if (cont >= 30)
{
GPIOB->BSRR = ((GPIOB->ODR & GPIO_Pin_0) << 16) | (~GPIOB->ODR & GPIO_Pin_0);
cont = 0;
}
}
else if ((isCharging == 1) && (batteryLevel == 0))
{
GPIO_ResetBits(GPIOB, GPIO_Pin_0);
}
else
{
GPIO_SetBits(GPIOB, GPIO_Pin_0);
}
return 0;
}
void Power_button_init()
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;
GPIO_Init(GPIOA, &GPIO_InitStructure);
}
uint8_t Check_Power_buttun()
{
if (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0) == 0)
{
delay_ms(30);
if (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0) == 0)
return 0;
}
else if (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0) == 1)
{
delay_ms(30);
if (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0) == 1)
return 1;
}
return 0;
}
static void ADC1_Init(void);
static uint16_t getADC1Value(uint8_t ADC_channelx);
ADC1ClassStruct ADC1Class = {
.Init = ADC1_Init,
.GetValue = getADC1Value};
#define ADC1_DMA_CHANNEL DMA1_Channel1
#define ADC1_DMA_CLK RCC_AHBPeriph_DMA1
#define ADC1_CLK RCC_APB2Periph_ADC1
#define ADC1_A0_A7_GPIO_PORT GPIOA
#define ADC1_A0_A7_GPIO_CLK RCC_APB2Periph_GPIOA
#define ADC1_A0_GPIO_PIN GPIO_Pin_0
#define ADC1_A1_GPIO_PIN GPIO_Pin_1
#define ADC1_A2_GPIO_PIN GPIO_Pin_2
#define ADC1_A3_GPIO_PIN GPIO_Pin_3
#define ADC1_A4_GPIO_PIN GPIO_Pin_4
#define ADC1_A5_GPIO_PIN GPIO_Pin_5
#define ADC1_A6_GPIO_PIN GPIO_Pin_6
#define ADC1_A7_GPIO_PIN GPIO_Pin_7
#define ADC1_B0_B1_GPIO_PORT GPIOB
#define ADC1_B0_B1_GPIO_CLK RCC_APB2Periph_GPIOB
#define ADC1_B0_GPIO_PIN GPIO_Pin_0
#define ADC1_B1_GPIO_PIN GPIO_Pin_1
#define ADC1_A0_CHANNEL ADC_Channel_0
#define ADC1_A1_CHANNEL ADC_Channel_1
#define ADC1_A2_CHANNEL ADC_Channel_2
#define ADC1_A3_CHANNEL ADC_Channel_3
#define ADC1_A4_CHANNEL ADC_Channel_4
#define ADC1_A5_CHANNEL ADC_Channel_5
#define ADC1_A6_CHANNEL ADC_Channel_6
#define ADC1_A7_CHANNEL ADC_Channel_7
#define ADC1_B0_CHANNEL ADC_Channel_8
#define ADC1_B1_CHANNEL ADC_Channel_9
__IO uint16_t ADC_ConvertedValue[10] = {0};
/**
* @brief ADC1初始化
* @param None
* @retval None
* @note None
*/
static void ADC1_Init(void)
{
/*定义一个GPIO_InitTypeDef类型的结构体*/
GPIO_InitTypeDef GPIO_InitStructure;
ADC_InitTypeDef ADC_InitStructure;
DMA_InitTypeDef DMA_InitStructure;
RCC_APB2PeriphClockCmd(ADC1_A0_A7_GPIO_CLK, ENABLE);
/***************************************************************/
// 1. 修改为所使用的引脚
GPIO_InitStructure.GPIO_Pin = ADC1_A5_GPIO_PIN;
/***************************************************************/
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_Init(ADC1_A0_A7_GPIO_PORT, &GPIO_InitStructure);
// 打开DMA时钟
RCC_AHBPeriphClockCmd(ADC1_DMA_CLK, ENABLE);
// 打开ADC时钟
RCC_APB2PeriphClockCmd(ADC1_CLK, ENABLE);
// 复位DMA控制器
DMA_DeInit(ADC1_DMA_CHANNEL);
// 配置 DMA 初始化结构体
// 外设基址为ADC 数据寄存器地址
DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)(&(ADC1->DR));
// 存储器地址
DMA_InitStructure.DMA_MemoryBaseAddr = (u32)ADC_ConvertedValue;
// 数据源来自外设
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
/***************************************************************/
// 2.缓存数量 使用1个ADC引脚就是1 使用2个ADC引脚就是2
DMA_InitStructure.DMA_BufferSize = 1;
/***************************************************************/
// 外设寄存器只有一个,地址不用递增
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
// 存储器地址递增
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
// 外设数据大小为半字,即两个字节
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
// 内存数据大小也为半字,跟外设数据大小相同
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
// 循环传输模式
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
// DMA 传输通道优先级为高当使用一个DMA通道时优先级设置不影响
DMA_InitStructure.DMA_Priority = DMA_Priority_High;
// 禁止存储器到存储器模式,因为是从外设到存储器
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
// 初始化DMA
DMA_Init(ADC1_DMA_CHANNEL, &DMA_InitStructure);
// 使能 DMA 通道
DMA_Cmd(ADC1_DMA_CHANNEL, ENABLE);
// ADC 模式配置
// 只使用一个ADC属于单模式
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
// 扫描模式
ADC_InitStructure.ADC_ScanConvMode = ENABLE;
// 连续转换模式
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
// 不用外部触发转换,软件开启即可
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
// 转换结果右对齐
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
/***************************************************************/
// 3.转换通道个数 使用1个ADC引脚就是1 使用2个ADC引脚就是2
ADC_InitStructure.ADC_NbrOfChannel = 1;
/***************************************************************/
// 初始化ADC
ADC_Init(ADC1, &ADC_InitStructure);
// 配置ADC时钟狿CLK2的8分频即9MHz
RCC_ADCCLKConfig(RCC_PCLK2_Div8);
/***************************************************************/
// 4.配置ADC通道的转换顺序和采样时间 将所使用的所有引脚按自定义顺序填写
ADC_RegularChannelConfig(ADC1, ADC1_A5_CHANNEL, 1, ADC_SampleTime_55Cycles5);
// ADC_RegularChannelConfig(ADC1, ADC1_A2_CHANNEL, 2, ADC_SampleTime_55Cycles5);
/***************************************************************/
// 使能ADC DMA 请求
ADC_DMACmd(ADC1, ENABLE);
// 开启ADC ,并开始转换
ADC_Cmd(ADC1, ENABLE);
// 初始化ADC 校准寄存器
ADC_ResetCalibration(ADC1);
// 等待校准寄存器初始化完成
while (ADC_GetResetCalibrationStatus(ADC1))
;
// ADC开始校准
ADC_StartCalibration(ADC1);
// 等待校准完成
while (ADC_GetCalibrationStatus(ADC1))
;
// 由于没有采用外部触发所以使用软件触发ADC转换
ADC_SoftwareStartConvCmd(ADC1, ENABLE);
}
/* 0-9对应第四步中的ADC引脚通道顺序 例如顺序为ADC1_A3 ADC1_A4 则A3引脚对应0 A4引脚对应1*/
static uint16_t getADC1Value(uint8_t ADC_channelx)
{
uint16_t ADCTemp = 0;
switch (ADC_channelx)
{
case 0:
ADCTemp = (float)ADC_ConvertedValue[0] * 3.3 * 100 / 4096;
break;
case 1:
ADCTemp = (float)ADC_ConvertedValue[1] / 4096 * 3.3 * 100;
break;
case 2:
ADCTemp = (float)ADC_ConvertedValue[2] / 4096 * 3.3 * 100;
break;
case 3:
ADCTemp = (float)ADC_ConvertedValue[3] / 4096 * 3.3 * 100;
break;
case 4:
ADCTemp = (float)ADC_ConvertedValue[4] / 4096 * 3.3 * 100;
break;
case 5:
ADCTemp = (float)ADC_ConvertedValue[5] / 4096 * 3.3 * 100;
break;
case 6:
ADCTemp = (float)ADC_ConvertedValue[6] / 4096 * 3.3 * 100;
break;
case 7:
ADCTemp = (float)ADC_ConvertedValue[7] / 4096 * 3.3 * 100;
break;
case 8:
ADCTemp = (float)ADC_ConvertedValue[8] / 4096 * 3.3 * 100;
break;
case 9:
ADCTemp = (float)ADC_ConvertedValue[9] / 4096 * 3.3 * 100;
break;
}
return ADCTemp;
}

View File

@@ -0,0 +1,459 @@
#include "FreeRTOS.h"
#include "semphr.h"
#include "task.h"
#include "main.h"
#define KEY_ENABLED(bit) ((rc_data()->Kv_get.key_en_table & (1 << (bit - 1))) != 0)
typedef enum
{
S1_BUTTON = 0,
S2_BUTTON,
S3_BUTTON,
S4_BUTTON,
S5_BUTTON,
S6_BUTTON,
S7_BUTTON,
S8_BUTTON,
S9_BUTTON,
S10_BUTTON,
S11_BUTTON,
S12_BUTTON,
BUTTON_MAX
} user_button_t;
static flex_button_t user_button[BUTTON_MAX];
/**
*@brief 按键1回调函数
*@param *btn 按键结构体
*@retval None
*/
static void btn_1_cb(flex_button_t *btn)
{
switch (btn->event)
{
case FLEX_BTN_PRESS_DOWN:
break;
case FLEX_BTN_PRESS_CLICK:
rc_data()->Lock_state = !rc_data()->Lock_state;
rc_data()->Lock_time_tick = 0;
LEDClass.Set(BLACK);
rc_data()->isLEDTog = DISABLE;
rc_data()->isSend = DISABLE;
ASR_Power_OFF;
rc_data()->Asr_state = LOCK;
break;
case FLEX_BTN_PRESS_LONG_START:
BOARD_Power_OFF;
break;
}
}
/**
*@brief 按键2回调函数
*@param *btn 按键结构体
*@retval None
*/
static void btn_2_cb(flex_button_t *btn)
{
switch (btn->event)
{
case FLEX_BTN_PRESS_DOWN:
break;
case FLEX_BTN_PRESS_CLICK:
if (KEY_ENABLED(2))
{
rc_data()->Lock_time_tick = 0;
rc_data()->Key_data[1] = 1;
rc_data()->isLEDTog = ENABLE;
}
break;
case FLEX_BTN_PRESS_LONG_START:
break;
}
}
/**
*@brief 按键3回调函数
*@param *btn 按键结构体
*@retval None
*/
static void btn_3_cb(flex_button_t *btn)
{
switch (btn->event)
{
case FLEX_BTN_PRESS_DOWN:
break;
case FLEX_BTN_PRESS_CLICK:
if (KEY_ENABLED(3))
{
rc_data()->Lock_time_tick = 0;
rc_data()->isLEDTog = ENABLE;
rc_data()->Key_data[0] = 3;
}
break;
case FLEX_BTN_PRESS_LONG_START:
break;
}
}
/**
*@brief 按键4回调函数
*@param *btn 按键结构体
*@retval None
*/
static void btn_4_cb(flex_button_t *btn)
{
switch (btn->event)
{
case FLEX_BTN_PRESS_DOWN:
break;
case FLEX_BTN_PRESS_CLICK:
if (KEY_ENABLED(4))
{
rc_data()->Lock_time_tick = 0;
rc_data()->isLEDTog = ENABLE;
rc_data()->Key_data[0] = 4;
}
break;
case FLEX_BTN_PRESS_LONG_START:
break;
}
}
/**
*@brief 按键5回调函数
*@param *btn 按键结构体
*@retval None
*/
static void btn_5_cb(flex_button_t *btn)
{
switch (btn->event)
{
case FLEX_BTN_PRESS_DOWN:
break;
case FLEX_BTN_PRESS_CLICK:
if (KEY_ENABLED(5))
{
rc_data()->Lock_time_tick = 0;
rc_data()->isLEDTog = ENABLE;
rc_data()->Key_data[0] = 5;
}
break;
case FLEX_BTN_PRESS_LONG_START:
break;
}
}
/**
*@brief 按键6回调函数
*@param *btn 按键结构体
*@retval None
*/
static void btn_6_cb(flex_button_t *btn)
{
switch (btn->event)
{
case FLEX_BTN_PRESS_DOWN:
break;
case FLEX_BTN_PRESS_CLICK:
if (KEY_ENABLED(6))
{
rc_data()->Lock_time_tick = 0;
rc_data()->isLEDTog = ENABLE;
rc_data()->Key_data[0] = 6;
}
break;
case FLEX_BTN_PRESS_LONG_START:
break;
}
}
/**
*@brief 按键7回调函数
*@param *btn 按键结构体
*@retval None
*/
static void btn_7_cb(flex_button_t *btn)
{
switch (btn->event)
{
case FLEX_BTN_PRESS_DOWN:
break;
case FLEX_BTN_PRESS_CLICK:
if (KEY_ENABLED(7))
{
rc_data()->Lock_time_tick = 0;
rc_data()->isLEDTog = ENABLE;
rc_data()->Key_data[0] = 7;
}
break;
case FLEX_BTN_PRESS_LONG_START:
break;
}
}
/**
*@brief 按键8回调函数
*@param *btn 按键结构体
*@retval None
*/
static void btn_8_cb(flex_button_t *btn)
{
switch (btn->event)
{
case FLEX_BTN_PRESS_DOWN:
break;
case FLEX_BTN_PRESS_CLICK:
if (KEY_ENABLED(8))
{
rc_data()->Lock_time_tick = 0;
rc_data()->isLEDTog = ENABLE;
rc_data()->Key_data[0] = 8;
}
break;
case FLEX_BTN_PRESS_LONG_START:
break;
}
}
/**
*@brief 按键9回调函数
*@param *btn 按键结构体
*@retval None
*/
static void btn_9_cb(flex_button_t *btn)
{
switch (btn->event)
{
case FLEX_BTN_PRESS_DOWN:
break;
case FLEX_BTN_PRESS_CLICK:
if (KEY_ENABLED(9))
{
rc_data()->Lock_time_tick = 0;
rc_data()->isLEDTog = ENABLE;
rc_data()->Key_data[2] = 1;
rc_data()->Key_data[3] = 0;
}
break;
case FLEX_BTN_PRESS_LONG_START:
break;
}
}
/**
*@brief 按键10回调函数
*@param *btn 按键结构体
*@retval None
*/
static void btn_10_cb(flex_button_t *btn)
{
switch (btn->event)
{
case FLEX_BTN_PRESS_DOWN:
break;
case FLEX_BTN_PRESS_CLICK:
if (KEY_ENABLED(10))
{
rc_data()->Lock_time_tick = 0;
rc_data()->isLEDTog = ENABLE;
rc_data()->Key_data[2] = 0;
rc_data()->Key_data[3] = 1;
}
break;
case FLEX_BTN_PRESS_LONG_START:
break;
}
}
/**
*@brief 按键11回调函数
*@param *btn 按键结构体
*@retval None
*/
static void btn_11_cb(flex_button_t *btn)
{
switch (btn->event)
{
case FLEX_BTN_PRESS_DOWN:
break;
case FLEX_BTN_PRESS_CLICK:
if (KEY_ENABLED(11))
{
rc_data()->Lock_time_tick = 0;
if (rc_data()->Asr_state == UNLOCK && rc_data()->Lock_state == UNLOCK)
{
ASR_Power_OFF;
LEDClass.Set(BLACK);
rc_data()->Asr_state = LOCK;
}
else if (rc_data()->Asr_state == LOCK && rc_data()->Lock_state == UNLOCK)
{
ASR_Power_ON;
LEDClass.Set(BLACK);
rc_data()->Asr_state = UNLOCK;
}
}
break;
case FLEX_BTN_PRESS_LONG_START:
break;
}
}
/**
*@brief 按键12回调函数
*@param *btn 按键结构体
*@retval None
*/
static void btn_12_cb(flex_button_t *btn)
{
switch (btn->event)
{
case FLEX_BTN_PRESS_DOWN:
break;
case FLEX_BTN_PRESS_CLICK:
rc_data()->Lock_time_tick = 0;
rc_data()->isSend = ENABLE;
break;
case FLEX_BTN_PRESS_LONG_START:
break;
}
}
// 按键001平读取接口
static uint8_t button_s1_read(void)
{
return GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0);
}
// 按键02电平读取接口
static uint8_t button_s2_read(void)
{
return GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_11);
}
// 按键03平读取接口
static uint8_t button_s3_read(void)
{
return GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_12);
}
// 按键04电平读取接口
static uint8_t button_s4_read(void)
{
return GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_7);
}
// 按键05电平读取接口
static uint8_t button_s5_read(void)
{
return GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_14);
}
// 按键06电平读取接口
static uint8_t button_s6_read(void)
{
return GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_6);
}
// 按键07电平读取接口
static uint8_t button_s7_read(void)
{
return GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_13);
}
// 按键08电平读取接口
static uint8_t button_s8_read(void)
{
return GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_5);
}
// 按键09电平读取接口
static uint8_t button_s9_read(void)
{
return GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_15);
}
// 按键10电平读取接口
static uint8_t button_s10_read(void)
{
return GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_3);
}
// 按键11电平读取接口
static uint8_t button_s11_read(void)
{
return GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1);
}
// 按键12电平读取接口
static uint8_t button_s12_read(void)
{
return GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_12);
}
/**
*@brief 按键初始化
*@param None
*@retval None
*/
void user_button_init(void)
{
/*注册按键Callback函数*/
memset(&user_button[0], 0x0, sizeof(user_button));
user_button[S1_BUTTON].usr_button_read = button_s1_read;
user_button[S1_BUTTON].cb = (flex_button_response_callback)btn_1_cb;
user_button[S2_BUTTON].usr_button_read = button_s2_read;
user_button[S2_BUTTON].cb = (flex_button_response_callback)btn_2_cb;
user_button[S3_BUTTON].usr_button_read = button_s3_read;
user_button[S3_BUTTON].cb = (flex_button_response_callback)btn_3_cb;
user_button[S4_BUTTON].usr_button_read = button_s4_read;
user_button[S4_BUTTON].cb = (flex_button_response_callback)btn_4_cb;
user_button[S5_BUTTON].usr_button_read = button_s5_read;
user_button[S5_BUTTON].cb = (flex_button_response_callback)btn_5_cb;
user_button[S6_BUTTON].usr_button_read = button_s6_read;
user_button[S6_BUTTON].cb = (flex_button_response_callback)btn_6_cb;
user_button[S7_BUTTON].usr_button_read = button_s7_read;
user_button[S7_BUTTON].cb = (flex_button_response_callback)btn_7_cb;
user_button[S8_BUTTON].usr_button_read = button_s8_read;
user_button[S8_BUTTON].cb = (flex_button_response_callback)btn_8_cb;
user_button[S9_BUTTON].usr_button_read = button_s9_read;
user_button[S9_BUTTON].cb = (flex_button_response_callback)btn_9_cb;
user_button[S10_BUTTON].usr_button_read = button_s10_read;
user_button[S10_BUTTON].cb = (flex_button_response_callback)btn_10_cb;
user_button[S11_BUTTON].usr_button_read = button_s11_read;
user_button[S11_BUTTON].cb = (flex_button_response_callback)btn_11_cb;
user_button[S12_BUTTON].usr_button_read = button_s12_read;
user_button[S12_BUTTON].cb = (flex_button_response_callback)btn_12_cb;
/*按键检测IO初始化*/
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE);
GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_13 | GPIO_Pin_6 | GPIO_Pin_14 | GPIO_Pin_7 | GPIO_Pin_12 | GPIO_Pin_1 | GPIO_Pin_15 | GPIO_Pin_5;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_12 | GPIO_Pin_11;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;
GPIO_Init(GPIOA, &GPIO_InitStructure);
user_button[0].pressed_logic_level = 1;
user_button[0].click_start_tick = 20;
user_button[0].short_press_start_tick = 80;
user_button[0].long_press_start_tick = 200;
user_button[0].long_hold_start_tick = 300;
flex_button_register(&user_button[0]);
for (uint8_t i = 1; i < BUTTON_MAX; i++)
{
user_button[i].pressed_logic_level = 0;
user_button[i].click_start_tick = 20;
user_button[i].short_press_start_tick = 80;
user_button[i].long_press_start_tick = 200;
user_button[i].long_hold_start_tick = 300;
flex_button_register(&user_button[i]);
}
}

View File

@@ -0,0 +1,30 @@
#include "bsp_chipid.h"
uint32_t ChipUniqueID[3];
/*
* 函数名Get_ChipID
* 描述 获取芯片ID
* 输入 :无
* 输出 :无
*/
void Get_ChipID(void)
{
ChipUniqueID[0] = *(__IO u32 *)(0X1FFFF7F0); // 高字节
ChipUniqueID[1] = *(__IO u32 *)(0X1FFFF7EC); //
ChipUniqueID[2] = *(__IO u32 *)(0X1FFFF7E8); // 低字节
}
void WDOG_Init()
{
// 使能 预分频寄存器PR和重装载寄存器RLR可写
IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable);
// 设置预分频器值
IWDG_SetPrescaler(IWDG_Prescaler_16); // IWDG 1s 超时溢出40k/16/2500= 1s
// 设置重装载寄存器值
IWDG_SetReload(0xfff);
// 把重装载寄存器的值放到计数器中
IWDG_ReloadCounter();
// 使能 IWDG
IWDG_Enable();
}

View File

@@ -0,0 +1,117 @@
/*
* @Date: 2025-06-26 09:38:27
* @LastEditors: 路怀帅
* @LastEditTime: 2025-06-26 15:04:35
* @FilePath: \Andon_Remote_Control\MDK_PROJECT\Drive\Led.c
*/
#include "main.h"
static void LED_Init(void);
static void LED_Set(uint8_t led_x);
static void Green_Toggle_Fun(void);
static void Red_Toggle_Fun(void);
static void Blue_Toggle_Fun(void);
static void Yellow_Toggle_Fun(void);
typedef LED_Color_Enum Led_Color;
LEDClassStruct LEDClass = {
.Init = LED_Init,
.Set = LED_Set,
.GREEN_Toggle = Green_Toggle_Fun,
.RED_Toggle = Red_Toggle_Fun,
.BLUE_Toggle = Blue_Toggle_Fun,
.YELLOW_Toggle = Yellow_Toggle_Fun};
static void LED_Set(uint8_t led_x)
{
switch (led_x)
{
case BLACK:
LED_ALL_OFF;
break;
case BLUE:
BLUE_ON;
break;
case GREEN:
GREEN_ON;
break;
case RED:
RED_ON;
break;
case YELLOW:
RED_ON;
GREEN_ON;
break;
case WHITE:
RED_ON;
GREEN_ON;
BLUE_ON;
break;
default:
break;
}
}
static void Green_Toggle_Fun()
{
GREEN_TOG;
}
static void Blue_Toggle_Fun()
{
BLUE_TOG;
}
static void Red_Toggle_Fun()
{
RED_TOG;
}
static void Yellow_Toggle_Fun()
{
RED_TOG;
GREEN_TOG;
}
/**
* @brief LED初始化
* @param None
* @retval None
*/
void LED_Init()
{
/*初始化LED引脚*/
GPIO_InitTypeDef GPIO_InitStruct;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB, ENABLE);
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStruct);
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStruct);
GPIO_SetBits(GPIOA, GPIO_Pin_6 | GPIO_Pin_7);
GPIO_SetBits(GPIOB, GPIO_Pin_0);
}
void ASR_Power_Init()
{
/*初始化LED引脚*/
GPIO_InitTypeDef GPIO_InitStruct;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_8;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStruct);
}
void BOARD_Power_Init()
{
/*初始化LED引脚*/
GPIO_InitTypeDef GPIO_InitStruct;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_1;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStruct);
BOARD_Power_ON;
}

View File

@@ -0,0 +1,40 @@
#include "main.h"
typedef void (*ToggleFunc)(void);
typedef void (*DelayFunc)(uint32_t);
static void toggleLoop(ToggleFunc toggle, DelayFunc delayFunc, uint8_t iterations, uint32_t delayTime);
MODClassStruct MODClass = {
.Loop = toggleLoop,
};
static void toggleLoop(ToggleFunc toggle, DelayFunc delayFunc, uint8_t iterations, uint32_t delayTime)
{
for (uint8_t i = 0; i < iterations; i++)
{
toggle();
delayFunc(delayTime);
}
}
LoraCommandParams cmd_params[] = {
{"6", "1", "0", "0"},
{"3", "1", "0", "0"},
{"7", "1", "0", "0"},
{"5", "1", "0", "0"},
{"4", "1", "0", "0"},
{"6", "0", "0", "0"},
{"3", "0", "0", "0"},
{"7", "0", "0", "0"},
{"5", "0", "0", "0"},
{"4", "0", "0", "0"},
{"0", "0", "1", "0"},
{"0", "0", "0", "1"},
};
rc_data_t *rc_data(void)
{
static rc_data_t instance = {0};
return &instance;
}

View File

@@ -0,0 +1,370 @@
#include "main.h"
static void USART1_Init(uint32_t baudRate);
static void USART2_Init(uint32_t baudRate);
static void USART3_Init(uint32_t baudRate);
static void USART_SendString1(USART_TypeDef *USARTx, uint8_t *str, uint16_t len);
static void USART_SendString2(USART_TypeDef *USARTx, uint8_t *str);
USARTClassStruct USARTClass = {
.USART1_Init = USART1_Init,
.USART2_Init = USART2_Init,
.USART3_Init = USART3_Init,
.USART_SendString1 = USART_SendString1,
.USART_SendString2 = USART_SendString2};
#define use_USART1
#define use_USART2
#define use_USART3
#define DEBUG_USART USART1
/* 在别的地方复现此函数 串口1中断函数
void USART1_IRQHandler(void)
{
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) {
USART_ClearITPendingBit(USART1, USART_IT_RXNE);
}
}
*/
/* 在别的地方复现此函数 串口2中断函数
void USART2_IRQHandler(void)
{
if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET) {
USART_ClearITPendingBit(USART2, USART_IT_RXNE);
}
}
*/
/* 在别的地方复现此函数 串口3中断函数
void USART3_IRQHandler(void)
{
if(USART_GetITStatus(USART3, USART_IT_RXNE) != RESET) {
USART_ClearITPendingBit(USART3, USART_IT_RXNE);
}
}
*/
#define DEBUG_REVDATA_SIZE 500
uint8_t g_debugRxFlag = 0;
uint8_t g_debugRxBuf[DEBUG_REVDATA_SIZE];
uint16_t g_debugRxLen = 0;
uint8_t Lora_sendbuf[DEBUG_REVDATA_SIZE];
void USART1_IRQHandler(void)
{
if (USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
{
USART_ClearITPendingBit(USART1, USART_IT_RXNE);
g_debugRxBuf[g_debugRxLen] = USART_ReceiveData(USART1);
g_debugRxLen++;
}
else if (USART_GetFlagStatus(USART1, USART_FLAG_IDLE) != RESET)
{
USART1->SR;
USART1->DR;
// memcpy(Lora_sendbuf,g_debugRxBuf,g_debugRxLen);
// printf("send data:%s\r\n",g_debugRxBuf);
// USARTClass.USART_SendString1(USART3,g_debugRxBuf,g_debugRxLen);
// memset(g_debugRxBuf,0x00,DEBUG_REVDATA_SIZE);
// g_debugRxLen = 0;
g_debugRxFlag = 1;
}
}
/**
* @brief USART1初始化
* @param baudRate: 波特率
* @retval None
*/
static void USART1_Init(uint32_t baudRate)
{
#ifdef use_USART1
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
// 打开串口GPIO的时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
// 打开串口外设的时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
// 将USART Tx的GPIO配置为推挽复用模式
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
// 将USART Rx的GPIO配置为浮空输入模式
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);
// 配置波特率
USART_InitStructure.USART_BaudRate = baudRate;
// 配置 针数据字长
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
// 配置停止位
USART_InitStructure.USART_StopBits = USART_StopBits_1;
// 配置校验位
USART_InitStructure.USART_Parity = USART_Parity_No;
// 配置硬件流控制
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
// 配置工作模式,收发一起
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
// 完成串口的初始化配置
USART_Init(USART1, &USART_InitStructure);
// 使能串口
USART_Cmd(USART1, ENABLE);
// 使能接收中断
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
USART_ITConfig(USART1, USART_IT_IDLE, ENABLE); // 使能串口空闲中断
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4); // 中断控制器分组设置
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 4;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_Init(&NVIC_InitStructure);
#endif
}
#define ASR_REVDATA_SIZE 20
uint8_t g_asrRxFlag = 0;
uint8_t Asr_recbuff[ASR_REVDATA_SIZE];
uint16_t g_asrRxLen = 0;
void USART2_IRQHandler(void)
{
if (USART_GetITStatus(USART2, USART_IT_RXNE) != RESET)
{
USART_ClearITPendingBit(USART2, USART_IT_RXNE);
Asr_recbuff[g_asrRxLen] = USART_ReceiveData(USART2);
g_asrRxLen++;
}
else if (USART_GetFlagStatus(USART2, USART_FLAG_IDLE) != RESET)
{
USART2->SR;
USART2->DR;
g_asrRxLen = 0;
// printf("asr data:");
// for(int i = 0;i<=2;i++)
// {
// printf("%02X",Asr_recbuff[i]);
// }
// printf("\r\n");
g_asrRxFlag = 1;
}
}
/**
* @brief USART2初始化
* @param baudRate: 波特率
* @retval None
*/
static void USART2_Init(uint32_t baudRate)
{
#ifdef use_USART2
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
// 打开串口GPIO的时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
// 打开串口外设的时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
// 将USART Tx的GPIO配置为推挽复用模式
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
// 将USART Rx的GPIO配置为浮空输入模式
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);
// 配置波特率
USART_InitStructure.USART_BaudRate = baudRate;
// 配置 针数据字长
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
// 配置停止位
USART_InitStructure.USART_StopBits = USART_StopBits_1;
// 配置校验位
USART_InitStructure.USART_Parity = USART_Parity_No;
// 配置硬件流控制
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
// 配置工作模式,收发一起
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
// 完成串口的初始化配置
USART_Init(USART2, &USART_InitStructure);
// 使能串口
USART_Cmd(USART2, ENABLE);
// 使能接收中断
USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);
USART_ITConfig(USART2, USART_IT_IDLE, ENABLE); // 使能串口空闲中断
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4); // 中断控制器分组设置
NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_Init(&NVIC_InitStructure);
#endif
}
#define LORA_REVDATA_SIZE 400
uint8_t g_loraRxFlag = 0;
uint8_t Lora_recbuff[LORA_REVDATA_SIZE];
uint16_t g_loraRxLen = 0;
void USART3_IRQHandler(void)
{
if (USART_GetITStatus(USART3, USART_IT_RXNE) != RESET)
{
USART_ClearITPendingBit(USART3, USART_IT_RXNE);
Lora_recbuff[g_loraRxLen] = USART_ReceiveData(USART3);
g_loraRxLen++;
}
else if (USART_GetFlagStatus(USART3, USART_FLAG_IDLE) != RESET)
{
USART3->SR;
USART3->DR;
g_loraRxLen = 0;
printf("%s", Lora_recbuff);
memset(Lora_recbuff, 0x00, LORA_REVDATA_SIZE);
g_loraRxFlag = 1;
}
}
/**
* @brief USART3初始化
* @param baudRate: 波特率
* @retval None
*/
static void USART3_Init(uint32_t baudRate)
{
#ifdef use_USART3
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
// 打开串口GPIO的时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
// 打开串口外设的时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE);
// 将USART Tx的GPIO配置为推挽复用模式
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
// 将USART Rx的GPIO配置为浮空输入模式
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOB, &GPIO_InitStructure);
// 配置波特率
USART_InitStructure.USART_BaudRate = baudRate;
// 配置 针数据字长
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
// 配置停止位
USART_InitStructure.USART_StopBits = USART_StopBits_1;
// 配置校验位
USART_InitStructure.USART_Parity = USART_Parity_No;
// 配置硬件流控制
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
// 配置工作模式,收发一起
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
// 完成串口的初始化配置
USART_Init(USART3, &USART_InitStructure);
// 使能串口
USART_Cmd(USART3, ENABLE);
// 使能接收中断
USART_ITConfig(USART3, USART_IT_RXNE, ENABLE);
USART_ITConfig(USART3, USART_IT_IDLE, ENABLE); // 使能串口空闲中断
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4); // 中断控制器分组设置
NVIC_InitStructure.NVIC_IRQChannel = USART3_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_Init(&NVIC_InitStructure);
#endif
}
/**
* @brief USART发送定长字符串
* @param USARTx: 串口几发送
* @param str: 发送的内容
* @param len: 长度
* @retval None
*/
static void USART_SendString1(USART_TypeDef *USARTx, uint8_t *str, uint16_t len)
{
unsigned short count = 0;
for (; count < len; count++)
{
USART_SendData(USARTx, *str++); // 发送数据
while (USART_GetFlagStatus(USARTx, USART_FLAG_TC) == RESET)
; // 等待发送完成
}
}
/**
* @brief USART发送不定长字符串
* @param USARTx: 串口几发送
* @param str: 发送的内容
* @retval None
*/
static void USART_SendString2(USART_TypeDef *USARTx, uint8_t *str)
{
unsigned int k = 0;
do
{
USART_SendData(USARTx, *(str + k));
/* 等待发送数据寄存器为空 */
while (USART_GetFlagStatus(USARTx, USART_FLAG_TXE) == RESET)
{
}
k++;
} while (*(str + k) != '\0');
/* 等待发送完成 */
while (USART_GetFlagStatus(USARTx, USART_FLAG_TC) == RESET)
{
}
}
/// 重定向c库函数printf到串口重定向后可使用printf函数
int fputc(int ch, FILE *f)
{
/* 发送一个字节数据到串口 */
USART_SendData(DEBUG_USART, (uint8_t)ch);
/* 等待发送完毕 */
while (USART_GetFlagStatus(DEBUG_USART, USART_FLAG_TXE) == RESET)
;
return (ch);
}
void Usart_SendByte(USART_TypeDef *pUSARTx, uint8_t ch)
{
/* 发送一个字节数据到USART */
USART_SendData(pUSARTx, ch);
/* 等待发送数据寄存器为空 */
while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET)
;
}
void Usart_SendString(USART_TypeDef *pUSARTx, char *str)
{
unsigned int k = 0;
do
{
Usart_SendByte(pUSARTx, *(str + k));
k++;
} while (*(str + k) != '\0');
/* 等待发送完成 */
while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TC) == RESET)
{
}
}

View File

@@ -0,0 +1,153 @@
#include "delay.h"
//////////////////////////////////////////////////////////////////////////////////
//如果使用ucos,则包括下面的头文件即可.
#if SYSTEM_SUPPORT_UCOS
#include "includes.h" //ucos 使用
#endif
static u8 fac_us=0;//us延时倍乘数
static u16 fac_ms=0;//ms延时倍乘数
#ifdef OS_CRITICAL_METHOD //如果OS_CRITICAL_METHOD定义了,说明使用ucosII了.
//systick中断服务函数,使用ucos时用到
void SysTick_Handler(void)
{
OSIntEnter(); //进入中断
OSTimeTick(); //调用ucos的时钟服务程序
OSIntExit(); //触发任务切换软中断
}
#endif
//初始化延迟函数
//当使用ucos的时候,此函数会初始化ucos的时钟节拍
//SYSTICK的时钟固定为HCLK时钟的1/8
//SYSCLK:系统时钟
void delay_init()
{
#ifdef OS_CRITICAL_METHOD //如果OS_CRITICAL_METHOD定义了,说明使用ucosII了.
u32 reload;
#endif
SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8); //选择外部时钟 HCLK/8
fac_us=SystemCoreClock/8000000; //为系统时钟的1/8
#ifdef OS_CRITICAL_METHOD //如果OS_CRITICAL_METHOD定义了,说明使用ucosII了.
reload=SystemCoreClock/8000000; //每秒钟的计数次数 单位为K
reload*=1000000/OS_TICKS_PER_SEC;//根据OS_TICKS_PER_SEC设定溢出时间
//reload为24位寄存器,最大值:16777216,在72M下,约合1.86s左右
fac_ms=1000/OS_TICKS_PER_SEC;//代表ucos可以延时的最少单位
SysTick->CTRL|=SysTick_CTRL_TICKINT_Msk; //开启SYSTICK中断
SysTick->LOAD=reload; //每1/OS_TICKS_PER_SEC秒中断一次
SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk; //开启SYSTICK
#else
fac_ms=(u16)fac_us*1000;//非ucos下,代表每个ms需要的systick时钟数
#endif
}
#ifdef OS_CRITICAL_METHOD //使用了ucos
//延时nus
//nus为要延时的us数.
void delay_us(u32 nus)
{
u32 ticks;
u32 told,tnow,tcnt=0;
u32 reload=SysTick->LOAD; //LOAD的值
ticks=nus*fac_us; //需要的节拍数
tcnt=0;
told=SysTick->VAL; //刚进入时的计数器值
while(1)
{
tnow=SysTick->VAL;
if(tnow!=told)
{
if(tnow<told)tcnt+=told-tnow;//这里注意一下SYSTICK是一个递减的计数器就可以了.
else tcnt+=reload-tnow+told;
told=tnow;
if(tcnt>=ticks)break;//时间超过/等于要延迟的时间,则退出.
}
};
}
//延时nms
//nms:要延时的ms数
void delay_ms(u16 nms)
{
if(OSRunning==TRUE)//如果os已经在跑了
{
if(nms>=fac_ms)//延时的时间大于ucos的最少时间周期
{
OSTimeDly(nms/fac_ms);//ucos延时
}
nms%=fac_ms; //ucos已经无法提供这么小的延时了,采用普通方式延时
}
delay_us((u32)(nms*1000)); //普通方式延时,此时ucos无法启动调度.
}
#else//不用ucos时
//延时nus
//nus为要延时的us数.
void delay_us(u32 nus)
{
u32 temp;
SysTick->LOAD=nus*fac_us; //时间加载
SysTick->VAL=0x00; //清空计数器
SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ; //开始倒数
do
{
temp=SysTick->CTRL;
}
while(temp&0x01&&!(temp&(1<<16)));//等待时间到达
SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk; //关闭计数器
SysTick->VAL =0X00; //清空计数器
}
//延时nms
//注意nms的范围
//SysTick->LOAD为24位寄存器,所以,最大延时为:
//nms<=0xffffff*8*1000/SYSCLK
//SYSCLK单位为Hz,nms单位为ms
//对72M条件下,nms<=1864
void delay_ms(u16 nms)
{
u32 temp;
SysTick->LOAD=(u32)nms*fac_ms;//时间加载(SysTick->LOAD为24bit)
SysTick->VAL =0x00; //清空计数器
SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ; //开始倒数
do
{
temp=SysTick->CTRL;
}
while(temp&0x01&&!(temp&(1<<16)));//等待时间到达
SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk; //关闭计数器
SysTick->VAL =0X00; //清空计数器
}
#endif

View File

@@ -0,0 +1,293 @@
/*
* @Date: 2025-06-26 10:58:20
* @LastEditors: 路怀帅
* @LastEditTime: 2025-06-26 14:47:21
* @FilePath: \Andon_Remote_Control\MDK_PROJECT\Drive\flexible_button.c
*/
/**
* @File: flexible_button.c
* @Author: MurphyZhao
* @Date: 2018-09-29
*
* Copyright (c) 2018-2019 MurphyZhao <d2014zjt@163.com>
* https://github.com/murphyzhao
* All rights reserved.
* License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Change logs:
* Date Author Notes
* 2018-09-29 MurphyZhao First add
* 2019-08-02 MurphyZhao 迁移代码到 murphyzhao 仓库
*
*/
#include "flexible_button.h"
#include <string.h>
#include <stdio.h>
static flex_button_t *btn_head = NULL;
#define EVENT_CB_EXECUTOR(button) if(button->cb) button->cb((flex_button_t*)button)
#define MAX_BUTTON_CNT 16
static uint16_t trg = 0;
static uint16_t cont = 0;
static uint16_t keydata = 0xFFFF;
static uint16_t key_rst_data = 0xFFFF;
static uint8_t button_cnt = 0;
/**
* @brief Register a user button
*
* @param button: button structure instance
* @return Number of keys that have been registered
*/
int8_t flex_button_register(flex_button_t *button)
{
flex_button_t *curr = btn_head;
if (!button || (button_cnt > MAX_BUTTON_CNT))
{
return -1;
}
while (curr)
{
if(curr == button)
{
return -1; //already exist.
}
curr = curr->next;
}
button->next = btn_head;
button->status = 0;
button->event = FLEX_BTN_PRESS_NONE;
button->scan_cnt = 0;
button->click_cnt = 0;
btn_head = button;
key_rst_data = key_rst_data << 1;
button_cnt ++;
return button_cnt;
}
/**
* @brief Read all key values in one scan cycle
*
* @param void
* @return none
*/
static void flex_button_read(void)
{
flex_button_t* target;
uint16_t read_data = 0;
keydata = key_rst_data;
int8_t i = 0;
for(target = btn_head, i = 0;
(target != NULL) && (target->usr_button_read != NULL);
target = target->next, i ++)
{
keydata = keydata |
(target->pressed_logic_level == 1 ?
((!(target->usr_button_read)()) << i) :
((target->usr_button_read)() << i));
}
read_data = keydata^0xFFFF;
trg = read_data & (read_data ^ cont);
cont = read_data;
}
/**
* @brief Handle all key events in one scan cycle.
* Must be used after 'flex_button_read' API
*
* @param void
* @return none
*/
static void flex_button_process(void)
{
int8_t i = 0;
flex_button_t* target;
for (target = btn_head, i = 0; target != NULL; target = target->next, i ++)
{
if (target->status > 0)
{
target->scan_cnt ++;
}
switch (target->status)
{
case 0: /* is default */
if (trg & (1 << i)) /* is pressed */
{
target->scan_cnt = 0;
target->click_cnt = 0;
target->status = 1;
target->event = FLEX_BTN_PRESS_DOWN;
EVENT_CB_EXECUTOR(target);
}
else
{
target->event = FLEX_BTN_PRESS_NONE;
}
break;
case 1: /* is pressed */
if (!(cont & (1 << i))) /* is up */
{
target->status = 2;
}
else if ((target->scan_cnt >= target->short_press_start_tick) &&
(target->scan_cnt < target->long_press_start_tick))
{
target->status = 4;
target->event = FLEX_BTN_PRESS_SHORT_START;
EVENT_CB_EXECUTOR(target);
}
break;
case 2: /* is up */
if ((target->scan_cnt < target->click_start_tick))
{
target->click_cnt++; // 1
if (target->click_cnt == 1)
{
target->status = 3; /* double click check */
}
else
{
target->click_cnt = 0;
target->status = 0;
target->event = FLEX_BTN_PRESS_DOUBLE_CLICK;
EVENT_CB_EXECUTOR(target);
}
}
else if ((target->scan_cnt >= target->click_start_tick) &&
(target->scan_cnt < target->short_press_start_tick))
{
target->click_cnt = 0;
target->status = 0;
target->event = FLEX_BTN_PRESS_CLICK;
EVENT_CB_EXECUTOR(target);
}
else if ((target->scan_cnt >= target->short_press_start_tick) &&
(target->scan_cnt < target->long_press_start_tick))
{
target->click_cnt = 0;
target->status = 0;
target->event = FLEX_BTN_PRESS_SHORT_UP;
EVENT_CB_EXECUTOR(target);
}
else if ((target->scan_cnt >= target->long_press_start_tick) &&
(target->scan_cnt < target->long_hold_start_tick))
{
target->click_cnt = 0;
target->status = 0;
target->event = FLEX_BTN_PRESS_LONG_UP;
EVENT_CB_EXECUTOR(target);
}
else if (target->scan_cnt >= target->long_hold_start_tick)
{
/* long press hold up, not deal */
target->click_cnt = 0;
target->status = 0;
target->event = FLEX_BTN_PRESS_LONG_HOLD_UP;
EVENT_CB_EXECUTOR(target);
}
break;
case 3: /* double click check */
if (trg & (1 << i))
{
target->click_cnt++;
target->status = 2;
target->scan_cnt --;
}
else if (target->scan_cnt >= target->click_start_tick)
{
target->status = 2;
}
break;
case 4: /* is short pressed */
if (!(cont & (1 << i))) /* is up */
{
target->status = 2;
}
else if ((target->scan_cnt >= target->long_press_start_tick) &&
(target->scan_cnt < target->long_hold_start_tick))
{
target->status = 5;
target->event = FLEX_BTN_PRESS_LONG_START;
EVENT_CB_EXECUTOR(target);
}
break;
case 5: /* is long pressed */
if (!(cont & (1 << i))) /* is up */
{
target->status = 2;
}
else if (target->scan_cnt >= target->long_hold_start_tick)
{
target->status = 6;
target->event = FLEX_BTN_PRESS_LONG_HOLD;
EVENT_CB_EXECUTOR(target);
}
break;
case 6: /* is long pressed */
if (!(cont & (1 << i))) /* is up */
{
target->status = 2;
}
break;
}
}
}
/**
* flex_button_event_read
*
* @brief Get the button event of the specified button.
*
* @param button: button structure instance
* @return button event
*/
flex_button_event_t flex_button_event_read(flex_button_t* button)
{
return (flex_button_event_t)(button->event);
}
/**
* flex_button_scan
*
* @brief Start key scan.
* Need to be called cyclically within the specified period.
* Sample cycle: 5 - 20ms
*
* @param void
* @return none
*/
void flex_button_scan(void)
{
flex_button_read();
flex_button_process();
}

View File

@@ -0,0 +1,77 @@
/**
******************************************************************************
* @file GPIO/IOToggle/stm32f10x_conf.h
* @author MCD Application Team
* @version V3.5.0
* @date 08-April-2011
* @brief Library configuration file.
******************************************************************************
* @attention
*
* THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS
* WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE
* TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY
* DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING
* FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE
* CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS.
*
* <h2><center>&copy; COPYRIGHT 2011 STMicroelectronics</center></h2>
******************************************************************************
*/
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __STM32F10x_CONF_H
#define __STM32F10x_CONF_H
/* Includes ------------------------------------------------------------------*/
/* Uncomment/Comment the line below to enable/disable peripheral header file inclusion */
#include "stm32f10x_adc.h"
//#include "stm32f10x_bkp.h"
//#include "stm32f10x_can.h"
//#include "stm32f10x_cec.h"
//#include "stm32f10x_crc.h"
//#include "stm32f10x_dac.h"
#include "stm32f10x_dbgmcu.h"
#include "stm32f10x_dma.h"
//#include "stm32f10x_exti.h"
#include "stm32f10x_flash.h"
//#include "stm32f10x_fsmc.h"
#include "stm32f10x_gpio.h"
//#include "stm32f10x_i2c.h"
#include "stm32f10x_iwdg.h"
//#include "stm32f10x_pwr.h"
#include "stm32f10x_rcc.h"
//#include "stm32f10x_rtc.h"
//#include "stm32f10x_sdio.h"
//#include "stm32f10x_spi.h"
#include "stm32f10x_tim.h"
#include "stm32f10x_usart.h"
//#include "stm32f10x_wwdg.h"
#include "misc.h" /* High level functions for NVIC and SysTick (add-on to CMSIS functions) */
/* Exported types ------------------------------------------------------------*/
/* Exported constants --------------------------------------------------------*/
/* Uncomment the line below to expanse the "assert_param" macro in the
Standard Peripheral Library drivers code */
/* #define USE_FULL_ASSERT 1 */
/* Exported macro ------------------------------------------------------------*/
#ifdef USE_FULL_ASSERT
/**
* @brief The assert_param macro is used for function's parameters check.
* @param expr: If expr is false, it calls assert_failed function which reports
* the name of the source file and the source line number of the call
* that failed. If expr is true, it returns no value.
* @retval None
*/
#define assert_param(expr) ((expr) ? (void)0 : assert_failed((uint8_t *)__FILE__, __LINE__))
/* Exported functions ------------------------------------------------------- */
void assert_failed(uint8_t* file, uint32_t line);
#else
#define assert_param(expr) ((void)0)
#endif /* USE_FULL_ASSERT */
#endif /* __STM32F10x_CONF_H */
/******************* (C) COPYRIGHT 2011 STMicroelectronics *****END OF FILE****/

View File

@@ -0,0 +1,169 @@
/**
******************************************************************************
* @file Project/STM32F10x_StdPeriph_Template/stm32f10x_it.c
* @author MCD Application Team
* @version V3.5.0
* @date 08-April-2011
* @brief Main Interrupt Service Routines.
* This file provides template for all exceptions handler and
* peripherals interrupt service routine.
******************************************************************************
* @attention
*
* THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS
* WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE
* TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY
* DIRECT, INDIRECT OR CONSEQUENTI
AL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING
* FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE
* CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS.
*
* <h2><center>&copy; COPYRIGHT 2011 STMicroelectronics</center></h2>
******************************************************************************
*/
/* Includes ------------------------------------------------------------------*/
#include "stm32f10x_it.h"
#include "FreeRTOS.h" //FreeRTOSʹÓÃ
#include "task.h"
/** @addtogroup STM32F10x_StdPeriph_Template
* @{
*/
/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
/* Private function prototypes -----------------------------------------------*/
/* Private functions ---------------------------------------------------------*/
/******************************************************************************/
/* Cortex-M3 Processor Exceptions Handlers */
/******************************************************************************/
/**
* @brief This function handles NMI exception.
* @param None
* @retval None
*/
void NMI_Handler(void)
{
}
/**
* @brief This function handles Hard Fault exception.
* @param None
* @retval None
*/
void HardFault_Handler(void)
{
/* Go to infinite loop when Hard Fault exception occurs */
while (1)
{
}
}
/**
* @brief This function handles Memory Manage exception.
* @param None
* @retval None
*/
void MemManage_Handler(void)
{
/* Go to infinite loop when Memory Manage exception occurs */
while (1)
{
}
}
/**
* @brief This function handles Bus Fault exception.
* @param None
* @retval None
*/
void BusFault_Handler(void)
{
/* Go to infinite loop when Bus Fault exception occurs */
while (1)
{
}
}
/**
* @brief This function handles Usage Fault exception.
* @param None
* @retval None
*/
void UsageFault_Handler(void)
{
/* Go to infinite loop when Usage Fault exception occurs */
while (1)
{
}
}
/**
* @brief This function handles SVCall exception.
* @param None
* @retval None
*/
//void SVC_Handler(void)
//{
//}
/**
* @brief This function handles Debug Monitor exception.
* @param None
* @retval None
*/
void DebugMon_Handler(void)
{
}
/**
* @brief This function handles PendSVC exception.
* @param None
* @retval None
*/
//void PendSV_Handler(void)
//{
//}
///**
// * @brief This function handles SysTick Handler.
// * @param None
// * @retval None
// */
extern void xPortSysTickHandler(void);
//systickÖжϷþÎñº¯Êý
void SysTick_Handler(void)
{
#if (INCLUDE_xTaskGetSchedulerState == 1 )
if (xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED)
{
#endif /* INCLUDE_xTaskGetSchedulerState */
xPortSysTickHandler();
#if (INCLUDE_xTaskGetSchedulerState == 1 )
}
#endif /* INCLUDE_xTaskGetSchedulerState */
}
/**
* @brief This function handles PPP interrupt request.
* @param None
* @retval None
*/
/*void PPP_IRQHandler(void)
{
}*/
/**
* @}
*/
/******************* (C) COPYRIGHT 2011 STMicroelectronics *****END OF FILE****/

View File

@@ -0,0 +1,54 @@
/**
******************************************************************************
* @file Project/STM32F10x_StdPeriph_Template/stm32f10x_it.h
* @author MCD Application Team
* @version V3.5.0
* @date 08-April-2011
* @brief This file contains the headers of the interrupt handlers.
******************************************************************************
* @attention
*
* THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS
* WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE
* TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY
* DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING
* FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE
* CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS.
*
* <h2><center>&copy; COPYRIGHT 2011 STMicroelectronics</center></h2>
******************************************************************************
*/
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __STM32F10x_IT_H
#define __STM32F10x_IT_H
#ifdef __cplusplus
extern "C" {
#endif
/* Includes ------------------------------------------------------------------*/
#include "stm32f10x.h"
/* Exported types ------------------------------------------------------------*/
/* Exported constants --------------------------------------------------------*/
/* Exported macro ------------------------------------------------------------*/
/* Exported functions ------------------------------------------------------- */
void NMI_Handler(void);
void HardFault_Handler(void);
void MemManage_Handler(void);
void BusFault_Handler(void);
void UsageFault_Handler(void);
void SVC_Handler(void);
void DebugMon_Handler(void);
void PendSV_Handler(void);
void SysTick_Handler(void);
#ifdef __cplusplus
}
#endif
#endif /* __STM32F10x_IT_H */
/******************* (C) COPYRIGHT 2011 STMicroelectronics *****END OF FILE****/