【FreeRTOS学习计划】 第八节 任务延时列表的实现

2023-10-30

在本节之前,为了实现任务的阻塞延时,在任务控制块中内置了一个延时变量xTicksToDelay。每当任务需要延时的时候,就初始化 xTicksToDelay 需要延时的时间,然后将任务挂起,这里的挂起只是将任务在优先级位图表 uxTopReadyPriority 中对应的位清零,并不会将任务从就绪列表中删除。当每次时基中断(SysTick 中断)来临时,就扫描就绪列表中的每个任务的 xTicksToDelay,如果 xTicksToDelay 大于 0 则递减一次,然后判断 xTicksToDelay 是否为 0,如果为 0 则表示延时时间到,将该任务就绪(即将任务在优先级位图表 uxTopReadyPriority 中对应的位置位),然后进行任务切换。这种延时的缺点是,在每个时基中断中需要对所有任务都扫描一遍,费时,优点是容易理解。之所以先这样讲解是为了慢慢地过度到 FreeRTOS 任务延时列表的讲解。

任务延时列表的工作原理

在 FreeRTOS 中,有一个任务延时列表(实际上有两个,为了方便讲解原理,我们假装合并为一个,其实两个的作用是一样的),当任务需要延时的时候,则先将任务挂起,即先将任务从就绪列表删除,然后插入到任务延时列表,同时更新下一个任务的解锁时刻变量:xNextTaskUnblockTime的值。

xNextTaskUnblockTime 的值等于系统时基计数器的值 xTickCount 加上任务需要延时的值 xTicksToDelay。当系统时基计数器 xTickCount 的值与 xNextTaskUnblockTime 相等时,就表示有任务延时到期了,需要将该任务就绪。与 RT-Thread 和 μC/OS 在解锁延时任务时要扫描定时器列表这种时间不确定性的方法相比,FreeRTOS 这个xNextTaskUnblockTime 全局变量设计的非常巧妙。

实现任务延时列表

定义任务延时列表

任务延时列表在 task.c 中定义

static List_t xDelayedTaskList1;//①
static List_t xDelayedTaskList2;//②
static List_t * volatile pxDelayedTaskList;//③
static List_t * volatile pxOverflowDelayedTaskList;//④

①②FreeRTOS定义了两个任务延时列表,当系统时基计数器xTickCount没有溢出时,用一条列表,当 xTickCount 溢出后,用另外一条列表。

③任务延时列表指针,指向 xTickCount 没有溢出时使用的那条列表。

④任务延时列表指针,指向 xTickCount 溢出时使用的那条列表。

任务延时列表初始化

任务延时列表属于任务列表的一种,在 prvInitialiseTaskLists() 函数中初始化

/* 初始化任务相关的列表 */
void prvInitialiseTaskLists( void )
{
  UBaseType_t uxPriority;
	
	for( uxPriority = ( UBaseType_t ) 0U; uxPriority < ( UBaseType_t ) configMAX_PRIORITIES; uxPriority++)
	{
	  vListInitialise( &( pxReadyTasksLists[ uxPriority ] ) );
	}
	vListInitialise( &xDelayedTaskList1 );
	vListInitialise( &xDelayedTaskList2 );
    
	pxDelayedTaskList = &xDelayedTaskList1;
	pxOverflowDelayedTaskList = &xDelayedTaskList2;
}

定义 xNextTaskUnblockTime

xNextTaskUnblockTime 是一个在 task.c 中定义的静态变量,用于表示下一个任务的解锁时刻。xNextTaskUnblockTime 的值等于系统时基计数器的值 xTickCount 加上任务需要延时值 xTicksToDelay。当系统时基计数器 xTickCount 的值与 xNextTaskUnblockTime 相等时,就表示有任务延时到期了,需要将该任务就绪。

初始化 xNextTaskUnblockTime

xNextTaskUnblockTime 在 vTaskStartScheduler() 函 数 中 初 始 化 为 portMAX_DELAY(portMAX_DELAY 是一个 portmacro.h 中定义的宏,默认为 0xffffffffUL)。

