FreeRTOS - 任务通知

news2024/10/18 15:37:16

1. 任务通知

所谓"任务通知",你可以反过来读"通知任务"。

我们使用队列、信号量、事件组等等方法时,并不知道对方是谁。使用任务通知时,可以明确指定:通知哪个任务。

使用队列、信号量、事件组时,我们都要事先创建对应的结构体,双方通过中间的结构体通信:

使用任务通知时,任务结构体TCB中就包含了内部对象,可以直接接收别人发过来的"通知":

1.1 任务通知的特性

每个任务都有一个 32 位的通知值,在大多数情况下,任务通知可以替代二值信号量、计数信号量、事件组,也可以替代 长度为 1 的队列(可以保存一个 32位整数或指针值)。

1.1.1 优势和限制

任务通知的优势:

  • 效率更高:使用任务通知来发送事件、数据给某个任务时,效率更高。比队列、信号量、事件组都有大的优势。
  • 更节省内存:使用其他方法时都要先创建对应的结构体。使用任务通知时无需额外创建结构体。 由于任务通知的数据结构包含在任务控制块中,只要任务存在,任务通知数据结构就已经创建完毕, 可以直接使用

FreeRTOS 提供以下几种方式发送通知给任务 :

  • 发送通知给任务, 如果有通知未读,不覆盖通知值。
  • 发送通知给任务,直接覆盖通知值。
  • 发送通知给任务,设置通知值的一个或者多个位,可以当做事件组来使用。
  • 发送通知给任务,递增通知值,可以当做计数信号量使用。

任务通知的限制:

  • 不能发送数据给ISR:
    • ISR并没有任务结构体,所以无法使用任务通知的功能给ISR发送数据。但是ISR可以使用任务通知的功能,发数据给任务。
  • 数据只能给该任务独享
    • 使用队列、信号量、事件组时,数据保存在这些结构体中,其他任务、ISR都可以访问这些数据。使用任务通知时,数据存放入目标任务中,只有它可以访问这些数据。
    • 在日常工作中,这个限制影响不大。因为很多场合是从多个数据源把数据发给某个任务,而不是把一个数据源的数据发给多个任务。
  • 无法缓冲数据
    • 使用队列时,假设队列深度为N,那么它可以保存N个数据。
    • 使用任务通知时,任务结构体中只有一个任务通知值,只能保存一个数据。
  • 无法广播给多个任务
    • 使用事件组可以同时给多个任务发送事件。
    • 使用任务通知,只能发个一个任务。
  • 如果发送受阻,发送方无法进入阻塞状态等待
    • 假设队列已经满了,使用 xQueueSendToBack() 给队列发送数据时,任务可以进入阻塞状态等待发送完成。
    • 使用任务通知时,即使对方无法接收数据,发送方也无法阻塞等待,只能即刻返回错误。
1.1.2 通知状态和通知值

每个任务都有一个结构体:TCB(Task Control Block),里面有2个成员:

  • ulNotifiedValue :uint32_t类型,任务通知值,可以保存一个32位整数或指针值
  • ucNotifyState:uint8_t类型,任务通知状态,用于标识任务是否在等待通知。

通知状态有3种取值:

  • taskNOT_WAITING_NOTIFICATION:任务没有在等待通知
  • taskWAITING_NOTIFICATION:任务在等待通知
  • taskNOTIFICATION_RECEIVED:任务接收到了通知,也被称为pending(有数据了,待处理)
##define taskNOT_WAITING_NOTIFICATION              ( ( uint8_t ) 0 )  /* 也是初始状态 */
##define taskWAITING_NOTIFICATION                  ( ( uint8_t ) 1 )
##define taskNOTIFICATION_RECEIVED                 ( ( uint8_t ) 2 )

通知值可以有很多种类型:

  • 计数值
  • 位(类似事件组)
  • 任意数值

1.2 发通知和取通知流程

发通知:

  1. 关中断
  2. 获取任务通知的原状态,发送通知,State更新为taskNOTIFICATION_RECEIVED
  3. 根据 eAction 更新任务通知值
  4. 被通知任务由于等待通知而挂起,即State= taskWAITING_NOTIFICATION,唤醒:
    * DelayList ——>ReadList
    * 如果唤醒的任务优先级 > 当前任务
    + 任务切换
  5. 开中断

等通知:

