前言
- 前几篇博客基本上已经将filex、levelx、threadx、modulex、shell 组件大概都记录了一遍.
- 本篇博客做个综合实际案例记录.
实现效果
代码程序
Modulex组件
源文件
/** Copyright (c) 2024-2024,shchl** SPDX-License-Identifier: Apache-2.0** Change Logs:* Date Author Notes* 2024-4-19 shchl first version*/
#include "includes.h"#if 1
#define MODULE_DATA_SIZE (20*1024) /* 供动态APP使用 */
#define OBJECT_MEM_SIZE (20*1024) /* 供动态APP的动态内存管理申请使用 */
/*
*******************************************************************************************************
* 外部引入变量
*******************************************************************************************************
*//*
*******************************************************************************************************
* 变量
*******************************************************************************************************
*/
TX_QUEUE ResidentQueue; /* 消息队列,用于主程序和动态APP通信 */
uint32_t MessageQueuesBuf[100];/*
*********************************************************************************************************
* 静态全局变量
*********************************************************************************************************
*/
static ULONG memory_faults = 0;
static UCHAR *module_data_area;
static UCHAR *object_memory;/*
*********************************************************************************************************
* 函数声明
*********************************************************************************************************
*/static VOID module_fault_handler(TX_THREAD *thread, TXM_MODULE_INSTANCE *module);
/*
*********************************************************************************************************
* 外部函数
*********************************************************************************************************
*//*
*********************************************************************************************************
* 函 数 名: module_application_define
* 功能说明: 创建 模块管理任务线程
* 形 参: 无
* 返 回 值: 无
*********************************************************************************************************
*/
int module_application_define(void) {UINT status;/*模块 分配内存空间(使用全局数组的方式)*/static UCHAR obj_mem[OBJECT_MEM_SIZE];static UCHAR data_area[MODULE_DATA_SIZE];/*使用 动态内存分配的方式*/
// object_memory = app_malloc(OBJECT_MEM_SIZE);
// module_data_area = app_malloc(MODULE_DATA_SIZE);object_memory = obj_mem;module_data_area = data_area;/* 创建常驻消息队列 */if (tx_queue_create(&ResidentQueue, "Resident Queue",TX_1_ULONG, MessageQueuesBuf,16 * sizeof(ULONG)) != TX_SUCCESS) {Error_Handler();}/* 初始化动态加载管理器,主要是给动态APP的数据空间使用 */status = txm_module_manager_initialize((VOID *) module_data_area, MODULE_DATA_SIZE);if (status != TX_SUCCESS) {Error_Handler();}/* 供动态APP使用的对象内存池,主要各种控制块申请 */status = txm_module_manager_object_pool_create(object_memory, OBJECT_MEM_SIZE);if (status != TX_SUCCESS) {Error_Handler();}/* 注册faults管理 */status = txm_module_manager_memory_fault_notify(module_fault_handler);if (status != TX_SUCCESS) {Error_Handler();}return TX_SUCCESS;
}TX_THREAD_EXPORT_LV1(module_application_define); /*首先创建模块应用*/
/*
*********************************************************************************************************
* 函 数 名: module_fault_handler
* 功能说明: 监测faults
* 形 参: ---
* 返 回 值: 无
*********************************************************************************************************
*/
static VOID module_fault_handler(TX_THREAD *thread, TXM_MODULE_INSTANCE *module) {tx_log("module_fault_handler: thread[%s],module[%s]\r\n", thread->tx_thread_name, module->txm_module_instance_name);/* 统计错误消息 */memory_faults++;
}#endif
Modulex 调用的api接口(对modulex提供的函数进行封装)
头文件
/** Copyright (c) 2024-2024,shchl** SPDX-License-Identifier: Apache-2.0** Change Logs:* Date Author Notes* 2024-4-19 shchl first version*/#ifndef STM32_PROJECT_APP_MODULE_API_H
#define STM32_PROJECT_APP_MODULE_API_H#include "txm_module.h"/*** @brief 模块信息结构体*/
typedef struct module_info_struct {
#define MODULE_NAME_LEN 32char module_name[MODULE_NAME_LEN]; /*模块名*/uint32_t load_addr; /*加载地址地址(适用于flash方式,定位bin数据)*/TXM_MODULE_INSTANCE instance; /* 模块实例 */
} module_info;UINT app_txm_module_read(module_info *info, void (*info_out_handle)(TXM_MODULE_INSTANCE *instance, ULONG prop));UINT app_txm_module_start(module_info *info);UINT app_txm_module_stop(module_info *info);UINT app_txm_module_unload(module_info *info);UINT app_txm_module_fx_load(module_info *info, FX_MEDIA *media_ptr, CHAR *file_name);UINT app_txm_module_file_load(module_info *info, CHAR *file_name, void (*bin_data_handle)(uint8_t *data, uint16_t len));#endif //STM32_PROJECT_APP_MODULE_API_H
源文件
/** Copyright (c) 2024-2024,shchl** SPDX-License-Identifier: Apache-2.0** Change Logs:* Date Author Notes* 2024-4-19 shchl first version*/#include "includes.h"
#include "app_file_api.h"static FX_FILE module_file;/*默认实现模块输出*/
static void app_txm_module_info_out(TXM_MODULE_INSTANCE *instance, ULONG prop) {tx_printf("===============================app_txm_module_info_out================================\r\n");tx_printf("------------------module build info------------------\r\n");tx_printf("\t--Compiled for %s compiler\r\n",((prop >> 25) == 1) ? "CubeIDE (GNU)" : ((prop >> 24) == 1) ? "ARM KEIL" : "IAR EW");tx_printf("\t--Shared/external memory access is %s\r\n", ((prop & 0x04) == 0) ? "Disabled" : "Enabled");tx_printf("\t--MPU protection is %s\r\n", ((prop & 0x02) == 0) ? "Disabled" : "Enabled");tx_printf("\t--%s mode execution is enabled for the module\r\n\r\n", ((prop & 0x01) == 0) ? "Privileged" : "User");tx_printf("------------------module application info------------------\r\n");tx_printf("\t--application id %#lx;instance id:%#lx;name:%s\r\n",instance->txm_module_instance_application_module_id,instance->txm_module_instance_id,instance->txm_module_instance_name);tx_printf("\t--code section【start addr: %#lx||end addr:%#lx||size(bytes):%lu】\r\n",(ULONG) instance->txm_module_instance_code_start,(ULONG) instance->txm_module_instance_code_end,instance->txm_module_instance_code_size);tx_printf("\t--data section【start addr: %#lx||end addr:%#lx||size(bytes):%lu】\r\n",(ULONG) instance->txm_module_instance_data_start,(ULONG) instance->txm_module_instance_data_end,instance->txm_module_instance_data_size);tx_printf("\t--shared_memory 【addr:%#lx||size(bytes): %lu】\r\n",instance->txm_module_instance_shared_memory_address,instance->txm_module_instance_shared_memory_length);
}UINT app_txm_module_start(module_info *info) {UINT status;/*确保模块已加载,校验逻辑内部已提供*/status = txm_module_manager_start(&info->instance);return status;
}UINT app_txm_module_stop(module_info *info) {UINT status;/*确保模块已加载,校验逻辑内部已提供*/status = txm_module_manager_stop(&info->instance);return status;
}UINT app_txm_module_unload(module_info *info) {UINT status;/*确保模块已加载,校验逻辑内部已提供*/status = txm_module_manager_unload(&info->instance);return status;}UINT app_txm_module_fx_load(module_info *info, FX_MEDIA *media_ptr, CHAR *file_name) {UINT status;/*判断media 设备是否挂载*/if (media_ptr->fx_media_id != FX_MEDIA_ID) {return TX_NOT_AVAILABLE;}if (info->instance.txm_module_instance_id == TXM_MODULE_ID) {return TXM_MODULE_ALREADY_LOADED;}/*确保模块已加载,校验逻辑内部已提供*/status = txm_module_manager_file_load(&info->instance,info->module_name,media_ptr,file_name);return status;
}/*** @brief 从文件中加载模块文件,自定义数据处理* @param addr* @param file_name* @return*/
UINT app_txm_module_file_load(module_info *info, CHAR *file_name,void (*bin_data_handle)(uint8_t *data, uint16_t len)) {TX_INTERRUPT_SAVE_AREAif (!bin_data_handle) {return TX_PTR_ERROR;}/*判断media 设备是否挂载*/if (info->instance.txm_module_instance_id == TXM_MODULE_ID) {return TXM_MODULE_ALREADY_LOADED;}
#define READ_BUF_SIZE 2048TX_DISABLEuint8_t *cache_buf = app_malloc(READ_BUF_SIZE);TX_RESTOREif (cache_buf == NULL) return TX_NO_MEMORY;UINT status = app_fx_file_open(&module_file, file_name, FX_OPEN_FOR_READ);ULONG actual_read;if (status) {tx_printf("app_txm_module_file_load error:%#x\r\n", status);return TX_NOT_AVAILABLE;}do {status = fx_file_read(&module_file, cache_buf, READ_BUF_SIZE, &actual_read);bin_data_handle(cache_buf, actual_read);} while (status &= FX_END_OF_FILE);fx_file_close(&module_file);TX_DISABLEapp_free(cache_buf);TX_RESTOREreturn TX_SUCCESS;
#undef READ_BUF_SIZE
}/*** @brief 模块信息读取* @param info* @param info_out_handle 信息输出(可为null,使用默认输出)* @return*/
UINT app_txm_module_read(module_info *info, void (*info_out_handle)(TXM_MODULE_INSTANCE *instance, ULONG prop)) {UINT status = TX_SUCCESS;ULONG module_properties;/*确定 模块是否已加载*/if (info->instance.txm_module_instance_id == TXM_MODULE_ID) {/* 获取动态APP属性 */status = txm_module_manager_properties_get(&info->instance,&module_properties);} else { // 未加载/* 加载动态APP */return TXM_MODULE_UNLOADED;}if (status) {tx_log("txm_module_manager_properties_get error:[%d]\r\n", status);return status;}if (!info_out_handle) {/*模块信息打印*/app_txm_module_info_out(&info->instance, module_properties);} else {info_out_handle(&info->instance, module_properties);}return status;
}
对接shell 组件(命令行控制)
/** Copyright (c) 2024-2024,shchl** SPDX-License-Identifier: Apache-2.0** Change Logs:* Date Author Notes* 2024-4-19 shchl first version*/
#include "includes.h"#if 1#include "app_module_api.h"static module_info app_module;/*** @brief sys_thread 命令* @param argc* @param argv* @return*/
int app_module_fnc(int argc, char *argv[]) {UINT status = TX_NOT_AVAILABLE;if (argc < 2 || argc > 3) {tx_printf("===========================USEG MANUAL=======================\r\n");tx_printf("module load ------- load module\r\n");return -1;}char *cmd = argv[1];if (argc == 3) {if (strstr(cmd, "load")) {status = app_txm_module_fx_load(&app_module, &g_fx_media, argv[2]);}} else {if (strstr(cmd, "info")) {status = app_txm_module_read(&app_module, NULL);} else if (strstr(cmd, "unload")) {status = app_txm_module_unload(&app_module);} else if (strstr(cmd, "start")) {status = app_txm_module_start(&app_module);} else if (strstr(cmd, "stop")) {status = app_txm_module_stop(&app_module);} else {logWarning("not support cmd");}}if (status)logError("err result:%d", status)return 0;
}SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0) |SHELL_CMD_TYPE(SHELL_TYPE_CMD_MAIN),module,app_module_fnc,"module load");#endif
bin 文件上传(通过ota方式)
/** Copyright (c) 2024-2024,shchl** SPDX-License-Identifier: Apache-2.0** Change Logs:* Date Author Notes* 2024-4-19 shchl first version*/
#include "includes.h"#include "ymodem.h"
#include "app_file_api.h"
static char upload_file_name[64] = {0};
static size_t update_file_total_size;
static size_t rec_data_len = 0;static FX_FILE ota_file;static enum rym_code ymodem_on_begin(struct rym_ctx *ctx, uint8_t *buf, size_t len) {static char *tmp_file_name, *file_size;UINT stat;/* calculate and store file size */tmp_file_name = (char *) &buf[0];file_size = (char *) &buf[strlen(tmp_file_name) + 1];memcpy(upload_file_name, tmp_file_name, strlen(tmp_file_name));/*这里是保存的路径,现在是直接保存到根路径下*/sprintf(upload_file_name, "%s", tmp_file_name);stat = app_fx_file_open(&ota_file, upload_file_name, FX_OPEN_FOR_WRITE);if (stat) {return RYM_CODE_NAK;}fx_file_seek(&ota_file, FX_SEEK_BEGIN);update_file_total_size = atol(file_size);return RYM_CODE_ACK;
}static enum rym_code ymodem_on_data(struct rym_ctx *ctx, uint8_t *buf, size_t len) {/*TODO 接收升级数据包处理*/fx_file_write(&ota_file, buf, len);rec_data_len += len;return RYM_CODE_ACK;
}static void rym_delay_ms(size_t ms) {tx_thread_sleep(ms);
}static uint32_t rym_sys_tick_get() {return tx_time_get();
}static uint32_t rym_millisecond_to_tick(uint32_t ms) {uint32_t tick;tick = TX_TIMER_TICKS_PER_SECOND * (ms / 1000);tick += (TX_TIMER_TICKS_PER_SECOND * (ms % 1000) + 999) / 1000;/* return the calculated tick */return tick;
}
static size_t rym_write(struct rym_ctx *self, size_t offset, const uint8_t *buf, size_t len) {comSendBuf(COM1, (uint8_t *) (buf + offset), len);return len;
}static size_t rym_read_wait(struct rym_ctx *self, size_t offset, uint8_t *buf, size_t len,uint32_t timeout) {UNUSED(self);size_t readCnt = 0;uint32_t cur_tick = rym_sys_tick_get();while (len) {if (comGetChar(COM1, (uint8_t *) (buf + readCnt + offset)) == 1) {readCnt++;len--;} else {if (rym_sys_tick_get() > cur_tick + timeout) {break;}}}return readCnt;
}static inline void ota_ymodem_lock() {/*由于shell 组件中已做了加锁处理,这里就可以不需要进行加锁*/
}static inline void ota_ymodem_unlock() {/*由于shell 组件中已做了加锁处理,这里就可以不需要进行解锁*/
}
void console_ota_func() {struct rym_ctx rctx;/*变量重新初始化*/{memset(upload_file_name, 0, strlen(upload_file_name));/*清除上次的文件名*/rec_data_len = 0;}rctx.driver.rym_delay_ms = rym_delay_ms;rctx.driver.rym_sys_tick_get = rym_sys_tick_get;rctx.driver.rym_millisecond_to_tick = rym_millisecond_to_tick;rctx.driver.rym_write = rym_write;rctx.driver.rym_read_wait = rym_read_wait;/*内存分配*/
#ifdef RYM_CTX_USE_ALLOC_CALLBACKrctx.rym_malloc = (void *(*)(size_t)) app_malloc;rctx.rym_free = (void (*)(void *)) app_free;
#endifota_ymodem_lock();/*加锁*/int32_t status = rym_recv_on_device(&rctx,ymodem_on_begin,ymodem_on_data,NULL, 1000);/*todo 是否需要重启芯片(针对于ota升级)*//*关闭文件*/app_fx_file_close(&ota_file);ota_ymodem_unlock();/*解锁*/tx_printf("\r\n------------stats is:%ld------------\r\n", status);tx_printf("file name:%s;file size:%d; rec lec:%d\r\n", upload_file_name, update_file_total_size, rec_data_len);
}#ifdef SHELL_USING_CMD_EXPORT/*shell 脚本来管理*/
SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0) | SHELL_CMD_TYPE(SHELL_TYPE_CMD_FUNC) | SHELL_CMD_DISABLE_RETURN,ota, console_ota_func, "ymodem ota upgrade");#endif
模块程序
#define TXM_MODULE#include "txm_module.h"/*
*********************************************************************************************************
* 宏定义
*********************************************************************************************************
*/
#define DEFAULT_STACK_SIZE 1024#define App_Printf_ID1 (TXM_APPLICATION_REQUEST_ID_BASE)
#define App_Printf_ID2 (TXM_APPLICATION_REQUEST_ID_BASE + 1)#define MODULE_THREAD_PRIO 3
#define MODULE_THREAD_PREEMPTION_THRESHOLD MODULE_THREAD_PRIOULONG AppModuleStk[DEFAULT_STACK_SIZE/4];/* 相关控制块 */
TX_THREAD *thread_0;
TX_QUEUE *resident_queue;/* 函数 */
void thread_0_entry(ULONG thread_input);
void Error_Handler1(void);/*
*********************************************************************************************************
* 函 数 名: default_module_start
* 功能说明: 动态APP入口
* 形 参: ---
* 返 回 值: 无
*********************************************************************************************************
*/
void demo_module_start(ULONG id)
{CHAR *pointer;/* 从主程序申请相关控制块,不在APP里面申请,防止APP出问题了影响主程序 */txm_module_object_allocate((void*)&thread_0, sizeof(TX_THREAD));/* 创建任务 */tx_thread_create(thread_0,"module thread qqq",thread_0_entry,0,&AppModuleStk[0],DEFAULT_STACK_SIZE,MODULE_THREAD_PRIO,MODULE_THREAD_PREEMPTION_THRESHOLD,TX_NO_TIME_SLICE,TX_AUTO_START);}/*
*********************************************************************************************************
* 函 数 名: thread_0_entry
* 功能说明: 动态APP里面的任务
* 形 参: ---
* 返 回 值: 无
*********************************************************************************************************
*/
void thread_0_entry(ULONG thread_input)
{/* 防止警告 */UINT num = 0;while(1){/* 调用主程序里面的串口打印 */num++;txm_module_application_request(App_Printf_ID1, num, 0, 0);tx_thread_sleep(1000);}
}/*
*********************************************************************************************************
* 函 数 名: thread_0_entry
* 功能说明: 执行出错
* 形 参: ---
* 返 回 值: 无
*********************************************************************************************************
*/
void Error_Handler1(void)
{tx_thread_sleep(TX_WAIT_FOREVER);
}
加入宏定义
在txm_module_port.h中加入,不在txm_module_user.h加,由于我们编译模块的时候,是没有加入相关宏定义的,所以
txm_module_user.h中是不会生效的
#define FX_FILEX_PRESENT
#define TXM_MODULE_KERNEL_STACK_SIZE 2048 // 内核栈设置大一些,由于使用了filex
构建模块bin
- 由于加入filex ,所以构建模块时,脚本文件配置加入对应的filex头文件相关路径,然后重新生成库文件和模块bin文件
测试
生成好的bin文件,通过ota上传到sd卡
加载module文件
查看module信息
启动模块
- 为了方便停止,10秒打印一次模块调用的信息
查看线程信息
停止模块
总结
- 目前已经可以动态加载模块了,后面再补充两个模块之间通信的记录