FreeRTOS教程5 信号量

这篇具有很好参考价值的文章主要介绍了FreeRTOS教程5 信号量。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1、准备材料

正点原子stm32f407探索者开发板V2.4

STM32CubeMX软件(Version 6.10.0)

Keil µVision5 IDE(MDK-Arm)

野火DAP仿真器

XCOM V2.6串口助手

一个滑动变阻器

2、学习目标

本文主要学习 FreeRTOS 信号量的相关知识,包括创建/删除信号量、释放信号量、获取信号量等知识

3、前提知识

3.1、信号量概述

信号量是进程间用于通信的一种手段,其是基于队列实现的,信号量更适用于进程间同步,信号量包括二值信号量(Binary Semaphores)和计数信号量(Counting Semaphores)

二值信号量就是只有一个项的队列,该队列不为空则为满(所谓二值),二值信号量就像一个标志,适和用于进程间同步的通信

举个例子:ADC 的周期采集中断负责采集完成后将采集到的 ADC 的值写入数据缓存区中并且释放信号量,总是尝试获取信号量的数据处理任务在 ADC 采集中断释放信号量之后成功获取,然后退出阻塞状态对写入数据缓存区中采集到的 ADC 值进行处理,上述过程如下图所示:(注释1)

如下图所示为使用二值信号量来同步任务和中断的工作流程 (注释2)

计数信号量就是有固定长度的队列,队列中每个单元都是一个标志,其通常用于对多个共享资源的访问进行控制

举个例子:一家餐馆有 4 张可供用餐的桌子,我们创建一个长度为 4 ,初值为 4 的计数信号量来表示当前可供用餐的桌子数量,当有客人进来用餐时会 “获取” 一张餐桌,这时用来表示可供用餐的桌子数量的计数信号量就会减少一个,当有客人离开时会 “释放” 一张餐桌,这时用来表示可供用餐的桌子数量的计数信号量就会增加一个,上述过程如下图所示:

如下图所示为计数信号量的工作流程

3.2、创建信号量

信号量在使用之前也必须先创建,信号量被创建完之后是无效的,也即为 0 ,而由于信号量分为二值信号量和计数信号量两种,因此FreeRTOS也提供了不同的API函数,具体如下所述

/**
  * @brief  动态分配内存创建二值信号量函数
  * @param  xSemaphore:创建的二值信号量句柄
  * @retval None
  */
void vSemaphoreCreateBinary(SemaphoreHandle_t xSemaphore);

/**
  * @brief  静态分配内存创建二值信号量函数
  * @param  pxSemaphoreBuffer:指向一个StaticSemaphore_t类型的变量,该变量将用于保存信号量的状态
  * @retval 返回创建成功的信号量句柄,如果返回NULL则表示因为pxSemaphoreBuffer为空无法创建
  */
SemaphoreHandle_t xSemaphoreCreateBinaryStatic(
									StaticSemaphore_t *pxSemaphoreBuffer);

/**
  * @brief  动态分配内存创建计数信号量函数
  * @param  uxMaxCount:可以达到的最大计数值
  * @param  uxInitialCount:创建信号量时分配给信号量的计数值
  * @retval 返回创建成功的信号量句柄,如果返回NULL则表示内存不足无法创建
  */
SemaphoreHandle_t xSemaphoreCreateCounting(UBaseType_t uxMaxCount, 
										   UBaseType_t uxInitialCount);

/**
  * @brief  静态分配内存创建计数信号量函数
  * @param  uxMaxCount:可以达到的最大计数值
  * @param  uxInitialCount:创建信号量时分配给信号量的计数值
  * @param  pxSempahoreBuffer:指向StaticSemaphore_t类型的变量,该变量然后用于保存信号量的数据结构体
  * @retval 返回创建成功的信号量句柄,如果返回NULL则表示因为pxSemaphoreBuffer为空无法创建
  */
SemaphoreHandle_t xSemaphoreCreateCountingStatic(
									UBaseType_t uxMaxCount,
									UBaseType_t uxInitialCount,
									StaticSemaphore_t pxSempahoreBuffer);

3.3、释放信号量

以下两个函数不仅仅可以用于释放二值信号量,还可以用于释放计数信号量和互斥量,具体如下所示