ulTaskNotifyTake:

  1. 关中断
  2. 没收到通知(value = 0)
    1. 标记 state为 等待通知,taskWAITING_NOTIFICATION
    2. 休眠
      1. 根据超时时间放入延时链表
      2. 切换任务
  3. 收到了通知(若其他任务或中断向该任务发送了通知,或超时唤醒)
    1. 取 value,
    2. 是否清除,不清除,则减一。
    3. 重设 State = taskNOT_WAITING_NOTIFICATION。
  4. 开中断

xTaskNotifyWait:

  1. 关中断
  2. 没有收到通知,即 State != taskNOTIFICATION_RECEIVED
    1. 在收通知前是否将某些位清除,value &= ~ulBitsToClearOnEntry
    2. 标识 State = taskWAITING_NOTIFICATION,等待通知
    3. 休眠(阻塞)
      1. 根据超时时间放入延时链表
      2. 切换任务
  3. 收到了通知(若其他任务或中断向该任务发送了通知,或超时唤醒)
    1. 取出通知值 value;
    2. 退出前清零,Value &= ~ulBitsToClearOnExit
    3. 重设 State = taskNOT_WAITING_NOTIFICATION。
  4. 开中断

2. 任务通知函数

使用任务通知,可以实现轻量级的队列(长度为1)、邮箱(覆盖的队列)、计数型信号量、二进制信号量、事件组。

2.1 两类函数

任务通知有2套函数,简化版、专业版,列表如下:

  • 简化版函数的使用比较简单,它实际上也是使用专业版函数实现的
  • 专业版函数支持很多参数,可以实现很多功能
简化版专业版
发出通知xTaskNotifyGive
vTaskNotifyGiveFromISR
xTaskNotify
xTaskNotifyFromISR
取出通知ulTaskNotifyTakexTaskNotifyWait
2.1.1 简化版—xTaskNotifyGive/ulTaskNotifyTake

任务中使用xTaskNotifyGive函数,在ISR中使用vTaskNotifyGiveFromISR函数,都是直接给其他任务发送通知

  • 使得通知值加一
  • 并使得通知状态变为"pending",也就是taskNOTIFICATION_RECEIVED,表示有数据了、待处理

可以使用ulTaskNotifyTake函数来取出通知值

  • 如果通知值等于0,则阻塞(可以指定超时时间)
  • 当通知值大于0时,任务从阻塞态进入就绪态
  • 在ulTaskNotifyTake返回之前,还可以做些清理工作:把通知值减一,或者把通知值清零

使用ulTaskNotifyTake函数可以实现轻量级的、高效的二进制信号量、计数型信号量。

2.1.2 专业版–xTaskNotify/xTaskNotifyWait

xTaskNotify 函数功能更强大,可以使用不同参数实现各类功能,比如:

  • 让接收任务的通知值加一:这时 xTaskNotify() 等同于 xTaskNotifyGive()
  • 设置接收任务的通知值的某一位、某些位,这就是一个轻量级的、更高效的事件组
  • 把一个新值写入接收任务的通知值:上一次的通知值被读走后,写入才成功。这就是轻量级的、长度为1的队列
  • 用一个新值覆盖接收任务的通知值:无论上一次的通知值是否被读走,覆盖都成功。类似 xQueueOverwrite() 函数,这就是轻量级的邮箱。

xTaskNotify() 比 xTaskNotifyGive() 更灵活、强大,使用上也就更复杂。xTaskNotifyFromISR() 是它对应的ISR版本。

这两个函数用来发出任务通知,使用哪个函数来取出任务通知呢?

使用 xTaskNotifyWait() 函数!它比 ulTaskNotifyTake() 更复杂:

  • 可以让任务等待(可以加上超时时间),等到任务状态为"pending"(也就是有数据)
  • 还可以在函数进入、退出时,清除通知值的指定位

这几个函数的原型如下:

BaseType_t xTaskNotify( TaskHandle_t xTaskToNotify, 
                       uint32_t ulValue, 
                       eNotifyAction eAction );

BaseType_t xTaskNotifyFromISR( TaskHandle_t xTaskToNotify,
                               uint32_t ulValue, 
                               eNotifyAction eAction, 
                               BaseType_t *pxHigherPriorityTaskWoken );

BaseType_t xTaskNotifyWait( uint32_t ulBitsToClearOnEntry, 
                            uint32_t ulBitsToClearOnExit, 
                            uint32_t *pulNotificationValue, 
                            TickType_t xTicksToWait );