#if( configUSE_16_BIT_TICKS == 1)
    typedef uint16_t TickType_t;
    #define portMAX_DELAY ( TickType_t ) 0xffff
#else
    typedef uint32_t TickType_t;
    #define portMAX_DELAY ( TickType_t ) 0xffffffffUL
#endif

初始化 xNextTaskUnblockTime

void vTaskStartScheduler( void )
{
/*======================================创建空闲任务start==============================================*/     
    TCB_t *pxIdleTaskTCBBuffer = NULL;               /* 用于指向空闲任务控制块 */
    StackType_t *pxIdleTaskStackBuffer = NULL;       /* 用于空闲任务栈起始地址 */
    uint32_t ulIdleTaskStackSize;
    
    /* 获取空闲任务的内存:任务栈和任务TCB */
    vApplicationGetIdleTaskMemory( &pxIdleTaskTCBBuffer, 
                                   &pxIdleTaskStackBuffer, 
                                   &ulIdleTaskStackSize );    
    
    xIdleTaskHandle = xTaskCreateStatic(	(TaskFunction_t)prvIdleTask,              /* 任务入口 */
																					(char *)"IDLE",                           /* 任务名称,字符串形式 */
																					(uint32_t)ulIdleTaskStackSize ,           /* 任务栈大小,单位为字 */
																					(void *) NULL,                            /* 任务形参 */
																					(UBaseType_t) tskIDLE_PRIORITY,           /* 任务优先级,数值越大,优先级越高 */
																					(StackType_t *)pxIdleTaskStackBuffer,     /* 任务栈起始地址 */
																					(TCB_t *)pxIdleTaskTCBBuffer );           /* 任务控制块 */					/* 任务控制块指针 */

		
//    /* 将任务添加到就绪列表 */                                 
//    vListInsertEnd( &( pxReadyTasksLists[0] ), &( ((TCB_t *)pxIdleTaskTCBBuffer)->xStateListItem ) );
///*======================================创建空闲任务end================================================*/
//                                         
//    /* 手动指定第一个运行的任务 */
//    pxCurrentTCB = &Task1TCB;
    xNextTaskUnblockTime = portMAX_DELAY;                                    
    /* 初始化系统时基计数器 */
    xTickCount = ( TickType_t ) 0U;
    
    /* 启动调度器 */
    if( xPortStartScheduler() != pdFALSE )
    {
        /* 调度器启动成功,则不会返回,即不会来到这里 */
    }
}

修改代码,支持任务延时列表

修改 vTaskDelay() 函数

void vTaskDelay( const TickType_t xTicksToDelay )
{
    TCB_t *pxTCB = NULL;
    
    /* 获取当前任务的TCB */
    pxTCB = pxCurrentTCB;
    
    /* ①设置延时时间 */
    //pxTCB->xTicksToDelay = xTicksToDelay;
	
	/* ②将任务插入到延时列表 */
    prvAddCurrentTaskToDelayedList( xTicksToDelay );
    
    /* 任务切换 */
    taskYIELD();
}

①从本节开始,添加了任务的延时列表,延时的时候不用再依赖任务TCB 中内置的延时变量 xTicksToDelay。

②将任务插入到延时列表。函数prvAddCurrentTaskToDelayedList() 在task.c 中定义。

prvAddCurrentTaskToDelayedList() 函数

static void prvAddCurrentTaskToDelayedList( TickType_t xTicksToWait )
{
    TickType_t xTimeToWake;
    
    /* ①获取系统时基计数器xTickCount的值 */
    const TickType_t xConstTickCount = xTickCount;

    /* ②将任务从就绪列表中移除 */
	if( uxListRemove( &( pxCurrentTCB->xStateListItem ) ) == ( UBaseType_t ) 0 )
	{
		/* 将任务在优先级位图中对应的位清除 */
        portRESET_READY_PRIORITY( pxCurrentTCB->uxPriority, uxTopReadyPriority );
	}

    /* ③计算延时到期时,系统时基计数器xTickCount的值是多少 */
    xTimeToWake = xConstTickCount + xTicksToWait;

    /* ④将延时到期的值设置为节点的排序值 */
    listSET_LIST_ITEM_VALUE( &( pxCurrentTCB->xStateListItem ), xTimeToWake );

    /* ⑤溢出 */
    if( xTimeToWake < xConstTickCount )
    {
        vListInsert( pxOverflowDelayedTaskList, &( pxCurrentTCB->xStateListItem ) );
    }
    else /* ⑥没有溢出 */
    {

        vListInsert( pxDelayedTaskList, &( pxCurrentTCB->xStateListItem ) );

        /* ⑦更新下一个任务解锁时刻变量xNextTaskUnblockTime的值 */
        if( xTimeToWake < xNextTaskUnblockTime )
        {
            xNextTaskUnblockTime = xTimeToWake;
        }
    }	
}

