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

网站开发的趋势信息流广告推广

网站开发的趋势,信息流广告推广,南京制作网站公司哪家好,wordpress title代码目录 1. 实现空闲任务 1.1 定义空闲任务的栈 1.2 定义空闲任务的任务控制块 1.3 创建空闲任务 2. 实现阻塞延时 2.1 vTaskDelay()函数 2.2 修改 vTaskSwitchContext()函数 3. SysTick 中断服务函数 4. SysTick 初始化函数 通过之前我们了解知道,任…

目录

1.  实现空闲任务

1.1  定义空闲任务的栈

1.2  定义空闲任务的任务控制块

1.3  创建空闲任务

2.  实现阻塞延时

2.1  vTaskDelay()函数

2.2  修改 vTaskSwitchContext()函数

3.  SysTick 中断服务函数

4.  SysTick 初始化函数


        通过之前我们了解知道,任务体内的延时使用的是软件延时,也就是还是让CPU空等待来达到延时的效果,也就是delay函数,或者使用一个大的循环,只计数不进行任务处理,但是我们在使用 RTOS 的很大优势就是榨干 CPU 的性能,永远不然它闲着,任务如果需要延时也不能让 CPU 空等待来实现延时的效果。

        RTOS 中的延时叫阻塞延时,即任务需要延时的时候,任务会放弃 CPU 的使用权,CPU 可以去干其它的事情,当任务延时时间到,重新获取 CPU 使用权,任务继续运行,这样就充分地利用了 CPU 的资源,而不是干等着。

        当任务需要延时,进入阻塞状态,那 CPU 又去干什么事情了?

        如果没有其它任务可以运行,RTOS 都会为 CPU 创建一个空闲任务,这个时候 CPU 就运行空闲任务。在 FreeRTOS 中,空闲任务是系统在启动调度器的时候创建的优先级最低的任务,空闲任务主体主要是做一些系统内存的清理工作。

        在实际应用中,当系统进入空闲任务的时候,可在空闲任务中让单片机进入休眠或者低功耗等操作。

1.  实现空闲任务

        目前我们在创建任务时使用的栈和 TCB 都使用的是静态的内存,即需要预先定义好内存,空闲任务也不例外。有关空闲任务的栈和 TCB 需要用到的内存空间均在 main.c 中定义。

1.1  定义空闲任务的栈

        空闲任务的栈是一个定义好的数组,大小由 FreeRTOSConfig.h 中定义的宏 configMINIMAL_STACK_SIZE 控制,默认为 128,单位为字,即 512个字节。

/* 定义空闲任务的栈 */
#define configMINIMAL_STACK_SIZE ( ( unsigned short ) 128 ) 
StackType_t IdleTaskStack[configMINIMAL_STACK_SIZE]; 

1.2  定义空闲任务的任务控制块

        任务控制块是每一个任务必须的,空闲任务的的任务控制块我们在 main.c 中定义,是一个全局变量:

/* 定义空闲任务的任务控制块 */
TCB_t IdleTaskTCB;

1.3  创建空闲任务

        当定义好空闲任务的栈,任务控制块后,就可以创建空闲任务。空闲任务在调度器启动函数 vTaskStartScheduler()中创建:

extern TCB_t IdleTaskTCB;
void vApplicationGetIdleTaskMemory( TCB_t **ppxIdleTaskTCBBuffer,StackType_t **ppxIdleTaskStackBuffer,uint32_t *pulIdleTaskStackSize );
void vTaskStartScheduler( void )
{/*=======================创建空闲任务 start=======================*/ TCB_t *pxIdleTaskTCBBuffer = NULL; /* 用于指向空闲任务控制块 */ StackType_t *pxIdleTaskStackBuffer = NULL; /* 用于空闲任务栈起始地址 */ uint32_t ulIdleTaskStackSize; /* 获取空闲任务的内存:任务栈和任务 TCB */ (1) vApplicationGetIdleTaskMemory( &pxIdleTaskTCBBuffer, &pxIdleTaskStackBuffer, &ulIdleTaskStackSize ); /* 创建空闲任务 */ (2) xIdleTaskHandle = xTaskCreateStatic( (TaskFunction_t)prvIdleTask, /* 任务入口 */ (char *)"IDLE", /* 任务名称,字符串形式 */ (uint32_t)ulIdleTaskStackSize , /* 任务栈大小,单位为字 */ (void *) NULL, /* 任务形参 */ (StackType_t *)pxIdleTaskStackBuffer, /* 任务栈起始地址 */ (TCB_t *)pxIdleTaskTCBBuffer ); /* 任务控制块 */ 
/* 将任务添加到就绪列表 */ (3) vListInsertEnd( &( pxReadyTasksLists[0] ), &( ((TCB_t *)pxIdleTaskTCBBuffer)->xStateListItem ) ); /*==========================创建空闲任务 end=====================*/ /* 手动指定第一个运行的任务 */pxCurrentTCB = &Task1TCB;/* 启动调度器 */if ( xPortStartScheduler() != pdFALSE ){/* 调度器启动成功,则不会返回,即不会来到这里 */}
}

