当前位置: 首页 > news >正文

FreeRTOS从入门到精通 第十六章(任务通知)

参考教程:【正点原子】手把手教你学FreeRTOS实时系统_哔哩哔哩_bilibili

一、任务通知简介

1、概述

(1)任务通知顾名思义是用来通知任务的,任务控制块中的结构体成员变量ulNotifiedValue就是这个通知值。

(2)队列、信号量、事件标志组与任务通知的区别:

①使用队列、信号量、事件标志组时都需另外创建一个结构体,通过中间的结构体进行任务间的通信。

②使用任务通知时,任务结构体TCB中就包含了内部对象,可以直接接收其它任务发过来的“通知”。

(3)只要合理、灵活地利用任务通知的特点,可以在一些场合中替代队列、信号量、事件标志组。

(4)任务通知的优势及劣势:

①优势:

[1]使用任务通知向任务发送事件或数据,比使用队列、事件标志组或信号量快得多。

[2]使用其它方法进行任务间通讯时都要先创建对应的结构体,而使用任务通知则无需额外创建结构体。

②劣势:

[1]ISR没有任务结构体,所以无法给ISR发送数据,但是ISR可以使用任务通知的功能,发数据给任务。

[2]任务通知只能是被指定的一个任务接收并处理,无法广播给多个任务。

[3]任务通知是通过更新任务通知值来发送数据的,任务结构体中只有一个任务通知值,只能保持一个数据,无法缓存多个数据。

[4]发送方无法进入阻塞状态等待。

2、任务通知值和通知状态

(1)任务都有一个结构体——任务控制块TCB,它里边有两个结构体成员变量与任务通知相关,一个是uint32_t类型,用来表示通知值,另一个是uint8_t类型,用来表示通知状态。

typedef struct tskTaskControlBlock 
{… …#if ( configUSE_TASK_NOTIFICATIONS  ==  1 )volatile uint32_t ulNotifiedValue[configTASK_NOTIFICATION_ARRAY_ENTRIES];volatile uint8_t ucNotifyState[configTASK_NOTIFICATION_ARRAY_ENTRIES];#endif… …
}tskTCB;
#define configTASK_NOTIFICATION_ARRAY_ENTRIES	1  	//定义任务通知数组的大小,默认为1

(2)任务通知值的更新方式有多种类型:

①计数值(数值累加,类似信号量)。

②相应位置1(类似事件标志组)。

③写任意数值(支持覆写和不覆写,类似队列)。

(3)任务通知状态共有3种取值:

①任务未等待通知:任务通知默认的初始化状态。

②任务在等待通知:接收方已经准备好了(调用了接收任务通知函数),等待发送方给通知。

③任务在等待接收:发送方已经将通知发送出去(调用了发送任务通知函数),等待接收方接收。

#define	    taskNOT_WAITING_NOTIFICATION ( ( uint8_t ) 0 )		//任务未等待通知
#define 	taskWAITING_NOTIFICATION	 ( ( uint8_t ) 1 )		//任务在等待通知
#define 	taskNOTIFICATION_RECEIVED    ( ( uint8_t ) 2 )		//任务在等待接收

二、任务通知相关API函数介绍

1、任务通知相关API函数概览

(1)任务通知API函数主要有两类——发送通知、接收通知,发送通知API函数可以用于任务和中断服务函数中,而接收通知API函数只能用在任务中。

(2)发送通知相关API函数:

函数

描述

xTaskNotify()

发送通知,带有通知值

xTaskNotifyAndQuery()

发送通知,带有通知值并且保留接收任务的原通知值

xTaskNotifyGive()

发送通知,不带通知值

xTaskNotifyFromISR()

在中断中发送任务通知

xTaskNotifyAndQueryFromISR()

vTaskNotifyGiveFromISR()

(3)接收通知相关API函数:

函数

描述

ulTaskNotifyTake()

获取任务通知,可以设置在退出此函数的时候将任务通知值清零或者减一;

当任务通知用作二值信号量或者计数信号量的时候,使用此函数来获取信号量

xTaskNotifyWait()

获取任务通知,比 ulTaskNotifyTak()更为复杂,可获取通知值和清除通知值的指定位;

