ThreadX应用开发笔记之一:移植ThreadX到STM32平台

现在一些小型系统中也往往有多任务处理的需求,这就为实时操作系统提供了用武之地。事实上国内外各种各样的RTOS有很多,而且基本都在走开源的路线,ThreadX也不例外,在这一篇中我们就来学习ThreadX初步应用并将其移植到STM32平台中。

1、前期准备

在开始将ThreadX一直到STM32平台之间我们需要做一些前期准备。首先我们需要准备一个硬件平台,这次我们采用STM32F407IG控制单元来作为目标平台。其次我们需要准备一个该硬件平台下可以正常运行的裸机项目。这两点其实我们都已经具备了。

最主要的我们需要获得ThreadX的源码,这是我们移植它的基础。ThreadX的源码已经开源到Github上,其地址为:https://github.com/azure-rtos/threadx,直接下载源码就可以了。目前发布的最新版本是6.0.1,在我们的移植中我们使用6.0.0的版本来实现。

2、系统移植

首先我们先来了解一下获得的ThreadX源码。解压下载下来的压缩包,其包含有以下文件及文件夹,我们先来具体看一看都有哪些文件,如下图:

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

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

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

$PROJ_DIR$\..\..\ThreadX\common\inc

$PROJ_DIR$\..\..\ThreadX\ports\cortex_m4\iar\inc

事实上到这了,我们已经完成了对ThreadX内核文件以及接口文件的移植,但现在ThreadX不会运行,我们还需要做一些工作。我们要将内核与主函数联系起来,首先我们要在调用内核的地方添加头文件“tx_api.h”,我们这里将其添加到主函数文件中。

然后有两个函数我们需要处理,分别是:tx_kernel_enter和tx_application_define,这两个函数在头文件“tx_api.h”中被声明。tx_kernel_enter实际是一个宏,真正的函数是_tx_initialize_kernel_enter,用于启动内核,这个函数需要我们在主函数中调用。而tx_application_define函数只有声明没有实现,在_tx_initialize_kernel_enter函数中被调用,用于任务的创建。这个函数的实现是我们的主要工作,后续将详细说明。

3、任务实现

我们已经说过了tx_application_define用于任务的创建,它的具体内容需要我们来实现,接下来我们就来实现tx_application_define这个函数。

我们先来规划一下我们将要实现的内容。我们计划实现5个任务,包括启动任务、红灯闪烁任务、绿灯闪烁任务、空闲任务及统计任务。其中为启动任务用于初始化一些配置并执行一些如系统心跳、看门狗之类的工作;用于红灯闪烁任务和绿灯闪烁任务用于实现我们要操作的指示灯控制;空闲任务在其他任务不运行时其运行,优先级最低。统计任务再次我们实现系统空闲率的统计。接下来我们就按此思路来实现之。