①获取系统时基计数器 xTickCount 的值,xTickCount 是一个在 task.c 中定义的全局变量,用于记录 SysTick 的中断次数。

②调用函数 uxListRemove() 将任务从就绪列表移除,uxListRemove() 会返回当前链表下节点的个数,如果为 0,则表示当前列表下没有任务就绪,则调用函数portRESET_READY_PRIORITY() 将任务在优先级位图表 uxTopReadyPriority 中对应的位清除。因为 FreeRTOS 支持同一个优先级下可以有多个任务,所以在清除优先级位图表 uxTopReadyPriority 中对应的位时要判断下该优先级下的就绪列表是否还有其他的任务。目前为止,我们还没有支持同一个优先级下有多个任务的功能,这个功能我们将在下一节“支持时间片”里面实现。

③计算任务延时到期时,系统时基计数器 xTickCount 的值是多少。

④将任务延时到期的值设置为节点的排序值。将任务插入到延时列表时就是根据这个值来做升序排列的,最先延时到期的任务排在最前面。

⑤:xTimeToWake 溢出,将任务插入到溢出任务延时列表。溢出?什么意思?xTimeToWake 等于系统时基计数器 xTickCount 的值加上任务需要延时的时间 xTicksToWait。举例:如果当前 xTickCount 的值等于 0xfffffffdUL,xTicksToWait 等于 0x03,那么xTimeToWake = 0xfffffffdUL + 0x03 = 1,显然得出的值比任务需要延时的时间 0x03 还小,这肯定不正常,说明溢出了,这个时候需要将任务插入到溢出任务延时列表。

⑥xTimeToWake 没有溢出,则将任务插入到正常任务延时列表。

⑦更新下一个任务解锁时刻变量 xNextTaskUnblockTime 的值。这一步很重要,在 xTaskIncrementTick() 函数中,我们只需要让系统时基计数器 xTickCount 与xNextTaskUnblockTime 的值先比较就知道延时最快结束的任务是否到期。

修改 xTaskIncrementTick() 函数

void xTaskIncrementTick( void )
{
	TCB_t * pxTCB;
	TickType_t xItemValue;

	const TickType_t xConstTickCount = xTickCount + 1;//①
	xTickCount = xConstTickCount;

	/* ②如果xConstTickCount溢出,则切换延时列表 */
	if( xConstTickCount == ( TickType_t ) 0U )
	{
		taskSWITCH_DELAYED_LISTS();
	}

	/* ③最近的延时任务延时到期 */
	if( xConstTickCount >= xNextTaskUnblockTime )
	{
		for( ;; )
		{
			if( listLIST_IS_EMPTY( pxDelayedTaskList ) != pdFALSE )//④
			{
				/* 延时列表为空,设置xNextTaskUnblockTime为可能的最大值 */
				xNextTaskUnblockTime = portMAX_DELAY;
				break;
			}
			else /* ⑤延时列表不为空 */
			{
				pxTCB = ( TCB_t * ) listGET_OWNER_OF_HEAD_ENTRY( pxDelayedTaskList );
				xItemValue = listGET_LIST_ITEM_VALUE( &( pxTCB->xStateListItem ) );//⑥

				/* ⑦直到将延时列表中所有延时到期的任务移除才跳出for循环 */
                if( xConstTickCount < xItemValue )
				{
					xNextTaskUnblockTime = xItemValue;
					break;
				}

				/* ⑧将任务从延时列表移除,消除等待状态 */
				( void ) uxListRemove( &( pxTCB->xStateListItem ) );

				/* ⑨将解除等待的任务添加到就绪列表 */
				prvAddTaskToReadyList( pxTCB );
			}
		}
	}/* xConstTickCount >= xNextTaskUnblockTime */
    
    /* ⑩任务切换 */
    portYIELD();
}