当任务通知用作于事件标志组或队列时,使用此函数来获取

2、发送通知相关API函数

(1)xTaskNotifyAndQuery函数、xTaskNotify函数和xTaskNotifyGive函数,它们的底层函数实际上都是同一个函数。

#define xTaskNotifyAndQuery(xTaskToNotify, ulValue, eAction, pulPreviousNotifyValue)	 \   xTaskGenericNotify( ( xTaskToNotify ), ( tskDEFAULT_INDEX_TO_NOTIFY ), ( ulValue ), ( eAction ),( pulPreviousNotifyValue ) )#define	xTaskNotify(xTaskToNotify, ulValue, eAction) 	 \   xTaskGenericNotify( ( xTaskToNotify ),( tskDEFAULT_INDEX_TO_NOTIFY ), ( ulValue ), ( eAction ), NULL )#define 	xTaskNotifyGive(xTaskToNotify)	  \   xTaskGenericNotify( ( xTaskToNotify ),( tskDEFAULT_INDEX_TO_NOTIFY ),( 0 ),eIncrement,NULL )

(2)xTaskGenericNotify函数:

①函数入口定义:

BaseType_t xTaskGenericNotify
(TaskHandle_t  	xTaskToNotify,        //接收任务通知的任务句柄UBaseType_t 		uxIndexToNotify,      //任务的指定通知(任务通知相关数组成员)uint32_t 			ulValue,             //任务通知值eNotifyAction 	eAction,             //通知方式(通知值更新方式)uint32_t * 		pulPreviousNotificationValue  //用于保存更新前的任务通知值(为NULL则不保存)
)

②任务通知方式共有以下几种:

typedef  enum
{    eNoAction = 0, 			//无操作eSetBits					//更新指定biteIncrement				//通知值加1eSetValueWithOverwrite	//覆写的方式更新通知值eSetValueWithoutOverwrite	//不覆写通知值
}eNotifyAction;

③函数源码剖析:

