• 作者:zzssdd2
  • E-mail:zzssdd2@foxmail.com

一、前言

在uCOS全家桶宣布开源之后被微软收购的ThreadX也开源了,真是喜大普奔,对于我们这些嵌入式行业从业者来说,能够学习这些高含金量的代码对于眼界的开拓和能力的提升都是很有帮助的。ThreadX还包含了NETX、GUIX、FILEX、USBX等丰富的组件,几乎包含了嵌入式开发中的所有场景,这些组件和ThreadX-RTOS能够完美的配合完成一条龙的开发。ThreadX的github地址:[Azure RTOS (github.com)]:

二、准备

ThreadX在更新了几个版本之后已经提供了多个平台的移植端口,包括AC5、AC6、GCC、IAR,现在可以更方便地移植到各编译器平台了。本次移植是在STM32H743 + MDK-AC6 + ThreadX6.1.2下完成。首先使用STM32CubeMX新建一个工程,配置了LED、KEY、UART,然后将下载的ThreadX放在同一工程下(移植用到commonports/cortex_m7/ac6下的文件)。

三、配置

1、添加文件及参数

  • 工程新增ThreadX/PortThreadX/Src分组,ThreadX/Portf组中添加ports/cortex_m7/ac6/src目录下所有文件以及ports\cortex_m7\ac6\example_build\sample_threadx目录下的tx_initialize_low_level.S文件; ThreadX/Src组中添加threadx\common\src下所有文件。添加完成如下图所示:

  • 配置工程宏定义参数和文件包含路径如下所示。

其中USE_HAL_DRIVERSTM32H743xx是使用HAL库创建工程需要包含的定义,TX_ENABLE_FPU_SUPPORT是使能ThreadX的浮点运算支持需要定义的,在readme_threadx.txt文档中有如下描述:

If saving the context of the FPU registers
is needed, the ThreadX library should be re-built with TX_ENABLE_FPU_SUPPORT defined.

当然要正确的使用硬件浮点运算功能还应该开启MDK中的如下配置:

2、修改适配文件