/*tx_application_define函数实现*/
void  tx_application_define(void *first_unused_memory)
{/**************创建启动任务*********************/tx_thread_create(&AppTaskStartTCB,              /* 任务控制块地址 */  "App Task Start",              /* 任务名 */AppTaskStart,                  /* 启动任务函数地址 */0,                             /* 传递给任务的参数 */&AppTaskStartStk[0],            /* 堆栈基地址 */APP_CFG_TASK_START_STK_SIZE,  /* 堆栈空间大小 */ APP_CFG_TASK_START_PRIO,      /* 任务优先级*/APP_CFG_TASK_START_PRIO,      /* 任务抢占阀值 */TX_NO_TIME_SLICE,              /* 不开启时间片 */TX_AUTO_START);                /* 创建后立即启动 *//**************创建红灯闪烁任务*********************/tx_thread_create(&AppTaskRedLedTCB,           /* 任务控制块地址 */"App Msp Pro",                 /* 任务名 */AppTaskRedLED,              /* 启动任务函数地址 */0,                           /* 传递给任务的参数 */&AppTaskMsgProStk[0],        /* 堆栈基地址 */APP_CFG_TASK_RedLED_STK_SIZE,  /* 堆栈空间大小 */APP_CFG_TASK_REDLED_PRIO,      /* 任务优先级*/APP_CFG_TASK_REDLED_PRIO,     /* 任务抢占阀值 */TX_NO_TIME_SLICE,               /* 不开启时间片 */TX_AUTO_START);                /* 创建后立即启动 *//**************创建绿灯闪烁任务*********************/tx_thread_create(&AppTaskGreenLEDTCB,        /* 任务控制块地址 */"App Task UserIF",              /* 任务名 */AppTaskGreenLED,             /* 启动任务函数地址 */0,                         /* 传递给任务的参数 */&AppTaskUserIFStk[0],         /* 堆栈基地址 */APP_CFG_TASK_GreenLED_STK_SIZE, /* 堆栈空间大小 */APP_CFG_TASK_GREENLED_PRIO,     /* 任务优先级*/APP_CFG_TASK_GREENLED_PRIO,   /* 任务抢占阀值 */TX_NO_TIME_SLICE,               /* 不开启时间片 */TX_AUTO_START);             /* 创建后立即启动 *//**************创建统计任务*********************/tx_thread_create(&AppTaskStatTCB,               /* 任务控制块地址 */   "App Task STAT",              /* 任务名 */AppTaskStat,                  /* 启动任务函数地址 */0,                           /* 传递给任务的参数 */&AppTaskStatStk[0],           /* 堆栈基地址 */APP_CFG_TASK_STAT_STK_SIZE,  /* 堆栈空间大小 */ APP_CFG_TASK_STAT_PRIO,      /* 任务优先级*/APP_CFG_TASK_STAT_PRIO,     /* 任务抢占阀值 */TX_NO_TIME_SLICE,            /* 不开启时间片 */TX_AUTO_START);             /* 创建后立即启动 *//**************创建空闲任务*********************/tx_thread_create(&AppTaskIdleTCB,               /* 任务控制块地址 */   "App Task IDLE",              /* 任务名 */AppTaskIDLE,               /* 启动任务函数地址 */0,                          /* 传递给任务的参数 */&AppTaskIdleStk[0],          /* 堆栈基地址 */APP_CFG_TASK_IDLE_STK_SIZE,  /* 堆栈空间大小 */APP_CFG_TASK_IDLE_PRIO,       /* 任务优先级*/APP_CFG_TASK_IDLE_PRIO,     /* 任务抢占阀值 */TX_NO_TIME_SLICE,           /* 不开启时间片 */TX_AUTO_START);            /* 创建后立即启动 */
}

我们实现了tx_application_define函数,在其中创建了任务,理所当然我们还需要实现相应的任务函数。

/*系统启动任务*/
static  void  AppTaskStart (ULONG thread_input)
{(void)thread_input;/* 任务统计前先挂起其它任务 */tx_thread_suspend(&AppTaskRedLedTCB);tx_thread_suspend(&AppTaskGreenLEDTCB);OSStatInit();/* 任务统计完毕后,恢复其它任务 */      tx_thread_resume(&AppTaskRedLedTCB);tx_thread_resume(&AppTaskGreenLEDTCB);/* 内核开启后,恢复HAL里的时间基准 */HAL_ResumeTick();while (1){ sysHeartBeat++;tx_thread_sleep(1000);}
}/*红灯闪烁控制*/
static void AppTaskRedLED(ULONG thread_input)
{(void)thread_input;while(1){HAL_GPIO_TogglePin(GPIOI,GPIO_PIN_8);tx_thread_sleep(500);}  
}/*绿灯闪烁控制*/
static void AppTaskGreenLED(ULONG thread_input)
{(void)thread_input;while(1){       HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_13);tx_thread_sleep(2000);}
}/*统计任务函数*/
static void AppTaskStat(ULONG thread_input)
{(void)thread_input;while (OSStatRdy == false){tx_thread_sleep(200);     /* 等待统计任务就绪 */}OSIdleCtrMax /= 100uL;if (OSIdleCtrMax == 0uL){OSCPUUsage = 0u;}OSIdleCtr = OSIdleCtrMax * 100uL;  /* 设置初始CPU利用率 0% */for (;;){OSIdleCtrRun = OSIdleCtr;    /* 获得100ms内空闲计数 */OSIdleCtr    = 0uL;          /* 复位空闲计数 */OSCPUUsage   = (100uL - (float)OSIdleCtrRun / OSIdleCtrMax);tx_thread_sleep(100);        /* 每100ms统计一次 */}
}/*空闲任务函数*/
static void AppTaskIDLE(ULONG thread_input)
{    TX_INTERRUPT_SAVE_AREA(void)thread_input;while(1){TX_DISABLEOSIdleCtr++;TX_RESTORE}                          
}

实现了上面这些函数后,我们一个基于ThreadX的最基础的系统就建立起来了,对于更复杂的系统也没有问题,其实现的基本思路也是与此相同的。

