ThreadX是非常优秀的RTOS,微软收购了ThreadX后就开源了,后来又交给Eclipse基金会,

本文讲述如何在STM32上运行ThreadX,使用CubeMX来实现。本人环境如下,
- CM4芯片:STM32F407ZGT6,内存192KB,flash是1MB,频率最高168M Hz
- OS:Win11
- CubeMX:6.12.0
- Keil:5.40.0
- 下载器:ST-Link V2
一 搭建工程
使用CubeMX搭建工程,打开CubeMX后点击ACCESS TO MCU SELECTOR,

在新界面里输入STM32F407ZGT6,

此时在右侧会显示出MCU信息,这里点击第一个,然后点击右上角的Start Project来创建工程,
![![[Pasted image 20240818081628.png]]](https://i-blog.csdnimg.cn/direct/0c45b1c903f6428d948fb74711d6b02c.png)
设置时钟
在新界面里的Pinout & Configuration里选择RCC,然后高速时钟和低速时钟都选择Crystal/Ceramic Resonator
![![[Pasted image 20240818082030.png]]](https://i-blog.csdnimg.cn/direct/22dd07514732453b9399a853a12fc5e3.png)
PS:启明欣欣的板子,其外部接了2个晶振,一个8M,一个32.768K,前者用于高速时钟,后者用于低速时钟,如果低速时钟没有外接晶振,那么就选disable。
此时点击Clock Configuration,
![![[Pasted image 20240818082621.png]]](https://i-blog.csdnimg.cn/direct/bda17be21fd74ae7adaed487cce46cbf.png)
然后按照红框里的步骤一个个更改,
![![[Pasted image 20240818082811.png]]](https://i-blog.csdnimg.cn/direct/99d897f0cc484fa39c043abd6ee88e05.png)
第4步输入频率后回车,CubeMX会自动调整,非常方便。另外,可以看到低速时钟LSE的输入频率是32.768K,这个也可以修改,需要结合实际。
设置SYS
回到Pinout & Configuration,然后点击SYS,右侧的选项按照如下进行选择,
![![[Pasted image 20240818083603.png]]](https://i-blog.csdnimg.cn/direct/82e431b5b64147c5a54e9fc7e653d2a9.png)
设置输出引脚
开发板上有3个LED,如下,
![![[Pasted image 20240817223755.png]]](https://i-blog.csdnimg.cn/direct/2560a29c38e04885a14e2a1744ed70b9.png)
LED连接在MCU的引脚上,连接情况如下,
- LED0 ---- PE3
- LED1 ---- PE4
- LED2 ---- PG9
当引脚输出低电平灯就会点亮,输出高电平灯就会熄灭。美中不足的地方是这3个灯都是红灯,如果是不同颜色的灯就更好了。
回到Pinout & Configuration,然后在右侧的Pinout view里找到这三个引脚并设置为输出,可以在右下角的搜索框里输入引脚名,软件会自动定位到这个引脚,例如输入PE3,
![![[Pasted image 20240818083706.png]]](https://i-blog.csdnimg.cn/direct/d585b7299c7f473f8e7804897fd2309c.png)
PE3对应的引脚会闪烁黑色,单击该引脚会弹出配置选项,这里选择GPIO_Output,
![![[Pasted image 20240818084404.png]]](https://i-blog.csdnimg.cn/direct/96b8477cf21846169db48d79606a9fd5.png)
同理把PE4和PG9也配置为输出,然后点击左侧的GPIO,在右侧选择PE3,最后在下面的配置里把PE3设置为默认输出高,上拉,中等速度。
![![[Pasted image 20240818084819.png]]](https://i-blog.csdnimg.cn/direct/0aa877cca4f343f9a19a0cecbe1bb8d4.png)
同理,对PE4和PG9进行相同配置。
引入ThreadX
在Software Packs里点击Select Components,
![![[Pasted image 20240818085921.png]]](https://i-blog.csdnimg.cn/direct/4c495557ca014651852681a79c434ffa.png)
在弹出的界面里找到X-CUBE-AZRTOS-F4,如果第一次打开,右边会有个Install按钮,点击进行下载,本人因为已经下载过了,所以没有这个按钮
![![[Pasted image 20240818090045.png]]](https://i-blog.csdnimg.cn/direct/7408951765e1463287ca3a0da2a23d2e.png)
PS:如果你是别的芯片,可以根据实际情况选择对应的版本
点击后展开,然后勾选这个Core就可以了,最后点击右下角的OK,
![![[Pasted image 20240818090255.png]]](https://i-blog.csdnimg.cn/direct/a4612950edf54463bb60cbdfb5001991.png)
回到主界面后点击展开Middleware and Software Packs,然后按照如下红框步骤引入ThreadX
![![[Pasted image 20240818090441.png]]](https://i-blog.csdnimg.cn/direct/4ba318c9ae4e4b2383c7a6e52dcc93ec.png)
工程配置
点击Project Manager,然后在Project里对红框标注的地方进行自定义修改
![![[Pasted image 20240818090956.png]]](https://i-blog.csdnimg.cn/direct/4580f69eec584561912abd01cdc7d5d6.png)
接着是Code Generator,按照如下勾选,也可以根据自己需要进行修改
![![[Pasted image 20240818091151.png]]](https://i-blog.csdnimg.cn/direct/1c5f3b8a6fe54ddd830a0972ff948894.png)
设置完毕后点击右上角的GENERATE CODE来生成Keil工程
![![[Pasted image 20240818091237.png]]](https://i-blog.csdnimg.cn/direct/ab83a6420b274c56a7d5a378757f58ba.png)
二 添加任务
在源码里打开app_thread.c,这个是ThreadX留给用户添加自定义任务的源文件,
![![[Pasted image 20240817224221.png]]](https://i-blog.csdnimg.cn/direct/8cc71b3202ad47f492e9234886a0bf55.png)
打开后先在USER CODE BEGIN Includes下引入"main.h",
![![[Pasted image 20240817224420.png]]](https://i-blog.csdnimg.cn/direct/4c5a71bc77dd4b8aa5a327fdacb81bd5.png)
然后在USER CODE BEGIN PD下添加栈大小的宏定义,
![![[Pasted image 20240817224800.png]]](https://i-blog.csdnimg.cn/direct/8ac89339637b463b998769534fded12a.png)
然后在USER CODE BEGIN PV下添加栈空间数组和线程指针定义,总共3个,
![![[Pasted image 20240817224930.png]]](https://i-blog.csdnimg.cn/direct/391d0b99b8e8457795e9a588187931e4.png)
接着在USER CODE BEGIN PFP里添加任务的函数声明,
![![[Pasted image 20240817225226.png]]](https://i-blog.csdnimg.cn/direct/f4ba7737a20946f4ab61826b6b588781.png)
接着在函数App_ThreadX_Init里使用tx_thread_create来创建三个任务,
![![[Pasted image 20240817225355.png]]](https://i-blog.csdnimg.cn/direct/01f542a44747492fadd32d03fdad8b6f.png)
最后是在USER CODE BEGIN 1里添加任务函数的定义,
![![[Pasted image 20240817225918.png]]](https://i-blog.csdnimg.cn/direct/3c31e399668246109b1f15ad029e7684.png)
三个函数里都有一个while循环,循环里以不同的间隔来闪烁LED灯。
最后完整源文件内容如下,
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file app_threadx.c
* @author MCD Application Team
* @brief ThreadX applicative file
******************************************************************************
* @attention
*
* Copyright (c) 2021 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "app_threadx.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "main.h"
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
#define THREAD_STACK_SIZE 1024
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
UCHAR thread_stack1[THREAD_STACK_SIZE];
TX_THREAD thread_ptr1;
UCHAR thread_stack2[THREAD_STACK_SIZE];
TX_THREAD thread_ptr2;
UCHAR thread_stack3[THREAD_STACK_SIZE];
TX_THREAD thread_ptr3;
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
/* USER CODE BEGIN PFP */
VOID LED1_thread_entry(ULONG thread_input);
VOID LED2_thread_entry(ULONG thread_input);
VOID LED3_thread_entry(ULONG thread_input);
/* USER CODE END PFP */
/**
* @brief Application ThreadX Initialization.
* @param memory_ptr: memory pointer
* @retval int
*/
UINT App_ThreadX_Init(VOID *memory_ptr)
{
UINT ret = TX_SUCCESS;
TX_BYTE_POOL *byte_pool = (TX_BYTE_POOL*)memory_ptr;
/* USER CODE BEGIN App_ThreadX_Init */
(void)byte_pool;
tx_thread_create(&thread_ptr1, "LED1_thread", LED1_thread_entry, 0, thread_stack1, THREAD_STACK_SIZE, 15, 15, 1, TX_AUTO_START);
tx_thread_create(&thread_ptr2, "LED2_thread", LED2_thread_entry, 0, thread_stack2, THREAD_STACK_SIZE, 15, 15, 1, TX_AUTO_START);
tx_thread_create(&thread_ptr3, "LED3_thread", LED3_thread_entry, 0, thread_stack3, THREAD_STACK_SIZE, 15, 15, 1, TX_AUTO_START);
/* USER CODE END App_ThreadX_Init */
return ret;
}
/**
* @brief MX_ThreadX_Init
* @param None
* @retval None
*/
void MX_ThreadX_Init(void)
{
/* USER CODE BEGIN Before_Kernel_Start */
/* USER CODE END Before_Kernel_Start */
tx_kernel_enter();
/* USER CODE BEGIN Kernel_Start_Error */
/* USER CODE END Kernel_Start_Error */
}
/* USER CODE BEGIN 1 */
VOID LED1_thread_entry(ULONG thread_input)
{
while(1)
{
HAL_GPIO_TogglePin(GPIOE, GPIO_PIN_3);
HAL_Delay(100);
}
}
VOID LED2_thread_entry(ULONG thread_input)
{
while(1)
{
HAL_GPIO_TogglePin(GPIOE, GPIO_PIN_4);
HAL_Delay(300);
}
}
VOID LED3_thread_entry(ULONG thread_input)
{
while(1)
{
HAL_GPIO_TogglePin(GPIOG, GPIO_PIN_9);
HAL_Delay(500);
}
}
/* USER CODE END 1 */
三 编译和运行
使用Keil进入编译,编译器记得选成6.x系列的,
![![[Pasted image 20240817230313.png]]](https://i-blog.csdnimg.cn/direct/4a25ff36db314dd5af77d44b05085e9c.png)
为了加快编译速度,取消Browse Information的勾选,
![![[Pasted image 20240817230348.png]]](https://i-blog.csdnimg.cn/direct/c761544c817c4d1585a5c421cde18140.png)
编译完毕后把程序烧录到开发板上,然后复位一下,就可以发现3个LED灯以不同的频率进行闪烁。

