①更新系统时基计数器 xTickCount 的值。

②如果系统时基计数器 xTickCount 溢出,则切换延时列表。taskSWITCH_DELAYED_LISTS() 函数在 task.c 中定义

taskSWITCH_DELAYED_LISTS() 函数

/* 
 * 当系统时基计数器溢出的时候,延时列表pxDelayedTaskList 和
 * pxOverflowDelayedTaskList要互相切换
 */
#define taskSWITCH_DELAYED_LISTS()
{
	List_t *pxTemp;//(1)
	pxTemp = pxDelayedTaskList;
	pxDelayedTaskList = pxOverflowDelayedTaskList;
	pxOverflowDelayedTaskList = pxTemp;
	xNumOfOverflows++;
	prvResetNextTaskUnblockTime();//②
}

(1)切换延时列表,实际就是更换pxDelayedTaskList和pxOverflowDelayedTaskList 这两个指针的指向。

(2)复位 xNextTaskUnblockTime 的值。prvResetNextTaskUnblockTime() 函数在 task.c 中定义。

prvResetNextTaskUnblockTime 函数

static void prvResetNextTaskUnblockTime( void )
{
    TCB_t *pxTCB;

	if( listLIST_IS_EMPTY( pxDelayedTaskList ) != pdFALSE )
	{
		/* (1)The new current delayed list is empty.  Set xNextTaskUnblockTime to
		the maximum possible value so it is	extremely unlikely that the
		if( xTickCount >= xNextTaskUnblockTime ) test will pass until
		there is an item in the delayed list. */
		xNextTaskUnblockTime = portMAX_DELAY;
	}
	else
	{
		/* (2)The new current delayed list is not empty, get the value of
		the item at the head of the delayed list.  This is the time at
		which the task at the head of the delayed list should be removed
		from the Blocked state. */
		( pxTCB ) = ( TCB_t * ) listGET_OWNER_OF_HEAD_ENTRY( pxDelayedTaskList );
		xNextTaskUnblockTime = listGET_LIST_ITEM_VALUE( &( ( pxTCB )->xStateListItem ) );
	}
}

(1)当前延时列表为空,则设置 xNextTaskUnblockTime 等于最大值。

(2)当前列表不为空,则有任务在延时,则获取当前列表下第一个节点的排序值,然后将该节点的排序值更新到xNextTaskUnblockTime。

③有任务延时到期,则进入下面的 for 循环,一一将这些延时到期的任务从延时列表移除。

④延时列表为空,则将 xNextTaskUnblockTime 设置为最大值,然后跳出 for 循环。

⑤延时列表不为空,则需要将延时列表里面延时到期的任务删除,并将它们添加到就绪列表。

⑥取出延时列表第一个节点的排序辅助值。

⑦直到将延时列表中所有延时到期的任务移除才跳出 for 循环。延时列表中有可能存在多个延时相等的任务。

⑧将任务从延时列表移除,消除等待状态。

⑨将解除等待的任务添加到就绪列表。

⑩执行一次任务切换。

修改 taskRESET_READY_PRIORITY() 函数

在没有添加任务延时列表之前,与任务相关的列表只有一个,就是就绪列表,无论任务在延时还是就绪都只能通过扫描就绪列表来找到任务的 TCB,从而实现系统调度。所以在上一节“支持多优先级”中,实现 taskRESET_READY_PRIORITY() 函数的时候,不用先判断当前优先级下就绪列表中的列表的节点是否为 0,而是直接把任务在优先级位图表uxTopReadyPriority 中对应的位清零。因为当前优先级下就绪列表中的列表的节点不可能为 0,目前我们还没有添加其他列表来存放任务的 TCB,只有一个就绪列表。