4、最后测试

完成前述的全部内容后,我们编译下载到目标平台,两个指示灯按照我们的预期正常闪烁,说明的们的移植是成功的。

事实上ThreadX的移植相对简单,接下来我们总结一下移植ThreadX的步骤。我们觉得大体可分为如下过程进行:

首先,将ThreadX的文件及引用,包括内核文件和接口文件,添加到我们的项目中,并做好相关的项目配置。

其次,将 tx_api.h 文件包含于所有使用 ThreadX 服务和数据结构的应用程序。如前面我们将其包含在主函数文件中。

然后,在主函数中调用 tx_kernel_enter函数以达到启动ThreadX内核的目的。如果没有经过ThreadX特定的初始化,可以通过增加其优先权而进入到内核中。

再其次,建立 tx_application_define 函数。这是初始系统资源创建的地方。这些资源包括线程、队列、内存缓冲池、事件标志组以及信号。

最后,编译下载到目标平台测试。

欢迎关注:

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/499356.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

外设驱动库开发笔记12:TSEV01CL55红外温度传感器驱动

有时候我们需要检测一些无法直接接触的器件的温度。为了实现这一需求,我们通常会选择红外温度传感器来实现这一功能。考虑到复用的问题,我们一般会将操作元器件的代码抽象为驱动函数以备调用。这里我们就来设计并实现TSEV01CL55红外温度传感器的驱动。 …

FreeRTOS应用开发笔记之一:FreeRTOS在STM32的移植

FreeRTOS是如今在小型嵌入式领域应用比较广泛的一种实时操作系统。它是一种开源且免费的操作系统,而且移植和使用都非常的简单。在这里我们将学习并移植FreeRTOS。 1、必要的准备 工欲善其事,必先利其器,在开始学习和移植之前,相…

外设驱动库开发笔记13:MLX90614红外温度传感器驱动

红外温度传感器一般用于非接触式的温度检测。在我们的系统中经常会有这样的需求。所以我们将其设计为通用的驱动库以备复用。这一篇我们将讲述MLX90614红外温度传感器驱动的设计与实现。 1、功能概述 MLX90614是一种红外温度计,用于非接触式温度测量。红外测温是根…

Modbus协议栈应用实例之三:Modbus TCP客户端应用

自从开源了我们自己开发的Modbus协议栈之后,有很多朋友建议我针对性的做几个示例。所以我们就基于平时我们的应用整理了几个简单但可以说明基本的应用方法的示例,这一篇中我们将解说如何使用协议栈实现一个Modbus TCP客户端。 1、何为TCP客户端 Modbus…

Modbus协议栈应用实例之四:ModbusTCP服务器应用

自从开源了我们自己开发的Modbus协议栈之后,有很多朋友建议我针对性的做几个示例。所以我们就基于平时我们的应用整理了几个简单但可以说明基本的应用方法的示例,这一篇中我们来简述如何使用协议栈实现一个Modbus TCP服务器应用。 1、何为TCP服务器 Mo…

外设驱动库开发笔记14:DS18B20温度变送器驱动

在一时候我们需要相对简单的检测温度信号,而DS18B20就是一款功能和应用都相对简单的温度传感器,通过单线就可以实现检测温度信号的需求。这一篇我们就来实现操作DS18B20获取温度数据的驱动。 1、功能概述 DS18B20是常用的数字温度传感器,其…

Modbus协议栈应用实例之五:Modbus ASCII主站应用

自从开源了我们自己开发的Modbus协议栈之后,有很多朋友建议我针对性的做几个示例。所以我们就基于平时我们的应用整理了几个简单但可以说明基本的应用方法的示例,这一篇中我们来使用协议栈实现Modbus ASCII主站应用。 1、何为ASCII主站 我们知道Modbus…

Modbus协议栈应用实例之六:Modbus ASCII从站应用

自从开源了我们自己开发的Modbus协议栈之后,有很多朋友建议我针对性的做几个示例。所以我们就基于平时我们的应用整理了几个简单但可以说明基本的应用方法的示例,这一篇中我们来使用协议栈实现Modbus ASCII从站应用。 1、何为ASCII从站 我们知道Modbus…

外设驱动库开发笔记15:DHT11温湿度传感器驱动

与DS18B20一样DHT11也是采用单总线,但所不同的是DHT11可同时实现温度和湿度的检测。在我们的产品中经常使用它来检测环境的温湿度信息。这一篇我们将设计并封装DHT11的驱动程序,以方便重复使用。 1、功能概述 DHT11数字温湿度传感器是一款含有已校准数…

