【FreeRTOS 教程 六】二进制信号量与计数信号量

目录

一、FreeRTOS 二进制信号量:

(1)二进制信号量作用:

(2)二进制信号量与互斥锁的区别:

(3)信号量阻塞时间:

(4)信号量的获取与提供:

(5)优先级在信号量中的应用: 

二、FreeRTOS 计数信号量:

(1)盘点事件:

(2)资源管理:

三、二进制信号量与计数信号量 API:

(1)动态创建二进制信号量:

(2)静态创建二进制信号量:

(3)队列机制创建信号量:

(4)动态创建计数信号量:

(5)静态创建计数信号量:

 四、信号量控制 API :

(1)获取指定的互斥锁的任务句柄:

(2)返回信号量计数:

(3)获取信号量: 

(4)获取信号量(ISR): 

(5)递归获取互斥锁信号量:

(6)释放信号量:

(7)递归释放互斥锁信号量:

(8)释放信号量(ISR):

(9)删除信号量:

五、信号量使用示例:

(1)二进制信号量:

(2)计数信号量:

六、FreeRTOS教程示例代码下载:


一、FreeRTOS 二进制信号量:

在许多情况下,任务通知可以提供二进制信号量的轻量级替代方案。

(1)二进制信号量作用:

二进制信号量用于互斥和同步目的。

(2)二进制信号量与互斥锁的区别:

二进制信号量和互斥锁极为相似,但存在一些细微差别:互斥锁包括优先继承机制,而二进制信号量则不然。因此,二进制信号量是实现同步的更好选择(任务之间或任务与中断之间),而互斥锁是实现简单互斥的更好选择。 互斥锁如何用作互斥机制的描述同样适用于二进制信号量。文章只描述使用二进制信号量进行同步。

(3)信号量阻塞时间:

信号量 API 函数允许指定阻塞时间。阻塞时间表示在尝试“获取”信号量时,如果信号量不足立即可用,任务应进入阻塞状态的最大“滴答”数。如果多个任务在同一信号量上阻塞,则具有最高优先级的任务将成为下次信号量可用时解除阻塞的任务。

(4)信号量的获取与提供:

可以把二进制信号量看作一个只能保存一个项目的队列。因此,队列只能为空或满(因此为二进制)。使用队列的任务和中断并不关心队列持有什么——它们只想知道队列是空的还是满的。可以利用这种机制来同步(例如)具有中断的任务。 

考虑使用任务为外围设备提供服务的情形。轮询外围设备将会耗费 CPU 资源,阻止执行其他任务。因此,最好让任务大部分时间处于阻塞状态(允许其他任务执行),只有在确实有事情需要执行时才执行自身。可以通过使用二进制信号量来实现,方法是“获取”信号量时使任务阻塞。然后为外围设备编写中断例程,当外围设备需要服务时,只是“提供”信号量。任务始终“接收”信号量(从队列中读取信号以使队列变空),但从不“提供”信号量。中断始终“提供”信号量(将写入队列使其为满),但从不获取信号量。另外 RTOS 任务通知,在某些情况下,它可以作为二进制信号量的更快、更轻的替代品。

(5)优先级在信号量中的应用: 

任务优先级可确保外围设备及时获得服务,进而有效生成“延迟中断”方案。(注意 FreeRTOS 还具有内置的延迟中断机制)。一种替代方法是使用队列代替信号量。完成此操作后,中断例程可以捕获与外设事件关联的数据并将其发送到任务的队列中。队列数据可用时,任务将取消阻塞,从队列中检索数据,然后执行必要的数据处理。此第二种方案要求中断尽可能短,在一个任务中进行所有后置处理。请注意,中断只能调用以“FromISR”结尾的 API 函数。

使用信号量同步任务与中断。中断仅“提供”信号量,而任务仅“获取”信号量。

二、FreeRTOS 计数信号量:

在许多情况下, “任务通知”可以提供计数信号量的轻量级替代方案。

正如二进制信号量可以被认为是长度为 1 的队列那样, 计数信号量也可以被认为是长度大于 1 的队列。同样,信号量的使用者对存储在队列中的数据并不感兴趣, 他们只关心队列是否为空。

计数信号量通常用于两种情况:

(1)盘点事件:

在此使用场景中,事件处理程序将在每次事件发生时“提供”信号量(递增信号量计数值), 而处理程序任务将在每次处理事件时“获取”信号量 (递减信号量计数值)。因此,计数值是已发生的事件数与已处理的事件数之间的差值。在这种情况下, 创建信号量时希望计数值为零

(2)资源管理:

在此使用情景中,计数值表示可用资源的数量。为了获得对资源的控制,任务必须首先获得信号量——递减信号量计数值。当计数值达到零时, 表示没有空闲资源可用。当任务结束使用资源时, 它会“返还”一个信号量——同时递增信号量计数值。在这种情况下, 创建信号量时希望计数值等于最大计数值。

三、二进制信号量与计数信号量 API:

在许多使用场景中,使用直达任务通知要比使用二进制信号量的速度更快,内存效率更高。

(1)动态创建二进制信号量:

函数原型:

SemaphoreHandle_t xSemaphoreCreateBinary(void);

作用:创建一个二进制信号量,并返回一个可以引用该信号量的句柄。

  • configSUPPORT_DYNAMIC_ALLOCATION 必须在 FreeRTOSConfig.h 中设置为 1,或处于未定义状态(默认为 1),才可使用此 RTOS API 函数。
  • 每个二进制信号量需要少量 RAM,用于保存信号量状态。如果使用 xSemaphoreCreateBinary() 创建了二进制信号量,那么所需的 RAM 将自动从 FreeRTOS 堆中分配。如果二进制信号量是使用 xSemaphoreCreateBinaryStatic() 创建的,则 RAM 由应用程序写入器提供,这需要一个附加参数,但允许 RAM 在编译时进行静态分配。
  • 信号量是在“空”状态下创建的,这意味着必须先用 xSemaphoreGive() API 函数给出信号量,然后才能使用 xSemaphoreTake() 函数来获取(获得)该信号量
  • 二进制信号量和互斥锁非常相似,但有一些细微差异:互斥锁具有优先级继承机制,但二进制信号量没有。因此,二进制信号量是实现同步的更好选择(任务之间或任务与中断之间),而互斥锁是实现简单互斥的更好选择。
  • 二值信号量并不需要在得到后立即释放,因此,任务同步可以通过一个任务/中断持续释放信号量而另外一个持续获得信号量来实现。请注意,使用直达任务通知往往可以更有效地实现相同的功能。
  • 如果另一个优先级更高的任务试图获得相同的互斥锁,那么“获取”互斥锁的任务的优先级就有可能被提高。拥有互斥锁的任务“继承”了试图“获取”相同互斥锁任务的优先级,这意味着必须始终“返回”互斥锁,否则优先级较高的任务将永远无法获得互斥锁,而优先级较低的任务将永远无法“取消继承”优先级。
  • 互斥锁和二进制信号量都由 SemaphoreHandle_t 类型的变量引用,同时可以在任何采用该类型参数的任务级 API 函数中使用。与互斥锁不同,二进制信号量可用于中断服务程序。