xTaskNotifyFromISR函数跟xTaskNotify很类似,就多了最后一个参数pxHigherPriorityTaskWoken。在很多ISR函数中,这个参数的作用都是类似的,使用场景如下:

  • 被通知的任务,可能正处于阻塞状态
  • xTaskNotifyFromISR函数发出通知后,会把接收任务从阻塞状态切换为就绪态
  • 如果被唤醒的任务的优先级,高于当前任务的优先级,则"*pxHigherPriorityTaskWoken"被设置为pdTRUE,这表示在中断返回之前要进行任务切换。

2.2 发送任务通知函数

2.2.1 发送任务通知函数xTaskGenericNotify()
  • xTaskToNotify:被通知的任务句柄;
  • ulValue:发送的通知值;
  • eAction:枚举类型,指明更新通知值的方式。
typedef enum
{
	eNoAction = 0,				/* 不更新通知值就通知任务 */
	eSetBits,					/* 在任务通知值中设置位 */
	eIncrement,					/* 任务通知值递增 1 */
	eSetValueWithOverwrite,		/* (可覆盖)将任务通知值设置为特定值 */
	eSetValueWithoutOverwrite	/* (不可覆盖)将任务通知值设置为特定值*/
} eNotifyAction;
  • pulPreviousNotificationValue:任务原本的通知值返回。
#if( configUSE_TASK_NOTIFICATIONS == 1 )

	BaseType_t xTaskGenericNotify( TaskHandle_t xTaskToNotify,
                                  uint32_t ulValue, 
                                  eNotifyAction eAction, 
                        uint32_t *pulPreviousNotificationValue )
	{
	TCB_t * pxTCB;
	BaseType_t xReturn = pdPASS;
	uint8_t ucOriginalNotifyState;

		configASSERT( xTaskToNotify );
		pxTCB = ( TCB_t * ) xTaskToNotify;

		taskENTER_CRITICAL();
		{
			if( pulPreviousNotificationValue != NULL )
			{
                /* 回传未被更新的任务通知值 */
				*pulPreviousNotificationValue = pxTCB->ulNotifiedValue;
			}
            /* 获取任务通知的状态,
            看看任务是否在等待通知,方便在发送通知后恢复任务 */
			ucOriginalNotifyState = pxTCB->ucNotifyState;
            /* 不管状态是怎么样的,反正现在发送通知,
                任务就收到任务通知 */
			pxTCB->ucNotifyState = taskNOTIFICATION_RECEIVED;
            /* 指定更新任务通知的方式 */
			switch( eAction )
			{
              /*通知值按位或上 ulValue。
                使用这种方法可以某些场景下代替事件组,
                但执行速度更快。*/  
				case eSetBits	:
					pxTCB->ulNotifiedValue |= ulValue;
					break;
                    
              /* 被通知任务的通知值增加 1,
                 这种发送通知方式,参数 ulValue 未使用 */
				case eIncrement	:
					( pxTCB->ulNotifiedValue )++;
					break;
              /* 将被通知任务的通知值设置为 ulValue。
                 无论任务是否还有通知,都覆盖当前任务通知值。
                 使用这种方法,
                 可以在某些场景下代替 xQueueoverwrite()函数,
                  但执行速度更快。 */
				case eSetValueWithOverwrite	:
					pxTCB->ulNotifiedValue = ulValue;
					break;

            /* 如果被通知任务当前没有通知,
               则被通知任务的通知值设置为 ulValue;
               在某些场景下替代长度为 1 的 xQueuesend(),
                    但速度更快。 */        
				case eSetValueWithoutOverwrite :
					if( ucOriginalNotifyState != taskNOTIFICATION_RECEIVED )
					{
						pxTCB->ulNotifiedValue = ulValue;
					}
					else
					{
						/* 如果被通知任务还没取走上一个通知,本次发送通知,
                        任务又接收到了一个通知,则这次通知值丢弃,
                        在这种情况下,函数调用失败并返回pdFAIL
                        */
						xReturn = pdFAIL;
					}
					break;
                /* 发送通知但不更新通知值,这意味着参数 ulValue 未使用*/
				case eNoAction:
					/* The task is being notified without its notify value being
					updated. */
					break;
			}

			traceTASK_NOTIFY();

			/* 如果被通知任务由于等待任务通知而挂起 */
			if( ucOriginalNotifyState == taskWAITING_NOTIFICATION )
			{
                /* 唤醒,从阻塞链表删除,放入就绪链表 */
				( void ) uxListRemove( &( pxTCB->xStateListItem ) );
				prvAddTaskToReadyList( pxTCB );

                /* 刚刚唤醒的任务优先级比当前任务高 */
				if( pxTCB->uxPriority > pxCurrentTCB->uxPriority )
				{
					/* 任务切换 */
					taskYIELD_IF_USING_PREEMPTION();
				}
				else
				{
					mtCOVERAGE_TEST_MARKER();
				}
			}
			else
			{
				mtCOVERAGE_TEST_MARKER();
			}
		}
		taskEXIT_CRITICAL();

		return xReturn;
	}

