测试RT-Thread 动态模块

      听说国人开发的RT-Thread OS 已经有一段时间了,一直没有尝试。毕竟学习一个新的平台花费的时间和精力非常多。这次来测试RT-Thread 的主要目的是希望使用它的动态库功能。现在Cortex-M 系列的Arm 处理器已经非常强大了。但是由于Cortex-M没有内存管理器。程序缺乏重定位的功能。所以难以实现类似linux OS 的共享库(lib.so )。而共享库对于系统的功能扩展非常重要。比如在控制器中,需要动态地导入功能块。如果没有共享库功能。那么新增加的程序库必须要和内核,OS 一起重新编译下载。实现远程添加,更新功能块就更加麻烦。所以,共享库对于控制设备的嵌入式软件而言是非常重要的。

初识RT-Thread

RT-Thread Studio 开发环境非常方便

     RT-Thread 已经提供了一个一体化的开发环境RT-Thread Studio。使用起来十分方便。为了减少一开始设置的麻烦,购买了一个Art PI 开发板。开发一个闪灯程序非常快捷和顺利。

RT-Thread的 依赖项

  RT-Thread 使用ST link 作为程序下载工具,需要下载和安装STlink,同时也是有Python 需要安装Python 2.7

RT-Thread 的msh

     与其他嵌入式OS 不同,RT-Thread 提供了一个类似Linux shell 的命令工具, 前叫做msh。通过开发板上的USB 与PC 链接。你可以使用一个串口软件使用。以前好像叫做Finsh 。不知道为什么不直接叫做shell ?

摆脱不了 RT-Thread 的ENV 工具

    以前RT-Thread 是使用ENV 工具开发的。目前开发动态模块,好像依然脱不了ENV 工具。它是是在windows cmd 下使用的。在相关的目录下 击右键,选择 conEMU Here 进入。

吐槽一下

     也许是一些历史的包袱,RT-Thread 的某些概念,命名和使用方式有点古怪,与linux ,或者其他的开发环境不同。初学起来有点摸不着头脑。正如我所指出的。开发一个软件,要十分小心地起名字和术语。尽量符合社会化语义(也就是人们的习惯)。

   RT-Thread 的另一个问题是国人开发的软件普遍存在一个问题,不是软件本身的技术不好,而是社区不好。遇到问题在网络上很难找到答案。网络上的除了原厂的文档以外,回答问题,相互讨论和分享经验比较少。大多数是文章是相互转发的,或者是自己的学习笔记,或者是培训老师的文案。说的话都一个样。一个地方出错,许多地方都是错。这是使用国内软件的最大困惑。

使用动态模块

主要参考【STM32H750】玩转ART-Pi(八)——添加动态模块 这篇博文。视乎没有遇到太大的问题。

  源码来自于 rtthread-apps 里面包括了两个程序,一个是库lib,一个是主程序hello

lib.c

#include <stdio.h>

int lib_func(void)
{
    printf("hello world from RTT::dynamic library!\n");

    return 0;
}

int add_func(int a, int b)
{
    return (a + b);
}

在hello目录中的main.c

#include <stdio.h>
 #include <dlfcn.h>
 #include <rtthread.h> 
 
 #define APP_PATH "/sdcard/lib.so"
 
 typedef int (*add_func_t)(int, int); 
 typedef void (*lib_func_t)(void);
  int dlmodule_sample(void) 
  { 
      void* handle; 
      lib_func_t lib_function; 
      add_func_t add_function; 
      
       handle = dlopen(APP_PATH,RTLD_LAZY);
        if(!handle) {
             printf("dlopen %s failed!\n",APP_PATH); return -1;
              } 
         
         
              lib_function = 
              (lib_func_t)dlsym(handle,"lib_func");
               if(!lib_function) { 
                   printf("dlsym %p failed!\n",handle); return -1; } 
                  
                   lib_function(); 
               
               
                    add_function = (add_func_t)dlsym(handle,"add_func");
                     if(!add_function) { printf("dlsym %p failed!\n",handle); return -1; }                         
                     printf("add_function result is:%d\n",add_function(3,4));
 dlclose(handle); return 0; }
   MSH_CMD_EXPORT(dlmodule_sample, dlmodule sample);
   int main(void) { 
       printf("rt-thread dynamic module Test!\n");
        return 0; 
}

仔细对比一下的话,我们可做了一点改动。

原来的 #define APP_PATH "/lib.so" 改成

 #define APP_PATH "/sdcard/lib.so",也许某个地方设置一下使用/lib.so 也是可以的。

另外,不知道为什么

MSH_CMD_EXPORT(dlmodule_sample, dlmodule sample);   不起作用,按说这个语句可以将dlmount_sample 程序转变成为一条msh 命令。在msh 下运行这个程序。我后来直接在main 中调用dlmount_sample这个程序了。

编译的命令

 set RTT_ROOT=C:\RT-ThreadStudio\workspace\art_pi_module\rt-thread
 set BSP_ROOT=C:\RT-ThreadStudio\workspace\art_pi_module
 scons --target=ua -s
 scons --lib=lib
 scons --app=hello

