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来创建工程,
设置时钟
在新界面里的Pinout & Configuration里选择RCC,然后高速时钟和低速时钟都选择Crystal/Ceramic Resonator
PS:启明欣欣的板子,其外部接了2个晶振,一个8M,一个32.768K,前者用于高速时钟,后者用于低速时钟,如果低速时钟没有外接晶振,那么就选disable。
此时点击Clock Configuration,
然后按照红框里的步骤一个个更改,
第4步输入频率后回车,CubeMX会自动调整,非常方便。另外,可以看到低速时钟LSE的输入频率是32.768K,这个也可以修改,需要结合实际。
设置SYS
回到Pinout & Configuration,然后点击SYS,右侧的选项按照如下进行选择,
设置输出引脚
开发板上有3个LED,如下,
LED连接在MCU的引脚上,连接情况如下,
- LED0 ---- PE3
- LED1 ---- PE4
- LED2 ---- PG9
当引脚输出低电平灯就会点亮,输出高电平灯就会熄灭。美中不足的地方是这3个灯都是红灯,如果是不同颜色的灯就更好了。
回到Pinout & Configuration,然后在右侧的Pinout view里找到这三个引脚并设置为输出,可以在右下角的搜索框里输入引脚名,软件会自动定位到这个引脚,例如输入PE3,
PE3对应的引脚会闪烁黑色,单击该引脚会弹出配置选项,这里选择GPIO_Output,
同理把PE4和PG9也配置为输出,然后点击左侧的GPIO,在右侧选择PE3,最后在下面的配置里把PE3设置为默认输出高,上拉,中等速度。
同理,对PE4和PG9进行相同配置。
引入ThreadX
在Software Packs里点击Select Components,
在弹出的界面里找到X-CUBE-AZRTOS-F4,如果第一次打开,右边会有个Install按钮,点击进行下载,本人因为已经下载过了,所以没有这个按钮
PS:如果你是别的芯片,可以根据实际情况选择对应的版本
点击后展开,然后勾选这个Core就可以了,最后点击右下角的OK,
回到主界面后点击展开Middleware and Software Packs,然后按照如下红框步骤引入ThreadX
工程配置
点击Project Manager,然后在Project里对红框标注的地方进行自定义修改
接着是Code Generator,按照如下勾选,也可以根据自己需要进行修改
设置完毕后点击右上角的GENERATE CODE来生成Keil工程
二 添加任务
在源码里打开app_thread.c,这个是ThreadX留给用户添加自定义任务的源文件,
打开后先在USER CODE BEGIN Includes下引入"main.h",
然后在USER CODE BEGIN PD下添加栈大小的宏定义,
然后在USER CODE BEGIN PV下添加栈空间数组和线程指针定义,总共3个,
接着在USER CODE BEGIN PFP里添加任务的函数声明,
接着在函数App_ThreadX_Init
里使用tx_thread_create
来创建三个任务,
最后是在USER CODE BEGIN 1里添加任务函数的定义,
三个函数里都有一个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系列的,
为了加快编译速度,取消Browse Information的勾选,
编译完毕后把程序烧录到开发板上,然后复位一下,就可以发现3个LED灯以不同的频率进行闪烁。