#endif /* configUSE_TASK_NOTIFICATIONS */

创建了三个任务:

2.3 获取任务通知函数
2.3.1 ulTaskNotifyTake
uint32_t ulTaskNotifyTake( BaseType_t xClearCountOnExit, 
                          TickType_t xTicksToWait )

对于这个函数,任务通知值为 0,对应信号量无效,如果任务设置了阻塞等待,任务 被阻塞挂起。当其他任务或中断发送了通知值使其不为 0 后,通知变为有效,等待通知的 任务将获取到通知,并且在退出时候根据用户传递的第一个参数 xClearCountOnExit 选择清 零通知值或者执行减一操作。

作为二值信号量和计数信号量的一种轻量级实现,速度更快。

退出的时候处理任务的通知值的时候有两种方法:

  • 在函数退出时将通知值清零,这种方法适用于实现二值信号量;
  • 在函数退出时将通知 值减 1,这种方法适用于实现计数信号量。
#if( configUSE_TASK_NOTIFICATIONS == 1 )

	uint32_t ulTaskNotifyTake( BaseType_t xClearCountOnExit, 
                               TickType_t xTicksToWait )
	{
	uint32_t ulReturn;

		taskENTER_CRITICAL();//进入中断临界区
		{
			/* 如果通知值为 0 ,阻塞任务,
            默认初始化通知值为 0, 说明没有未读通知 */
			if( pxCurrentTCB->ulNotifiedValue == 0UL )
			{
				/* 标记任务状态:等待消息通知 */
				pxCurrentTCB->ucNotifyState = taskWAITING_NOTIFICATION;
                /* 用户指定超时时间,那就进入等待状态 */
				if( xTicksToWait > ( TickType_t ) 0 )
				{
                    /* 根据用户指定超时时间将任务添加到延时链表*/
					prvAddCurrentTaskToDelayedList( xTicksToWait, pdTRUE );
					traceTASK_NOTIFY_TAKE_BLOCK();

					/* 切换任务*/
					portYIELD_WITHIN_API();
				}
				else
				{
					mtCOVERAGE_TEST_MARKER();
				}
			}
			else
			{
				mtCOVERAGE_TEST_MARKER();
			}
		}
		taskEXIT_CRITICAL();
        /* 到这里说明其他任务或中断向这个任务发送了通知,
        或者任务阻塞超时,
            现在继续处理*/
		taskENTER_CRITICAL();
		{
            // 获取任务通知值
			traceTASK_NOTIFY_TAKE();
			ulReturn = pxCurrentTCB->ulNotifiedValue;
            
            // 看看任务通知是否有效,有效则返回
			if( ulReturn != 0UL )
			{
                //是否需要清除通知
				if( xClearCountOnExit != pdFALSE )
				{
					pxCurrentTCB->ulNotifiedValue = 0UL;
				}
				else
				{   // 不清除,就减一
					pxCurrentTCB->ulNotifiedValue = ulReturn - ( uint32_t ) 1;
				}
			}
			else
			{
				mtCOVERAGE_TEST_MARKER();
			}
            //恢复任务通知状态
			pxCurrentTCB->ucNotifyState = taskNOT_WAITING_NOTIFICATION;
		}
		taskEXIT_CRITICAL();

		return ulReturn;
	}

#endif /* configUSE_TASK_NOTIFICATIONS */

2.3.2 xTaskNotifyWait
BaseType_t xTaskNotifyWait( uint32_t ulBitsToClearOnEntry, 
                           uint32_t ulBitsToClearOnExit, 
                           uint32_t *pulNotificationValue, 
                           TickType_t xTicksToWait )