外设驱动库开发笔记16:MS5536C压力变送器驱动

压力检测也是经常会遇到的需求,比如环境压力或者低压气体等都会用到压力检测。这类检测压力都比较低,一般不会超过大气压,有时甚至是负压。这一篇我们要讨论的MS5536C就属于这类器件。接下来我们将设计并实现MS5536C的驱动。 1、功能概述 M…

外设驱动库开发笔记17:MS5803压力变送器驱动

很多时候我们需要检测被控对象的绝对压力,而且在我们的多款产品中也有这样的需求。当然检测绝对压力的传感器有很多,我们经常使用MS5803来实现压力检测。本篇中我们将设计并实现MS5803系列压力传感器的驱动。 1、功能概述 MS5803系列产品包含压阻传感器…

通讯接口应用笔记1:RS485通讯上下拉电阻的选择

RS485是一种常见的通讯接口方式,在我们的实际产品中也是多次使用。但我们平常并不会去过多考虑某一实现的细节问题,不过最近我们遇到了一个因如上下拉电阻的选择问题而造成的通讯故障,所以在这一片中我们来讨论一下RS485总线上下拉电阻的选择…

外设驱动库开发笔记18:MS5837压力变送器驱动

绝对压力的检测是常见的需求。在我们的系统中也常常会遇到。而MS5837压力传感器也是我们进场会采用的方案。在这篇里我们将讨论并实现MS5837压力传感器的驱动。 1、功能概述 MS5837压力传感器是一种可用于电路板上,适用于检测10-1200mbar压力范围的传感器&#xf…

外设驱动库开发笔记19:BMP280压力温度传感器驱动

压力和温度监测在嵌入式系统开发中是非常常见的需求,特别是对环境大气压力和温度的检测需求就更常见了。我们一般都会选择一些封装较小操作比较方便的压力传感器。BMP280就是满足这一要求的器件。在这一篇中我们将设计并实现BMP280的驱动。 1、功能概述 BMP280是一…

一个简单的空气质量数据监测站项目

大气质量数据监测站用于测试空气质量监测及数据采集,实现野外或者室内空气质量的检测。并通过网络将数据上传到OneNet​显示。​ 1、项目概述 本项目是一个定制项目,要求采集大气的压力、温度、湿度、PM25、位置等数据并上传到指定的后台服务器。但有时…

一个基于STM32实现的多组分气体分析仪项目

本篇将简要的总结一下一个基于STM32F412ZG实现的多组分气体分析仪的项目。简要描述该项目的软硬件设计及其验证。 一、项目概述 多组分气体分析仪是我公司近期研发的三个主要产品之一。采用模块化设计,可增减配置,可分析混合气体中的氧气、氢气、甲烷、…

外设驱动库开发笔记20:BME280压力湿度温度传感器驱动

嵌入式产品开发中,我们常常会有检测环境温度、压力、湿度的需求。如果有一个集成有这3个传感器的元件,无疑将是很方便的。博世的BME280就能实现这一要求。在这一篇中我们将讨论BME280的驱动设计与实现。 1、功能概述 BME280是一款专为移动应用而开发的…

外设驱动库开发笔记21:BME680环境传感器驱动

环境传感器是一类我们很常用的传感器。它可以方便我们获取压力、温度、湿度以及空气质量等数据。在这一篇中,我们将分析BME680环境传感器的功能,并设计和实现BME680环境传感器的驱动。 1、功能概述 BME680是一款专为移动应用和可穿戴设备开发的集成环境…

外设驱动库开发笔记22:ADXL345三轴数字加速度计驱动

移动设备的广泛应用增加对移动过程中各种参数的检测需求。ADXL345三轴数字加速度计可以用来检测加速度、进而测量倾斜角度等。在这一篇中,我们将讨论ADXL345三轴数字加速度计驱动程序的设计与实现。 1、功能概述 ADXL345是一款小而薄的超低功耗3轴加速度计&#x…

外设驱动库开发笔记23:AT24Cxx外部存储器驱动

在我们的应用开发过程中,经常会使用到外部的EEPROM外部存储器来保存一些参数和配置数据等。而比较常用的就是AT24Cxx系列产品,这一节我们来开发用于操作AT24Cxx系列产品的驱动。 1、功能概述 AT24Cxx系列EEPROM包括从1Kbit到2Mbit的各种容量。AT24Cxx系…