BaseType_t xTaskGenericNotify( TaskHandle_t xTaskToNotify,UBaseType_t uxIndexToNotify,uint32_t ulValue,eNotifyAction eAction,uint32_t * pulPreviousNotificationValue )
{TCB_t * pxTCB;BaseType_t xReturn = pdPASS;uint8_t ucOriginalNotifyState;traceENTER_xTaskGenericNotify( xTaskToNotify, uxIndexToNotify, ulValue, eAction, pulPreviousNotificationValue );configASSERT( uxIndexToNotify < configTASK_NOTIFICATION_ARRAY_ENTRIES );configASSERT( xTaskToNotify );pxTCB = xTaskToNotify;taskENTER_CRITICAL();{if( pulPreviousNotificationValue != NULL )  //判断是否需要保存原先的任务通知值{*pulPreviousNotificationValue = pxTCB->ulNotifiedValue[ uxIndexToNotify ];  //保存原先的任务通知值至缓冲区}//记录目标任务先前的通知状态ucOriginalNotifyState = pxTCB->ucNotifyState[ uxIndexToNotify ];//赋值当前的任务状态(等待接收状态,即发送方的数据等待接收方接收)pxTCB->ucNotifyState[ uxIndexToNotify ] = taskNOTIFICATION_RECEIVED;switch( eAction )    //有四种通知值的更新方式,需要判断使用哪种{case eSetBits:                  //按位更新(类似事件标志组)pxTCB->ulNotifiedValue[ uxIndexToNotify ] |= ulValue;break;case eIncrement:                //用计数的方式更新通知值(类似信号量)( pxTCB->ulNotifiedValue[ uxIndexToNotify ] )++;break;case eSetValueWithOverwrite:     //覆写的方式更新通知值(类似队列)pxTCB->ulNotifiedValue[ uxIndexToNotify ] = ulValue;break;case eSetValueWithoutOverwrite:  //不覆写的方式更新通知值(类似队列)if( ucOriginalNotifyState != taskNOTIFICATION_RECEIVED ){pxTCB->ulNotifiedValue[ uxIndexToNotify ] = ulValue;}else{xReturn = pdFAIL;   //如果有通知值还未被接收,不能覆写}break;case eNoAction:break;default:configASSERT( xTickCount == ( TickType_t ) 0 );break;}traceTASK_NOTIFY( uxIndexToNotify );if( ucOriginalNotifyState == taskWAITING_NOTIFICATION )   //如果接收方已准备好接收通知值{listREMOVE_ITEM( &( pxTCB->xStateListItem ) );   //将接收方任务从阻塞列表中移出prvAddTaskToReadyList( pxTCB );       //将接收方任务添加进就绪列表中configASSERT( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) == NULL );#if ( configUSE_TICKLESS_IDLE != 0 ){prvResetNextTaskUnblockTime();}#endiftaskYIELD_ANY_CORE_IF_USING_PREEMPTION( pxTCB );  //如因优先级问题需要任务切换,执行即可}else{mtCOVERAGE_TEST_MARKER();}}taskEXIT_CRITICAL();traceRETURN_xTaskGenericNotify( xReturn );return xReturn;
}

3、接收通知相关API函数

(1)ulTaskNotifyTake函数:

①函数定义:

#define ulTaskNotifyTake(xClearCountOnExit, xTicksToWait) 				\ulTaskGenericNotifyTake( ( tskDEFAULT_INDEX_TO_NOTIFY ),			\( xClearCountOnExit ), 						\( xTicksToWait ) ) 

②函数参数:

形参

描述

xClearCountOnExit 

指定在成功接收通知后,将通知值清零或减 1

pdTRUE:把通知值清零;pdFALSE:把通知值减1

xTicksToWait 

阻塞等待任务通知值的最大时间

③返回值:

返回值

描述

0

接收失败

非 0 

接收成功,返回任务通知的通知值

④ulTaskGenericNotifyTake函数源码剖析:

uint32_t ulTaskGenericNotifyTake( UBaseType_t uxIndexToWaitOn,BaseType_t xClearCountOnExit,TickType_t xTicksToWait )
{uint32_t ulReturn;BaseType_t xAlreadyYielded, xShouldBlock = pdFALSE;traceENTER_ulTaskGenericNotifyTake(uxIndexToWaitOn,xClearCountOnExit,xTicksToWait);configASSERT( uxIndexToWaitOn < configTASK_NOTIFICATION_ARRAY_ENTRIES );vTaskSuspendAll();{taskENTER_CRITICAL();{if( pxCurrentTCB->ulNotifiedValue[ uxIndexToWaitOn ] == 0U )  //判断任务通知值是否为0,是则说明发送方还未发送非0通知值{pxCurrentTCB->ucNotifyState[ uxIndexToWaitOn ] = taskWAITING_NOTIFICATION;  //将状态更改为在等待通知if( xTicksToWait > ( TickType_t ) 0 )  //如果阻塞时间大于0xShouldBlock = pdTRUE;  //任务需要阻塞elsemtCOVERAGE_TEST_MARKER();}else{mtCOVERAGE_TEST_MARKER();}}taskEXIT_CRITICAL();if( xShouldBlock == pdTRUE )  //如果任务需要阻塞{traceTASK_NOTIFY_TAKE_BLOCK( uxIndexToWaitOn );prvAddCurrentTaskToDelayedList( xTicksToWait, pdTRUE ); //将任务添加进阻塞列表中}else{mtCOVERAGE_TEST_MARKER();}}xAlreadyYielded = xTaskResumeAll();if( ( xShouldBlock == pdTRUE ) && ( xAlreadyYielded == pdFALSE ) ){taskYIELD_WITHIN_API();  //如果任务进入阻塞态,则需要执行任务切换(if判断的作用是防止重复切换)}else{mtCOVERAGE_TEST_MARKER();}taskENTER_CRITICAL();{traceTASK_NOTIFY_TAKE( uxIndexToWaitOn );ulReturn = pxCurrentTCB->ulNotifiedValue[ uxIndexToWaitOn ];if( ulReturn != 0U )   //如果任务通知值不为0,说明发送方已将非0通知值发送{if( xClearCountOnExit != pdFALSE )  //根据函数参数判断通知值如何处理{pxCurrentTCB->ulNotifiedValue[ uxIndexToWaitOn ] = ( uint32_t ) 0U; //通知值清零(用于模拟二值信号量)}else{pxCurrentTCB->ulNotifiedValue[uxIndexToWaitOn] = ulReturn - (uint32_t)1;  //通知值-1(用于模拟计数型信号量)}}else{mtCOVERAGE_TEST_MARKER();}pxCurrentTCB->ucNotifyState[ uxIndexToWaitOn ] = taskNOT_WAITING_NOTIFICATION;  //状态更改为未等待通知}taskEXIT_CRITICAL();traceRETURN_ulTaskGenericNotifyTake( ulReturn );return ulReturn;
}

(2)xTaskNotifyWait函数:

①函数定义:

#define xTaskNotifyWait(ulBitsToClearOnEntry, 				\ulBitsToClearOnExit, 						\pulNotificationValue, 						\xTicksToWait) 							\xTaskGenericNotifyWait(tskDEFAULT_INDEX_TO_NOTIFY, 	\( ulBitsToClearOnEntry ), 					\( ulBitsToClearOnExit ), 					\( pulNotificationValue ), 					\( xTicksToWait )) 

②函数参数:

形参

描述

uxIndexToWaitOn 

任务的指定通知(任务通知相关数组成员)

ulBitesToClearOnEntry 

等待前清零指定任务通知值的比特位(旧值对应bit清0)

ulBitesToClearOnExit 

成功等待后清零指定的任务通知值比特位(新值对应bit清0)

pulNotificationValue 

用来取出通知值(如果不需要取出,可设为NULL)

xTicksToWait 

阻塞等待任务通知值的最大时间

③返回值:

返回值

描述

pdTRUE 

等待任务通知成功

pdFALSE 

等待任务通知失败

④xTaskGenericNotifyWait函数源码剖析:

BaseType_t xTaskGenericNotifyWait( UBaseType_t uxIndexToWaitOn,uint32_t ulBitsToClearOnEntry,uint32_t ulBitsToClearOnExit,uint32_t * pulNotificationValue,TickType_t xTicksToWait )
{BaseType_t xReturn, xAlreadyYielded, xShouldBlock = pdFALSE;traceENTER_xTaskGenericNotifyWait( uxIndexToWaitOn, ulBitsToClearOnEntry, ulBitsToClearOnExit, pulNotificationValue, xTicksToWait );configASSERT( uxIndexToWaitOn < configTASK_NOTIFICATION_ARRAY_ENTRIES );vTaskSuspendAll();{taskENTER_CRITICAL();{if( pxCurrentTCB->ucNotifyState[ uxIndexToWaitOn ] != taskNOTIFICATION_RECEIVED )  //判断任务是否不在等待接收{pxCurrentTCB->ulNotifiedValue[ uxIndexToWaitOn ] &= ~ulBitsToClearOnEntry;       //将原先通知值的指定位清零pxCurrentTCB->ucNotifyState[ uxIndexToWaitOn ] = taskWAITING_NOTIFICATION;     //将任务状态更改为在等待通知if( xTicksToWait > ( TickType_t ) 0 )  //如果阻塞时间大于0xShouldBlock = pdTRUE;  //任务需要阻塞elsemtCOVERAGE_TEST_MARKER();}else{mtCOVERAGE_TEST_MARKER();}}taskEXIT_CRITICAL();if( xShouldBlock == pdTRUE )  //如果任务需要阻塞{traceTASK_NOTIFY_WAIT_BLOCK( uxIndexToWaitOn );prvAddCurrentTaskToDelayedList( xTicksToWait, pdTRUE );  //将任务添加进阻塞列表中}else{mtCOVERAGE_TEST_MARKER();}}xAlreadyYielded = xTaskResumeAll();if( ( xShouldBlock == pdTRUE ) && ( xAlreadyYielded == pdFALSE ) ){taskYIELD_WITHIN_API();  //如果任务进入阻塞态,则需要执行任务切换(if判断的作用是防止重复切换)}else{mtCOVERAGE_TEST_MARKER();}taskENTER_CRITICAL();{traceTASK_NOTIFY_WAIT( uxIndexToWaitOn );if( pulNotificationValue != NULL )  //判断是否需要保存原先的任务通知值{*pulNotificationValue = pxCurrentTCB->ulNotifiedValue[ uxIndexToWaitOn ];  //保存原先的任务通知值}if( pxCurrentTCB->ucNotifyState[ uxIndexToWaitOn ] != taskNOTIFICATION_RECEIVED )  //判断任务是否不在等待接收{xReturn = pdFALSE;  //接收失败}else{pxCurrentTCB->ulNotifiedValue[ uxIndexToWaitOn ] &= ~ulBitsToClearOnExit;   //将通知值接收,并把指定位清零xReturn = pdTRUE;   //接收成功}pxCurrentTCB->ucNotifyState[ uxIndexToWaitOn ] = taskNOT_WAITING_NOTIFICATION;  //状态更改为未等待通知}taskEXIT_CRITICAL();traceRETURN_xTaskGenericNotifyWait( xReturn );return xReturn;
}

三、任务通知模拟二值信号量实验

1、原理图与实验目标

(1)原理图:

(2)实验目标:

①设计4个任务——start_task、task1、task2、task3:

[1]start_task:用于创建task1、task2和task3任务。

[2]task1:当获取到LED1的硬件资源后,控制LED1约每500ms完成亮暗翻转的状态切换,每完成一次即将资源释放。

[3]task2:当获取到LED2的硬件资源后,控制LED2约每1000ms完成亮暗翻转的状态切换,每完成一次即将资源释放。

[4]task3:按下按键1,获取(或者说霸占)LED1和LED2的硬件资源;按下按键2,释放LED1和LED2的硬件资源。

②预期实验现象:

[1]程序下载到板子上后,两个LED灯闪烁。

[2]按下按键1,LED1和LED2停止闪烁(允许有1秒左右的延迟)。

[3]按下按键2,LED1和LED2恢复闪烁。

2、实验步骤

(1)将“二值信号量实验”的工程文件夹复制一份,在拷贝版中进行实验。

(2)将FreeRTOS_experiment.c文件中关于信号量的代码全部移除,并更改task1、task2和task3函数的实现。

①task1函数的思路:需要使用LED的硬件资源时调用ulTaskNotifyTake函数等待任务通知,并且必须等待到通知(同时将通知值清零)才可进行下一步操作——翻转LED1的状态。

②task2函数的思路:需要使用LED的硬件资源时调用ulTaskNotifyTake函数等待任务通知,并且必须等待到通知(同时将通知值清零)才可进行下一步操作——翻转LED2的状态。

③task3函数的思路:

[1]task1和task2需要task3的通知才能使用LED硬件资源,与二值信号量实验不同,二值信号量实验有单独的两个“队列”分别管理两个LED硬件资源,而本实验则是把task3当作了二值信号量队列管理员,两个二值信号量分别由task1和task2的TCB的任务通知相关成员代替。

[2]task1和task2不断申请和归还硬件资源,那么task3也要不断处理task1和task2的申请,要不断地管理资源的分配,而不是等待按键事件到来的一刻才做一次资源分配操作,于是原本的控制算法需要做变更。

[3]在未按下任何按键时,task3可以一直做释放LED硬件资源给task1和task2的操作,即使手上没有LED资源也不会陷入阻塞,而是直接执行下一条语句(当然,在实际项目中通常不建议这么做,此处仅仅是为了功能演示),task1和task2哪个先结束阻塞,哪个就先调用ulTaskNotifyTake函数等待任务通知,或者已经调用ulTaskNotifyTake函数但之前未等待到通知而进入无限阻塞,但task3一旦分配资源以后就能立刻被唤醒。

[4]在按下按键1之后,task3不做资源分配操作,task1和task2只能无限等待任务通知,无法执行后续的任何操作。

[5]在按下按键2之后(以下执行流程图的情形为按下按键1后再按下按键2),task3可以继续一直做释放LED硬件资源给task1和task2的操作。

void task1(void)
{while(1){ulTaskNotifyTake(pdTRUE, portMAX_DELAY); //接收LED硬件资源的使用通知LED1_Turn();      //LED1状态翻转vTaskDelay(500);   //延时(自我阻塞)500ms}
}void task2(void)
{while(1){ulTaskNotifyTake(pdTRUE, portMAX_DELAY); //接收LED硬件资源的使用通知LED2_Turn();      //LED2状态翻转vTaskDelay(1000);  //延时(自我阻塞)1000ms}
}void task3(void)
{uint8_t Key_memory, key = 0;while(1){key = Key_GetNum();    //读取按键键值if(key != 0)Key_memory = key;if(Key_memory == 1);if(Key_memory == 2 || Key_memory == 0){xTaskNotifyGive(task1_handler);         //释放LED硬件资源给task1xTaskNotifyGive(task2_handler);         //释放LED硬件资源给task2}vTaskDelay(10);  //延时(自我阻塞)10ms}
}

(3)程序完善好后点击“编译”,然后将程序下载到开发板上,根据程序注释进行调试。

相关文章:

FreeRTOS从入门到精通 第十六章(任务通知)

参考教程&#xff1a;【正点原子】手把手教你学FreeRTOS实时系统_哔哩哔哩_bilibili 一、任务通知简介 1、概述 &#xff08;1&#xff09;任务通知顾名思义是用来通知任务的&#xff0c;任务控制块中的结构体成员变量ulNotifiedValue就是这个通知值。 &#xff08;2&#…...

TensorFlow 简单的二分类神经网络的训练和应用流程

展示了一个简单的二分类神经网络的训练和应用流程。主要步骤包括&#xff1a; 1. 数据准备与预处理 2. 构建模型 3. 编译模型 4. 训练模型 5. 评估模型 6. 模型应用与部署 加载和应用已训练的模型 1. 数据准备与预处理 在本例中&#xff0c;数据准备是通过两个 Numpy 数…...

无人机图传模块 wfb-ng openipc-fpv,4G

openipc 的定位是为各种模块提供底层的驱动和linux最小系统&#xff0c;openipc 是采用buildroot系统编译而成&#xff0c;因此二次开发能力有点麻烦。为啥openipc 会用于无人机图传呢&#xff1f;因为openipc可以将现有的网络摄像头ip-camera模块直接利用起来&#xff0c;从而…...

.cc扩展名是什么语言?C语言必须用.c为扩展名吗?主流编程语言扩展名?Java为什么不能用全数字的文件名?

.cc扩展名是什么语言? .cc是C语言使用的扩展名&#xff0c;一种说法是它是c with class的简写&#xff0c;当然C语言使用的扩展名不止.cc和.cpp, 还包含.cxx, .c, .C等&#xff0c;这些在不同编译器系统采用的默认设定不同&#xff0c;需要区分使用。当然&#xff0c;编译器提…...

【MyDB】4-VersionManager 之 3-死锁及超时检测

【MyDB】4-VersionManager 之 3-死锁及超时检测 死锁及超时检测案例背景LockTable锁请求与等待管理 addvm调用addputIntoList&#xff0c;isInList&#xff0c;removeFromList 死锁检测 hasDeadLock方法资源释放与重分配 参考资料 死锁及超时检测 本章涉及代码&#xff1a;top/…...

【Linux】使用管道实现一个简易版本的进程池

文章目录 使用管道实现一个简易版本的进程池流程图代码makefileTask.hppProcessPool.cc 程序流程&#xff1a; 使用管道实现一个简易版本的进程池 流程图 代码 makefile ProcessPool:ProcessPool.ccg -o $ $^ -g -stdc11 .PHONY:clean clean:rm -f ProcessPoolTask.hpp #pr…...

【OpenGL】OpenGL游戏案例(二)

文章目录 特殊效果数据结构生成逻辑更新逻辑 文本渲染类结构构造函数加载函数渲染函数 特殊效果 为提高游戏的趣味性&#xff0c;在游戏中提供了六种特殊效果。 数据结构 PowerUp 类只存储存活数据&#xff0c;实际逻辑在游戏代码中通过Type字段来区分执行 class PowerUp …...

28. 【.NET 8 实战--孢子记账--从单体到微服务】--简易报表--报表定时器与报表数据修正

这篇文章是《.NET 8 实战–孢子记账–从单体到微服务》系列专栏的《单体应用》专栏的最后一片和开发有关的文章。在这片文章中我们一起来实现一个数据统计的功能&#xff1a;报表数据汇总。这个功能为用户查看月度、年度、季度报表提供数据支持。 一、需求 数据统计方面&…...

Java 泛型<? extends Object>

在 Java 泛型中&#xff0c;<? extends Object> 和 <?> 都表示未知类型&#xff0c;但它们在某些情况下有细微的差异。泛型的引入是为了消除运行时错误并增强类型安全性&#xff0c;使代码更具可读性和可维护性。 在 JDK 5 中引入了泛型&#xff0c;以消除编译时…...

FPGA|使用quartus II通过AS下载POF固件

1、将开发板设置到AS下载挡位&#xff0c;或者把下载线插入到AS端口 2、打开quartus II&#xff0c;选择Tools→Programmer→ Mode选择Active Serial Programming 3、点击左侧Add file…&#xff0c;选择 .pof 文件 →start 4、勾选program和verify&#xff08;可选&#xff0…...

“新月之智”智能战术头盔系统(CITHS)

新月人物传记&#xff1a;人物传记之新月篇-CSDN博客 相关文章链接&#xff08;更新&#xff09;&#xff1a; 星际战争模拟系统&#xff1a;新月的编程之道-CSDN博客 新月智能护甲系统CMIA--未来战场的守护者-CSDN博客 目录 一、引言 二、智能头盔控制系统概述 三、系统架…...

php:代码中怎么搭建一个类似linux系统的crontab服务

一、前言 最近使用自己搭建的php框架写一些东西&#xff0c;需要用到异步脚本任务的执行&#xff0c;但是是因为自己搭建的框架没有现成的机制&#xff0c;所以想自己搭建一个类似linux系统的crontab服务的功能。 因为如果直接使用linux crontab的服务配置起来很麻烦&#xff0…...

【LeetCode: 958. 二叉树的完全性检验 + bfs + 二叉树】

&#x1f680; 算法题 &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&#xff0c;…...

MinDoc 安装与部署

下载可执行文件 mindoc mindoc_linux_amd64.zip 上传并解压压缩包 cd /opt mkdir mindoc cd mindocunzip mindoc_linux_amd64.zip 创建数据库 CREATE DATABASE mindoc_db DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_general_ci; 配置数据库 将解压目录下 conf/app.conf.exam…...

从0开始使用面对对象C语言搭建一个基于OLED的图形显示框架(基础组件实现)

目录 基础组件实现 如何将图像和文字显示到OLED上 如何绘制图像 如何绘制文字 如何获取字体&#xff1f; 如何正确的访问字体 如何抽象字体 如何绘制字符串 绘制方案 文本绘制 更加方便的绘制 字体附录 ascii 6x8字体 ascii 8 x 16字体 基础组件实现 我们现在离手…...

windows系统如何检查是否开启了mongodb服务

windows系统如何检查是否开启了mongodb服务&#xff01;我们有很多软件开发&#xff0c;网站开发时候需要使用到这个mongodb数据库&#xff0c;下面我们看看&#xff0c;如何在windows系统内排查&#xff0c;是否已经启动了本地服务。 在 Windows 系统上&#xff0c;您可以通过…...

VS安卓仿真器下载失败怎么办?

如果网络不稳定&#xff0c;则VS的安卓仿真器很容易下载失败&#xff0c;如下 Downloaded file <USER_HOME>\AppData\Local\Temp\xamarin-android-sdk\x86_64-35_r08.zip not found for Android SDK archive https://dl.google.com/android/repository/sys-img/google_a…...

计算机网络一点事(24)

TCP可靠传输&#xff0c;流量控制 可靠传输&#xff1a;每字节对应一个序号 累计确认&#xff1a;收到ack则正确接收 返回ack推迟确认&#xff08;不超过0.5s&#xff09; 两种ack&#xff1a;专门确认&#xff08;只有首部无数据&#xff09; 捎带确认&#xff08;带数据…...

视频拼接,拼接时长版本

目录 视频较长&#xff0c;分辨率较大&#xff0c;这个效果很好&#xff0c;不耗用内存 ffmpeg imageio&#xff0c;适合视频较短 视频较长&#xff0c;分辨率较大&#xff0c;这个效果很好&#xff0c;不耗用内存 ffmpeg import subprocess import glob import os from nats…...

制造企业的成本核算

一、生产成本与制造费用的区别 (1)生产成本,是直接用于产品生产,构成产品实体的材料成本。 包括企业在生产经营过程中实际消耗的原材料、辅助材料、备品备件、外购半成品、燃料、动力包装物以及其它直接材料,和直接参加产品生产的工人工资,以及按生产工人的工资总额和规…...

铭豹扩展坞 USB转网口 突然无法识别解决方法

当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…...

椭圆曲线密码学(ECC)

一、ECC算法概述 椭圆曲线密码学&#xff08;Elliptic Curve Cryptography&#xff09;是基于椭圆曲线数学理论的公钥密码系统&#xff0c;由Neal Koblitz和Victor Miller在1985年独立提出。相比RSA&#xff0c;ECC在相同安全强度下密钥更短&#xff08;256位ECC ≈ 3072位RSA…...

理解 MCP 工作流:使用 Ollama 和 LangChain 构建本地 MCP 客户端

&#x1f31f; 什么是 MCP&#xff1f; 模型控制协议 (MCP) 是一种创新的协议&#xff0c;旨在无缝连接 AI 模型与应用程序。 MCP 是一个开源协议&#xff0c;它标准化了我们的 LLM 应用程序连接所需工具和数据源并与之协作的方式。 可以把它想象成你的 AI 模型 和想要使用它…...

spring:实例工厂方法获取bean

spring处理使用静态工厂方法获取bean实例&#xff0c;也可以通过实例工厂方法获取bean实例。 实例工厂方法步骤如下&#xff1a; 定义实例工厂类&#xff08;Java代码&#xff09;&#xff0c;定义实例工厂&#xff08;xml&#xff09;&#xff0c;定义调用实例工厂&#xff…...

Spring Boot+Neo4j知识图谱实战:3步搭建智能关系网络!

一、引言 在数据驱动的背景下&#xff0c;知识图谱凭借其高效的信息组织能力&#xff0c;正逐步成为各行业应用的关键技术。本文聚焦 Spring Boot与Neo4j图数据库的技术结合&#xff0c;探讨知识图谱开发的实现细节&#xff0c;帮助读者掌握该技术栈在实际项目中的落地方法。 …...

MySQL账号权限管理指南:安全创建账户与精细授权技巧

在MySQL数据库管理中&#xff0c;合理创建用户账号并分配精确权限是保障数据安全的核心环节。直接使用root账号进行所有操作不仅危险且难以审计操作行为。今天我们来全面解析MySQL账号创建与权限分配的专业方法。 一、为何需要创建独立账号&#xff1f; 最小权限原则&#xf…...

A2A JS SDK 完整教程:快速入门指南

目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库&#xff…...

Golang——6、指针和结构体

指针和结构体 1、指针1.1、指针地址和指针类型1.2、指针取值1.3、new和make 2、结构体2.1、type关键字的使用2.2、结构体的定义和初始化2.3、结构体方法和接收者2.4、给任意类型添加方法2.5、结构体的匿名字段2.6、嵌套结构体2.7、嵌套匿名结构体2.8、结构体的继承 3、结构体与…...

根目录0xa0属性对应的Ntfs!_SCB中的FileObject是什么时候被建立的----NTFS源代码分析--重要

根目录0xa0属性对应的Ntfs!_SCB中的FileObject是什么时候被建立的 第一部分&#xff1a; 0: kd> g Breakpoint 9 hit Ntfs!ReadIndexBuffer: f7173886 55 push ebp 0: kd> kc # 00 Ntfs!ReadIndexBuffer 01 Ntfs!FindFirstIndexEntry 02 Ntfs!NtfsUpda…...

协议转换利器,profinet转ethercat网关的两大派系,各有千秋

随着工业以太网的发展&#xff0c;其高效、便捷、协议开放、易于冗余等诸多优点&#xff0c;被越来越多的工业现场所采用。西门子SIMATIC S7-1200/1500系列PLC集成有Profinet接口&#xff0c;具有实时性、开放性&#xff0c;使用TCP/IP和IT标准&#xff0c;符合基于工业以太网的…...