返回值类型描述
NULL因为可用 FreeRTOS 堆不足,所以无法创建信号量。
其他任何值信号量已成功创建。返回值是一个句柄,通过该句柄可以引用信号量。

用法示例: 

SemaphoreHandle_t xSemaphore;

void vATask( void * pvParameters )
{
    //尝试创建一个信号量。
    xSemaphore = xSemaphoreCreateBinary();

    if( xSemaphore == NULL )
    {
      // 没有足够的FreeRTOS堆可用的信号量创建成功。
    }
    else
    {
       // 信号量现在可以被使用。它的句柄存储在xSemahore变量。
       // 在这个信号量上调用xSemaphoreTake()将失败,直到第一次给出信号量。
    }
}

(2)静态创建二进制信号量:

函数原型:

SemaphoreHandle_t xSemaphoreCreateBinaryStatic(
                          StaticSemaphore_t *pxSemaphoreBuffer );

作用:创建一个二进制信号量,并返回一个可以引用该信号量的句柄。

  • configSUPPORT_STATIC_ALLOCATION 必须在 FreeRTOSConfig.h 中设置为 1,才可使用此 RTOS API 函数。
  • 每个二进制信号量需要少量 RAM,用于保存信号量状态。如果使用 xSemaphoreCreateBinary() 创建了二进制信号量,那么所需的 RAM 将自动从 FreeRTOS 堆中分配。如果二进制信号量是使用 xSemaphoreCreateBinaryStatic() 创建的,则 RAM 由应用程序写入器提供,这需要一个附加参数,但允许 RAM 在编译时进行静态分配。
参数/返回值类型描述
pxSemaphoreBuffer必须指向 StaticSemaphore_t 类型的变量,该变量将用于保存信号量的状态。
NULL因为 pxSemaphoreBuffer 为 NULL,所以无法创建信号量。
其他任何值信号量已成功创建。返回值是一个句柄,通过该句柄可以引用信号量。

附加参数见:

 【FreeRTOS 教程 一】任务结构体及其基础创建使用

用法示例: 

SemaphoreHandle_t xSemaphore = NULL; // 声明一个信号量句柄变量,并初始化为NULL。
StaticSemaphore_t xSemaphoreBuffer; // 声明一个静态信号量缓冲区变量,用于保存信号量的状态。

void vATask( void * pvParameters ) // 定义一个任务函数vATask,它接收一个void指针类型的参数pvParameters。
{
    /* 创建一个二进制信号量,不使用任何动态内存分配。
       信号量的数据结构将保存到xSemaphoreBuffer变量中。 */
    xSemaphore = xSemaphoreCreateBinaryStatic( &xSemaphoreBuffer );

    /* pxSemaphoreBuffer不为NULL,因此预期句柄也不会为NULL。 */
    configASSERT( xSemaphore ); // 使用configASSERT宏来断言xSemaphore不为NULL,如果为NULL则触发断言失败。

    /* 任务代码的其余部分在这里。 */
}

(3)队列机制创建信号量:

函数原型:

vSemaphoreCreateBinary( SemaphoreHandle_t xSemaphore )
  • 注意:vSemaphoreCreateBinary() 宏仍保留在源代码中,以确保向后兼容性,但不应在新设计中使用。使用 xSemaphoreCreateBinary() 函数代替。
  • 此外,在许多情况下,使用直达任务通知来代替二进制信号量更快、更节省内存。
  • 使用现有队列机制创建信号量的宏。队列长度为 1,因为这是二进制信号量。数据大小为 0,因为实际上我们并不会存储任何数据,只想知道队列为空还是满。
  • 二进制信号量和互斥锁非常相似,但有一些小差异:互斥锁包含优先级继承机制,而二进制信号量不包含。这使得二进制信号量成为实现同步(任务之间或任务与中断之间)的更好选择,而互斥锁则成为实现简单互斥的更好选择。
  • 在获得二进制信号量后无需返回,因此任务同步可以通过一个任务/中断持续“给予”信号量,而另一个任务/中断持续“获取”信号量来实现。
  • 如果另一个优先级更高的任务尝试获取相同的互斥锁,那么“获取”互斥锁的任务的优先级就有可能被提高。拥有互斥锁的任务“继承”了试图“获取”相同互斥锁任务的优先级,这意味着必须始终“返回”互斥锁,否则优先级较高的任务将永远无法获得互斥锁,而优先级较低的任务将永远无法“取消继承”优先级。
  • 互斥锁和二进制信号量都分配给了 SemaphoreHandle_t 类型的变量,可在任何采用此类型参数的 API 函数中使用。
参数类型描述
xSemaphoreSemaphoreHandle_t已创建信号量的句柄,应为 SemaphoreHandle_t 类型。

用法示例: 

SemaphoreHandle_t xSemaphore; // 声明一个信号量句柄变量 xSemaphore,用于引用信号量。

void vATask( void * pvParameters ) // 定义一个任务函数 vATask,它接收一个 void 指针类型的参数 pvParameters。
{
    // 在调用 vSemaphoreCreateBinary() 之前,信号量不能被使用。
    // 这是一个宏,因此直接传递变量。
    vSemaphoreCreateBinary( xSemaphore ); // 创建一个二进制信号量,并将信号量的句柄存储在 xSemaphore 中。

    if( xSemaphore != NULL ) // 检查信号量是否创建成功
    {
        // 信号量创建成功。
        // 现在可以使用信号量了。
    }
}

(4)动态创建计数信号量:

函数原型:

SemaphoreHandle_t xSemaphoreCreateCounting( UBaseType_t uxMaxCount,
                                            UBaseType_t uxInitialCount);

作用:创建一个计数信号量,并返回一个可以引用该新建信号量的句柄。

  • configSUPPORT_DYNAMIC_ALLOCATION 必须在 FreeRTOSConfig.h 中设置为 1,或处于未定义状态(在这种情况下,默认为 1),该 RTOS API 函数才可用。
  • 每个计数信号量需要少量 RAM,用于保存信号量的状态。如果使用 xSemaphoreCreateCounting() 创建计数信号量 则所需的 RAM 将从 FreeRTOS 堆自动分配。如果使用 xSemaphoreCreateCountingStatic() 创建计数信号量,则 RAM 会由应用程序编写器提供,这需要其他的但允许在编译时静态分配 RAM。
  • configUSE_COUNTING_SEMAPHORES 必须在 FreeRTOSConfig.h 中设置为 1。
参数/返回值类型描述
uxMaxCount可以达到的最大计数值。当信号量达到此值时,它不能再被“给定”。
uxInitialCount创建信号量时分配给信号量的计数值。
信号量句柄如果已成功创建信号量,则将返回该信号量的句柄。
NULL如果因为保留信号量所需的 RAM 无法分配而无法创建信号量,则会返回 NULL。

用法示例:

