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

空间站做网站有什么深圳房产 网站建设

空间站做网站有什么,深圳房产 网站建设,京东电器家电,国外摄影网站写在前面 本文是基于野火 RTOS 教程对空闲任务和阻塞延时的详解。 一、什么是任务中的阻塞延时 说到阻塞延时#xff0c;笔者的第一反应就是在单片机的 while 循环中#xff0c;使用一个 for 循环不断递减一个大数#xff0c;通过 CPU 不断执行一条指令的耗时进行延时。这…写在前面 本文是基于野火 RTOS 教程对空闲任务和阻塞延时的详解。 一、什么是任务中的阻塞延时 说到阻塞延时笔者的第一反应就是在单片机的 while 循环中使用一个 for 循环不断递减一个大数通过 CPU 不断执行一条指令的耗时进行延时。这种延时会占用 CPU 资源执行指令在延时的时候 CPU 不能执行其他的指令。但是注意我们现在是想在 RTOS 中的任务实现阻塞延时RTOS 可以有多个任务所有所谓任务中的阻塞延时虽然也是阻塞其后的代码运行但是只阻塞了他所在的那个任务中阻塞延时函数后面的代码。也就是说RTOS 中任务中的阻塞延时就是先阻塞一下这个任务然后把 CPU 使用权交给其他代码虽然也是阻塞下文的代码执行但是只阻塞这个任务的下文CPU 在这个过程中可以执行其他任务中的指令大大提高 CPU 利用率和笔者印象中的阻塞延时并不一样。 二、空闲任务有什么用 空闲任务的优先级是所有任务中优先级最低的当其他任务都在阻塞延时中CPU 就会切换到空闲任务运行。一般来说在空闲任务里面运行一些系统内存的清理工作或者在空闲任务中让单片机休眠或者进入低功耗模式。 三、空闲任务的实现 定义空闲任务的任务栈定义空闲任务的 TCB空闲任务的创建 注意空闲任务的任务栈和 TCB 变量我们都在 main.c 中声明为全局变量但是同时我们想在开启任务调度器的时候自动创建一个空闲任务而 RTOS 的开发人员不用显式地去创建空闲任务所以我们把空闲任务的创建集成在 void vTaskStartScheduler( void ) 这个函数中。这样我们在启动调度器的同时就会自动创建一个空闲任务。代码如下 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, /* 任务形参 */(StackType_t *)pxIdleTaskStackBuffer, /* 任务栈起始地址 */(TCB_t *)pxIdleTaskTCBBuffer ); /* 任务控制块 *//* 将任务添加到就绪列表 */ vListInsertEnd( ( pxReadyTasksLists[0] ), ( ((TCB_t *)pxIdleTaskTCBBuffer)-xStateListItem ) ); /*创建空闲任务end*//* 手动指定第一个运行的任务 */pxCurrentTCB Task1TCB;/* 初始化系统时基计数器 */xTickCount ( TickType_t ) 0U;/* 启动调度器 */if( xPortStartScheduler() ! pdFALSE ){/* 调度器启动成功则不会返回即不会来到这里 */} }上面这段代码调用了 xTaskCreateStatic() 这个函数进行空闲任务的创建但是这个函数需要传入空闲任务的任务栈和 TCB 变量而我们把这些变量定义在了 main.c 中所以需要使用 vApplicationGetIdleTaskMemory() 这个函数来使 vTaskStartScheduler() 函数中的任务指针等等变量指向定义在 main.c 中的任务栈和 TCB然后再把这些任务指针等传入 xTaskCreateStatic() 中。vApplicationGetIdleTaskMemory() 的具体代码如下 void vApplicationGetIdleTaskMemory( TCB_t **ppxIdleTaskTCBBuffer, StackType_t **ppxIdleTaskStackBuffer, uint32_t *pulIdleTaskStackSize ) {*ppxIdleTaskTCBBufferIdleTaskTCB;*ppxIdleTaskStackBufferIdleTaskStack; *pulIdleTaskStackSizeconfigMINIMAL_STACK_SIZE; }四、任务中的阻塞延时怎么实现 具体想法如下 为 TCB 添加记录延时时间的参数在任务中调用阻塞延时函数时会给 TCB 记录延时时间的参数进行赋值然后调用任务切换函数调用任务切换函数会产生 PendSV 中断在 PendSV中断服务函数中会调用上下文切换函数 vTaskSwitchContext()在上下文切换函数中我们更新当前执行任务的指针。现在我们的思想是如果当前任务是空闲任务那么查看其他任务的延时是否结束如果没有结束就继续执行空闲任务如果当前执行的不是空闲任务那么检查一下其他任务是否在延时中如果不在延时中就不忘初心进行任务切换如果在延时中就判断现在这个任务是否要延时如果要延时就切换到空闲任务否则就不进行任何切换。上面检查任务是否在延时状态都是通过检查 TCB 的延时参数是否为 0 来实现的我们使用 SysTick 中断来对 TCB 的延时参数进行定时修改在每次 SysTick 中断触发时我们更新一下系统时基计数器以后有用然后扫描一下就绪列表中所有 TCB 的延时参数不为 0 就减 1最后尝试任务切换 1. 为 TCB 添加记录延时时间的参数 typedef struct tskTaskControlBlock {volatile StackType_t *pxTopOfStack; /* 栈顶 */ListItem_t xStateListItem; /* 任务节点 */StackType_t *pxStack; /* 任务栈起始地址 *//* 任务名称字符串形式 */char pcTaskName[ configMAX_TASK_NAME_LEN ]; TickType_t xTicksToDelay; /* 用于延时 */ } tskTCB; typedef tskTCB TCB_t;2. 阻塞延时函数 vTaskDelay() 给 TCB 记录延时时间的参数进行赋值然后调用任务切换函数。 void vTaskDelay( const TickType_t xTicksToDelay ) {TCB_t *pxTCB NULL;/* 获取当前任务的TCB */pxTCB pxCurrentTCB;/* 设置延时时间 */pxTCB-xTicksToDelay xTicksToDelay;/* 任务切换 */taskYIELD(); }3. 上下文切换函数 vTaskSwitchContext() 如果当前任务是空闲任务 查看其他任务的延时是否结束 没有结束 - 继续执行空闲任务结束 - 跳转到其他任务 如果当前执行的不是空闲任务 检查一下其他任务是否在延时中 不在延时中 - 进行任务切换在延时中 - 判断现在这个任务是否要延时 要延时就切换到空闲任务否则就不进行任何切换 void vTaskSwitchContext( void ) {/* 如果当前线程是空闲线程那么就去尝试执行线程1或者线程2看看他们的延时时间是否结束如果线程的延时时间均没有到期那就返回继续执行空闲线程 */if( pxCurrentTCB IdleTaskTCB ){if(Task1TCB.xTicksToDelay 0){ pxCurrentTCB Task1TCB;}else if(Task2TCB.xTicksToDelay 0){pxCurrentTCB Task2TCB;}else{return; /* 线程延时均没有到期则返回继续执行空闲线程 */} }else{/*如果当前线程是线程1或者线程2的话检查下另外一个线程,如果另外的线程不在延时中就切换到该线程否则判断下当前线程是否应该进入延时状态如果是的话就切换到空闲线程。否则就不进行任何切换 */if(pxCurrentTCB Task1TCB){if(Task2TCB.xTicksToDelay 0){pxCurrentTCB Task2TCB;}else if(pxCurrentTCB-xTicksToDelay ! 0){pxCurrentTCB IdleTaskTCB;}else {return; /* 返回不进行切换因为两个线程都处于延时中 */}}else if(pxCurrentTCB Task2TCB){if(Task1TCB.xTicksToDelay 0){pxCurrentTCB Task1TCB;}else if(pxCurrentTCB-xTicksToDelay ! 0){pxCurrentTCB IdleTaskTCB;}else {return; /* 返回不进行切换因为两个线程都处于延时中 */}}} }4. SysTick 中断对 TCB 的延时参数进行定时修改 /* ************************************************************************* * SysTick中断服务函数 ************************************************************************* */ void xPortSysTickHandler( void ) {/* 关中断 */vPortRaiseBASEPRI();/* 更新系统时基 */xTaskIncrementTick();/* 开中断 */vPortClearBASEPRIFromISR(); }每次 SysTick 中断触发时我们更新一下系统时基计数器以后有用然后扫描一下就绪列表中所有 TCB 的延时参数不为 0 就减 1最后尝试任务切换 void xTaskIncrementTick( void ) {TCB_t *pxTCB NULL;BaseType_t i 0;/* 更新系统时基计数器xTickCountxTickCount是一个在port.c中定义的全局变量 */const TickType_t xConstTickCount xTickCount 1;xTickCount xConstTickCount;/* 扫描就绪列表中所有线程的xTicksToDelay如果不为0则减1 */for(i0; iconfigMAX_PRIORITIES; i){pxTCB ( TCB_t * ) listGET_OWNER_OF_HEAD_ENTRY( ( pxReadyTasksLists[i] ) );if(pxTCB-xTicksToDelay 0){pxTCB-xTicksToDelay --;}}/* 任务切换 */portYIELD(); }关于上面这段代码有一段写得很奇怪 /* 更新系统时基计数器xTickCountxTickCount是一个在port.c中定义的全局变量 */const TickType_t xConstTickCount xTickCount 1;xTickCount xConstTickCount;笔者刚开始看到的时候想问直接递增xTickCount不行吗为什么要写成 const TickType_t xConstTickCount xTickCount 1; xTickCount xConstTickCount; 这样不是画蛇添足吗使代码更复杂。 其实不然在任务调度器中xTickCount 变量用于记录系统的时基计数器。它的目的是跟踪系统运行的时间并且根据需要递增。 直接递增 xTickCount 可能会导致并发问题。在多线程或多任务的情况下如果有多个任务同时尝试递增 xTickCount并且中间存在竞争条件可能会导致计数不准确或不一致。 为了避免这种并发问题代码中将递增操作分解为两个步骤 首先通过 const TickType_t xConstTickCount xTickCount 1; 将 xTickCount 的值复制到一个中间变量 xConstTickCount 中并递增这个中间变量。 然后将中间变量 xConstTickCount 的值赋回给 xTickCount完成递增操作。 这样做的好处是无论何时进行递增操作代码都使用了一个稳定的中间值 xConstTickCount 来执行计算和更新。这确保了计数器 xTickCount 在整个递增过程中保持一致并且不会受到其他任务的干扰。这样可以避免并发问题提高代码的可靠性和正确性。 5. 最后是 SysTick 的相关初始化代码 在调度器启动函数 xPortStartScheduler() 函数中调用 vPortSetupTimerInterrupt() /* ************************************************************************* * 调度器启动函数 ************************************************************************* */BaseType_t xPortStartScheduler( void ) {/*PendSV是一个用于低优先级任务切换的软件中断。通过触发PendSV中断可以请求处理器在合适的时间切换到更高优先级的任务。PendSV中断具有最低的中断优先级因此可以在其他中断处理完成后立即执行。*//* 配置PendSV 和 SysTick 的中断优先级为最低 */portNVIC_SYSPRI2_REG | portNVIC_PENDSV_PRI;portNVIC_SYSPRI2_REG | portNVIC_SYSTICK_PRI;//初始化SysTick中断vPortSetupTimerInterrupt();/* 启动第一个任务不再返回 */prvStartFirstTask();/* 不应该运行到这里 */return 0; }初始化 SysTick 的函数 vPortSetupTimerInterrupt() /* ************************************************************************* * 初始化SysTick ************************************************************************* */ void vPortSetupTimerInterrupt( void ) {/* 设置重装载寄存器的值 */portNVIC_SYSTICK_LOAD_REG ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ ) - 1UL;/* 设置系统定时器的时钟等于内核时钟使能SysTick 定时器中断使能SysTick 定时器 */portNVIC_SYSTICK_CTRL_REG ( portNVIC_SYSTICK_CLK_BIT | portNVIC_SYSTICK_INT_BIT |portNVIC_SYSTICK_ENABLE_BIT ); }这里解释一下重装载寄存器的值怎么设置。计时器实际上是一个计数器当接收到设定数量的脉冲后进行一次中断而这个设定的数量就是重装载寄存器的值。 我们把计时器接入到 CPU 晶振后由于晶振每隔一段固定时间发出一个脉冲信号此时计时器就将重装载寄存器的值减 1当重装载寄存器的值减到 0 后就触发一次中断由此完成了对晶振的高频率信号的分频。 注意重装载寄存器的值是从 0 开始减的所以设置时要减 1。 可以看到我们使用 configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ ) - 1UL 进行设置configSYSTICK_CLOCK_HZ 实际上就是 CPU 的晶振频率而 configTICK_RATE_HZ 就是我们设置 SysTick 的中断频率。 其中的宏定义为 #define configCPU_CLOCK_HZ ( ( unsigned long ) 25000000 ) #define configTICK_RATE_HZ ( ( TickType_t ) 100 )/* SysTick 配置寄存器 */ #define portNVIC_SYSTICK_CTRL_REG ( * ( ( volatile uint32_t * ) 0xe000e010 ) ) #define portNVIC_SYSTICK_LOAD_REG ( * ( ( volatile uint32_t * ) 0xe000e014 ) )#ifndef configSYSTICK_CLOCK_HZ#define configSYSTICK_CLOCK_HZ configCPU_CLOCK_HZ/* 确保SysTick的时钟与内核时钟一致 */#define portNVIC_SYSTICK_CLK_BIT ( 1UL 2UL ) #else#define portNVIC_SYSTICK_CLK_BIT ( 0 ) #endif#define portNVIC_SYSTICK_INT_BIT ( 1UL 1UL ) #define portNVIC_SYSTICK_ENABLE_BIT ( 1UL 0UL )后记 如果您觉得本文写得不错可以点个赞激励一下作者 如果您发现本文的问题欢迎在评论区或者私信共同探讨 共勉
http://www.w-s-a.com/news/519431/