/**
  * @brief  释放信号量函数
  * @param  xSemaphore:要释放的信号量的句柄
  * @retval 如果信号量释放成功,则返回pdTRUE;如果发生错误,则返回pdFALSE
  */
BaseType_t xSemaphoreGive(SemaphoreHandle_t xSemaphore);

/**
  * @brief  释放信号量的中断安全版本函数
  * @param  pxHigherPriorityTaskWoken:用于通知应用程序编写者是否应该执行上下文切换
  * @retval 如果成功给出信号量,则返回pdTRUE,否则errQUEUE_FULL
  */
BaseType_t xSemaphoreGiveFromISR(SemaphoreHandle_t xSemaphore, 
								 BaseType_t *pxHigherPriorityTaskWoken);

3.4、获取信号量

以下两个函数不仅仅可以用于获取二值信号量,还可以用于获取计数信号量和互斥量,具体如下所示

/**
  * @brief  获取信号量函数
  * @param  xSemaphore:正在获取的信号量的句柄
  * @param  xTicksToWait:等待信号量变为可用的时间
  * @retval 成功获得信号量则返回pdTRUE;如果xTicksToWait过期,信号量不可用,则返回pdFALSE
  */
BaseType_t xSemaphoreTake(SemaphoreHandle_t xSemaphore, TickType_t xTicksToWait);
/**
  * @brief  获取信号量的中断安全版本函数
  * @param  xSemaphore:正在获取的信号量的句柄
  * @param  pxHigherPriorityTaskWoken:用于通知应用程序编写者是否应该执行上下文切换
  * @retval 成功获取则返回pdTRUE,未成功获取则返回pdFALSE
  */
BaseType_t xSemaphoreTakeFromISR(SemaphoreHandle_t xSemaphore, 
								 signed BaseType_t *pxHigherPriorityTaskWoken);

3.5、删除信号量

/**
  * @brief  删除信号量,包括互斥锁型信号量和递归信号量
  * @param  xSemaphore:被删除的信号量的句柄
  * @retval None
  */
void vSemaphoreDelete(SemaphoreHandle_t xSemaphore);

3.6、工具函数

/**
  * @brief  获取信号量计数
  * @param  xSemaphore:正在查询的信号量的句柄
  * @retval 如果信号量是计数信号量,则返回信号量的当前计数值。如果信号量是二进制信号量,则当信号量可用时,返回1,当信号量不可用时,返回 0
  */
UBaseType_t uxSemaphoreGetCount(SemaphoreHandle_t xSemaphore);

4、实验一:二值信号量的应用

4.1、实验目标

  1. 创建一个二值信号量 BinarySem_ADC
  2. 配置 ADC1 IN5 在 500ms 的定时器驱动下周期采集 ADC 值,采集完成后将采集值写入缓存数组,然后释放二值信号量 BinarySem_ADC
  3. 创建一个任务 TASK_ADC,该任务总是尝试获取二值信号量 BinarySem_ADC,获取成功之后,将写入缓存数组的 ADC 采集值进行转换,然后通过 USART1 输出给用户

4.2、CubeMX相关配置

首先读者应按照 "FreeRTOS教程1 基础知识" 章节配置一个可以正常编译通过的 FreeRTOS 空工程,然后在此空工程的基础上增加本实验所提出的要求

本实验需要初始化 USART1 作为输出信息渠道,具体配置步骤请阅读“STM32CubeMX教程9 USART/UART 异步通信”,如下图所示

本实验需要设置 TIM3 作为 ADC1 IN5 触发源的单通道 ADC 采集,采集周期为 500ms ,因此需要配置 ADC1 和 TIM3,感兴趣读者可以阅读”STM32CubeMX教程13 ADC - 单通道转换“实验,如下图所示

由于我们将要在 ADC 采集完成中断中使用 FreeRTOS 的释放二值信号量的函数,因此需要将其优先级设置在 15~5 之间,在这里设置为 7,具体如下图所示

单击 Middleware and Software Packs/FREERTOS,在 Configuration 中单击 Tasks and Queues 选项卡双击默认任务修改其参数,具体如下图所示

