完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
创客的兄弟姐妹们大家好,我是杰杰。又到了更新的时候了。听首歌缓解一下心情。
开始今天的内容之前,先补充一下上篇文章的一点点遗漏的知识点。 1BaseType_t xTaskCreate( TaskFunction_t pvTaskCode, 2 const char * const pcName, 3 uint16_t usStackDepth, 4 void *pvParameters, 5 UBaseType_t uxPriority, 6 TaskHandle_t *pvCreatedTask 7 ); 8创建任务中的堆栈大小问题,在task.h中有这样子的描述: 9/** 10* @param usStackDepth The size of the task stack specified as the number of variables the stack * can hold - not the number of bytes. For example, if the stack is 16 bits wide and 11* usStackDepth is defined as 100, 200 byteswill be allocated for stack storage. 12*/代码可左右滑动 当任务创建时,内核会分为每个任务分配属于任务自己的唯一堆栈。usStackDepth 值用于告诉内核为它应该分配多大的栈空间。这个值指定的是栈空间可以保存多少个字(word) ,而不是多少个字节(byte)。文档也有说明,如果是16位宽度的话,假如usStackDepth = 100;那么就是200个字节(byte)。当然,我用的是STM32,32位宽度的, usStackDepth=100;那么就是400个字节(byte)。 好啦,补充完毕。下面正式开始我们今天的主题。 我自己学的是应用层的东西,很多底层的东西我也不懂,水平有限,出错了还请多多包涵。 其实我自己写文章的时候也去跟着火哥的书看着底层的东西啦,但是本身自己也是不懂,不敢乱写。所以,这个《从单片机到操作系统》系列的文章,我会讲一点底层,更多的是应用层,主要是用的方面。 按照一般的写代码的习惯,在main函数里面各类初始化完毕了,并且创建任务成功了,那么,可以开启任务调度了。 1int main(void) 2{ 3 NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//设置系统中断优先级分组4 4 Delay_Init(); //延时函数初始化 5 Uart_Init(115200); //初始化串口 6 LED_Init(); //初始化LED 7 KEY_Init(); 8 //创建开始任务 9 xTaskCreate((TaskFunction_t )start_task, //任务函数 10 (const char* )"start_task", //任务名称 11 (uint16_t )START_STK_SIZE, //任务堆栈大小 12 (void* )NULL, //传递给任务函数的参数 13 (UBaseType_t )START_TASK_PRIO, //任务优先级 14 (TaskHandle_t* )&StartTask_Handler); //任务句柄 15 vTaskStartScheduler(); //开启任务调度 16} 来大概看看分析一下创建任务的过程,虽然说会用就行,但是也是要知道了解一下的。注意:下面说的创建任务均为xTaskCreate(动态创建)而非静态创建。 1pxStack = ( StackType_t * ) pvPortMalloc( ( ( ( size_t ) usStackDepth ) * sizeof( StackType_t ) ) ); 2/*lint !e961 MISRA exception as the casts are only redundant for some ports. */ 3 if( pxStack != NULL ) 4 { 5 /* Allocate space for the TCB. */ 6 pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) ); 7 /*lint !e961 MISRA exception as the casts are only redundant for some paths. */ 8 if( pxNewTCB != NULL ) 9 { 10 /* Store the stack location in the TCB. */ 11 pxNewTCB->pxStack = pxStack; 12 } 13 else 14 { 15 /* The stack cannot be used as the TCB was not created. Free 16 it again. */ 17 vPortFree( pxStack ); 18 } 19 } 20 else 21 { 22 pxNewTCB = NULL; 23 } 24 } 首先是利用pvPortMalloc给任务的堆栈分配空间,if( pxStack != NULL )如果内存申请成功,就接着给任务控制块申请内存。pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) );同样是使用pvPortMalloc();如果任务控制块内存申请失败则释放 之前已经申请成功的任务堆栈的内存vPortFree( pxStack ); 然后就初始化任务相关的东西,并且将新初始化的任务控制块添加到列表中prvAddNewTaskToReadyList( pxNewTCB ); 最后返回任务的状态,如果是成功了就是pdPASS,假如失败了就是返回errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY; 1prvInitialiseNewTask( pxTaskCode, 2 pcName, 3 ( uint32_t ) usStackDepth, 4 pvParameters, 5 uxPriority, 6 pxCreatedTask, 7 pxNewTCB, 8 NULL ); 9 prvAddNewTaskToReadyList( pxNewTCB ); 10 xReturn = pdPASS; 11 } 12 else 13 { 14 xReturn = errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY; 15 } 16 return xReturn; 17 } 18// 相关宏定义 19#define pdPASS ( pdTRUE ) 20#define pdTRUE ( ( BaseType_t ) 1 ) 21/* FreeRTOS error definitions. */ 22#define errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY ( -1 ) 具体的static void prvInitialiseNewTask(()实现请参考FreeRTOS的tasks.c文件的767行代码。具体的static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB )实现请参考FreeRTOS的tasks.c文件的963行代码。 因为这些是tasks.c中的静态的函数,仅供xTaskCreate创建任务内部调用的,我们无需理会这些函数的实现过程,当然如果需要请自行了解。 创建完任务就开启任务调度了:1vTaskStartScheduler(); //开启任务调度 在任务调度里面,会创建一个空闲任务(我们将的都是动态创建任务,静态创建其实一样的) 1xReturn = xTaskCreate( prvIdleTask, 2 "IDLE", configMINIMAL_STACK_SIZE, 3 ( void * ) NULL, 4 ( tskIDLE_PRIORITY | portPRIVILEGE_BIT ), 5 &xIdleTaskHandle ); 6/*lint !e961 MISRA exception, justified as it is not a redundant explicit cast to all supported compilers. */ 7 } 8相关宏定义: 9#define tskIDLE_PRIORITY ( ( UBaseType_t ) 0U ) 10#ifndef portPRIVILEGE_BIT 11 #define portPRIVILEGE_BIT ( ( UBaseType_t ) 0x00 ) 12#endif 13#define configUSE_TIMERS 1 14 //为1时启用软件定时器 从上面的代码我们可以看出,空闲任务的优先级是tskIDLE_PRIORITY为0,也就是说空闲任务的优先级最低。当CPU没事干的时候才执行空闲任务,以待随时切换优先级更高的任务。如果使用了软件定时器的话,我们还需要创建定时器任务,创建的函数是:1#if ( configUSE_TIMERS == 1 ) 2 BaseType_t xTimerCreateTimerTask( void ) 3然后还要把中断关一下1portDISABLE_INTERRUPTS(); 至于为什么关中断,也有说明: 1/* Interrupts are turned off here, toensure a tick does not occur 2before or during the call toxPortStartScheduler(). The stacks of 3the created tasks contain a status wordwith interrupts switched on 4so interrupts will automatically getre-enabled when the first task 5starts to run. */ 6/ *中断在这里被关闭,以确保不会发生滴答 7在调用xPortStartScheduler()之前或期间。堆栈 8创建的任务包含一个打开中断的状态字 9因此中断将在第一个任务时自动重新启用 10开始运行。*/ 那么如何打开中断呢????这是个很重要的问题别担心,我们在SVC中断服务函数里面就会打开中断的看代码: 1__asm void vPortSVCHandler( void ) 2{ 3 PRESERVE8 4 ldr r3, =pxCurrentTCB /* Restore the context. */ 5 ldrr1, [r3] /* UsepxCurrentTCBConst to get the pxCurrentTCB address. */ 6 ldrr0, [r1] /* Thefirst item in pxCurrentTCB is the task top of stack. */ 7 ldmiar0!, {r4-r11} /* Pop theregisters that are not automatically saved on exception entry and the criticalnesting count. */ 8 msrpsp, r0 /*Restore the task stack pointer. */ 9 i*** 10 movr0, #0 11 msr basepri, r0 12 orrr14, #0xd 13 bxr14 14} 1msr basepri, r0 就是它把中断打开的。看不懂没所谓,我也不懂汇编,看得懂知道就好啦。 1xSchedulerRunning = pdTRUE; 任务调度开始运行 1/* If configGENERATE_RUN_TIME_STATS isdefined then the following 2macro must be defined to configure thetimer/counter used to generate 3the run time counter time base. */ 4portCONFIGURE_TIMER_FOR_RUN_TIME_STATS(); 如果configGENERATE_RUN_TIME_STATS使用时间统计功能,这个宏为1,那么用户必须实现一个宏portCONFIGURE_TIMER_FOR_RUN_TIME_STATS();用来配置一个定时器或者计数器。 来到我们的重点了,开启任务调度,那么任务到这了就不会返回了。1if( xPortStartScheduler() != pdFALSE ) 2 { 3 /*Should not reach here as if the scheduler is running the 4 functionwill not return. */ 5 }然后就能开启第一个任务了,感觉好难是吧,我一开始也是觉得的,但是写了这篇文章,觉得还行吧,也不算太难,可能也是在查看代码跟别人的书籍吧,写东西其实还是蛮好的,能加深理解,写过文章的人就知道,懂了不一定能写出来,所以,我还是很希望朋友们能投稿的。杰杰随时欢迎。。。 开始任务就按照套路模板添加自己的代码就好啦,很简单的。先创建任务: 1 xTaskCreate((TaskFunction_t )led0_task, 2 (const char* )"led0_task", 3 (uint16_t )LED0_STK_SIZE, 4 (void* )NULL, 5 (UBaseType_t )LED0_TASK_PRIO, 6 (TaskHandle_t* )&LED0Task_Handler); 7 //创建LED1任务 8 xTaskCreate((TaskFunction_t )led1_task, 9 (const char* )"led1_task", 10 (uint16_t )LED1_STK_SIZE, 11 (void* )NULL, 12 (UBaseType_t )LED1_TASK_PRIO, 13 (TaskHandle_t* )&LED1Task_Handler); 创建完任务就开启任务调度:1vTaskStartScheduler(); //开启任务调度 然后具体实现任务函数: 1//LED0任务函数 2void led0_task(void *pvParameters) 3{ 4 while(1) 5 { 6 LED0=~LED0; 7 vTaskDelay(500); 8 } 9} 10//LED1任务函数 11void led1_task(void *pvParameters) 12{ 13 while(1) 14 { 15 LED1=0; 16 vTaskDelay(200); 17 LED1=1; 18 vTaskDelay(800); 19 } 20} 好啦,今天的介绍到这了为止,后面还会持续更新,敬请期待哦~ 欢迎大家一起来讨论操作系统的知识我们的群号是:783234154 创客飞梦空间是开源公众号欢迎大家分享出去也欢迎大家投稿 |
|
相关推荐
|
|
1307 浏览 1 评论
AD7686芯片不传输数据给STM32,但是手按住就会有数据。
1234 浏览 3 评论
2313 浏览 0 评论
如何解决MPU-9250与STM32通讯时,出现HAL_ERROR = 0x01U
1404 浏览 1 评论
hal库中i2c卡死在HAL_I2C_Master_Transmit
1834 浏览 1 评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-11-30 09:07 , Processed in 0.776025 second(s), Total 70, Slave 54 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号