主要是对tx_initialize_low_level.S文件进行修改,该文件作用是初始化栈地址和向量表、配置系统心跳节拍、配置部分中断优先级、定义部分中断处理函数。但是其中有些工作在STM32H743的启动文件startup_stm32h743xx.s中已经做了,而STM32启动文件所实现的一些功能这个文件又没有实现。因为该文件是针对cortex_m7内核芯片做的,没有针对具体某一款芯片,所以没有STM32的启动文件实现的功能完善,因此不能替代启动文件。这里的修改方法是不动STM32启动文件,修改tx_initialize_low_level.S文件,在此提供两种修改方案:1、根据冲突修改tx_initialize_low_level.S文件内容;2、将tx_initialize_low_level.S文件改为C语言实现(STM32启动文件已经实现的不管,只需将ThreadX需要配置的功能用C实现)。

  • 方案1修改后内容如下(修改处已标注):

    @/**************************************************************************/
    @/* */
    @/* Copyright (c) Microsoft Corporation. All rights reserved. */
    @/* */
    @/* This software is licensed under the Microsoft Software License */
    @/* Terms for Microsoft Azure RTOS. Full text of the license can be */
    @/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */
    @/* and in the root directory of this software. */
    @/* */
    @/**************************************************************************/
    @
    @
    @/**************************************************************************/
    @/**************************************************************************/
    @/** */
    @/** ThreadX Component */
    @/** */
    @/** Initialize */
    @/** */
    @/**************************************************************************/
    @/**************************************************************************/
    @
    @
    .global _tx_thread_system_stack_ptr
    .global _tx_initialize_unused_memory
    .global _tx_timer_interrupt
    .global __main
    .global __tx_SVCallHandler
    .global __tx_PendSVHandler
    .global __tx_NMIHandler @ NMI
    .global __tx_BadHandler @ HardFault
    .global __tx_SVCallHandler @ SVCall
    .global __tx_DBGHandler @ Monitor
    .global __tx_PendSVHandler @ PendSV
    .global __tx_SysTickHandler @ SysTick
    .global __tx_IntHandler @ Int 0
    @
    @
    SYSTEM_CLOCK = 480000000 @---modify by user(系统频率)
    SYSTICK_CYCLES = ((SYSTEM_CLOCK / 1000) -1) @---modify by user(时钟节拍) .text 32
    .align 4
    .syntax unified
    @/**************************************************************************/
    @/* */
    @/* FUNCTION RELEASE */
    @/* */
    @/* _tx_initialize_low_level Cortex-M7/AC6 */
    @/* 6.1 */
    @/* AUTHOR */
    @/* */
    @/* William E. Lamie, Microsoft Corporation */
    @/* */
    @/* DESCRIPTION */
    @/* */
    @/* This function is responsible for any low-level processor */
    @/* initialization, including setting up interrupt vectors, setting */
    @/* up a periodic timer interrupt source, saving the system stack */
    @/* pointer for use in ISR processing later, and finding the first */
    @/* available RAM memory address for tx_application_define. */
    @/* */
    @/* INPUT */
    @/* */
    @/* None */
    @/* */
    @/* OUTPUT */
    @/* */
    @/* None */
    @/* */
    @/* CALLS */
    @/* */
    @/* None */
    @/* */
    @/* CALLED BY */
    @/* */
    @/* _tx_initialize_kernel_enter ThreadX entry function */
    @/* */
    @/* RELEASE HISTORY */
    @/* */
    @/* DATE NAME DESCRIPTION */
    @/* */
    @/* 09-30-2020 William E. Lamie Initial Version 6.1 */
    @/* */
    @/**************************************************************************/
    @VOID _tx_initialize_low_level(VOID)
    @{
    .global _tx_initialize_low_level
    .thumb_func
    _tx_initialize_low_level:
    @
    @ /* Disable interrupts during ThreadX initialization. */
    @
    CPSID i
    @
    @ /* Set base of available memory to end of non-initialised RAM area. */
    @
    LDR r0, =_tx_initialize_unused_memory @ Build address of unused memory pointer
    LDR r1, =__initial_sp @ Image$$ARM_LIB_STACKHEAP$$ZI$$Limit @ Build first free address ---modify by user
    ADD r1, r1, #4 @
    STR r1, [r0] @ Setup first unused memory pointer
    @
    @ /* Setup Vector Table Offset Register. */
    @
    MOV r0, #0xE000E000 @ Build address of NVIC registers
    LDR r1, =__Vectors @ vector_table @ Pickup address of vector table ---modify by user
    STR r1, [r0, #0xD08] @ Set vector table address
    @
    @ /* Set system stack pointer from vector value. */
    @
    LDR r0, =_tx_thread_system_stack_ptr @ Build address of system stack pointer
    LDR r1, =__Vectors @ vector_table @ Pickup address of vector table ---modify by user
    LDR r1, [r1] @ Pickup reset stack pointer
    STR r1, [r0] @ Save system stack pointer
    @
    @ /* Enable the cycle count register. */
    @
    LDR r0, =0xE0001000 @ Build address of DWT register
    LDR r1, [r0] @ Pickup the current value
    ORR r1, r1, #1 @ Set the CYCCNTENA bit
    STR r1, [r0] @ Enable the cycle count register
    @
    @ /* Configure SysTick for 100Hz clock, or 16384 cycles if no reference. */
    @
    MOV r0, #0xE000E000 @ Build address of NVIC registers
    LDR r1, =SYSTICK_CYCLES
    STR r1, [r0, #0x14] @ Setup SysTick Reload Value
    MOV r1, #0x7 @ Build SysTick Control Enable Value
    STR r1, [r0, #0x10] @ Setup SysTick Control
    @
    @ /* Configure handler priorities. */
    @
    LDR r1, =0x00000000 @ Rsrv, UsgF, BusF, MemM
    STR r1, [r0, #0xD18] @ Setup System Handlers 4-7 Priority Registers LDR r1, =0xFF000000 @ SVCl, Rsrv, Rsrv, Rsrv
    STR r1, [r0, #0xD1C] @ Setup System Handlers 8-11 Priority Registers
    @ Note: SVC must be lowest priority, which is 0xFF LDR r1, =0x40FF0000 @ SysT, PnSV, Rsrv, DbgM
    STR r1, [r0, #0xD20] @ Setup System Handlers 12-15 Priority Registers
    @ Note: PnSV must be lowest priority, which is 0xFF
    @
    @ /* Return to caller. */
    @
    BX lr
    @}
    @ @/* Define shells for each of the unused vectors. */
    @
    .global __tx_BadHandler
    .thumb_func
    __tx_BadHandler:
    B __tx_BadHandler @ /* added to catch the hardfault */ .global __tx_HardfaultHandler
    .thumb_func
    __tx_HardfaultHandler:
    B __tx_HardfaultHandler @ /* added to catch the SVC */ .global __tx_SVCallHandler
    .thumb_func
    __tx_SVCallHandler:
    B __tx_SVCallHandler @ /* Generic interrupt handler template */
    .global __tx_IntHandler
    .thumb_func
    __tx_IntHandler:
    @ VOID InterruptHandler (VOID)
    @ {
    PUSH {r0, lr}
    #ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY
    BL _tx_execution_isr_enter @ Call the ISR enter function
    #endif @ /* Do interrupt handler work here */
    @ /* BL <your C Function>.... */ #ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY
    BL _tx_execution_isr_exit @ Call the ISR exit function
    #endif
    POP {r0, lr}
    BX LR
    @ } @ /* System Tick timer interrupt handler */
    .global __tx_SysTickHandler
    .global SysTick_Handler
    .thumb_func
    __tx_SysTickHandler:
    .thumb_func
    SysTick_Handler:
    @ VOID TimerInterruptHandler (VOID)
    @ {
    @
    PUSH {r0, lr}
    #ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY
    BL _tx_execution_isr_enter @ Call the ISR enter function
    #endif
    BL _tx_timer_interrupt
    #ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY
    BL _tx_execution_isr_exit @ Call the ISR exit function
    #endif
    POP {r0, lr}
    BX LR
    @ } @ /* NMI, DBG handlers */
    .global __tx_NMIHandler
    .thumb_func
    __tx_NMIHandler:
    B __tx_NMIHandler .global __tx_DBGHandler
    .thumb_func
    __tx_DBGHandler:
    B __tx_DBGHandler
  • 方案2修改内容如下(工程中用该文件tx_initialize_low_level.C替换tx_initialize_low_level.S文件)

    #include "stm32h7xx_hal.h"
    #include "tx_api.h" static const uint32_t SYSTEM_CLOCK = 480000000;
    static const uint32_t SYSTICK_CYCLES = ((SYSTEM_CLOCK / 1000) -1); extern void _tx_timer_interrupt(void); /*in "tx_timer_interrupt.S" file*/ void _tx_initialize_low_level(void)
    {
    /* Disable interrupts during ThreadX initialization. */
    __set_PRIMASK(1); /* Configure SysTick for 1000Hz clock, or 16384 cycles if no reference. */
    SysTick_Config(SYSTICK_CYCLES); /* Configure handler priorities. */
    HAL_NVIC_SetPriority(SVCall_IRQn, 15, 0); /*Note: SVC must be lowest priority*/
    HAL_NVIC_EnableIRQ(SVCall_IRQn); HAL_NVIC_SetPriority(PendSV_IRQn, 15, 0);
    HAL_NVIC_EnableIRQ(PendSV_IRQn); /*Note: PnSV must be lowest priority*/ HAL_NVIC_SetPriority(SysTick_IRQn, 4, 0);
    HAL_NVIC_EnableIRQ(SysTick_IRQn);
    } /* System Tick timer interrupt handler */
    void SysTick_Handler (void)
    {
    /*BL _tx_timer_interrupt*/
    _tx_timer_interrupt();
    }

通过上述修改文件可见ThreadX修改底层移植文件的内容并不多,移植还是比较方便。

最后stm32h7xx_it.c文件中的PendSV_HandlerSysTick_Handler中断处理函数屏蔽(前者用于实现RTOS的任务调度,后者用于实现RTOS的心跳节拍)。

#ifndef ENABLE_RTOS
void PendSV_Handler(void)
{
} void SysTick_Handler(void)
{
}
#endif

四、测试

经过上述步骤,工程编译已经没有错误,接下来就是创建任务测试ThreadX能否正常运行起来。

threadx\ports\cortex_m7\ac6\example_build\sample_threadx下的sample_threadx.c文件中演示了任务的创建,依葫芦画瓢创建自己的任务即可。main.c文件内容如下:

#include "main.h"
#include "usart.h"
#include "gpio.h"
#include "tx_api.h" #define DEMO_STACK_SIZE (2 * 1024)
#define DEMO_BYTE_POOL_SIZE (32 * 1024) TX_BYTE_POOL byte_pool_0;
UCHAR memory_area[DEMO_BYTE_POOL_SIZE]; TX_THREAD thread_0;
TX_THREAD thread_1;
TX_THREAD thread_2; void thread_0_entry(ULONG thread_input);
void thread_1_entry(ULONG thread_input);
void thread_2_entry(ULONG thread_input); int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_USART1_UART_Init();
while (1)
{
tx_kernel_enter();
}
} void tx_application_define(void *first_unused_memory)
{
CHAR *pointer = TX_NULL; /* Create a byte memory pool from which to allocate the thread stacks. */
tx_byte_pool_create(&byte_pool_0, "byte pool 0", memory_area, DEMO_BYTE_POOL_SIZE); /* Allocate the stack for thread 0. */
tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_STACK_SIZE, TX_NO_WAIT);
/* Create the main thread. */
tx_thread_create(&thread_0, "thread 0", thread_0_entry, 0,
pointer, DEMO_STACK_SIZE,
1, 1, TX_NO_TIME_SLICE, TX_AUTO_START); /* Allocate the stack for thread 1. */
tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_STACK_SIZE, TX_NO_WAIT);
/* Create threads 1 */
tx_thread_create(&thread_1, "thread 1", thread_1_entry, 0,
pointer, DEMO_STACK_SIZE,
2, 2, TX_NO_TIME_SLICE, TX_AUTO_START); /* Allocate the stack for thread 2. */
tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_STACK_SIZE, TX_NO_WAIT);
/* Create threads 1 */
tx_thread_create(&thread_2, "thread 2", thread_2_entry, 0,
pointer, DEMO_STACK_SIZE,
3, 3, TX_NO_TIME_SLICE, TX_AUTO_START);
} void thread_0_entry(ULONG thread_input)
{
while(1)
{
HAL_GPIO_TogglePin(LED1_GPIO_Port,LED1_Pin);
tx_thread_sleep(200);
}
} void thread_1_entry(ULONG thread_input)
{
while(1)
{
HAL_GPIO_TogglePin(LED2_GPIO_Port,LED2_Pin);
tx_thread_sleep(200);
}
} void thread_2_entry(ULONG thread_input)
{
double a = 0.1, b = 1.0; while(1)
{
a += 0.00000000001;
b -= 0.00000000001;
printf("floating test:\ta = %.11f, b = %.11f\r\n", a, b);
tx_thread_sleep(2000);
}
}

到这里,任务就正常运行起来了。

补充

任务创建函数说明

  • 参数:

    • thread_ptr:指向线程控制块的指针
    • name_ptr :指向线程名称的指针
    • entry_function:指定用于线程执行的初始C函数。 当线程从此入口函数返回时,它将处于完成状态并无限期挂起。
    • entry_input:首次执行时传递给线程的入口函数的32位值。 此输入的使用完全由应用程序确定
    • stack_start:堆栈内存区域的起始地址
    • stack_size:堆栈内存区中的字节数
    • priority:线程的优先级。 有效值的范围是0 ~(TX_MAX_PRIORITES-1),其中0表示最高优先级
    • preempt_threshold:禁用的抢占的最高优先级。 只有高于此级别的优先级才可以抢占该线程,该值必须小于或等于指定的优先级,等于线程优先级的值将禁用抢占阈值
    • time_slice:在其他具有相同优先级的就绪线程有机会运行之前,允许此线程运行的时钟节拍数。请注意,使用抢占阈值将禁用时间片。合法的时间片值范围从1到0xffffff (包括)。TX_NO_TIME_SLICE (值为0)禁用此线程的时间片。(使用时间片会导致少量的系统开销。由于时间片仅在多个线程共享相同优先级的情况下有用,因此不应为具有唯一优先级的线程分配时间片)
    • auto_start:指定线程是立即启动还是处于暂停状态。指定选项是TX_AUTO_START (0x01)和 TX_DONT_START (0x00)。如果指定了TX_DONT_START,则应用程序必须调用 tx_thread_resume 才能使线程运行。
  • 返回值
    • TX_SUCCESS (0x00):线程创建成功
    • TX_THREAD_ERROR (0x0E):无效的线程控制指针。指针为 NULL 或线程已创建
    • TX_PTR_ERROR (0x03):入口点的起始地址无效或堆栈区域无效,通常为 NULL。
    • TX_SIZE_ERROR (0x05):堆栈区域的大小无效。 线程必须至少具有TX_MINIMUM_STACK字节才能执行。
    • TX_PRIORITY_ERROR (0x0F):无效的线程优先级,该值超出(0 ~(TX_MAX_PRIORITIES-1))的范围。
    • TX_THRESH_ERROR (0x18):指定的抢占阈值无效。 该值的有效优先级必须小于或等于线程的初始优先级。
    • TX_START_ERROR (0x10):自动启动选择无效
    • TX_CALLER_ERROR (0x13):该服务的调用者无效
UINT tx_thread_create(
TX_THREAD *thread_ptr,
CHAR *name_ptr,
VOID (*entry_function)(ULONG),
ULONG entry_input,
VOID *stack_start,
ULONG stack_size,
UINT priority,
UINT preempt_threshold,
ULONG time_slice,
UINT auto_start);

ThreadX移植——STM32H7+MDK-AC6平台的更多相关文章

  1. 移植samba到ios平台,最新支持ios8.3

    移植samba到ios平台,最新支持ios8.3 下载https://github.com/kolyvan/kxsmb 开源项目 1  cd samba-4.1.14 2  vi Rakefile 修 ...

  2. SWMM代码移植到64位平台

    在32位平台上运行SWMM模型,当节点数量到达60万以上的时候,模型运行占用内存接近1.85G的时候就会因为内存不够而无法计算.这种情况还是单独运行SWMM.exe的时候出现,如果采用SWMM.DLL ...

  3. 假防病毒软件从电脑移植到了 Android 平台

    以前有位女研究生点击网络钓鱼的链接.随即出现实时扫毒画面的方式,接着呈现了扫毒结果,跑出十余笔病毒数据,记录了被感染的计算机的具体位置,并提示她必须更新防病毒软件,而她在付费两千元后收到"防 ...

  4. 手把手教您将 libreoffice 移植到函数计算平台

    LibreOffice 是由文档基金会开发的自由及开放源代码的办公室套件.LibreOffice 套件包含文字处理器.电子表格.演示文稿程序.矢量图形编辑器和图表工具.数据库管理程序及创建和编辑数学公 ...

  5. MySql移植到嵌入式Linux平台

    最近在做考勤机系统,硬件采用的cortex-A8,哈哈,其实是有点浪费的,2410就可以的.所以就要考虑到考勤数据的存储问题,本来是打算用sqlite数据库存储的,可是后来发现,这个数据库只是一个本地 ...

  6. ubuntu移植jsoncpp到Android平台(转)

    NDK开发模块的时候,如果涉及到网络请求,类似json数据传递的时候,有现成的第三方json库可以移植,后台C++开发中使用的比较多的是jsoncpp,今天记录一下jsoncpp移植到Android平 ...

  7. 移植memtester到android平台

    硬件搭建起来能进入系统,首要就是测试内存的稳定性,需要一款内存测试工具. 一般都是选择memtester这款linux软件,下载地址如下:http://pyropus.ca/software/memt ...

  8. 移植TensorFlow到Windows平台

    2015年11月,Google宣布开源旗下机器学习工具TensorFlow,引发业界热潮.TensorFlow原生支持*unix系和安卓平台,但并不提供对Windows平台的支持.如果想在Window ...

  9. OpenCV开发笔记(七十四):OpenCV3.4.1+ffmpeg3.4.8交叉编译移植到海思平台Hi35xx平台

    前言   移植opencv到海思平台,opencv支持对视频进行解码,需要对应的ffmpeg支持.   Ffmpeg的移植   Ffmpeg的移植请参考之前的文章:<FFmpeg开发笔记(十): ...

随机推荐

  1. datagrip2020最新安装破解教程方法激活码安装参数

    现在,datagrip的版本已更新至2020.3,尚未升级的用户请赶快升级. 本文教您如何安装datagrip2020.3版本并破解它. 此方法可以100%永久激活datagrip2020.3(低版本 ...

  2. 最简 Spring IOC 容器源码分析

    前言 BeanDefinition BeanFactory 简介 Web 容器启动过程 bean 的加载 FactoryBean 循环依赖 bean 生命周期 公众号 前言 许多文章都是分析的 xml ...

  3. rocketMq指定broker ip地址,适合解决云主机部署问题

      在工作中遇到了一个这个问题,就是我们rocketmq是部署在云主机上的 但是我们的开发同事在自己的电脑连接rocketmq链接不上 报错显示Caused by: org.apache.rocket ...

  4. 精尽Spring MVC源码分析 - 寻找遗失的 web.xml

    该系列文档是本人在学习 Spring MVC 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释 Spring MVC 源码分析 GitHub 地址 进行阅读 Spring 版本:5.2. ...

  5. 【MySQL】Novicat 连接mysql 报错1251的问题处理,Novicat12 破解方法

    1.远程连接时,报错 是因为我们的navicat版本太低 在网上查的是,出现这个原因是mysql8之前的版本中加密规则是mysql_native_password,而在mysql8之后,加密规则是ca ...

  6. 【Django Python版本对应】

    使用Python36 时应该使用Django版本1.11.4 pip install django==1.11.4 版本对应表: Django version Python versions 1.8 ...

  7. Day5 - 03 函数的参数-位置参数和默认参数

    位置参数    调用函数时,传入函数的参数,按照位置顺序依次赋值给函数的参数.#计算乘方的函数                def power(x, n):            s = 1     ...

  8. String概述

    String的基本特性 String是字符串,使用一对引号("")包装. String声明是final的,不可被继承. String实现了Serializable接口,表示字符串是 ...

  9. Hbase备份以及清表脚本

    脚本主要是方便自己工作使用,服务器环境中配置了hbase相关环境变量 1.hbase备份脚本 #!/bin/bash tableList=("table1" "table ...

  10. [日常摸鱼]Luogu1801 黑匣子(NOI导刊)

    题意:写一个数据结构,要求滋兹两种操作,ADD:插入一个数,GET:令$i++$然后输出第$i$小的数 这个数据结构当然是平衡树啦!(雾) 写个Treap直接过掉啦- #include<cstd ...