void vATask( void * pvParameters )
{
    SemaphoreHandle_t xSemaphore; // 声明一个信号量句柄变量 xSemaphore,用于引用信号量。

    /* 创建一个计数信号量,其最大计数值(最大可“给定”次数)为 10,初始计数值(创建时的计数值)为 0。 */
    xSemaphore = xSemaphoreCreateCounting( 10, 0 );

    if( xSemaphore != NULL ) // 检查信号量是否创建成功
    {
        /* 信号量创建成功。 */
        // 这里可以添加代码来使用信号量进行同步或资源管理。
    }
}

(5)静态创建计数信号量:

函数原型:

SemaphoreHandle_t xSemaphoreCreateCountingStatic(
                                 UBaseType_t uxMaxCount,
                                 UBaseType_t uxInitialCount
                                 StaticSemaphore_t *pxSemaphoreBuffer );

作用:创建一个计数信号量,并返回一个可以引用该新建信号量的句柄。

  • configSUPPORT_STATIC_ALLOCATION 必须在 FreeRTOSConfig.h 中设置为 1,才可使用此 RTOS API 函数。
  • 每个计数信号量需要少量 RAM,用于保存信号量的状态。如果使用 xSemaphoreCreateCounting() 创建计数信号量,则所需的 RAM 将从 FreeRTOS 堆自动分配。如果使用 xSemaphoreCreateCountingStatic() 创建计数信号量,则 RAM 由应用程序编写器提供,这需要用到一个附加参数,但允许在编译时静态分配 RAM。

 附加参数见:

 【FreeRTOS 教程 一】任务结构体及其基础创建使用

参数/返回值类型描述
uxMaxCount可以达到的最大计数值。当信号量达到此值时,它不能再被“给出”。
uxInitialCount创建信号量时分配给信号量的计数值。
pxSemaphoreBuffer必须指向一个 StaticSemaphore_t 类型的变量,然后用它来保存信号量的数据结构体。
信号量句柄如果已成功创建信号量,则将返回该信号量的句柄。
NULL如果 pxSemaphoreBuffer 为 NULL,则返回 NULL。

用法示例:

static StaticSemaphore_t xSemaphoreBuffer; // 声明一个静态信号量缓冲区变量,用于保存信号量的状态。

void vATask( void * pvParameters ) // 定义一个任务函数 vATask,它接收一个 void 指针类型的参数 pvParameters。
{
    SemaphoreHandle_t xSemaphore; // 声明一个信号量句柄变量 xSemaphore,用于引用信号量。

    /* 创建一个计数信号量,其最大计数值(最大可“给定”次数)为 10,初始计数值(创建时的计数值)为 0。
       信号量的数据结构将存储在 xSemaphoreBuffer 变量中,不进行动态内存分配。 */
    xSemaphore = xSemaphoreCreateCountingStatic( 10, 0, &xSemaphoreBuffer );

    /* pxSemaphoreBuffer 不为 NULL,因此预期信号量将被创建。 */
    configASSERT( xSemaphore ); // 使用 configASSERT 宏来断言 xSemaphore 不为 NULL,如果为 NULL 则触发断言失败。
}

 四、信号量控制 API :

(1)获取指定的互斥锁的任务句柄:

函数原型:

TaskHandle_t xSemaphoreGetMutexHolder( SemaphoreHandle_t xMutex );
  • 必须在 FreeRTOSConfig.h 中将 INCLUDE_xSemaphoreGetMutexHolder 设置为 1, 此函数才可用。
  • 返回保存函数参数指定的互斥锁的任务的句柄(若有)。
  • xSemaphoreGetMutexHolder () 可以可靠地用于确定调用任务是否是互斥锁持有者,但如果由调用任务之外的任务持有互斥锁,则无法可靠地使用 xSemaphoreGetMutexHolder () 。这是因为 MUTEX 支架可能会在调用该函数的调用任务与测试该函数返回值之间更改。
  • configUSE_MUTEXES 必须在 FreeRTOSConfig.h 中设置为 1,xSemaphoreGetMutexHolder() 才可用。
参数/返回值描述
xMutex正在查询的互斥体的句柄。
保存 xMutex 参数指定的互斥锁的任务的句柄。如果在 xMutex 参数中传递的信号量不是互斥锁型信号量,或者如果互斥锁可用,但未被任何任务保存,则返回 NULL。

(2)返回信号量计数:

函数原型:

UBaseType_t uxSemaphoreGetCount( SemaphoreHandle_t xSemaphore );

作用:返回信号量计数。

参数/返回描述
xSemaphore正在查询的信号量的句柄。
返回值如果信号量是计数信号量,则返回信号量的当前计数值。如果信号量是二进制信号量,则当信号量可用时,返回 1,当信号量不可用时,返回 0。

(3)获取信号量: 

函数原型:

 xSemaphoreTake( SemaphoreHandle_t xSemaphore,
                 TickType_t xTicksToWait );
  • 用于获取信号量的宏。信号量必须在之前已经通过调用 xSemaphoreCreateBinary()、xSemaphoreCreateMutex() 或 xSemaphoreCreateCounting()创建。
  • 不得从 ISR 调用此宏。如果需要,xQueueReceiveFromISR()可用来从中断中获取一个信号量, 尽管这不是正常操作。
  • 信号量使用队列作为其底层机制,因此函数在某种程度上可互操作。
参数/返回值描述
xSemaphore正在获取的信号量的句柄——在创建信号量时获得。
xTicksToWait等待信号量变为可用的时间(以滴答为单位)。宏 portTICK_PERIOD_MS 可以将其转换为实际时间。可以用一个为零的阻塞时间来轮询信号量。如果 INCLUDE_vTaskSuspend 设置为 1,则将阻塞时间指定为 portMAX_DELAY 会导致任务无限期地阻塞(没有超时限制)。
返回值如果获得信号量,则返回 pdTRUE。如果 xTicksToWait 过期,信号量不可用,则返回 pdFALSE

用法示例:

SemaphoreHandle_t xSemaphore = NULL; // 定义一个信号量句柄变量,初始化为NULL。

/* 创建信号量的任务 */
void vATask( void * pvParameters )
{
    /* 创建一个信号量来保护共享资源。由于我们使用信号量进行互斥,所以我们创建一个互斥信号量而不是二进制信号量。 */
    xSemaphore = xSemaphoreCreateMutex(); // 创建一个互斥信号量。
}

/* 使用信号量的任务 */
void vAnotherTask( void * pvParameters )
{
    /* ... 做其他事情。 */

    if( xSemaphore != NULL ) // 检查信号量是否已经被创建。
    {
        /* 尝试获取信号量。如果信号量不可用,等待10个时钟周期看看它是否变得可用。 */
        if( xSemaphoreTake( xSemaphore, ( TickType_t ) 10 ) == pdTRUE ) // 尝试获取信号量,最多等待10个时钟周期。
        {
            /* 我们成功获取了信号量,现在可以访问共享资源。 */

            /* ... */

            /* 我们已经完成了对共享资源的访问。释放信号量。 */
            xSemaphoreGive( xSemaphore ); // 释放信号量。
        }
        else
        {
            /* 我们未能获取信号量,因此无法安全地访问共享资源。 */
        }
    }
}

