ThreadX信号量是如何工作的?

ThreadX提供32位计数信号量,其值范围在0到4294967295之间。有两种用于计数信号量的操作:tx_semaphore_get和tx_semaphole_put。get操作将信号量减少一。如果信号量为0,则获取操作不成功。get运算的逆运算是put运算。它将信号量增加一。每个计数信号量都是一个公共资源。ThreadX对计数信号量的使用方式没有任何限制。计数信号量通常用于互斥。然而,计数信号量也可以用作事件通知的方法。

1.目标

本文的目的是通过一个关于信号量如何工作的工作示例提供一个简短的解释

尽管该示例使用NUCLEO-H223ZG,但您可以对其他基于STM32H7的板使用相同的步骤。主要区别通常是引脚输出和时钟配置。
本文将向您展示如何从头开始一个项目,目标是获得具有相同优先级的3个线程,以展示计数信号量的使用,从而在收到两次信号量令牌时仅打印给定的消息。在这个演示中,Thread3将等待,直到Thread1和Thread2使用tx_semaphore_put函数来同步printf消息
演示框图如下图所示:

2.STM32CubeIDE–逐步演示:

启动STM32CubeIDE(1.7.0使用的版本)

选择您喜欢的工作区路径,单击启动

在信息中心,选择启动新的STM32项目
对于这个演示,我们将使用NUCLEO-H223ZG作为起点:

为您的项目命名,只需记住避免使用空格和特殊字符——如果您缺少任何库,STM32CubeIDE将立即自动开始下载


 

一个弹出窗口询问我们是否应该在默认模式下初始化外围设备,单击“是”。然后第二个弹出窗口要求打开设备配置透视图,再次单击“是”

添加软件包的时间:

在组件选择窗口中浏览并定位AzureRTOS包:

打开RTOS ThreadX并选中Core(核心)框,然后单击OK(确定)

这将在类别的下部添加软件包。现在,通过单击它,您可以添加RTOS ThreadX框,这将显示AzureRTOS应用程序的配置选项

由于这个演示只是为了创建小的打印来指示它是哪个线程,所以默认设置是可以的,但我们确实需要做另一个修改。默认情况下,HAL驱动程序将使用Systick作为其主要时基,但此计时器应仅留给AzureRTOS。为了防止这种冲突,我们可以简单地为HAL选择一个不同的时基,方法是点击System Core/SYS并选择时基Source作为TIM6:

最后一个修改是,为了防止生成太多未使用的代码,我们可以决定不创建USB和以太网的功能,因为它们是在我们基于板创建设计时本地添加的,这可以通过删除它们的生成代码来完成:

一切就绪,我们可以按Alt+K生成代码,按Ctrl+S保存代码:


 

最后一部分是创建我们的演示代码。Open Core\Src\app_threadx.c–在这里,我们将使用命令tx_thread_create和信号量,通过使用tx_semaphore_create来创建3个线程。

所有3个线程都很简单,其中Thread1和Thread2睡眠2秒,然后在无休止的循环中使用tx_semaphore_put。Thread3将连续两次使用tx_semaphore_get,以等待两个线程共享其信号量令牌,然后才能使用printf。您可以复制并粘贴以下代码-请使用“USER code BEGIN”作为参考粘贴生成的代码:

/*用户代码开始包含*/
#包括“stdio.h”
/*用户代码结束包括*/

/*用户代码开始PD*/
#定义THREAD_STACK_SIZE 512
/*用户代码端PD*/

/*用户代码开始PV*/
uint8_t thread_stack1[thread_STACK_SIZE];
uint8_t thread_stack2[thread_STACK_SIZE];
uint8_t thread_stack3[thread_STACK_SIZE];
TX_THREAD螺纹_ptr1;
TX_THREAD螺纹_ptr2;
TX_THREAD螺纹_ptr3;
TX_SEMAPHORE信号量_0;
/*用户代码端PV*/

/*用户代码开始PFP*/
无效线程1(ULONG initial_input);
无效线程2(ULONG initial_input);
无效线程3(ULONG initial_input);
/*用户代码端PFP*/