(1) : 获取空闲任务的内存 , 即 将 pxIdleTaskTCBBuffer 和 pxIdleTaskStackBuffer 这两个接下来要作为形参传到 xTaskCreateStatic() 函数的指针分别指向空闲任务的 TCB 和栈的起始地址,这个操作由函数 vApplicationGetIdleTaskMemory() 来实现,该函数需要用户自定义。

(2) :调用 xTaskCreateStatic()函数创建空闲任务。

(3) :将空闲任务插入到就绪列表的开头,空闲任务默认的优先级是最低的,即排在就绪列表的开头。

2.  实现阻塞延时

2.1  vTaskDelay()函数

        阻塞延时的阻塞是指任务调用该延时函数后,任务会被剥离 CPU 使用权,然后进入阻塞状态,直到延时结束,任务重新获取 CPU 使用权才可以继续运行。在任务阻塞的这段时间,CPU 可以去执行其它的任务,如果其它的任务也在延时状态,那么 CPU 就将运行空闲任务。

void vTaskDelay( const TickType_t xTicksToDelay )
{TCB_t *pxTCB = NULL;/* 获取当前任务的 TCB */pxTCB = pxCurrentTCB; (1)/* 设置延时时间 */pxTCB->xTicksToDelay = xTicksToDelay; (2)/* 任务切换 */taskYIELD(); (3)
}

(1):获取当前任务的任务控制块。pxCurrentTCB 是一个在 task.c 定义的全局指针,用于指向当前正在运行或者即将要运行的任务的任务控制块。

(2):xTicksToDelay 是任务控制块的一个成员,用于记录任务需要延时的时间,单位为 SysTick 的中断周期。比如我们本书当中 SysTick 的中断周期为 10ms,调用 vTaskDelay( 2 )则完成 2*10ms 的延时。

xTicksToDelay 定义:

typedef struct tskTaskControlBlock
{volatile StackType_t *pxTopOfStack; /* 栈顶 */ListItem_t xStateListItem; /* 任务节点 */StackType_t *pxStack; /* 任务栈起始地址 *//* 任务名称,字符串形式 */char pcTaskName[ configMAX_TASK_NAME_LEN ];TickType_t xTicksToDelay; /* 用于延时 */ 
} tskTCB;

2.2  修改 vTaskSwitchContext()函数

        taskYIELD();任务切换。调用 tashYIELD()会产生 PendSV中断,在 PendSV中断服务函数中会调用上下文切换函数 vTaskSwitchContext(),该函数的作用是寻找最高优先级的就绪任务,然后更新 pxCurrentTCB。

        这里我们创建两个任务以及一个空闲任务进行在这三个任务之间进行切换。

#if 0
void vTaskSwitchContext( void )
{ /* 两个任务轮流切换 */if ( pxCurrentTCB == &Task1TCB ){pxCurrentTCB = &Task2TCB;}else{pxCurrentTCB = &Task1TCB;}
}
#elsevoid vTaskSwitchContext( void ) 
{ /* 如果当前任务是空闲任务,那么就去尝试执行任务 1 或者任务 2, 看看他们的延时时间是否结束,如果任务的延时时间均没有到期, 那就返回继续执行空闲任务 */ if ( pxCurrentTCB == &IdleTaskTCB ) (1) { if (Task1TCB.xTicksToDelay == 0) { pxCurrentTCB =&Task1TCB; } else if (Task2TCB.xTicksToDelay == 0) { pxCurrentTCB =&Task2TCB; } else { return; /* 任务延时均没有到期则返回,继续执行空闲任务 */ } } else /* 当前任务不是空闲任务则会执行到这里 */ (2) { /*如果当前任务是任务 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; /* 返回,不进行切换,因为两个任务都处于延时中 */ } } } 
} 
#endif