(4)获取信号量(ISR): 

函数原型:

xSemaphoreTakeFromISR
 (
 SemaphoreHandle_t xSemaphore,
 signed BaseType_t *pxHigherPriorityTaskWoken
 )
  • 可从 ISR 调用的 xSemaphoreTake() 版本。与 xSemaphoreTake() 不同,xSemaphoreTakeFromISR() 不允许指定阻塞时间。
参数/返回值描述
xSemaphore信号量被“获取”。信号量由 SemaphoreHandle_t 类型的变量引用,必须在使用之前显式创建。
pxHigherPriorityTaskWoken信号量可能阻塞一个或多个任务,等待给出信号量。调用 xSemaphoreTakeFromISR() 将使被阻塞的任务等待信号量离开阻塞状态。如果调用 API 函数导致任务离开阻塞状态,且未阻塞任务的优先级等于或高于当前正在执行的任务(被中断的任务),那么 API 函数将从内部把 *pxHigherPriorityTaskWoken 设置为 pdTRUE。如果 xSemaphoreTakeFromISR() 将 *pxHigherPriorityTaskWoken 设置为 pdTRUE,则应在退出中断之前执行上下文切换。这将确保中断直接返回到最高优先级的就绪状态任务。该机制与 xQueueReceiveFromISR() 函数中使用的机制相同,读者可以参考 xQueueReceiveFromISR() 文档以获得进一步解释。从 FreeRTOS V7.3.0 开始,pxHigherPriorityTaskWoken 是一个可选参数,可设置为 NULL
返回值如果成功获取信号量,则返回 pdTRUE。如果因为信号量不可用而未成功获取信号量,则返回 pdFALSE

(5)递归获取互斥锁信号量:

函数原型:

xSemaphoreTakeRecursive( SemaphoreHandle_t xMutex,
                         TickType_t xTicksToWait );
  • 递归地获得或“获取”一个互斥锁型信号量的此互斥锁必须已经事先通过调用 xSemaphoreCreateRecursiveMutex() 完成创建;
  • 必须在 FreeRTOSConfig.h 中将 configUSE_RECURSIVE_MUTEXES 设置为 1, 此宏才可用。
  • 不得在使用 xSemaphoreCreateMutex() 创建的互斥锁上使用此宏。
  • 所有者可以反复“获取”递归使用的互斥锁。在所有者为每个成功的“获取”请求调用 xSemaphoreGiveRecursive() 之前,该互斥锁不会再次变得可用。例如, 如果一个任务成功地“获取”了同一个互斥锁 5 次, 那么任何其他任务都无法使用此互斥锁, 直到任务也把这个互斥锁“解锁”5 次。
参数描述
xMutex正在获得的互斥锁的句柄。这是由 xSemaphoreCreateRecursiveMutex() 返回的句柄。
xTicksToWait等待信号量变为可用的时间(以滴答为单位)。可以使用 portTICK_PERIOD_MS 宏将其转换为实际时间。可以用一个为零的阻塞时间来轮询信号量。如果任务已有信号量,则无论 xTicksToWait 的值是多少,xSemaphoreTakeRecursive() 都将立即返回。
返回值如果获得信号量,则返回 pdTRUE;如果 xTicksToWait 过期,信号量不可用,则返回 pdFALSE

用法示例: 

SemaphoreHandle_t xMutex = NULL; // 定义一个信号量句柄变量,初始化为NULL。

// 创建互斥锁的任务
void vATask( void * pvParameters )
{
    // 创建一个互斥锁来保护共享资源。
    xMutex = xSemaphoreCreateRecursiveMutex(); // 创建一个可递归的互斥锁。
}

// 使用互斥锁的任务
void vAnotherTask( void * pvParameters )
{
    // ... 执行其他任务。

    if( xMutex != NULL ) // 检查互斥锁是否已经被创建。
    {
        // 尝试获取互斥锁。如果互斥锁不可用,等待10个时钟周期看看它是否变得可用。
        if( xSemaphoreTakeRecursive( xMutex, ( TickType_t ) 10 ) == pdTRUE ) // 尝试获取互斥锁,最多等待10个时钟周期。
        {
            // 我们成功获取了互斥锁,现在可以访问共享资源。

            // ...
            // 由于代码的特性,可能会进一步调用 xSemaphoreTakeRecursive() 来获取同一个互斥锁。
            // 在实际代码中,这些调用不会是简单的连续调用,而是可能嵌套在更复杂的调用结构中。
            xSemaphoreTakeRecursive( xMutex, ( TickType_t ) 10 ); // 再次获取互斥锁
            xSemaphoreTakeRecursive( xMutex, ( TickType_t ) 10 ); // 再次获取互斥锁

            // 现在互斥锁已经被“获取”三次,所以在释放三次之前,其他任务无法获取它。
            // 同样,在实际代码中,这些调用不会是简单的连续调用,而是可能嵌套在更复杂的调用结构中。
            // 这里只是为了演示。
            xSemaphoreGiveRecursive( xMutex ); // 释放互斥锁
            xSemaphoreGiveRecursive( xMutex ); // 释放互斥锁
            xSemaphoreGiveRecursive( xMutex ); // 释放互斥锁

            // 现在其他任务可以获取互斥锁。
        }
        else
        {
            // 我们未能获取互斥锁,因此无法安全地访问共享资源。
        }
    }
}

(6)释放信号量:

函数原型:

xSemaphoreGive( SemaphoreHandle_t xSemaphore );
  • 用于释放信号量的宏。释放前信号量必须已经通过调用 xSemaphoreCreateBinary()、xSemaphoreCreateMutex() 或 xSemaphoreCreateCounting()创建。
  • 不得在 ISR 中使用此宏。可以参考 xSemaphoreGiveFromISR(),可以从 ISR 中使用的替代方案。
  • 此宏也不得用于使用 xSemaphoreCreateRecursiveMutex() 创建的信号量。
参数/返回值描述
xSemaphore要释放的信号量的句柄。这是创建信号量时返回的句柄。
返回值如果信号量被释放,则返回 pdTRUE。如果发生错误,则返回 pdFALSE。信号量是使用队列实现的。发布消息时,如果队列上没有空间,那么可能会发生错误,这表明最初未能正确获取信号量。

用法示例:

SemaphoreHandle_t xSemaphore = NULL; // 定义一个信号量句柄变量,初始化为NULL。

void vATask( void * pvParameters )
{
    // 创建一个信号量来保护共享资源。由于我们使用信号量进行互斥,所以我们创建一个互斥信号量而不是二进制信号量。
    xSemaphore = xSemaphoreCreateMutex(); // 创建一个互斥信号量。

    if( xSemaphore != NULL ) // 检查信号量是否创建成功。
    {
        if( xSemaphoreGive( xSemaphore ) != pdTRUE ) // 尝试释放信号量。
        {
            // 我们预期这个调用会失败,因为我们不能在没有先“获取”信号量的情况下释放它!
        }
        // 获取信号量 - 如果信号量不可立即获得,则不阻塞。
        if( xSemaphoreTake( xSemaphore, ( TickType_t ) 0 ) ) // 尝试立即获取信号量。
        {
            // 我们现在拥有信号量,可以访问共享资源。
            // ...
            // 我们已经完成了对共享资源的访问,所以可以释放信号量。
            if( xSemaphoreGive( xSemaphore ) != pdTRUE ) // 释放信号量。
            {
                // 我们不预期这个调用会失败,因为我们必须已经获取了信号量才能到达这里。
            }
        }
    }
}