但是从本节开始,我们额外添加了延时列表,当任务要延时的时候,将任务从就绪列表移除,然后添加到延时列表,同时将任务在优先级位图表 uxTopReadyPriority 中对应的位清除。在清除任务在优先级位图表 uxTopReadyPriority 中对应的位的时候,与上一章不同的是需要判断就绪列表pxReadyTasksLists[] 在当前优先级下对应的链表的节点是否为 0,只有当该列表下没有任务时才真正地将任务在优先级位图表 uxTopReadyPriority 中对应的位清零。

taskRESET_READY_PRIORITY() 函数的具体修改见代码如下。那什么情况下就绪列表的列表里面会有多个任务节点?即同一优先级下有多个任务?这个就是我们下一节“支持时间片”要讲的内容。

#if 0 //本节实现方法
	#define taskRESET_READY_PRIORITY( uxPriority )														
	{																									
		if( listCURRENT_LIST_LENGTH( &( pxReadyTasksLists[ ( uxPriority ) ] ) ) == ( UBaseType_t ) 0 )	
		{																								
			portRESET_READY_PRIORITY( ( uxPriority ), ( uxTopReadyPriority ) );							
		}																								
	}
#else //上一节实现方法
    #define taskRESET_READY_PRIORITY( uxPriority )											    
    {																							
            portRESET_READY_PRIORITY( ( uxPriority ), ( uxTopReadyPriority ) );					
    }
#endif

main函数

int main(void)
{
	
	/* 硬件初始化 */
	/* 将硬件相关的初始化放在这里,如果是软件仿真则没有相关初始化代码 */
	
	
	/* 创建任务 */
	Task1_Handle = xTaskCreateStatic( ( TaskFunction_t )Task1_Entry,
	                                  (char *)"Task1",
																		(uint32_t)TASK1_STACK_SIZE,
																		(void *)NULL,
																		(UBaseType_t) 1,               
																		(StackType_t *)Task1Stack,
																		(TCB_t *)&Task1TCB);
//	/* 将任务添加到就绪列表 */ 
//	vListInsertEnd( &( pxReadyTasksLists[1]), &( ((TCB_t *)(&Task1TCB))->xStateListItem ) );

	Task2_Handle = xTaskCreateStatic( ( TaskFunction_t )Task2_Entry,
	                                  (char *)"Task2",
																		(uint32_t)TASK2_STACK_SIZE,
																		(void *)NULL,
																		(UBaseType_t) 2,
																		(StackType_t *)Task2Stack,
																		(TCB_t *)&Task2TCB);
//	/* 将任务添加到就绪列表 */ 
//	vListInsertEnd( &( pxReadyTasksLists[2]), &( ((TCB_t *)(&Task2TCB))->xStateListItem ) );
  /* 在启动调度器前,关闭中断 */                                  
   portDISABLE_INTERRUPTS();
																		
	/* 启动调度器,开始多任务调度,启动成功则不返回 */
    vTaskStartScheduler();
	for(;;)
	{
			/*啥事不干*/
	}

}

实验现象

实验现象与上一节一样,虽说一样,但是实现延时的方法本质却变了,需要好好理解代码的实现,特别是当系统时基计数器 xTickCount 发生溢出时,延时列表的更换是难点。
在这里插入图片描述
在这里插入图片描述