用于实现全功能版的等待任务通知,根据用户指定的参数的不 同,可以灵活的用于实现轻量级的消息队列队列、二值信号量、计数信号量和事件组功能, 并带有超时等待。

#if( configUSE_TASK_NOTIFICATIONS == 1 )

	BaseType_t xTaskNotifyWait( uint32_t ulBitsToClearOnEntry, 
                               uint32_t ulBitsToClearOnExit, 
                               uint32_t *pulNotificationValue, 
                               TickType_t xTicksToWait )
	{
	BaseType_t xReturn;

		taskENTER_CRITICAL(); /* 进入临界段 */
		{
			/* 只有任务当前没有收到任务通知,才会将任务阻塞 */
			if( pxCurrentTCB->ucNotifyState != taskNOTIFICATION_RECEIVED )
			{
				/* 使用任务通知值之前,根据用户指定参数 
                ulBitsToClearOnEntry
                将通知值的某些或全部位清零 */
				pxCurrentTCB->ulNotifiedValue &= ~ulBitsToClearOnEntry;

				/* 设置任务状态标识:等待通知 */
				pxCurrentTCB->ucNotifyState = taskWAITING_NOTIFICATION;
                
                /* 挂起任务等待通知或者进入阻塞态 */
				if( xTicksToWait > ( TickType_t ) 0 )
				{
                    /* 根据用户指定超时时间将任务添加到延时列表 */
					prvAddCurrentTaskToDelayedList( xTicksToWait, pdTRUE );
					traceTASK_NOTIFY_WAIT_BLOCK();

					/* 任务切换 */
					portYIELD_WITHIN_API();
				}
				else
				{
					mtCOVERAGE_TEST_MARKER();
				}
			}
			else
			{
				mtCOVERAGE_TEST_MARKER();
			}
		}
		taskEXIT_CRITICAL();
        /*程序能执行到这里说明其它任务或中断向这个任务发送了通知
        或者任务阻塞超时,
            现在继续处理*/
		taskENTER_CRITICAL();
		{
			traceTASK_NOTIFY_WAIT();

			if( pulNotificationValue != NULL )
			{
				/* 返回当前通知值,通过指针参数传递 */
				*pulNotificationValue = pxCurrentTCB->ulNotifiedValue;
			}

			/* 没有收到任务通知 */
			if( pxCurrentTCB->ucNotifyState != taskNOTIFICATION_RECEIVED )
			{
				/* A notification was not received. */
				xReturn = pdFALSE;
			}
			else
			{
				/* 收到任务值,先将参数 ulBitsToClearOnExit 
                   取反后与通知值位做按位与运算
                   在退出函数前,将通知值的某些或者全部位清零.  */
				pxCurrentTCB->ulNotifiedValue &= ~ulBitsToClearOnExit;
				xReturn = pdTRUE;
			}
            /* 重新设置任务通知状态 */
			pxCurrentTCB->ucNotifyState = taskNOT_WAITING_NOTIFICATION;
		}
		taskEXIT_CRITICAL();

		return xReturn;
	}

#endif /* configUSE_TASK_NOTIFICATIONS */

3. 任务通知实验

3.1 代码-任务通知

本节代码为:27_tasknotification_car_game,主要看nwatch\game2.c。

car1运行到终点后,给car2发送轻量级信号量给car3发送数值。car2等待轻量级信号量,car3等待特定的通知值。

使用任务通知时,需要知道对方的任务句柄,创建任务时要记录任务句柄,代码如下:

40 static TaskHandle_t g_TaskHandleCar2;

41 static TaskHandle_t g_TaskHandleCar3;

/* 省略 */

315 xTaskCreate(Car1Task, "car1", 128, &g_cars[0], osPriorityNormal, NULL);

316 xTaskCreate(Car2Task, "car2", 128, &g_cars[1], osPriorityNormal+2, &g_TaskHandleCar2);

317 xTaskCreate(Car3Task, "car3", 128, &g_cars[2], osPriorityNormal+2, &g_TaskHandleCar3);	

car1到达终点后,向car2、car3发出任务通知,代码如下:

145   /* 发出任务通知给car2 */

146   xTaskNotifyGive(g_TaskHandleCar2);

147		/* 发出任务通知给car3 */
        /* 覆盖。 无论如何,不管通知状态是否为"pendng", 
         * 通知值 = ulValue。 */
