STM32F1+HAL库+FreeTOTS学习14——数值信号量

STM32F1+HAL库+FreeTOTS学习13——数值信号量

  • 1. 数值信号量
  • 2. 相关API函数
    • 2.1 创建计数信号量
    • 2.2 获取信号量
    • 2.3 释放信号量
    • 2.4 删除信号量
    • 2.5 获取信号量的计数值
  • 3. 操作实验
    • 1. 实验内容
    • 2. 代码实现:
    • 运行结果

上一期我们学习了二值信号量 ,这一期学习计数信号量

1. 数值信号量

和二值信号量类似的,数值信号量也是队列的一种特殊情况。只不过二值信号量是队列长度为1的队列,而数值信号量则是队列长度大于0的队列(可以为1,但是这样的话就和二值信号量无异),数值信号量能够容纳多个资源,其资源最大数在创建时就已经确定,一般适用于一下两个场合:

  1. 事件计数

在这种场合下,每次事件发生后,在事件处理函数中释放计数信号量(资源数+1),其他等待事件发生的任务获取计数信号量(资源数-1),等待事件发生的任务就可以在成功获取到计数信号量之后执行相应的动作。在这种场合下,计数信号量的资源数一般在创建时设置为0。

  1. 资源管理

在这种场合下,计数信号量的资源数代表共享资源的可用数量,一个任务想要访问共享资源,就必须先获取这个共享资源的计数信号量,获取成功之后,才可以对这个共享资源进行操作,在使用完这个共享资源之后,也需要释放它,在这中场合下,计数信号量的资源数一般在创建时设置为其管理的资源最大数。

2. 相关API函数

计数信号量的使用过程:创建计数信号量->释放信号量-> 获取信号量 -> 删除信号量 ( 可选 ),下面我们围绕几个步骤介绍计数信号量的相关API函数
常用的二值信号量API函数如下表:

函数描述
xSemaphoreCreateCounting()使用动态方式创建数值信号量
xSemaphoreCreateCountingStatic()使用静态方式创建数值信号量
xSemaphoreTake()获取信号量
xSemaphoreTakeFromISR()在中断中获取信号量
xSemaphoreGive()释放信号量
xSemaphoreGiveFromISR()在中断中释放信号量
vSemaphoreDelete()删除信号量
uxSemaphoreGetCount()获取信号量的计数值

【注】:获取和释放信号量的函数和前面二值信号量的获取和释放是一模一样的。

2.1 创建计数信号量

  1. xSemaphoreCreateCounting()

此函数用于动态方式创建计数信号量,创建所需要的内存,有FreeRTOS自动分配,该函数实际上是一个宏定义,在semphr.h文件中有定义,具体的代码如下:

#define xSemaphoreCreateCounting( uxMaxCount, \uxInitialCount ) \xQueueCreateCountingSemaphore( ( uxMaxCount ), \( uxInitialCount ))

可以看到xSemaphoreCreateCounting() 内部是调用了xQueueCreateCountingSemaphore() ,由于该函数不经常使用,我们这里不赘述。所以我们直接把xSemaphoreCreateCounting() 当作一个函数介绍(实际上是一个宏,我们当作函数来使用,FreeRTOS的官方文档也是这样的),下面是函数原型:

/*** @brief       xSemaphoreCreateCounting* @param       uxMaxCount:计数信号量的资源数最大值* @param       uxInitialCount:计数信号量创建时分配的资源数* @retval      返回值为NULL,表示创建失败,其他值表示为创建数值信号量的句柄*/
SemaphoreHandle_t xSemaphoreCreateCounting( UBaseType_t uxMaxCount,UBaseType_t uxInitialCount);
  1. xSemaphoreCreateCountingStatic()

此函数用于静态方式创建计数信号量,创建计数信号量所需要的内存,需要用户手动分配,该函数实际上是一个宏定义,在semphr.h文件中有定义,具体的代码如下:

#define xSemaphoreCreateCountingStatic( uxMaxCount, \uxInitialCount, \pxSemaphoreBuffer) \xQueueCreateCountingSemaphoreStatic( ( uxMaxCount ), \( uxInitialCount ), \( pxSemaphoreBuffer ))