参考资料:《FreeRTOS 内核实现与应用开发实战—基于RT1052》

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【FreeRTOS学习计划】 第八节 任务延时列表的实现 的相关文章

  • 为什么这么多人自学黑客,但没过多久就放弃了(掌握正确的网络安全学习路线很重要)

    网络安全是一个 不断发展和演变 的领域 以下是一个 网络安全学习路线规划 旨在帮助初学者快速入门和提高自己的技能 基础知识 网络安全的 基础知识 包括 网络结构 操作系统 编程语言 等方面的知识 学习这些基础知识对理解网络安全的原理和技术至
  • Java 学习路线 2024 最新版!

    又对上次分享的 Java 学习路线进行了简单修改完善 并增加了免登录下载和黑夜模式 这里重发一下 花了一个月零碎的时间 我根据当下 Java 后端求职和招聘的最新要求 对之前写的 Java 后端学习路线进行了全面的优化和改进 添加图片注释
  • Freertos低功耗管理

    空闲任务中的低功耗Tickless处理 在整个系统运行得过程中 其中大部分时间都是在执行空闲任务的 空闲任务之所以执行 因为在系统中的其他任务处于阻塞或者被挂起时才会执行 因此可以将空闲任务的执行时间转换成低功耗模式 在其他任务解除阻塞而准
  • 太阳诱电 | 电容器为什么会发热?什么是纹波电流

    电容器中存在寄生于电极和电介质的电阻成分 当纹波电流等交流电流通过电容器时 电阻的成分会产生热量 为了抑制发热 选择ESR较低的电容器非常重要 陶瓷电容器在电容器中ESR较低 非常适合抑制发热 电容器中的纹波电流主要是指电源电路中由于IC的
  • 【计算机毕业设计】精品课程在线学习系统

    如今社会上各行各业 都喜欢用自己行业的专属软件工作 互联网发展到这个时候 人们已经发现离不开了互联网 新技术的产生 往往能解决一些老技术的弊端问题 因为传统精品课程学习信息管理难度大 容错率低 管理人员处理数据费工费时 所以专门为解决这个难
  • 2024年网络安全十10大发展趋势发布

    2023年网络安全十10大发展趋势发布 近日 中国计算机学会 CCF 计算机安全专委会中 来自国家网络安全主管部门 高校 科研院所 大型央企 民营企业的委员投票评选出2023年网络安全十大发展趋势 福利 趋势一 数据安全治理成为数字经济的基
  • c语言学生管理系统

    创建结构体里面包含学生的各种信息 struct xs int xh char xm 20 int gs yy wl double pj struct xs next 创建菜单 void menu printf n n printf 学生管理
  • 华为OD机试真题-API集群负载统计-Java-OD统一考试(C卷)

    题目描述 某个产品的RESTful API集合部署在服务器集群的多个节点上 近期对客户端访问日志进行了采集 需要统计各个API的访问频次 根据热点信息在服务器节点之间做负载均衡 现在需要实现热点信息统计查询功能 RESTful API的由多
  • 【GRNN-RBFNN-ILC算法】【轨迹跟踪】基于神经网络的迭代学习控制用于未知SISO非线性系统的轨迹跟踪(Matlab代码实现)

    欢迎来到本博客 博主优势 博客内容尽量做到思维缜密 逻辑清晰 为了方便读者 座右铭 行百里者 半于九十 本文目录如下 目录 1 概述 2 运行结果 2 1 第1部分 2 2 第2部分
  • 特殊寄存器

    特殊寄存器 文章目录 前言 一 背景 二 2 1 2 2 总结 前言 前期疑问 STM32特殊寄存器到底是什么 特殊寄存器怎么查看和调试代码 本文目标 记录和理解特殊寄存器 一 背景 最近在看ucosIII文章是 里面提到特殊寄存器 这就进
  • 【js学习之路】遍历数组api之 `filter `和 `map`的区别

    一 前言 数组是我们在项目中经常使用的数据类型 今天我们主要简述作用于遍历数组的api filter 和 map 的区别 二 filter和map的共同点 首先 我们主要阐述一下 filter 和 map 的共同点 api的参数都是回调函数
  • 肿瘤的转录调控:Cell子刊揭示原发性肝癌中转录因子活性的全基因组图谱|国自然热点

    转录调控的研究历史比较长 相关研究在近十年来仍一直增长 也是近年来高分文章的焦点之一 在2023年最佳国自然 中标 研究热点 转录调控中标率高达189 作为国自然热点之一的肿瘤微环境的研究在近几年也一直处于上升趋势 转录调控在肿瘤发生 发展
  • 核心耦合内存在 STM32F4xx 上可执行吗?

    尝试从 STM32F429s CCM 运行代码 但每当我命中 CCM 中的第一条指令时 我总是会遇到硬故障 并且 IBUSERR 标志被设置 该指令有效且一致 STM32F4xx 是否可能不允许从 CCM 执行 数据访问效果良好 alios
  • STM32 Nucleo 上的上升沿中断多次触发

    我正在使用 STM32 NUCLEO F401RE 微控制器板 我有一个扬声器 经过编程 当向上 向下推操纵杆时 可以按设定的量改变频率 我的问题是 有时 通常 当向上 向下推动操纵杆时 频率会增加 减少多次 这意味着 ISR 正在执行多次
  • STM32 上的 ADC 单次转换

    我正在研究 STM32 F103x 上的 ADC 编程 并从最简单的情况 单次转换开始 测量内部温度传感器 连接到 ADC1 的值 并使用 USART 将其发送到 COM 端口 目标似乎很明确 但是当我尝试将源代码下载到闪存时 它不会向 C
  • 在 Contiki 程序中使用 malloc

    考虑以下 Contiki 程序 include
  • 哪些变量类型/大小在 STM32 微控制器上是原子的?

    以下是 STM32 微控制器上的数据类型 http www keil com support man docs armcc armcc chr1359125009502 htm http www keil com support man d
  • 当端点和 PMA 地址均更改时,CubeMX 生成的 USB HID 设备发送错误数据

    我正在调试我正在创建的复合设备的问题 并在新生成的仅 CubeMX 代码中重新创建了该问题 以使其更容易解决 我添加了少量代码main 让我发送 USB HID 鼠标点击 并在按下蓝色按钮时使 LED 闪烁 uint8 t click re
  • 移动数组中的元素

    我需要一点帮助 我想将数组中的元素向上移动一个元素 以便新位置 1 包含位置 1 中的旧值 new 2 包含 old 1 依此类推 旧的最后一个值被丢弃 第一个位置的新值是我每秒给出的新值 我使用大小为 10 的数组 uint32 t TE
  • GCC 变量映射和 MISRA-C

    我主要知道两种使用 GCC 声明内存映射寄存器的方法 有许多变体 使用双字段 每个外设的数据结构等 要么使用初始化为正确地址的指针 例如volatile uint32 t pMyRegister uint32 t 0xDEADBEEFUL