然后在 Configuration 中单击 Timers and Semaphores ,在 Binary Semaphores 中单击 Add 按钮新增加一个名为 BinarySem_ADC 的二值信号量,具体如下图所示

配置 Clock Configuration 和 Project Manager 两个页面,接下来直接单击 GENERATE CODE 按钮生成工程代码即可

4.3、添加其他必要代码

按照 “STM32CubeMX教程9 USART/UART 异步通信” 实验 “6、串口printf重定向” 小节增加串口 printf 重定向代码,具体不再赘述

首先应该在 freertos.c 中添加信号量的头文件,如下所述

/*freertos.c中添加头文件*/
#include "semphr.h"

然后在该文件中重新实现 ADC 采集完成中断回调函数,在该函数中获取采集完成的 ADC 值,将其保存在全局变量 adc_value 中,然后释放二值信号量 BinarySem_ADC ,如下所述

/*转换完成中断回调*/
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *hadc)
{
    /*定时器中断启动单通道转换*/
    if(hadc->Instance == ADC1)
    {
        adc_value = HAL_ADC_GetValue(hadc);
		BaseType_t highTaskWoken = pdFALSE;
		if(BinarySem_ADCHandle != NULL)
		{
			xSemaphoreGiveFromISR(BinarySem_ADCHandle, &highTaskWoken);
			portYIELD_FROM_ISR(highTaskWoken);
		}
    }
}

接下来仍然在该文件中实现任务 TASK_ADC 的函数体内容,该任务函数总是尝试获取二值信号量,一旦获取成功表示 ADC 转换完成,就将 ADC 转换完成的值变为电压值,然后通过 USART1 输出给用户显示,如下所述

/*ADC任务函数*/
void TASK_ADC(void *argument)
{
	/* USER CODE BEGIN TASK_ADC */
	/* Infinite loop */
	for(;;)
	{
		if(xSemaphoreTake(BinarySem_ADCHandle, portMAX_DELAY) == pdTRUE)
		{
			uint32_t Volt = (3300 * adc_value)>>12;
			printf("val:%d, Volt:%d\r\n", adc_value, Volt);
		}
	}
	/* USER CODE END TASK_ADC */
}

最后在 main.c 文件主函数 main() 中以中断方式启动 ADC 转换即可,如下所述

//以中断方式启动ADC1
HAL_ADC_Start_IT(&hadc1);
//启动ADC1触发源定时器TIM3
HAL_TIM_Base_Start(&htim3);

4.4、烧录验证

烧录程序,打开串口助手,可以发现每隔一段时间就会输出当前 ADC1 IN5 通道采集到的 ADC 的值,将其接入一个滑动变阻器,当滑动变阻器从一端滑动到另一端时,串口输出的采集值也在从 0 逐渐变为最大值 4095 ,整个过程串口输出信息如下图所示

5、实验二:计数信号量的应用

5.1、实验目标

  1. 创建一个计数信号量 CountingSem_Tables ,设置最大值为 5 ,初始值设为 5 ,表示饭店内初始有 5 张桌子
  2. 启动 RTC 周期唤醒中断,唤醒周期为 3s ,在 RTC 唤醒中断中释放信号量,模拟有客人离开饭店
  3. 创建任务 TASK_KEY2 ,当按键 KEY2 按下时尝试获取信号量,模拟客人进店,同时调用获取信号量计数查询函数,查询当前可用餐桌个数

5.2、CubeMX相关配置

首先读者应按照 "FreeRTOS教程1 基础知识" 章节配置一个可以正常编译通过的 FreeRTOS 空工程,然后在此空工程的基础上增加本实验所提出的要求

本实验需要初始化开发板上 KEY2 用户按键做普通输入,具体配置步骤请阅读 “STM32CubeMX教程3 GPIO输入 - 按键响应” ,注意虽开发板不同但配置原理一致,如下图所示

本实验需要初始化 USART1 作为输出信息渠道,具体配置步骤请阅读 “STM32CubeMX教程9 USART/UART 异步通信” ,如下图所示

本实验需要配置 RTC 周期唤醒中断,具体配置步骤和参数介绍读者可阅读”STM32CubeMX教程10 RTC 实时时钟 - 周期唤醒、闹钟A/B事件和备份寄存器“实验,此处不再赘述,这里参数、时钟配置如下图所示