(7)递归释放互斥锁信号量:

函数原型:

xSemaphoreGiveRecursive( SemaphoreHandle_t xMutex )
  • 递归地释放或“给出”一个互斥锁型信号量的 此互斥锁必须已经事先通过调用 xSemaphoreCreateRecursiveMutex() 完成创建;
  • 必须在 FreeRTOSConfig.h 中将 configUSE_RECURSIVE_MUTEXES 设置为 1, 此宏才可用。
  • 不得在使用 xSemaphoreCreateMutex() 创建的互斥锁上使用此宏。
  • 所有者可以反复“获取”递归使用的互斥锁。在所有者为每个成功的“获取”请求调用 xSemaphoreGiveRecursive() 之前,该互斥锁不会再次变得可用。例如, 如果一个任务成功地“获取”了同一个互斥锁 5 次, 那么任何其他任务都无法使用此互斥锁, 直到任务也把这个互斥锁“解锁”5 次。
参数/描述说明
xMutex正在释放或“给出”的互斥锁的句柄。这是由 xSemaphoreCreateRecursiveMutex() 返回的句柄。
返回值如果成功给出信号量,则返回 pdTRUE

 用法示例:

// 声明一个互斥量句柄,初始值为NULL,表示尚未创建互斥量。
SemaphoreHandle_t xMutex = NULL;

// 创建互斥量的任务
void vATask( void * pvParameters )
{
    // 创建一个递归互斥量,用于保护共享资源。
    // 递归互斥量允许同一个任务多次获取同一个互斥量,
    // 但必须相应地多次释放它。
    xMutex = xSemaphoreCreateRecursiveMutex();
}

// 使用互斥量的任务
void vAnotherTask( void * pvParameters )
{
    // ... 执行其他任务逻辑。

    // 检查互斥量是否已经创建。
    if( xMutex != NULL )
    {
        // 尝试获取互斥量。如果互斥量当前不可用,
        // 等待最多10个系统滴答(tick)时间,看看它是否会变为空闲。
        if( xSemaphoreTakeRecursive( xMutex, ( TickType_t ) 10 ) == pdTRUE )
        {
            // 成功获取了互斥量,可以安全地访问共享资源。

            // ...
            // 由于代码的性质,可能需要进一步调用xSemaphoreTakeRecursive()
            // 来获取同一个互斥量。在实际代码中,这些调用通常不会是连续的,
            // 而是嵌入在更复杂的调用结构中。
            xSemaphoreTakeRecursive( xMutex, ( TickType_t ) 10 );
            xSemaphoreTakeRecursive( xMutex, ( TickType_t ) 10 );

            // 此时,互斥量已经被“获取”了三次,
            // 因此在另一个任务可以获取它之前,
            // 它也必须被“释放”三次。在实际代码中,
            // 这些释放调用通常不会是连续的,而是在调用栈展开时调用。
            // 这里只是为了演示目的。
            xSemaphoreGiveRecursive( xMutex );
            xSemaphoreGiveRecursive( xMutex );
            xSemaphoreGiveRecursive( xMutex );

            // 现在,互斥量可以被其他任务获取了。
        }
        else
        {
            // 无法获取互斥量,因此无法安全地访问共享资源。
        }
    }
}

(8)释放信号量(ISR):

函数原型:

xSemaphoreGiveFromISR
 (
 SemaphoreHandle_t xSemaphore,
 signed BaseType_t *pxHigherPriorityTaskWoken
 )
  • 用于释放信号量的。释放前信号量必须已经 通过调用 xSemaphoreCreateBinary() 或 xSemaphoreCreateCounting() 创建。
  • 互斥锁型信号量(那些调用 xSemaphoreCreateMutex() 创建的信号量) 不得与此宏一起使用。
  • 此宏可在 ISR 中使用。
参数/返回描述
xSemaphore要释放的信号量的句柄。这是创建信号量时返回的句柄。
pxHigherPriorityTaskWoken如果给出信号量会导致任务解除阻塞,并且解除阻塞的任务的优先级高于当前正在运行的任务,则 xSemaphoreGiveFromISR() 会将 *pxHigherPriorityTaskWoken 设置为 pdTRUE。如果 xSemaphoreGiveFromISR() 将此值设置为 pdTRUE,则应在退出中断之前请求上下文切换。从 FreeRTOS V7.3.0 开始,pxHigherPriorityTaskWoken 为可选参数,可设置为 NULL
返回值如果成功给出信号量,则返回 pdTRUE,否则返回 errQUEUE_FULL

用法示例:

#define LONG_TIME 0xffff  // 定义一个常量,表示等待信号量的最长时间
#define TICKS_TO_WAIT 10   // 定义一个常量,表示等待的滴答数

SemaphoreHandle_t xSemaphore = NULL;  // 声明一个信号量句柄,初始值为NULL

/* 重复执行的任务 */
void vATask( void * pvParameters )
{
    /* 我们使用信号量进行同步,因此创建一个二进制信号量而不是互斥量。
       必须确保在创建信号量之前中断不会尝试使用它! */
    xSemaphore = xSemaphoreCreateBinary();  // 创建一个二进制信号量

    for( ;; )  // 无限循环
    {
        /* 我们希望这个任务每10个定时器滴答执行一次。信号量在启动此任务之前创建。

           阻塞等待信号量变为可用。 */
        if( xSemaphoreTake( xSemaphore, LONG_TIME ) == pdTRUE )  // 如果成功获取信号量
        {
            /* 执行任务。 */

            ...

            /* 任务执行完毕。返回循环顶部,在那里我们将阻塞信号量,直到再次执行。
               注意,当以这种方式使用信号量与ISR进行同步时,无需“释放”信号量。 */
        }
    }
}

/* 定时器ISR */
void vTimerISR( void * pvParameters )
{
    static unsigned char ucLocalTickCount = 0;  // 静态变量,用于计数滴答数
    BaseType_t xHigherPriorityTaskWoken = pdFALSE;  // 用于指示是否唤醒了更高优先级的任务

    /* 定时器滴答发生。 */

    ... 执行其他定时器功能。

    /* 是否到了vATask()执行的时间? */
    xHigherPriorityTaskWoken = pdFALSE;  // 初始化为pdFALSE
    ucLocalTickCount++;  // 增加计数
    if( ucLocalTickCount >= TICKS_TO_WAIT )  // 如果计数达到指定的滴答数
    {
        /* 通过释放信号量来解除任务的阻塞。 */
        xSemaphoreGiveFromISR( xSemaphore, &xHigherPriorityTaskWoken );  // 释放信号量

        /* 重置计数,以便在10个滴答时间后再次释放信号量。 */
        ucLocalTickCount = 0;
    }

    /* 如果xHigherPriorityTaskWoken为true,则进行上下文切换。实际使用的宏是特定于端口的。 */
    portYIELD_FROM_ISR( xHigherPriorityTaskWoken );  // 请求上下文切换(如果需要)
}