相关文章:

  • 自己创建网站教程河南省建设厅官方网站李学军
  • 一个网站需要多少容量怎样免费设计网站建设
  • 建设工程交易中心网站12306的网站是哪个公司做的
  • 建设网站经营范围自己给公司做网站
  • 河北省住房建设厅政务网站网络营销推广的岗位职责有哪些
  • 上海网站建设优化价格孝义做网站的公司
  • 哪个公司网站做的最好义乌 网站 制作
  • 百度站长工具综合查询wordpress 上传pdf
  • 旅游短租公寓网站建设深圳龙岗招聘网
  • 做海淘是在哪个网站网络查控系统设计方案
  • o2o网站建设代理商微信公众号开发文档
  • 网站设计课程总结关于网站备案的公告
  • 网站建设与运营意义到哪查找网站域名
  • 网站及单位网站建设情况眉县住房和城市建设局网站
  • 网站是否能够被恶意镜像wordpress占用
  • 经典设计网站网站等保测评怎么做
  • 重庆做网站公司贴吧廊坊公司快速建站
  • 海外贸易在什么网站做怎么排名到百度第一页
  • 线上注册公司是在哪个网站做高仿网站
  • 网站构架图网上推广平台哪个好
  • 公司网站首页图片素材vi设计的目的和意义
  • 网站的需求分析都有哪些内容济南营销型网站建设团队
  • 怎么选择优秀的网站建设公司生鲜网站开发
  • 如何编写网站建设销售的心得网站的权限管理怎么做
  • 网站业务员好做吗无忧网站优化
  • 网站随机代码网站建设费 账务处理
  • 商洛网站建设哪家好网站建设 织梦者
  • 怎么创建收费网站宁夏住房和城乡建设部网站
  • 怎么确认网站是什么语言做的用php和mysql做网站
  • 安徽做网站的公司有哪些星子网络公司