由于需要在 RTC 周期唤醒中断中使用 FreeRTOS 的 API 函数,因此 RTC 周期唤醒中断的优先级应该设置在 15~5 之间,此处设置为 7 ,具体如下图所示

单击 Middleware and Software Packs/FREERTOS,在 Configuration 中单击 Tasks and Queues 选项卡,双击默认任务修改其参数,具体如下图所示

然后在 Configuration 中单击 Timers and Semaphores 选项卡,在最下方的 Counting Semaphores 中单击右下角的 Add 按钮增加一个计数信号量,具体如下图所示

配置 Clock Configuration 和 Project Manager 两个页面,接下来直接单击 GENERATE CODE 按钮生成工程代码即可

5.3、添加其他必要代码

按照 “STM32CubeMX教程9 USART/UART 异步通信” 实验 “6、串口printf重定向” 小节增加串口 printf 重定向代码,具体不再赘述

首先应该在 freertos.c 中添加信号量的头文件,如下所述

/*freertos.c中添加头文件*/
#include "stdio.h"
#include "semphr.h"

然后在该文件中重新实现周期唤醒回调函数,该函数用于周期释放计数信号量,具体如下所示

/*周期唤醒回调函数*/
void HAL_RTCEx_WakeUpTimerEventCallback(RTC_HandleTypeDef *hrtc)
{
	if(CountingSem_TablesHandle != NULL)
	{
		BaseType_t highTaskWoken = pdFALSE;
		//释放计数信号量
		xSemaphoreGiveFromISR(CountingSem_TablesHandle, &highTaskWoken);
		portYIELD_FROM_ISR(highTaskWoken);
	}
}

最后仍然在该文件中实现任务 TASK_KEY2 ,该任务负责当按键 KEY2 按下时尝试获取计数信号量,当无任何按键按下时不断输出当前计数信号量可用数量,具体如下所示

void TASK_KEY2(void *argument)
{
  /* USER CODE BEGIN TASK_KEY2 */
  /* Infinite loop */
  for(;;)
  {
	if(HAL_GPIO_ReadPin(KEY2_GPIO_Port,KEY2_Pin) == GPIO_PIN_RESET)
	{
		//获取计数信号量
		BaseType_t result = xSemaphoreTake(CountingSem_TablesHandle, pdMS_TO_TICKS(100));
		if(result == pdTRUE) printf("Check In OK\r\n");
		else printf("Check In Fail\r\n");
		//按键消抖
		osDelay(pdMS_TO_TICKS(300));
	}
	else
	{
		UBaseType_t AvailableTables = uxSemaphoreGetCount(CountingSem_TablesHandle);
		printf("Now AvailableTables is : %d\r\n", (uint16_t)AvailableTables);
		osDelay(pdMS_TO_TICKS(10));	
	}
  }
  /* USER CODE END TASK_KEY2 */
}

5.4、烧录验证

烧录程序,打开串口助手,发现不断输出当前可用计数信号量数量,当按住按键 KEY2 不松开,连续模拟客人进店,可以发现在模拟 3 个客人进店之后,剩余可用计数信号量的数量变为了 2 个,然后每隔一段时间计算信号量的数量慢慢增加直到最大值 5 ,接着按住按键 KEY2 不松开,连续模拟 5 个客人进店,当 5 个客人进店之后,再次按下 KEY2 按键可以发现串口输出 ”Check In Fail“,表示当前已无剩余可用计数信号量,上述整个过程如下图所示

6、注释详解

注释1:图片来源于 STM32Cube高效开发教程(高级篇) 第五章

注释2:图片来源于 Mastering_the_FreeRTOS_Real_Time_Kernel-A_Hands-On_Tutorial_Guide.pdf

参考资料

STM32Cube高效开发教程(基础篇)

Mastering_the_FreeRTOS_Real_Time_Kernel-A_Hands-On_Tutorial_Guide.pdf文章来源地址https://www.toymoban.com/news/detail-840147.html