(9)删除信号量:

函数原型:

void vSemaphoreDelete(SemaphoreHandle_t xSemaphore);
  • 删除信号量,包括互斥锁型信号量和递归信号量。
  • 请勿删除已有阻塞任务的信号量(正在等待信号灯可用的阻塞状态任务)。
参数描述
xSemaphore被删除的信号量的句柄。

五、信号量使用示例:

(1)二进制信号量:

  • 使用二进制信号量在两个任务之间实现同步。程序初始化UART和DMA1,创建一个二进制信号量,并定义两个任务:任务 vBinaryTask2 每2秒释放一次二进制信号量,而任务 vBinaryTask 在获取到信号量后会打印一条消息,处理1秒钟,然后再次释放信号量。

方法一:动态创建二进制信号量

#include "stm32f10x.h"              // 包含STM32F10x系列微控制器的头文件
#include "FreeRTOS.h"
#include "task.h"                   // 包含任务相关函数的头文件,用于任务创建和管理。
#include "semphr.h"           		// 引入信号量相关的头文件
#include "stdio.h"
#include "uart.h"

/***********************************
* @method      使用二进制信号量在两个任务之间实现同步
*      
* @Platform  	CSDN	 
* @author  		The_xzs
* @date    		2025.2.2
************************************/

SemaphoreHandle_t xBinarySemaphore = NULL; // 声明一个二进制信号量的句柄变量,并初始化为 NULL

// 定义一个任务函数,该任务将使用二进制信号量
void vBinaryTask(void *pvParameters)
{
    for (;;)
    {
        // 尝试获取信号量,如果信号量不可用,则任务将被阻塞,直到超时
        if (xSemaphoreTake(xBinarySemaphore, portMAX_DELAY) == pdTRUE)
        {
            printf("Binary Semaphore Taken\n"); 		 // 通过 UART 打印信号量已获取的信息
            vTaskDelay(pdMS_TO_TICKS(1000));             // 模拟任务处理时间,延迟 1 秒
            printf("Binary Semaphore Given\n");  		 // 通过 UART 打印信号量已释放的信息
        }
    }
}

// 定义另一个任务函数,该任务将释放二进制信号量
void vBinaryTask2(void *pvParameters)
{
    for (;;)
    {
        vTaskDelay(pdMS_TO_TICKS(2000)); // 模拟外部事件,延迟 2 秒
        xSemaphoreGive(xBinarySemaphore); // 释放信号量
        printf("Binary Semaphore Given from Task 2\n"); // 通过 UART 打印信号量已释放的信息
    }
}

int main(void)
{
    Uart_Init(115200); // 初始化 UART,波特率设置为 115200
    DMA1_Init();       // 初始化 DMA1

    // 创建一个二进制信号量
    xBinarySemaphore = xSemaphoreCreateBinary();
    if (xBinarySemaphore == NULL) // 检查信号量是否创建成功
    {
        printf("Failed to create binary semaphore\n"); // 如果创建失败,通过 UART 打印错误信息
        return 0; // 返回 0,表示程序异常终止
    }

    // 创建两个任务
    xTaskCreate(vBinaryTask, "Binary Task 1", configMINIMAL_STACK_SIZE, NULL, 1, NULL); // 创建任务 1
    xTaskCreate(vBinaryTask2, "Binary Task 2", configMINIMAL_STACK_SIZE, NULL, 2, NULL); // 创建任务 2

    // 启动 RTOS 调度器
    vTaskStartScheduler(); // 启动任务调度器,开始执行任务

    return 0; // 程序不应该到达这里,除非有错误
}

 方法二:静态创建二进制信号量

#include "stm32f10x.h"              // 包含STM32F10x系列微控制器的头文件
#include "FreeRTOS.h"
#include "task.h"                   // 包含任务相关函数的头文件,用于任务创建和管理。
#include "semphr.h"           		// 引入信号量相关的头文件
#include "stdio.h"
#include "uart.h"

/***********************************
* @method      静态创建二进制信号量,
*      			在两个任务之间实现同步
* @Platform  	CSDN	 
* @author  		The_xzs
* @date    		2025.2.2
************************************/

// 声明一个静态信号量缓冲区变量,并初始化
StaticSemaphore_t xBinarySemaphoreBuffer;

// 声明一个二进制信号量的句柄变量,并初始化为 NULL
SemaphoreHandle_t xBinarySemaphore = NULL;

// 定义一个任务函数,该任务将使用二进制信号量
void vBinaryTask(void *pvParameters)
{
    for (;;)
    {
        // 尝试获取信号量,如果信号量不可用,则任务将被阻塞,直到超时
        if (xSemaphoreTake(xBinarySemaphore, portMAX_DELAY) == pdTRUE)
        {
            printf("Binary Semaphore Taken\n"); 		 // 通过 UART 打印信号量已获取的信息
            vTaskDelay(pdMS_TO_TICKS(1000));             // 模拟任务处理时间,延迟 1 秒
            printf("Binary Semaphore Given\n");  		 // 通过 UART 打印信号量已释放的信息
        }
    }
}

// 定义另一个任务函数,该任务将释放二进制信号量
void vBinaryTask2(void *pvParameters)
{
    for (;;)
    {
        vTaskDelay(pdMS_TO_TICKS(2000)); // 模拟外部事件,延迟 2 秒
        xSemaphoreGive(xBinarySemaphore); // 释放信号量
        printf("Binary Semaphore Given from Task 2\n"); // 通过 UART 打印信号量已释放的信息
    }
}

int main(void)
{
    Uart_Init(115200); // 初始化 UART,波特率设置为 115200
    DMA1_Init();       // 初始化 DMA1

    // 创建一个二进制信号量,使用静态分配的内存
    xBinarySemaphore = xSemaphoreCreateBinaryStatic(&xBinarySemaphoreBuffer);
    if (xBinarySemaphore == NULL) // 检查信号量是否创建成功
    {
        printf("Failed to create binary semaphore\n"); // 如果创建失败,通过 UART 打印错误信息
        return 0; // 返回 0,表示程序异常终止
    }

    // 创建两个任务
    xTaskCreate(vBinaryTask, "Binary Task 1", configMINIMAL_STACK_SIZE, NULL, 1, NULL); // 创建任务 1
    xTaskCreate(vBinaryTask2, "Binary Task 2", configMINIMAL_STACK_SIZE, NULL, 2, NULL); // 创建任务 2

    // 启动 RTOS 调度器
    vTaskStartScheduler(); // 启动任务调度器,开始执行任务

    return 0; // 程序不应该到达这里,除非有错误
}