可以看到xSemaphoreCreateCountingStatic() 内部是调用了xQueueCreateCountingSemaphoreStatic() ,有用该函数不经常使用,我们这里不赘述。所以我们直接把xSemaphoreCreateCountingStatic() 当作一个函数介绍(实际上是一个宏,我们当作函数来使用,FreeRTOS的官方文档也是这样的),下面是函数原型:

/*** @brief       xSemaphoreCreateCountingStatic* @param       uxMaxCount:计数信号量的资源数最大值* @param       uxInitialCount:计数信号量创建时分配的资源数* @param       pxSemaphoreBuffer:指向StaticSemaphore_t 类型的指针,存放创建后的信号量结构体* @retval      返回值为NULL,表示创建失败,其他值表示为创建数值信号量的句柄*/
SemaphoreHandle_t xSemaphoreCreateCountingStatic(UBaseType_t uxMaxCount,UBaseType_t uxInitialCountStaticSemaphore_t *pxSemaphoreBuffer );

2.2 获取信号量

  1. xSemaphoreTake()

此函数用于获取信号量,如果信号量处于没有资源的状态,那么可以选择将任务进入阻塞状态,如果成功获取到了信号量,那么信号的资源数减1,该函数实际上是一个宏定义,在semphr.h文件中有定义,具体的代码如下:

#define xSemaphoreTake( xSemaphore, \xBlockTime) \xQueueSemaphoreTake( ( xSemaphore ), \( xBlockTime ))

可以看到xSemaphoreTake() 内部是调用了xQueueSemaphoreTake() ,关于xQueueSemaphoreTake函数的定义和使用,我们这里不赘述。所以我们直接把xSemaphoreTake() 当作一个函数介绍(实际上是一个宏,我们当作函数来使用,FreeRTOS的官方文档也是这样的),下面是函数原型:

/*** @brief       xSemaphoreTake* @param       xSemaphore:需要获取信号量的句柄* @param       xTicksToWait:阻塞时间* @retval      返回值为pdTRUE,表示获取成功,如果返回值为pdFALSE,表示获取失败。*/BaseType_t xSemaphoreTake( SemaphoreHandle_t xSemaphore,TickType_t xTicksToWait );
  1. xSemaphoreTakeFromISR()

此函数用于在中断中获取信号量,该函数实际上是一个宏定义,在semphr.h文件中有定义,具体的代码如下:

#define xSemaphoreTakeFromISR( xSemaphore, \pxHigherPriorityTaskWoken) \xQueueReceiveFromISR( ( QueueHandle_t ) \( xSemaphore ), \NULL, \( pxHigherPriorityTaskWoken ))

可以看到xSemaphoreTakeFromISR() 内部是调用了xQueueReceiveFromISR() ,关于xQueueReceiveFromISR函数的定义和使用,我们这里不赘述。所以我们直接把xSemaphoreTakeFromISR() 当作一个函数介绍(实际上是一个宏,我们当作函数来使用,FreeRTOS的官方文档也是这样的),下面是函数原型:

/*** @brief       xSemaphoreTakeFromISR* @param       xSemaphore:需要获取信号量的句柄* @param       pxHigherPriorityTaskWoken:获取信号量之后是否需要任务切换,需要切换则 *pxHigherPriorityTaskWoken = pdTRUE* @retval      返回值为pdTRUE,表示获取成功,如果返回值为pdFALSE,表示获取失败。*/
BaseType_t xSemaphoreTakeFromISR ( SemaphoreHandle_t xSemaphore, signed BaseType_t *pxHigherPriorityTaskWoken );

需要注意的是:

  • xSemaphoreTake()可以用来获取二值信号量、计数信号量、互斥信号量。
  • xSemaphoreTakeFromISR() 只能用来获取二值信号量和计数信号量,不能用于互斥信号量。

2.3 释放信号量

  1. xSemaphoreGive()

此函数用于释放信号量,如果信号量处于资源满的状态,那么可以选择将任务进入阻塞状态,如果成功释放了信号量,那么信号的资源数加1,该函数实际上是一个宏定义,在semphr.h文件中有定义,具体的代码如下:

#define xSemaphoreGive( xSemaphore) \xQueueGenericSend( ( QueueHandle_t ) ( xSemaphore ), \NULL, \semGIVE_BLOCK_TIME, \queueSEND_TO_BACK)