148   xTaskNotify(g_TaskHandleCar3, 100, eSetValueWithOverwrite);

car2等待轻量级信号量,代码如下:

/* 函数返回前清零,一直等待,直到通知值大于0*/
176   ulTaskNotifyTake(pdTRUE, portMAX_DELAY);

car3等待通知值为100,代码如下:

224   uint32_t val;

/* 省略 */

241   do

242   {
        /* 入口处全部清零,出口处全部清零,取出通知值给val,一直等待*/
243      xTaskNotifyWait(~0, ~0, &val, portMAX_DELAY);

244  } while (val != 100);

实验现象:car1到达终点后,car2、car3才会启动。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【DBA Part01】国产Linux上安装Oracle进行数据迁移

内容如下: 1.1.生产环境RHEL/OEL Linux8Oracle11gR2安装配置 1.2.国产麒麟操作系统Oracle11gR2安装配置 1.3.国产麒麟操作系统Oracle11gR2 RAC集群安装配置 1.4.Oracle11gR2迁移到国产麒麟操作系统(单机/RAC) 本阶段课程项目需求说明&am…

[C++刷题] 基础小知识点(1) 乘方函数pow()

乘方 pow() 该函数在math.h头文件中 例如: 求圆的面积公式 s3.14*pow(r,2); 也可用于开方 pow(4,1.0/3) 注意这里要写1.0, 不然1/30,该函数会失效 例题: #include<iostream> using namespace std; #include<math.h>) int main() {int h;int r;cin >> h &g…

c++算法第3天

本篇文章包含三道算法题&#xff0c;难度由浅入深&#xff0c;适合新手练习哟 目录 第一题 题目链接 题目解析 代码原理 代码编写 本题总结 第二题 题目链接 题目解析 代码原理 代码编写 第三题 题目链接 题目解析 代码原理 代码编写 第一题 题目链接 [NOIP2…

ai抠图软件哪个好?一些快速掌握的基本抠图技巧,学习

有谁和小编一样&#xff0c;不修图还好&#xff0c;一要修图&#xff0c;100%会踩坑&#xff01; 没错&#xff0c;就是踩了网页上各种ai抠图软件免费版广告的坑&#xff0c;抠图不干净就算了&#xff0c;还会损坏原来的图片文件就很过分&#xff01; 伤心事不再多说&#xff0…

Junit单元测试时提示:Method should have no parameters

场景 Junit中运行单元测试时提示&#xff1a; Method XXX should have no parameters 如图&#xff1a; 代码如下&#xff1a; package com.ws.test.common;import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.extensi…

软考倒计时!中项计算题带练来啦!

已经进入下半年软考最后一个月&#xff0c;各位小伙伴的软考复习也已经来到了最后冲刺阶段&#xff0c;大家在知识点的背诵同时也别忽略计算题的练习&#xff0c;那么和小编一起来中项计算题的跟练吧&#xff01; 01、试题一 某信息系统项目包含如下 A、B、C、D、E&#xff0c…

Helm入门到实战演示

目录 1、Helm介绍 2、Helm v3版本变化 3、安装Helm v3 4、配置国内存放chart仓库的地址 5、Helm基本使用 5.1 搜索和下载Chart 5.2 部署chart 5.2.1 helm部署memcached服务 5.3 release相关操作 6、自定义Chart模板 6.1 自定义一个Chart 6.2 Chart.yaml编写规则 6.…

字节撒钱啦,快来薅羊毛!!!!!!!!!!!!!!!!

豆包MarsCode是给开发用的一款智能助手&#xff0c;能够实现代码智能补全、AI问答、智能测试等功能。 目前有一个推广活动&#xff0c;体验送10火星币&#xff0c;邀请一个新用户得10火星币&#xff0c;20个火星币就可以获得20京东E卡&#xff0c;隔日到账。 整个过程10分钟不…

STM32CUBEIDE的使用【三】RTC

于正点原子潘多拉开发板&#xff0c;使用stm32官方免费软件进行开发 CubeMx 配置 使用CubeMx 配置RTC 勾选RTC 设置日期和时间 配置LCD的引脚用来显示 STM32CUBEIDE 在usbd_cdc_if.c中重定向printf函数用于打印 #include <stdarg.h>void usb_printf(const char *f…

海思hi3536c配置内核支持USB摄像头