(2)计数信号量:

  • 定义两个任务,其中一个任务(vCountingTask2)周期性地释放信号量,另一个任务(vCountingTask)在获取到计数信号量后执行延迟操作并释放信号量,从而演示了利用计数信号量进行任务间的同步和资源管理,确保了任务执行的有序性和资源访问的互斥性。

方法一:动态创建计数信号量

#include "stm32f10x.h"              // 包含STM32F10x系列微控制器的头文件
#include "FreeRTOS.h"
#include "task.h"                   // 包含任务相关函数的头文件,用于任务创建和管理。
#include "semphr.h"           		// 引入信号量相关的头文件
#include "stdio.h"
#include "uart.h"

/***********************************
* @method      动态创建计数信号量,
*      			进行任务间的同步和资源管理
* @Platform  	CSDN	 
* @author  		The_xzs
* @date    		2025.2.2
************************************/

SemaphoreHandle_t xCountingSemaphore = NULL; // 声明一个计数信号量的句柄变量,并初始化为 NULL

// 定义一个任务函数,该任务将使用计数信号量
void vCountingTask(void *pvParameters)
{
    for (;;)
    {
        // 尝试获取信号量,如果信号量不可用,则任务将被阻塞,直到超时
        if (xSemaphoreTake(xCountingSemaphore, portMAX_DELAY) == pdTRUE)
        {
            printf("Counting Semaphore Taken\n"); // 通过 UART 打印信号量已获取的信息
            vTaskDelay(pdMS_TO_TICKS(1000));              // 模拟任务处理时间,延迟 1 秒
            printf("Counting Semaphore Given\n");  // 通过 UART 打印信号量已释放的信息
        }
    }
}

// 定义另一个任务函数,该任务将释放计数信号量
void vCountingTask2(void *pvParameters)
{
    for (;;)
    {
        vTaskDelay(pdMS_TO_TICKS(2000)); // 模拟外部事件,延迟 2 秒
        for (int i = 0; i < 5; i++)
        {
            xSemaphoreGive(xCountingSemaphore); 			  // 释放信号量
            printf("Counting Semaphore Given from Task 2\n"); // 通过 UART 打印信号量已释放的信息
        }
    }
}

int main(void)
{
    Uart_Init(115200); // 初始化 UART,波特率设置为 115200
    DMA1_Init();       // 初始化 DMA1

    // 创建一个计数信号量,最大计数值为 5,初始计数值为 0
    xCountingSemaphore = xSemaphoreCreateCounting(5, 0);
    if (xCountingSemaphore == NULL) // 检查信号量是否创建成功
    {
        printf("Failed to create counting semaphore\n"); // 如果创建失败,通过 UART 打印错误信息
        return 0; // 返回 0,表示程序异常终止
    }

    // 创建两个任务
    xTaskCreate(vCountingTask, "Counting Task 1", configMINIMAL_STACK_SIZE, NULL, 1, NULL); // 创建任务 1
    xTaskCreate(vCountingTask2, "Counting Task 2", configMINIMAL_STACK_SIZE, NULL, 2, NULL); // 创建任务 2

    // 启动 RTOS 调度器
    vTaskStartScheduler(); // 启动任务调度器,开始执行任务

    return 0; // 程序不应该到达这里,除非有错误
}

方法二:静态创建计数信号量: 

#include "stm32f10x.h"              // 包含STM32F10x系列微控制器的头文件
#include "FreeRTOS.h"
#include "task.h"                   // 包含任务相关函数的头文件,用于任务创建和管理。
#include "semphr.h"           		// 引入信号量相关的头文件
#include "stdio.h"
#include "uart.h"

/***********************************
* @method      静态创建二进制信号量,
*      				在两个任务之间实现同步
* @Platform  	CSDN	 
* @author  		The_xzs
* @date    		2025.2.2
************************************/

// 声明一个静态信号量缓冲区变量,并初始化
StaticSemaphore_t xBinarySemaphoreBuffer;

// 声明一个二进制信号量的句柄变量,并初始化为 NULL
SemaphoreHandle_t xBinarySemaphore = NULL;

// 定义一个任务函数,该任务将使用二进制信号量
void vBinaryTask(void *pvParameters)
{
    for (;;)
    {
        // 尝试获取信号量,如果信号量不可用,则任务将被阻塞,直到超时
        if (xSemaphoreTake(xBinarySemaphore, portMAX_DELAY) == pdTRUE)
        {
            printf("Binary Semaphore Taken\n"); 		 // 通过 UART 打印信号量已获取的信息
            vTaskDelay(pdMS_TO_TICKS(1000));             // 模拟任务处理时间,延迟 1 秒
            printf("Binary Semaphore Given\n");  		 // 通过 UART 打印信号量已释放的信息
        }
    }
}

// 定义另一个任务函数,该任务将释放二进制信号量
void vBinaryTask2(void *pvParameters)
{
    for (;;)
    {
        vTaskDelay(pdMS_TO_TICKS(2000)); // 模拟外部事件,延迟 2 秒
        xSemaphoreGive(xBinarySemaphore); // 释放信号量
        printf("Binary Semaphore Given from Task 2\n"); // 通过 UART 打印信号量已释放的信息
    }
}

int main(void)
{
    Uart_Init(115200); // 初始化 UART,波特率设置为 115200
    DMA1_Init();       // 初始化 DMA1

    // 创建一个二进制信号量,使用静态分配的内存
    xBinarySemaphore = xSemaphoreCreateBinaryStatic(&xBinarySemaphoreBuffer);
    if (xBinarySemaphore == NULL) // 检查信号量是否创建成功
    {
        printf("Failed to create binary semaphore\n"); // 如果创建失败,通过 UART 打印错误信息
        return 0; // 返回 0,表示程序异常终止
    }

    // 创建两个任务
    xTaskCreate(vBinaryTask, "Binary Task 1", configMINIMAL_STACK_SIZE, NULL, 1, NULL); // 创建任务 1
    xTaskCreate(vBinaryTask2, "Binary Task 2", configMINIMAL_STACK_SIZE, NULL, 2, NULL); // 创建任务 2

    // 启动 RTOS 调度器
    vTaskStartScheduler(); // 启动任务调度器,开始执行任务

    return 0; // 程序不应该到达这里,除非有错误
}

六、FreeRTOS教程示例代码下载:

通过网盘分享的文件:FreeRTOS教程示例代码

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

python学opencv|读取图像(五十五)使用cv2.medianBlur()函数实现图像像素中值滤波处理

【1】引言 在前述学习过程中&#xff0c;已经探索了取平均值的形式进行图像滤波处理。 均值滤波的具体的执行对象是一个nXn的像素核&#xff0c;对这个像素核内所有像素点的BGR值取平均值&#xff0c;然后把这个平均的BGR值直接赋给像素核中心位置的核心像素点&#xff0c;由…

OpenAI 正式推出Deep Research

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

多模态论文笔记——NaViT

大家好&#xff0c;这里是好评笔记&#xff0c;公主号&#xff1a;Goodnote&#xff0c;专栏文章私信限时Free。本文详细解读多模态论文NaViT&#xff08;Native Resolution ViT&#xff09;&#xff0c;将来自不同图像的多个patches打包成一个单一序列——称为Patch n’ Pack—…