3.  SysTick 中断服务函数

        在任务上下文切换函数 vTaskSwitchContext ()中,会判断每个任务的任务控制块中的延时成员 xTicksToDelay 的值是否为 0,如果为 0就要将对应的任务就绪,如果不为 0 就继续延时。如果一个任务要延时,一开始 xTicksToDelay 肯定不为 0,当 xTicksToDelay 变为0 的时候表示延时结束,那么 xTicksToDelay 是以什么周期在递减?在哪里递减?在FreeRTOS 中,这个周期由 SysTick 中断提供,操作系统里面的最小的时间单位就是SysTick 的中断周期,我们称之为一个 tick。

SysTick 中断服务函数:

void xPortSysTickHandler( void )
{/* 关中断 */vPortRaiseBASEPRI(); /* 更新系统时基 */xTaskIncrementTick(); /* 开中断 */vPortClearBASEPRIFromISR(); 
}

xTaskIncrementTick()函数:

void xTaskIncrementTick( void )
{TCB_t *pxTCB = NULL;BaseType_t i = 0;/* 更新系统时基计数器 xTickCount,xTickCount 是一个在 port.c 中定义的全局变量 */const TickType_t xConstTickCount = xTickCount + 1;xTickCount = xConstTickCount;/* 扫描就绪列表中所有任务的 xTicksToDelay,如果不为 0,则减 1 */for (i=0; i<configMAX_PRIORITIES; i++){pxTCB = ( TCB_t * ) listGET_OWNER_OF_HEAD_ENTRY( ( &pxReadyTasksLists[i] ) );if (pxTCB->xTicksToDelay > 0){pxTCB->xTicksToDelay --;}}/* 任务切换 */portYIELD();
}

4.  SysTick 初始化函数

        SysTick 的中断服务函数要想被顺利执行,则 SysTick 必须先初始化。

vPortSetupTimerInterrupt()函数:

/* SysTick 控制寄存器 */
#define portNVIC_SYSTICK_CTRL_REG			( * ( ( volatile uint32_t * ) 0xe000e010 ) )/* SysTick 重装载寄存器寄存器 */
#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 )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 ); 
}

xPortStartScheduler()函数中调用 vPortSetupTimerInterrupt():

BaseType_t xPortStartScheduler( void )
{/* 配置 PendSV 和 SysTick 的中断优先级为最低 */portNVIC_SYSPRI2_REG |= portNVIC_PENDSV_PRI;portNVIC_SYSPRI2_REG |= portNVIC_SYSTICK_PRI;/* 初始化 SysTick */vPortSetupTimerInterrupt();/* 启动第一个任务,不再返回 */prvStartFirstTask();/* 不应该运行到这里 */return 0;
}

FreeRTOS实时操作系统_时光の尘的博客-CSDN博客

http://www.dtcms.com/wzjs/424975.html

相关文章:

  • 南通做网站南昌seo管理
  • 发外链的论坛网站公司网站
  • 个人网站建设大全谷歌浏览器 安卓下载
  • 沈阳网站建设哪家公司好seo网站推广推荐
  • 网站没有做实名认证网站怎么找
  • 新零售商业模式搜索引擎优化公司
  • 企业主体负责人和网站负责人seo外推软件
  • 化妆品网站建设的维护电商运营培训课程有哪些
  • 政府网站建设专业公司海南网站制作公司
  • Wordpress本地打开就很慢网站优化网站
  • 备案个人网站广东企业网站seo报价
  • 免费做抽奖的h5网站合肥百度关键词排名
  • 企业网站怎么管理系统seo优化报价公司
  • 化州市建设局网站怎么找平台推广自己的产品
  • 监控设备东莞网站建设b站刺激战场视频
  • 展览网站模板百度推广的渠道有哪些
  • seo做网站seo是哪个英文的简写
  • 广州有哪些科技公司重庆seo扣费
  • https部署 wordpressseo 什么意思
  • 潮汕美食网站怎么做网络服务网络推广
  • 易企互联网站建设正规电商培训学校排名
  • 虚拟币交易网站建设一手app推广接单平台
  • 江都城乡建设局网站seo交流论坛
  • 平台网站如何做推广方案设计怎么优化网站
  • 网站建设策划书提纲郑州seo优化顾问
  • 苏州建筑行业网站建设如何开发软件app
  • 做关于网站的开题报告网站建设品牌公司
  • 网站国际化怎么做谷歌商店下载
  • 真人性做爰直播网站网络优化工具
  • 需要自己的网站需要怎么做360投放广告怎么收费