/*用户代码开始应用程序线程初始化*/
tx_semaphore_create(&ssemaphore_0,“信号量0”,1);
tx_thread_create(&thread_ptr1,“Thread1”,Thread1,0,thread_stack1,thread_STACK_SIZE,15,15,1,tx_AUTO_START);
tx_thread_create(&thread_ptr2,“Thread2”,Thread2,0,thread_stack2,thread_STACK_SIZE,15,15,1,tx_AUTO_START);
tx_thread_create(&thread_ptr3,“Thread3”,Thread3,0,thread_stack3,thread_STACK_SIZE,15,15,1,tx_AUTO_START);
/*用户代码结束应用程序线程初始化*/

/*用户代码开始1*/
无效线程1(ULONG initial_input){
/*无限循环*/
对于(;){
/*睡眠2秒*/
tx_thread_sleep(200);
printf(“线程1释放计数信号量\r\n”);
/*释放信号灯*/
tx_semaphore_put(&semaphore_0);
}
}
无效线程2(ULONG initial_input){
对于(;){
/*睡眠2秒*/
tx_thread_sleep(200);
printf(“Thread2释放计数信号量\r\n”);
/*释放信号灯*/
tx_semaphore_put(&semaphore_0);
}
}
无效线程3(ULONG initial_input){
对于(;){
/*拿悬挂的信号灯*/
tx_semaphore_get(&semaphore_0,tx_WAIT_FOREVER);
/*拿悬挂的信号灯*/
tx_semaphore_get(&semaphore_0,tx_WAIT_FOREVER);
printf(“线程3已同步\r\n”);
}
}
/*用户代码端1*/

打开Core\Src\main.c–您可以复制并粘贴下面的代码,但请注意“用户代码开始”,以正确放置代码片段:

/*私人用户代码---------------------------------------------------------*/
/*用户代码以0开头*/
#定义USE_UART_PRINT
无效__io_putchar(字符ch){
#ifdef使用_启动_打印
//在UART上写入字符“ch”的代码
HAL_UART_Transmit(&huart3,(uint8_t*).ch,1,10);
#其他
ITM_SendChar(ch);
#结束语
}
/*用户代码结束0*/

从这一点开始,按Ctrl+B进行构建,您应该会看到0个错误和0个警告。

将板连接到计算机并进入调试模式,方法是首先单击项目名称,然后菜单“运行/调试为/STM32”:

在“编辑配置”窗口中,单击“调试”:

进入“调试”透视图后,单击简历即可享受ThreadX应用程序的semapahore工作。您也可以使用窗口/显示视图/ThreadX/ThreadX线程列表,并对ThreadX信号量t再次执行此操作o增强整体调试选项(一旦应用程序运行一段时间并暂停,线程列表将自动加载):

现在,最后一步是启用STM32CubeIDE中的控制台来查看打印消息。找到“控制台”选项卡,然后单击命令行外壳控制台:


在弹出窗口中,将“连接类型”更改为“串行端口”,然后单击“新建…”添加与STLINK相关的VCOM:


您可以给端口一个名称,这样以后也更容易使用,在这种情况下,它将被标记为NUCLEO_H723ZI:


单击finish(完成),控制台上的消息将指示它现在已连接,通过恢复应用程序,这是打印显示:


3.有用的链接:

  • STM32管:https://www.st.com/en/development-tools/stm32cubeide.html

  • 核子-H723ZG:https://www.st.com/en/evaluation-tools/nucleo-h723zg.html

  • ThreadX用户指南https://docs.microsoft.com/en-us/azure/rtos/threadx/about-this-guide

  • ThreadX API描述https://docs.microsoft.com/en-us/azure/rtos/threadx/chapter3#counting-信号量
  • Github:https://github.com/STMicroelectronics/x-cube-azrtos-h7

4.结论:

向ThreadX应用程序添加信号量包括:

    • 在STM32CubeMX/STM32CubeIDE中添加ThreadX组件

    • 从STM32CubeMX/STM32CubeIDE生成代码

    • 使用ThreadX API创建ThreadX组件线程和信号量