VLAN 基础 | 不同 VLAN 间通信实验

注&#xff1a;本文为 “ Vlan 间通信” 相关文章合辑。 英文引文&#xff0c;机翻未校。 图片清晰度限于原文图源状态。 未整理去重。 How to Establish Communications between VLANs? 如何在 VLAN 之间建立通信&#xff1f; Posted on November 20, 2015 by RouterSwi…

使用Pygame制作“吃豆人”游戏

本篇博客展示如何使用 Python Pygame 编写一个简易版的“吃豆人&#xff08;Pac-Man&#xff09;” 风格游戏。这里我们暂且命名为 Py-Man。玩家需要控制主角在一个网格地图里移动、吃掉散布在各处的豆子&#xff0c;并躲避在地图中巡逻的幽灵。此示例可帮助你理解网格地图、角…

springboot使用rabbitmq

使用springboot创建rabbitMQ的链接。 整个项目结构如下&#xff1a; 1.maven依赖 <dependency><groupId>com.rabbitmq</groupId><artifactId>amqp-client</artifactId><version>3.4.1</version> </dependency>application.y…

安卓(android)订餐菜单【Android移动开发基础案例教程(第2版)黑马程序员】

一、实验目的&#xff08;如果代码有错漏&#xff0c;可查看源码&#xff09; 1.掌握Activity生命周的每个方法。 2.掌握Activity的创建、配置、启动和关闭。 3.掌握Intent和IntentFilter的使用。 4.掌握Activity之间的跳转方式、任务栈和四种启动模式。 5.掌握在Activity中添加…

RabbitMQ快速上手及入门

概念 概念&#xff1a; publisher&#xff1a;生产者&#xff0c;也就是发送消息的一方 consumer&#xff1a;消费者&#xff0c;也就是消费消息的一方 queue&#xff1a;队列&#xff0c;存储消息。生产者投递的消息会暂存在消息队列中&#xff0c;等待消费者处理 exchang…

java命令详解

这里以jdk8为例子&#xff0c;查看默认的垃圾回收器 java -XX:PrintCommandLineFlags -version-XX:UseParallelGC : Parallel Scavenge 和 Parallel Old 组合 -XX:InitialHeapSize268435456 : 初始化堆大小&#xff08;字节&#xff09; -XX:MaxHeapSize4294967296 : 最大堆大…

自主Shell命令行解释器

什么是命令行 我们一直使用的"ls","cd","pwd","mkdir"等命令&#xff0c;都是在命令行上输入的&#xff0c;我们之前对于命令行的理解&#xff1a; 命令行是干啥的&#xff1f;是为我们做命令行解释的。 命令行这个东西实际上是我们…

分析哲学:从 语言解剖到 思想澄清的哲学探险

分析哲学&#xff1a;从 语言解剖 到 思想澄清 的哲学探险 第一节&#xff1a;分析哲学的基本概念与公式解释 【通俗讲解&#xff0c;打比方来讲解&#xff01;】 分析哲学&#xff0c;就像一位 “语言侦探”&#xff0c;专注于 “解剖语言”&#xff0c;揭示我们日常使用的语…

自定义数据集 使用paddlepaddle框架实现逻辑回归

导入必要的库 import numpy as np import paddle import paddle.nn as nn 数据准备&#xff1a; seed1 paddle.seed(seed)# 1.散点输入 定义输入数据 data [[-0.5, 7.7], [1.8, 98.5], [0.9, 57.8], [0.4, 39.2], [-1.4, -15.7], [-1.4, -37.3], [-1.8, -49.1], [1.5, 75.6…

QtCreator在配置Compilers时,有一个叫ABI的选项,那么什么是ABI?

问题提出 QtCreator在配置Compilers时,有一个叫ABI的选项,那么什么是ABI&#xff1f; ABI&#xff08;Application Binary Interface&#xff09;介绍 ABI&#xff08;Application Binary Interface&#xff0c;应用二进制接口&#xff09;是指应用程序与操作系统或其他程序…

[STM32 标准库]EXTI应用场景 功能框图 寄存器

一、EXTI 外部中断在嵌入式系统中有广泛的应用场景&#xff0c;如按钮开关控制&#xff0c;传感器触发&#xff0c;通信接口中断等。其原理都差不多&#xff0c;STM32会对外部中断引脚的边沿进行检测&#xff0c;若检测到相应的边沿会触发中断&#xff0c;在中断中做出相应的处…

Maven jar 包下载失败问题处理

Maven jar 包下载失败问题处理 1.配置好国内的Maven源2.重新下载3. 其他问题 1.配置好国内的Maven源 打开⾃⼰的 Idea 检测 Maven 的配置是否正确&#xff0c;正确的配置如下图所示&#xff1a; 检查项⼀共有两个&#xff1a; 确认右边的两个勾已经选中&#xff0c;如果没有请…

【JavaScript】Web API事件流、事件委托

目录 1.事件流 1.1 事件流和两个阶段说明 1.2 事件捕获 1.3 事件冒泡 1.4 阻止冒泡 1.5 解绑事件 L0 事件解绑 L2 事件解绑 鼠标经过事件的区别 两种注册事件的区别 2.事件委托 案例 tab栏切换改造 3.其他事件 3.1 页面加载事件 3.2 页面滚动事件 3.2 页面滚…

Spring Cloud工程搭建

目录 工程搭建 搭建父子工程 创建父工程 Spring Cloud版本 创建子项目-订单服务 声明项⽬依赖 和 项⽬构建插件 创建子项目-商品服务 声明项⽬依赖 和 项⽬构建插件 工程搭建 因为拆分成了微服务&#xff0c;所以要拆分出多个项目&#xff0c;但是IDEA只能一个窗口有一…

neo4j入门

文章目录 neo4j版本说明部署安装Mac部署docker部署 neo4j web工具使用数据结构图数据库VS关系数据库 neo4j neo4j官网Neo4j是用ava实现的开源NoSQL图数据库。Neo4作为图数据库中的代表产品&#xff0c;已经在众多的行业项目中进行了应用&#xff0c;如&#xff1a;网络管理&am…

selenium记录Spiderbuf例题C03

防止自己遗忘&#xff0c;故作此为记录。 鸢尾花数据集(Iris Dataset) 这道题牵扯到JS动态加载。 步骤&#xff1a; &#xff08;1&#xff09;进入例题&#xff0c;需要找到按钮规律。 flip_xpath: str r"//li/a[onclickgetIrisData({});]" &#xff08;2&…

自定义数据集 使用pytorch框架实现逻辑回归并保存模型,然后保存模型后再加载模型进行预测,对预测结果计算精确度和召回率及F1分数

导入必要的库&#xff1a; import numpy as np import torch import torch.nn as nn import torch.optim as optim from sklearn.metrics import precision_score, recall_score, f1_score 准备数据&#xff1a; class1_points np.array([[1.9, 1.2],[1.5, 2.1],[1.9, 0.5]…