可以看到xSemaphoreGive() 内部是调用了xQueueGenericSend() ,该函数在 STM32F1+HAL库+FreeTOTS学习12——队列 中有介绍,我们这里不赘述。所以我们直接把xSemaphoreGive() 当作一个函数介绍(实际上是一个宏,我们当作函数来使用,FreeRTOS的官方文档也是这样的),下面是函数原型:

/*** @brief       xSemaphoreGive* @param       xSemaphore:需要释放信号量的句柄* @retval      返回值为pdTRUE,表示释放成功,如果返回值为pdFALSE,表示释放失败。*/BaseType_t xSemaphoreGive( SemaphoreHandle_t xSemaphore );
  1. xSemaphoreGiveFromISR()

此函数用于在中断中释放信号量,该函数实际上是一个宏定义,在semphr.h文件中有定义,具体的代码如下:

#define xSemaphoreGiveFromISR( xSemaphore, \pxHigherPriorityTaskWoken) \xQueueGiveFromISR( ( QueueHandle_t ) ( xSemaphore ), \( pxHigherPriorityTaskWoken ))

可以看到xSemaphoreGiveFromISR() 内部是调用了xQueueGiveFromISR() ,关于xQueueGiveFromISR函数的定义和使用,我们这里不赘述。所以我们直接把xSemaphoreGiveFromISR() 当作一个函数介绍(实际上是一个宏,我们当作函数来使用,FreeRTOS的官方文档也是这样的),下面是函数原型:

/*** @brief       xSemaphoreGiveFromISR* @param       xSemaphore:需要释放信号量的句柄* @param       pxHigherPriorityTaskWoken:释放信号量之后是否需要任务切换,需要切换则 *pxHigherPriorityTaskWoken = pdTRUE* @retval      返回值为pdTRUE,表示释放成功,如果返回值为pdFALSE,表示释放失败。*/
BaseType_t xSemaphoreGiveFromISR ( SemaphoreHandle_t xSemaphore, signed BaseType_t *pxHigherPriorityTaskWoken )

需要注意的是:

  • xSemaphoreGive()可以用来释放二值信号量、计数信号量、互斥信号量。
  • xSemaphoreGiveFromISR () 只能用来释放二值信号量和计数信号量,不能用于互斥信号量。因为互斥信号量会有优先级继承的处理,而中断不属于任务,没有办法进行优先级继承。

2.4 删除信号量

  1. vSemaphoreDelete()
    此函数用于删除已创建的信号量。该函数实际上是一个宏定义,在 semphr.h 文件中有定义,具体的代码如下所示
#define vSemaphoreDelete(xSemaphore) \vQueueDelete ( QueueHandle_t ) \( xSemaphore ))

可以看到vSemaphoreDelete() 内部是调用了vQueueDelete () ,关于vQueueDelete 函数的定义和使用,我们这里不赘述。所以我们直接把vSemaphoreDelete() 当作一个函数介绍(实际上是一个宏,我们当作函数来使用,FreeRTOS的官方文档也是这样的),下面是函数原型:

/*** @brief       vSemaphoreDelete* @param       xSemaphore :需要删除信号量的句柄* @retval      无*/
void vSemaphoreDelete( SemaphoreHandle_t xSemaphore );

2.5 获取信号量的计数值

  1. uxSemaphoreGetCount()
    此函数用于获取信号量的计数值。该函数实际上是一个宏定义,在 semphr.h 文件中有定义,具体的代码如下所示
#define uxSemaphoreGetCount( xSemaphore )                uxQueueMessagesWaiting( ( QueueHandle_t ) ( xSemaphore ) )

可以看到uxSemaphoreGetCount() 内部是调用了uxQueueMessagesWaiting() ,关于uxQueueMessagesWaiting函数的定义和使用,我们这里不赘述。所以我们直接把uxSemaphoreGetCount() 当作一个函数介绍(实际上是一个宏,我们当作函数来使用,FreeRTOS的官方文档也是这样的),下面是函数原型:

/*** @brief       uxSemaphoreGetCount* @param       xSemaphore :需要查询的信号量句柄* @retval      计数信号量返回当前的计数值,二值信号量返回0表示信号量可以,返回1表示不可用*/
UBaseType_t uxSemaphoreGetCount( SemaphoreHandle_t xSemaphore );

3. 操作实验

1. 实验内容

在STM32F103RCT6上运行FreeRTOS,通过按键控制,完成对应的数值信号量操作,具体要求如下:

  • 定义一个数值信号量,资源数最大为100,初始为0
  • 定义任务1:每次按下按键0,释放数值信号量
  • 定义任务2:每1s获取1次数值信号量

2. 代码实现:

  • 由于本期内容涉及到按键扫描,会用到: STM32框架之按键扫描新思路 ,这里不做过多介绍。我们直接来看代码:
  1. freertos_demo.c
#include "freertos_demo.h"
#include "main.h"
#include "queue.h" 		//需要包含队列和任务相关的头文件
#include "task.h"
#include "key.h"		//包含按键相关头文件/*FreeRTOS*********************************************************************************************//******************************************************************************************************/
/*FreeRTOS配置*//* TASK1 任务 配置* 包括: 任务句柄 任务优先级 堆栈大小 */
#define TASK1_PRIO      1                  /* 任务优先级 */
#define TASK1_STK_SIZE  128                 /* 任务堆栈大小 */
TaskHandle_t            Task1Task_Handler;  /* 任务句柄 */
void task1(void *pvParameters);					/*任务函数*//* TASK2 任务 配置* 包括: 任务句柄 任务优先级 堆栈大小 */
#define TASK2_PRIO      2                  /* 任务优先级 */
#define TASK2_STK_SIZE  128                 /* 任务堆栈大小 */
TaskHandle_t            Task2Task_Handler;  /* 任务句柄 */
void task2(void *pvParameters);					/*任务函数*/SemaphoreHandle_t SemaphoreCount;				/* 定义数值信号量 *//******************************************************************************************************//*** @brief       FreeRTOS例程入口函数* @param       无* @retval      无*/
void freertos_demo(void)
{taskENTER_CRITICAL();           /* 进入临界区,关闭中断,此时停止任务调度*/SemaphoreCount = xSemaphoreCreateCounting(100,0);if(SemaphoreCount == NULL){printf("数值信号量创建失败\r\n");}else{printf("数值信号量创建成功\r\n");}/* 创建任务1 */xTaskCreate((TaskFunction_t )task1,(const char*    )"task1",(uint16_t       )TASK1_STK_SIZE,(void*          )NULL,(UBaseType_t    )TASK1_PRIO,(TaskHandle_t*  )&Task1Task_Handler);/* 创建任务2 */xTaskCreate((TaskFunction_t )task2,(const char*    )"task2",(uint16_t       )TASK2_STK_SIZE,(void*          )NULL,(UBaseType_t    )TASK2_PRIO,(TaskHandle_t*  )&Task2Task_Handler);taskEXIT_CRITICAL();            /* 退出临界区,重新开启中断,开启任务调度 */vTaskStartScheduler();		//开启任务调度
}/**
* @brief       task1:用于按键扫描,检测按键0按下时,释放计数型信号量(资源数+1)* @param       pvParameters : 传入参数(未用到)* @retval      无*/
void task1(void *pvParameters)
{while(1){Key_One_Scan(Key_Name_Key0,Key0_Up_Task,Key0_Down_Task);		/* 按键0扫描,按下后释放数值信号量 */}
}	
/**
* @brief       task2:每1s获取计数信号量(资源数-1),当获取成功后,打印信号量的计数值(资源数)* @param       pvParameters : 传入参数(未用到)* @retval      无*/
void task2(void *pvParameters)	
{	BaseType_t errMessage;		/* 错误信息 */while(1){	errMessage = xSemaphoreTake(SemaphoreCount,portMAX_DELAY);		/* 获取计数信号量 */if(errMessage == pdTRUE)	{printf("信号量的计数值为:%d\r\n",(int)uxSemaphoreGetCount(SemaphoreCount));}else{printf("获取信号量失败\r\n");}vTaskDelay(1000);}
}
  1. key.c
