zoukankan      html  css  js  c++  java
  • ThreadX应用开发笔记之二:移植ThreadX到STM32H7平台

      前面我们将ThreadX成功移植到了STM32F4平台,但这只是我们的部分应用。我们希望将ThreadX的优势发挥到我们的更多应用中,所以在这一篇中我们就来实现将ThreadX移植到STM32H7平台中。

    1、前期准备

      在开始将ThreadX移植到STM32H7平台之前,我们需要做一些软硬件方面的准备。

      首先,我们需要准备STM32H7的硬件平台。这次我们采用STM32H750VBT6为控制单元来作为目标平台。这是一款我们在实际项目中使用的,经过验证的,硬件能够稳定运行的平台。

      其次,我们需要准备相应的软件资源,也就是ThreadX的源码。ThreadX的源码已经开源到Github上,其地址为:https://github.com/azure-rtos/threadx,直接下载源码就可以了。我们将采用目前最新的版本。下载好ThreadX的源码后,我们将其解压,如下图:

      上图中一目了然,无需做太多解释。我们需要用到的文件主要存放在common文件夹和ports文件夹。其中common文件夹存放的是内核源码,ports文件夹存放的是不同平台的接口文件。我们的硬件采用的是STM32H750VBT6,软件开发环境用的是IAR EWARM,所以我们选择ports文件夹下cortex_m7下的IAR文件夹中的接口文件。

    2、系统移植

      我们准备好软件硬件平台后,就可以开始系统的移植了。首先我们找到一个基础的裸机项目,能正确实现硬件的启动及时钟初始化就好了。接下来的移植工作主要包括:添加源码,修改配置等。

      第一步,我们先向项目中添加ThreadX的相关源码文件。所以我们在项目下添加ThreadX组、并在ThreadX组下添加Source和Ports两个组用于添加文件。并将common文件夹和ports文件夹中的文件添加到对应的分组。如下所示:

      然后要在项目属性中为编译器指定头文件的引用路径,主要是内核函数的头文件以及接口文件的头文件两个路径,在我们这个项目中配置如下:

      (PROJ_DIR)....ThreadXcommoninc

      (PROJ_DIR)....ThreadXportscortex_m4iarinc

      第二步,修改stm32h7xx_it.c文件。将其中的中断响应函数void PendSV_Handler(void)和void SysTick_Handler(void)去除。因为在ThreadX中已经实现和使用。

      第三步,修改tx_initialize_low_level.s文件。这个文件负责建立各种系统数据结构,并提供定时中断源。这个文件应该是要针对不同的底层平台编写。但在微软提供的cortex_m7下IAR的接口例程中已经提供 了一个,所以我们基于这个文件进行修改就可以了,主要根据实际应用修改的是时钟频率。

      SYSTEM_CLOCK EQU 480000000

      SYSTICK_CYCLES EQU ((SYSTEM_CLOCK / 1000) -1)

      第四步,修改ThreadX的配置文件。ThreadX中要求使者提供一个tx_user.h的配置文件。当然这个文件并不需要从头编写,在commoninc目录下有一个tx_user_sample.h文件,我们根据这个文件修改就可以了。

      经过上述这四步操作,我们实际上已经完成了对ThreadX内核的移植,但还没有办法正确使用,因为我们还没有定义具体的任务。

    3、任务实现

      我们已经完成了对ThreadX内核文件以及接口文件的移植,接下来我们就来实现具体的应用任务。ThreadX内核实现基本应用很简单,只涉及到2个函数:tx_kernel_enter和tx_application_define,这两个函数在头文件“tx_api.h”中被声明。事实上ThreadX内核所有的对外函数都在“tx_api.h”中声明,所以凡是我们需要使用内核的地方必须引用“tx_api.h”头文件。

      其中tx_kernel_enter实际是一个宏,真正的函数是_tx_initialize_kernel_enter,用于启动内核,这个函数需要我们在主函数中调用。调用这个函数后,内核开始运行,多任务也将按照我们的设计循环运行。

      而tx_application_define函数只有声明没有实现,在_tx_initialize_kernel_enter函数中被调用,用于任务的创建。所有的任务都将在这个函数中被创建,而且不仅仅是任务在这个函数中创建,信号量、队列、互斥量等都在这个函数中创建。

      我们将在tx_application_define函数创建任务,这就需要用到tx_thread_create函数。这个函数的参数有10个,包括任务控制块、任务函数地址、任务栈的大小及地址、任务优先级等。这些参数都是我们需要定义或声明的。然后我们就可以编写tx_application_define函数:

    /*tx_application_define函数实现*/
    void tx_application_define(void *first_unused_memory)
    {
      /* 创建系统任务 */
      tx_thread_create(&ThreadSystemTCB,       /* 任务控制块地址 */  
                       "Thread System",       /* 任务名 */
                       ThreadSystem,         /* 启动任务函数地址 */
                       0,               /* 传递给任务的参数 */
                       &ThreadSystemStack[0],      /* 堆栈基地址 */
                       THREAD_SYSTEM_STK_SIZE,  /* 堆栈空间大小 */ 
                       THREAD_SYSTEM_PRIO,    /* 任务优先级*/
                       THREAD_SYSTEM_PRIO,    /* 任务抢占阀值 */
                       TX_NO_TIME_SLICE,        /* 不开启时间片 */
                       TX_AUTO_START);         /* 创建后立即启动 */
       
      
      /* 创建模拟量处理任务 */
      tx_thread_create(&ThreadAnalogTCB,        /* 任务控制块地址 */  
                       "Thread Analog",         /* 任务名 */
                       ThreadAnalog,         /* 启动任务函数地址 */
                       0,               /* 传递给任务的参数 */
                       &ThreadAnalogStack[0],      /* 堆栈基地址 */
                       THREAD_ANALOG_STK_SIZE,  /* 堆栈空间大小 */ 
                       THREAD_ANALOG_PRIO,    /* 任务优先级*/
                       THREAD_ANALOG_PRIO,    /* 任务抢占阀值 */
                       TX_NO_TIME_SLICE,        /* 不开启时间片 */
                       TX_AUTO_START);        /* 创建后立即启动 */
      
      
      /* 创建逻辑处理任务 */
      tx_thread_create(&ThreadLogicTCB,        /* 任务控制块地址 */   
                       "Thread Logic",       /* 任务名 */
                       ThreadLogic,         /* 启动任务函数地址 */
                       0,               /* 传递给任务的参数 */
                       &ThreadLogicStack[0],       /* 堆栈基地址 */
                       THREAD_LOGIC_STK_SIZE, /* 堆栈空间大小 */ 
                       THREAD_LOGIC_PRIO,   /* 任务优先级*/
                       THREAD_LOGIC_PRIO,   /* 任务抢占阀值 */
                       TX_NO_TIME_SLICE,        /* 不开启时间片 */
                       TX_AUTO_START);         /* 创建后立即启动 */
      
      /* 创建通讯处理任务 */
      tx_thread_create(&ThreadCommTCB,        /* 任务控制块地址 */   
                       "Thread Comm",       /* 任务名 */
                       ThreadComm,          /* 启动任务函数地址 */
                       0,               /* 传递给任务的参数 */
                       &ThreadCommStack[0],      /* 堆栈基地址 */
                       THREAD_COMM_STK_SIZE, /* 堆栈空间大小 */ 
                       THREAD_COMM_PRIO,   /* 任务优先级*/
                       THREAD_COMM_PRIO,   /* 任务抢占阀值 */
                       TX_NO_TIME_SLICE,        /* 不开启时间片 */
                       TX_AUTO_START);         /* 创建后立即启动 */
      
      /* 创建统计任务 */
      tx_thread_create(&ThreadStatTCB,        /* 任务控制块地址 */  
                       "Thread STAT",       /* 任务名 */
                       ThreadStat,         /* 启动任务函数地址 */
                       0,              /* 传递给任务的参数 */
                       &ThreadStatStack[0],      /* 堆栈基地址 */
                       THREAD_IDLE_STK_SIZE,  /* 堆栈空间大小 */ 
                       THREAD_STAT_PRIO,    /* 任务优先级*/
                       THREAD_STAT_PRIO,    /* 任务抢占阀值 */
                       TX_NO_TIME_SLICE,       /* 不开启时间片 */
                       TX_AUTO_START);        /* 创建后立即启动 */
      
      
      /* 创建空闲任务 */
      tx_thread_create(&ThreadIdleTCB,     /* 任务控制块地址 */  
                       "Thread IDLE",      /* 任务名 */
                       ThreadIdle,       /* 启动任务函数地址 */
                       0,            /* 传递给任务的参数 */
                       &ThreadIdleStack[0],   /* 堆栈基地址 */
                       THREAD_IDLE_STK_SIZE,  /* 堆栈空间大小 */ 
                       THREAD_IDLE_PRIO,    /* 任务优先级*/
                       THREAD_IDLE_PRIO,    /* 任务抢占阀值 */
                       TX_NO_TIME_SLICE,    /* 不开启时间片 */
                       TX_AUTO_START);     /* 创建后立即启动 */
    }
    

      还要在主函数中调用 tx_kernel_enter函数以达到启动ThreadX内核的目的。

    4、最后测试

      完成前述的全部内容后,我们编译下载到目标平台,系统能够正常运行。添加ThreadX调试插件可以查看个任务的执行情况如下:

      经过上述测试,我们已经成功的将ThreadX一直到立刻STM32H7平台,这样余下的事情就是开发具体的应用了。

    欢迎关注:

    如果阅读这篇文章让您略有所得,还请点击下方的【好文要顶】按钮。

    当然,如果您想及时了解我的博客更新,不妨点击下方的【关注我】按钮。

    如果您希望更方便且及时的阅读相关文章,也可以扫描上方二维码关注我的微信公众号【木南创智

  • 相关阅读:
    vue使用elementui合并table
    使用layui框架导出table表为excel
    vue使用elementui框架,导出table表格为excel格式
    前台传数据给后台的几种方式
    uni.app图片同比例缩放
    我的博客
    【C语言】取16进制的每一位
    SharePoint Solution 是如何部署的呢 ???
    无效的数据被用来用作更新列表项 Invalid data has been used to update the list item. The field you are trying to update may be read only.
    SharePoint 判断用户在文件夹上是否有权限的方法
  • 原文地址:https://www.cnblogs.com/foxclever/p/15227650.html
Copyright © 2011-2022 走看看