到了这里,关于FreeRTOS教程5 信号量的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • FreeRTOS的二值信号量和互斥量之间的区别和联系

    目录 1. 二值信号量和互斥量的作用 1.1 二值信号量的作用 1.2 互斥量的作用 2. FreeRTOS二值信号量和互斥锁关系 2.1 不同点 2.2 相同点 3. 如何选择 3.1 根据场景选择合适的同步机制 3.2 根据优先级设计同步机制 FreeRTOS是一款广泛应用于嵌入式系统中的实时操作系统,其中信号量(

    2024年02月06日
    浏览(40)
  • STM32 CubeMX (第二步Freertos任务通信:队列、信号量、互斥量,事件组,任务通知)

    学习使用Freertos第二步 在 FreeRTOS 中,任务通信可以通过以下函数来实现: xQueueCreate() :用于创建一个消息队列。可以设置队列长度和每个消息的大小。 xQueueSend() :将一条消息发送到队列中。可以选择阻塞或非阻塞发送。 xQueueReceive() :从队列中接收一条消息。可以选择阻塞

    2024年02月11日
    浏览(45)
  • 【Linux C | 多线程编程】线程同步 | 信号量(无名信号量) 及其使用例子

    😁博客主页😁:🚀https://blog.csdn.net/wkd_007🚀 🤑博客内容🤑:🍭嵌入式开发、Linux、C语言、C++、数据结构、音视频🍭 🤣本文内容🤣:🍭介绍 🍭 😎金句分享😎:🍭你不能选择最好的,但最好的会来选择你——泰戈尔🍭 ⏰发布时间⏰: 本文未经允许,不得转发!!!

    2024年04月26日
    浏览(38)
  • 【Linux从入门到精通】信号量(信号量的原理与相关操作接口)详解

      本篇文章重点对 信号量的概念,信号量的申请、初始化、释放、销毁等操作进行讲解。同时举例把信号量应用到生产者消费者模型来理解 。希望本篇文章会对你有所帮助。 目录 一、信号量概念 1、1 什么是信号量 1、2 为什么要有信号量 1、3 信号量的PV操作 二、信号量的相

    2024年02月08日
    浏览(47)
  • 信号量

    信号量(semaphore)和信号只有一字之差,却是不同的概念, 信号量与之前介绍的IPC不同,它是一个计数器,用于实现进程间的互斥于同步 本文参考: Linux 的信号量_linux 信号量_行孤、的博客-CSDN博客 【Linux】Linux的信号量集_Yngz_Miao的博客-CSDN博客 Linux进程间通信(九)——信

    2024年02月12日
    浏览(52)
  • linux信号量

    通过学习linux的信号量,对linux的信号量进行了编程。

    2024年02月10日
    浏览(43)
  • uCOSii信号量

    uCOSii 信号量 主要用来测试使用uCOSii“创建信号量,发送信号量,接收信号量,删除信号量”。 学习uCOSii一定要先了解os_cfg.h文件。 信号量管理函数如下: OSSemAccept () 无条件地等待请求一个信号量函数,中断服务子程序只能用OSSemAccept()而不能用OSSemPend(),因为中断服务子程序是不

    2024年02月07日
    浏览(56)
  • linux(信号量)

    1.回顾信号量的概念 2.认识信号量对应的操作函数 3.认识一个环形队列 4.结合sem+环形队列写生产者消费者模型 --------------------------------------------------------------------------------------------------------------------------------- 1.回顾信号量的概念  每个人想进放映厅看电影,第一件事就是买票

    2024年02月11日
    浏览(42)
  • 信号量Semaphore详解

    大家应该都用过 synchronized 加锁,用来保证某个时刻只允许一个线程运行。那么如果控制某个时刻允许指定数量的线程执行,有什么好的办法呢? 答案就是JUC提供的信号量 Semaphore 。 Semaphore (信号量)可以用来限制能同时访问共享资源的线程上限,它内部维护了一个 许

    2024年02月14日
    浏览(52)
  • 【Linux】浅谈信号量

    tips:system V 是一套标准,共享内存,信号量,消息队列属于system V。 进程A和进程B进行通信时,假如进程A向物理内存的共享区写入\\\"Hello World\\\",但是当进程A写入了\\\"Hello\\\"时,进程B就向内存读取了,所以只读取到了\\\"Hello\\\",这就导致进程A想向进程B发送的信息,进程B读取不完整,

    2024年02月05日
    浏览(44)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包