/* USER CODE BEGIN 2 */#include "freertos_demo.h"
#include "key.h"
#include "usart.h"extern QueueHandle_t SemaphoreCount;				/* 声明外部的计数信号量 */void Key0_Down_Task(void)
{BaseType_t errMessage;		/* 错误信息 */errMessage = xSemaphoreGive(SemaphoreCount);	/* 释放计数信号量 */if(errMessage == pdTRUE){printf("数值信号量释放成功\r\n");}else{printf("数值信号量释放失败\r\n");}
}
void Key0_Up_Task(void)
{}
void Key1_Down_Task(void)
{}
void Key1_Up_Task(void)
{}
void Key2_Down_Task(void)
{}
void Key2_Up_Task(void)
{}
void WKUP_Down_Task(void)
{}
void WWKUP_Up_Task(void)
{}void Key_One_Scan(uint8_t KeyName ,void(*OnKeyOneUp)(void), void(*OnKeyOneDown)(void))
{static uint8_t Key_Val[Key_Name_Max];    //按键值的存放位置static uint8_t Key_Flag[Key_Name_Max];   //KEY0~2为0时表示按下,为1表示松开,WKUP反之Key_Val[KeyName] = Key_Val[KeyName] <<1;  //每次扫描完,将上一次扫描的结果左移保存switch(KeyName){case Key_Name_Key0:  Key_Val[KeyName] = Key_Val[KeyName] | (HAL_GPIO_ReadPin(KEY0_GPIO_Port, KEY0_Pin));    //读取Key0按键值break;case Key_Name_Key1:  Key_Val[KeyName] = Key_Val[KeyName] | (HAL_GPIO_ReadPin(KEY1_GPIO_Port, KEY1_Pin));   //读取Key1按键值break;case Key_Name_Key2:  Key_Val[KeyName] = Key_Val[KeyName] | (HAL_GPIO_ReadPin(KEY2_GPIO_Port, KEY2_Pin));   //读取Key2按键值break;
//        case Key_Name_WKUP:  Key_Val[KeyName] = Key_Val[KeyName] | (HAL_GPIO_ReadPin(WKUP_GPIO_Port, WKUP_Pin));   //读取WKUP按键值
//            break; default:break;}
//    if(KeyName == Key_Name_WKUP)     //WKUP的电路图与其他按键不同,所以需要特殊处理
//    {
//        //WKUP特殊情况
//        //当按键标志为1(松开)是,判断是否按下,WKUP按下时为0xff
//        if(Key_Val[KeyName] == 0xff && Key_Flag[KeyName] == 1)
//        {
//            (*OnKeyOneDown)();
//           Key_Flag[KeyName] = 0;
//        }
//        //当按键标志位为0(按下),判断按键是否松开,WKUP松开时为0x00
//        if(Key_Val[KeyName] == 0x00 && Key_Flag[KeyName] == 0)
//        {
//            (*OnKeyOneUp)();
//           Key_Flag[KeyName] = 1;
//        } 
//    }
//    else                               //Key0~2按键逻辑判断
//    {//Key0~2常规判断//当按键标志为1(松开)是,判断是否按下if(Key_Val[KeyName] == 0x00 && Key_Flag[KeyName] == 1){(*OnKeyOneDown)();Key_Flag[KeyName] = 0;}//当按键标志位为0(按下),判断按键是否松开if(Key_Val[KeyName] == 0xff && Key_Flag[KeyName] == 0){(*OnKeyOneUp)();Key_Flag[KeyName] = 1;}  }//}
/* USER CODE END 2 */

运行结果

在这里插入图片描述
运行结果如上图,我们来简单的解释一下,为什么是这样:

  1. 数值信号量创建成功后,打印对应信息
  2. 任务2优先级大于任务1,但是由于信号量资源数初始化为0,所以无法获取信号量,任务2进入阻塞,执行任务1。
  3. 执行任务1的过程中,按键0按下(我这里是连续按下3次按键0),在识别到第一次按下(还没来得及大于释放成功的信息),信号量资源数+1,任务2恢复就绪态并执行。
  4. 任务2执行获取信号量,并且打印计数值为0,后再次进入阻塞态,切换回任务1,打印第一次数值信号量释放成功。
  5. 紧接着第二、三次按键按下,打印第二、三次数值信号量释放成功。
  6. 1s后,切换会任务2,释放幸好了,打印计数值为1。
  7. 再过1s,打印计数值为0。

以上就是本期使用到的核心代码,其他部分我这里就不做展示,直接去看我往期的内容,源代码都有的。至于按键的配置,可以参考:夜深人静学32系列9——GPIO驱动数码管/蜂鸣器/按键/LED

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

在线相亲系统:新时代的婚恋观与传统习俗的碰撞

随着互联网技术的发展&#xff0c;相亲交友平台已成为年轻人寻找伴侣的新方式。这些平台不仅改变了人们的社交习惯&#xff0c;也反映了当代婚恋观与传统习俗之间的碰撞与融合。开发h17711347205本文将探讨在线相亲系统是如何在尊重传统的基础上&#xff0c;为现代年轻人提供更…

21.1 k8s接口鉴权token认证和prometheus的实现

本节重点介绍 : k8s接口鉴权方式serviceaccount和token的关系手动curl访问metrics接口 k8s对象接口鉴权 以容器基础资源指标为例 对应就是访问node上的kubelet的/metrics/cadvisor接口&#xff0c;即访问https://nodeip:10250/metrics/cadvisor 直接curl访问 会报错&…

第一弹:llama.cpp编译

1.编译llama.cpp命令行&#xff08;电脑版本&#xff09;&#xff1b; 2.交叉编译安卓命令行版本。 一、Llama.cpp是什么&#xff1f; 二、Llama.cpp编译 首先我们尝试编译llama.cpp. 2.1 下载llama.cpp 项目的github地址&#xff1a; https://github.com/ggerganov/llama…

ubuntu18.04 NVIDIA驱动 CUDA cudnn Anaconda安装

1、安装NVIDIA驱动 a.查看推荐驱动 ubuntu-drivers devicesb.打开软件更新&#xff0c;选择相应的显卡 c.重启查看安装情况&#xff0c;输入nvidia-smi 2、安装CUDA 下载链接https://developer.nvidia.com/cuda-toolkit-archive 安装CUDA&#xff1a; sudo bash cuda_11…

完整网络模型训练(一)

文章目录 一、网络模型的搭建二、网络模型正确性检验三、创建网络函数 一、网络模型的搭建 以CIFAR10数据集作为训练例子 准备数据集&#xff1a; #因为CIFAR10是属于PRL的数据集&#xff0c;所以需要转化成tensor数据集 train_data torchvision.datasets.CIFAR10(root&quo…

前端工程规范-2:JS代码规范(Prettier + ESLint)

Prettier 和 ESLint 是两个在现代 JavaScript 开发中广泛使用的工具&#xff0c;它们结合起来可以提供以下作用和优势&#xff1a; 代码格式化和风格统一&#xff1a; Prettier 是一个代码格式化工具&#xff0c;能够自动化地处理代码的缩进、空格、换行等格式问题&#xff0c;…

【C++算法】8.双指针_三数之和

文章目录 题目链接&#xff1a;题目描述&#xff1a;解法C 算法代码&#xff1a;图解 题目链接&#xff1a; 15.三数之和 题目描述&#xff1a; 解法 解法一&#xff1a;排序暴力枚举利用set去重O(n3) 例如nums[-1&#xff0c;0&#xff0c;1&#xff0c;2&#xff0c;-1&…

【C++篇】领略模板编程的进阶之美:参数巧思与编译的智慧

文章目录 C模板进阶编程前言第一章: 非类型模板参数1.1 什么是非类型模板参数&#xff1f;1.1.1 非类型模板参数的定义 1.2 非类型模板参数的注意事项1.3 非类型模板参数的使用场景示例&#xff1a;静态数组的实现 第二章: 模板的特化2.1 什么是模板特化&#xff1f;2.1.1 模板…

基于单片机的催眠电路控制系统

** 文章目录 前言一 概要功能设计设计思路 软件设计效果图 程序文章目录 前言 &#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN特邀作者、博客专家、CSDN新星计划导师&#xff0c;一名热衷于单片机技术探索与分享的博主、专注于 精通51/STM32/MSP430/AVR等单片机设计 主…

Apache DolphinScheduler-1.3.9源码分析(一)

引言 随着大数据的发展&#xff0c;任务调度系统成为了数据处理和管理中至关重要的部分。Apache DolphinScheduler 是一款优秀的开源分布式工作流调度平台&#xff0c;在大数据场景中得到广泛应用。 在本文中&#xff0c;我们将对 Apache DolphinScheduler 1.3.9 版本的源码进…

html+css(如何用css做出京东页面,静态版)

<!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>京东</title><link rel"stylesheet&q…

AR 眼镜之-蓝牙电话-来电铃声与系统音效

目录 &#x1f4c2; 前言 AR 眼镜系统版本 蓝牙电话 来电铃声 系统音效 1. &#x1f531; Android9 原生的来电铃声&#xff0c;走的哪个通道&#xff1f; 2. &#x1f4a0; Android9 原生的来电铃声&#xff0c;使用什么播放&#xff1f; 2.1 来电铃声创建准备 2.2 来…

联宇集团:如何利用CRM实现客户管理精细化与业务流程高效协同

在全球化的浪潮中&#xff0c;跨境电商正成为国际贸易的新引擎。作为领先的跨境电商物流综合服务商&#xff0c;广东联宇物流有限公司(以下称“联宇集团”)以其卓越的物流服务和前瞻的数字化战略&#xff0c;在全球市场中脱颖而出。本文将基于联宇集团搭建CRM系统的实际案例&am…

PV大题--专题突破

写在前面&#xff1a; PV大题考查使用伪代码控制进程之间的同步互斥关系&#xff0c;它需要我们一定的代码分析能力&#xff0c;算法设计能力&#xff0c;有时候会给你一段伪代码让你补全使用信号量控制的操作&#xff0c;请一定不要相信某些人告诉你只要背一个什么模板&#…

国庆偷偷卷!小众降维!POD-Transformer多变量回归预测(Matlab)

目录 效果一览基本介绍程序设计参考资料 效果一览 基本介绍 1.Matlab实现POD-Transformer多变量回归预测&#xff0c;本征正交分解数据降维融合Transformer多变量回归预测&#xff0c;使用SVD进行POD分解&#xff08;本征正交分解&#xff09;&#xff1b; 2.运行环境Matlab20…

MobaXterm基本使用 -- 服务器状态、批量操作、显示/切换中文字体、修复zsh按键失灵

监控服务器资源 参考网址&#xff1a;https://www.cnblogs.com/144823836yj/p/12126314.html 显示效果 MobaXterm提供有这项功能&#xff0c;在会话窗口底部&#xff0c;显示服务器资源使用情况 如内存、CPU、网速、磁盘使用等&#xff1a; &#xff08;完整窗口&#xff0…

BEVDet---论文+源码解读

论文链接&#xff1a;https://arxiv.org/pdf/2112.11790.pdf&#xff1b; Github仓库源码&#xff1a;https://github.com/HuangJunJie2017/BEVDet&#xff1b; BEVDet这篇论文主要是提出了一种基于BEV空间下的3D目标检测范式&#xff0c;BEVDet算法模型的整体流程图如下&…

汽车总线之---- LIN总线

Introduction LIN总线的简介&#xff0c;对于传统的这种点对点的连接方式&#xff0c;我们可以看到ECU相关的传感器和执行器是直接连接到ECU的&#xff0c;当传感器和执行器的数量较少时&#xff0c;这样的连接方式是能满足要求的&#xff0c;但是随着汽车电控功能数量的不断增…

基于单片机的指纹打卡系统

目录 一、主要功能 二、硬件资源 三、程序编程 四、实现现象 一、主要功能 基于STC89C52RC&#xff0c;采用两个按键替代指纹&#xff0c;一个按键按下&#xff0c;LCD12864显示比对成功&#xff0c;则 采用ULN2003驱动步进电机转动&#xff0c;表示开门&#xff0c;另一个…

RTMP、RTSP直播播放器的低延迟设计探讨

技术背景 没有多少开发者会相信RTMP或RTSP播放器&#xff0c;延迟会做到150-300ms内&#xff0c;除非测试过大牛直播SDK的&#xff0c;以Android平台启动轻量级RTSP服务和推送RTMP&#xff0c;然后Windows分别播放RTSP和RTMP为例&#xff0c;整体延迟如下&#xff1a; 大牛直播…