进一步的测试

【STM32H750】玩转ART-Pi(八)——添加动态模块 的文章中到此结束了,但是真正要使用动态模块,好像还有一些事情要做

在studio 的项目文件中调用lib.so 库

这个好像比较简单,将dlmount_sample 拷贝到了项目的main中。

/*
 * Copyright (c) 2006-2020, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2020-09-02     RT-Thread    first version
 */

#include <rtthread.h>
#include <rtdevice.h>
#include "drv_common.h"
#include <stdio.h>
 #include <dlfcn.h>
#define LED_PIN GET_PIN(I, 8)
#define APP_PATH "/sdcard/lib.so"

 typedef int (*add_func_t)(int, int);
 typedef void (*lib_func_t)(void);


  int dlmodule_sample(void)
  {
      void* handle;
      lib_func_t lib_function;
      add_func_t add_function;
       handle = dlopen(APP_PATH,RTLD_LAZY);
        if(!handle) {
             printf("dlopen %s failed!\n",APP_PATH); return -1;
              }


              lib_function =
              (lib_func_t)dlsym(handle,"lib_func");
               if(!lib_function) {
                   printf("dlsym %p failed!\n",handle); return -1; }

                   lib_function();

                 //add function
                    add_function = (add_func_t)dlsym(handle,"add_func");
                     if(!add_function) { printf("dlsym %p failed!\n",handle); return -1; }
                     printf("add_function result is:%d\n",add_function(3,4));
           
 dlclose(handle);
 return 0;
  }
int main(void)
{
    rt_uint32_t count = 1;
    printf("rt-thread dynamic module Test!\n");
    rt_thread_mdelay(500);
    dlmodule_sample();
    rt_pin_mode(LED_PIN, PIN_MODE_OUTPUT);

    while(count++)
    {
        rt_thread_mdelay(500);
        rt_pin_write(LED_PIN, PIN_HIGH);
        rt_thread_mdelay(500);
        rt_pin_write(LED_PIN, PIN_LOW);
    }
    return RT_EOK;
}

#include "stm32h7xx.h"
static int vtor_config(void)
{
    /* Vector Table Relocation in Internal QSPI_FLASH */
    SCB->VTOR = QSPI_BASE;
    return 0;
}
INIT_BOARD_EXPORT(vtor_config);

     在这里唯一的一个问题是运行时,先运行main 线程,在mount sdcard 所以一开始就open lib.so 文件时,找不到 sdcard 。所以我在调用 dlmount_sample 之前添加了一个

 rt_thread_mdelay(500);

确保 sdcard 已经mount 成功了。

lib 调用外部函数 

      如何在lib程序中回调main 的函数。这里我们使用了C语言 callback 的方法。经测试是可行的。

lib.c

#include <stdio.h>

int lib_func(void)
{
    printf("hello world from RTT::dynamic library!\n");

    return 0;
}

int add_func(int a, int b)
{
    return (a + b);
}
int handle(int y, int (*Callback)(int)){
    printf("Entering Handle Function. \n");
        Callback(y);
        printf("Leaving Handle Function. \n");
        return 0;
}

dlmodule_sample 子程序

#include <rtdevice.h>
#include "drv_common.h"
#include <stdio.h>
 #include <dlfcn.h>
#define LED_PIN GET_PIN(I, 8)
#define APP_PATH "/sdcard/lib.so"

 typedef int (*add_func_t)(int, int);
 typedef void (*lib_func_t)(void);
 typedef void (*handle_func_t)(int,int (*Callback)(int));
 int Callback(int x) // Callback Function 1
 {
     printf("Hello, this is Callback_1: x = %d ", x);
     return 0;
 }

  int dlmodule_sample(void)
  {
      void* handle;
      lib_func_t lib_function;
      add_func_t add_function;
      handle_func_t handle_function;
       handle = dlopen(APP_PATH,RTLD_LAZY);
        if(!handle) {
             printf("dlopen %s failed!\n",APP_PATH); return -1;
              }


              lib_function =
              (lib_func_t)dlsym(handle,"lib_func");
               if(!lib_function) {
                   printf("dlsym %p failed!\n",handle); return -1; }

                   lib_function();

                 //add function
                    add_function = (add_func_t)dlsym(handle,"add_func");
                     if(!add_function) { printf("dlsym %p failed!\n",handle); return -1; }
                     printf("add_function result is:%d\n",add_function(3,4));
                 // callback
                     handle_function = (handle_func_t)dlsym(handle,"handle");
                                         if(!handle_function) { printf("dlsym %p failed!\n",handle); return -1; }
                  handle_function(100,&Callback);
 dlclose(handle);
 return 0;
  }

结束语

       完成了RT-Thread OS 外部模块调用的初步测试。外部模块调用对应嵌入式程序而言是很有用的,也许是因为RT-Thread 的外部模块功能使我爱上了RT-Thread。

上一篇:Linux kernel tcp 连接建立详解——由listen系统调用的backlog参数引发的长篇大论


下一篇:关于程序进入HardFault_Handle中断的问题分析