前言
- 前几篇博客基本上已经将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 32
char 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_AREA
if (!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 2048
TX_DISABLE
uint8_t *cache_buf = app_malloc(READ_BUF_SIZE);
TX_RESTORE
if (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_DISABLE
app_free(cache_buf);
TX_RESTORE
return 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_CALLBACK
rctx.rym_malloc = (void *(*)(size_t)) app_malloc;
rctx.rym_free = (void (*)(void *)) app_free;
#endif
ota_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_PRIO
ULONG 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秒打印一次模块调用的信息
查看线程信息
停止模块
总结
- 目前已经可以动态加载模块了,后面再补充两个模块之间通信的记录