linux内核版本&#xff1a;linux-3.18.20 配置步骤 进入Device Drivers 选择Multimedia support&#xff0c;并进入 选择Media USB Adapters&#xff0c;并进入 如下图&#xff0c;选择这几项&#xff1a; 保存退出&#xff0c;重新编译内核下载 内核更新后&#xff0c…

标准版admin后台页面添加及开发操作流程及注意事项

基础介绍 CRMEB后台管理是基于vue2技术栈进行开发搭建的 Vue Router 使用的是v3版本&#xff0c;mode为history模式 如需修改 mode 请在src/setting.js中修改routerMode 新建页面 新建路由 根据目录结构&#xff0c;需要在src/router/modules中对应模块中&#xff0c;添加对…

FineReport 数据显示方式

在客户端的浏览器中&#xff0c;查看报表的效果都是通过对基础数据进行加工而来的。 制作一张报表模板&#xff0c;首先需准备报表所需的基础数据。 基础数据的来源方式有多种&#xff0c;不管数据来源于哪种方式&#xff0c;经过哪些预处理&#xff0c;最终都是返回如下图1所示…

mysql数据同步ES方案---Canal

引言 之前公司开发社交APP的时候 在开发和初上线阶段&#xff0c;我们一直采用的是 MySQL 来存储用户的各种数据&#xff0c;满足基本的查询需求。当时系统业务量小&#xff0c;数据规模有限&#xff0c;因此 MySQL 能很好地支持查询操作&#xff0c;响应速度快&#xff0c;系…

你认为BI不需要建模,那就大错特错了

BI 是一种数据类的技术解决方案&#xff0c;将许多来自不同企业业务系统的数据提取有分析价值的数据进行清洗、转换和加载&#xff0c;就是抽取Extraction、转换 Transformation、加载Loading 的ETL过程&#xff0c;最终合并到一个数据仓库中&#xff0c;按照一定的建模方式例如…

【学习】安装cudf和cuml

为了把cpu上跑的SVM程序搬到GPU上跑&#xff0c;需要装这俩包&#xff0c;但是搞了半天装不上&#xff0c;最后发现是清华源的问题。换了中科大的源没问题了。 rapids官网&#xff1a;RAPIDS | GPU Accelerated Data Science 官网安装&#xff1a;Installation Guide - RAPID…

安装CentOS 8镜像和创建CentOS 8虚拟机教程

一、安装虚拟机 网上查找教程&#xff0c;我用的是VMware 17 二、下载CentOS 8镜像 1.阿里云下载CentOS 8镜像 centos安装包下载_开源镜像站-阿里云 (aliyun.com) 选择需要下载的版本&#xff0c;(建议)下载dvd1版本的iso&#xff08;也有下载boot版本的iso&#xff0c;创…

用柔性神经k-Opt学习搜索路径问题的可行和不可行区域(未完,先看前驱文章L2S)

文章目录 Abstract1 IntroductionAbstract 介绍了一种名为 Neural k-Opt(NeuOpt)的新型学习搜索(L2S)求解器,用于解决路径问题。它学习执行基于定制的动作分解方法和定制的循环双流(Recurrent Dual-Stream)解码器的灵活 k-opt 交换。 作为一项开创性的工作,我们绕过了…

使用YOLOv8进行实时人员跟踪和计数

在计算机视觉领域,实时跟踪和统计人数对于各种应用至关重要,从监控到事件管理。在这篇博客文章中,我们将探讨如何利用YOLOv8和ByteTracker实现准确的人数统计。 引言 YOLOv8(You Only Look Once,第八版)是一种以其速度和准确性而闻名的最新对象检测模型。 ByteTracker是一…

Oracle DECODE 丢失时间精度的原因与解决方案

在Oracle数据库中&#xff0c;DECODE 函数是一个非常实用的条件处理函数&#xff0c;通常用于替代简单的 CASE WHEN 语句。它根据给定的值列表进行匹配&#xff0c;如果匹配成功则返回相应的值。如果不匹配&#xff0c;返回一个默认值。 问题描述 SELECT DECODE(-21, -1, NU…

低代码可视化-uniapp购物车页面-代码生成器

购物车页面是电子商务网站或应用程序中的一个关键功能页面&#xff0c;它允许用户查看、编辑和管理他们选择加入购物车的商品。下面通过低代码可视化实现一个uniapp购物车页面&#xff0c;把购物车整个事件都集成进去。实现完成后可以保存为页面模板。 收货地址选择 如果尚未…