随机推荐

  • 第二章 感知机

    感知机 perceptron 是二类分类的线性分类模型 它包括输入空间 输出空间 模型结构 参数空间和假设空间 感知机学习旨在求出将训练数据进行线性划分的分离超平面 为此导入基于误分类的损失函数 利用梯度下降法对损失函数进行极小化 求得感知
  • STL的一些基本背景了解。

    STL源代码的头文件一般都是内联模式的 现在简单的把stl的集中类型进行分类说明一下 1 容器类 一般分为关联式容器和顺序式容器 典型的例子的就是vector为典型的顺序式容器 对于stl来说主要采用向量 链表 二叉树以及他们的组合为底层存
  • pandas中iloc()函数

    pandas中iloc 函数 DataFrame iloc 纯基于整数位置的索引 import pandas as pd mydict a 1 b 2 c 3 d 4 a 100 b 200 c 300 d 400 a 1000 b 200
  • TensorFlow数据归一化

    TensorFlow数据归一化 1 tf nn l2 normalize l2 normalize x dim epsilon 1e 12 name None output x sqrt max sum x 2 epsilon 2 使用sc
  • 【送书活动】深入浅出SSD:固态存储核心技术、原理与实战

    前言 作者主页 雪碧有白泡泡 个人网站 雪碧的个人网站 推荐专栏 java一站式服务 React从入门到精通 前端炫酷代码分享 从0到英雄 vue成神之路 uniapp 从构建到提升 从0到英雄 vue成神之路 解决算法 一个专栏就够了 架
  • 博士研究生如何做创新性研究?(蒲慕明院士)

    读文献不要只看文献描述的工作 What was done 还有四个 W 你也应知道 是谁做的 Who did it 什么时候做的 When 在哪里做的 Where 为什么会做这工作 Why 想了解重要的创新工作的来龙去脉 你就要读科学史 读
  • 全面深入了解python(一)

    全面深入了解python 一 写在开始前 此教程不是基础教程 在看之前你需要有一定的python基础 不然你可能无法理解教程到底教了哪些东西 环境 python版本是3 6 5 gt 3 4即可 1 Python数据模型 数据模型其实是对P
  • 高级排序技巧:提升Python中排序算法的效率和灵活性

    排序是计算机科学中常见的操作之一 它可以对数据进行按照特定顺序排列的操作 Python提供了多种排序算法 如冒泡排序 插入排序 选择排序等 然而 在处理大规模数据或对特定需求有限制时 我们需要使用更高级的排序技巧来提高算法的效率和灵活性 本
  • 7、ListView的几种适配器及自定义适配器

    span style font family none background color rgb 255 255 255 ListView是我们在Android应用开发中常用的组件 它通过列表的形式展示数据 具有很强的可扩展性 通常使用还会
  • npm查看一个包的版本信息

    我们npm install安装一个包之后 怎么查看这个包的版本信息呢 过一段时间之后 如何查看这个包最新的版本信息呢 下面将做一个全面的总结 1 查看项目中安装的所有的包 npm list 结果如下 vue2 0 test 0 1 0 C
  • scrapy 最简易爬虫

    创建一个项目 scrapy startproject baidu 这后面是名字 运行项目 要cd 进入项目才可以运行 scrapy crawl baidu import scrapy from bs4 import BeautifulSou
  • 关于OSB执行管道outbound的Content-Type自动添加charset=utf-8解决办法

    一 问题描述 在执行Content Type application x www form urlencoded类型的接口的管道时outbound自动添加charset utf 8 如下图所示 二 解决办法 将所有服务器都停掉 1 打补丁
  • 海康rtsp抓包分析

    海康h264 rtp rtsp rtcp包分析 1 DESCRIBE rtsp 192 168 0 186 554 mpeg4 ch01 main av stream RTSP 1 0 CSeq 0 Accept application s
  • 计算机经典书籍2

    推荐度参考 6分 可看可不看 7分 值得一读 8分 经典好书 9分 强烈推荐 10分 神品级 C语言类 C程序设计语言 推荐度 8分 书籍简介 在计算机发展的历史上 没有哪一种程序设计语言像C语言这样应用广泛 本书原著即为C语言的设计者之一
  • 1896-2021历届奥运会奖牌榜(Python数据处理)

    阅读本文大约需要 3 分钟 摘 要 这两天在平台上看到一些创作者失去创作动力的感慨 OF只想说往事如昙花一现 我们都需要时刻静下心来 认真地考虑下自己的创作目标并付诸行动 遥想当年OF做软件系统的时候 开源社区还没有像现在这样健全 再看看如
  • 多模态交互的概念与现状

    一 多模态概念 所谓 模态 英文是modality 用通俗的话说 就是 感官 多模态即将多种感官融合 目前的人机智能交互比如语言控制不如屏幕控制那么精准 很多时候会误判指令和错误唤醒 比较语言充满了不确定性 再比如 语音交互的物联网设备还是
  • linux 网络编程socket

    前言 socket 套接字 是linux下进程间通信的一种方式 通常使用C S 客户端 服务端 的方式通信 它可以是同一主机下的不同进程间通信或者不同主机的进程通信 socket是夹在应用层和TCP UDP协议层间的软件抽象 向应用层开发人
  • (简单易记版)考研复试英语自我介绍

    Good morning teachers I am very glad to be able to attend this interview First let me introduce myself my name is xxx 24
  • 当出现raise NotImplementedError报错

    class LeNet5 nn Module def init self super LeNet5 self init 定义卷积层 self conv nn Sequential nn Conv2d in channels 1 out ch
  • 【FreeRTOS学习计划】 第八节 任务延时列表的实现

    在本节之前 为了实现任务的阻塞延时 在任务控制块中内置了一个延时变量xTicksToDelay 每当任务需要延时的时候 就初始化 xTicksToDelay 需要延时的时间 然后将任务挂起 这里的挂起只是将任务在优先级位图表 uxTopRe