Thread实时核学习笔记
Thread线程 - Task任务线程控制块线程入口函数线程栈优先级抢占阈值启动方式创建函数//函数原型UINTtx_thread_create(TX_THREAD*thread_ptr,//相当于freeRTOS的TCB的句柄CHAR*name_ptr,//字符串指针VOID(*entry_function)(ULONG id),//入口函数地址ULONG entry_input,//启动配置可以三个线程用同一 个入口函数靠entry_input作为id区分代码写法VOID*stack_start,//栈地址ULONG stack_size,//栈大小单位是字节freeRTOS是字UINT priority,//优先级数字越小优先级越高UINT preempt_threshold,//抢占阈值当抢占阈值和线程本身优先级一致时相当于禁用抢占ULONG time_slice,//时间片主要用于同优先级线程即priority一致的线程UINT auto_start//TX_AUTO_START 创建后立即启动TX_DONT_START创建后先保持挂起)//示例代码TX_THREAD AppTaskStartTCB;#defineAppTaskNameApp Task StartvoidAppTaskStart(ULONG thread_input);#defineAPP_TASK_ENTRY_INPUT1uint8_tAppTaskStartStk[4096];#defineAppTaskStacksizeof(AppTaskStartStk)#defineAppTaskPriority5#defineAppTaskPreemp5#defineAppTaskTimeSliceTX_NO_TIME_SLICE#defineAppTaskAutoStartTX_AUTO_STARTtx_thread_create(AppTaskStartTCB,AppTaskName,AppTaskStart,APP_TASK_ENTRY_INPUT,AppTaskStartStk[0],AppTaskStack,AppTaskPriority,AppTaskPreemp,AppTaskTimeSlice,AppTaskAutoStart);//线程实现voidAppTaskStart(ULONG thread_input){while(1){tx_thread_sleep(10);//阻塞10ms}}voidmain(void){System_Init();tx_kernel_enter();//启动内核调度器不返回会调用tx_application_define(),在这里面创建线程相当于vTaskStartScheduler()while(1){}}/*tx_kernel_enter()实时核初始部分调用部分 void tx_application_define(void *first_unused_memory) { tx_thread_create(...);//创建线程 tx_thread_create(...); tx_thread_create(...); } *//*线程实现同一个入口函数区分 void LED_Task(ULONG led_id) { if(led_id 1) { ... } } */线程状态Running运行态Ready就绪态Blocked/Waiting阻塞态/挂起态调度器根据调度算法决定当前应该执行哪个线程挂起线程api函数传入的参数是控制块指针UINT tx_thread_suspend(TX_THREAD *thread_ptr);恢复线程api函数传入的参数是控制块指针UINT tx_thread_resume(TX_THREAD *thread_ptr);恢复线程并非马上执行而且恢复线程参与调度资格同优先级Ready线程让给别人tx_thread_relinquish();//示例代码TX_THREAD NetworkTaskTCB;tx_thread_suspend(NetworkTaskTCB);阻塞api函数传入的参数是延时的时间时间 Tick数量 x 一个Tick的时间UINT tx_thread_sleep(ULONG timer_ticks);Tick RTOS 的节拍器一个tick时间1/tick的频率tx_time_get();//获取系统当前累计运行的 Tick 数SYSTEM_CLOCK EQU400000000//CPU每秒400M个时钟周期SYSTICK_CYCLESEQU((SYSTEM_CLOCK/1000)-1)//systick每秒中断1000次//400Mhz下400000/40000001ms故每1ms产生一次systick中断Tick1消息队列Queue常用FIFO支持LIFO传的是数据而不仅仅是保存原变量地址将要发送的数据复制到消息队列里面创建消息队列message_size这里是4字节单位也就是4x832bit如果传入消息是uint32的那么message_size1即可如果传入2个uint32_t的数据的结构体那么2x32/88这里message_size2 ……//创建消息队列函数原型UINTtx_queue_create(TX_QUEUE*queue_ptr,//队列控制块指针区别于线程的CHAR*name_ptr,//线程字符型名字UINT message_size,//每条消息大小(单位4字节范围1-16)VOID*queue_start,//队列开始地址ULONG queue_size//队列大小);//代码示例//这里一条消息4字节缓冲区10个数据那么缓冲区总共40个字节TX_QUEUE ADCQueue;#defineADCQueueNameMy ADC Queue#defineADCMessageSize1uint32_tADCQueueBuff[10];#defineADCQueueSizesizeof(ADCQueueBuff)tx_queue_create(ADCQueue,ADCQueueName,ADCMessageSize,ADCQueueBuff,ADCQueueSize);发送消息(将消息存入到队列里)//函数原型UINTtx_queue_send(TX_QUEUE*queue_ptr,VOID*source_ptr,//数据从哪里复制数据类型和创建时保持一致ULONG wait_option//等待选择有三种模式//1.TX_NO_WAIT 可以放就放放不进去就马上返回//2.数字 指定tick如果信号满了最多等tick超时返回//3.TX_WAIT_FOREVER 一直等待直到有空间);uint32_tADCData123;tx_queue_send(ADCQueue,ADCData,TX_WAIT_FOREVER)接收消息(将消息从队列取出)//api函数原型UINTtx_queue_receive(TX_QUEUE*queue_ptr,VOID*destination_ptr,//目的地将数据复制到某个地址ULONG wait_option);//示例代码uint32_tvalue;statustx_queue_receive(ADCQueue,value,200);发送消息是相较于线程而言队列是接收方也就是说发送消息是把数据存进到队列里接收消息是把数据从队列取出来如果队列是空的调用接收消息队列线程进入等队列消息等待另一个线程发送消息到队列里接收消息线程被唤醒如果队列是满的调用发送消息队列线程进入等队列消息等待另一个线程接收队列的消息发送消息线程被唤醒中断里可以使用发送消息队列但是等待参数必须用TX_NO_WAIT信号量Semaphore重点是某件事发生了几次某种资源还有几个本质是一个信号量ThreadX里是32位计数信号量适合有N个相同资源需要就取不需要就归还还适用于同步创建信号量API函数//原型UINTtx_semaphore_create(TX_SEMAPHORE*semaphore_ptr,//信号量控制块CHAR*name_ptr,//控制块字符串名字ULONG initial_count//信号量初始计数);//示例代码TX_SEMAPHORE AdcSem;#defineADCSemNameADC Semaphore#defineADCInitCount0//0-还没有事件发生tx_semaphore_create(AdcSem,ADCSemName,ADCInitCount);-释放信号量/发送信号量//调用之后信号量1UINTtx_semaphore_put(TX_SEMAPHORE*semaphore_ptr);获取信号量//调用之后信号量-1UINTtx_semaphore_get(TX_SEMAPHORE*semaphore_ptr,ULONG wait_option//与消息队列一样都有三种模式//1.TX_NO_WAIT 可以放就放放不进去就马上返回//2.数字 指定tick如果信号满了最多等tick超时返回//3.TX_WAIT_FOREVER 一直等待直到有空间);互斥量保护共享资源同一时刻只能由一个线程访问基本使用模型get资源-使用-put资源//API原型UINTtx_mutex_create(TX_MUTEX*mutex_ptr,//互斥量控制块CHAR*name_ptr,//互斥量字符串名字UINT priority_inherit//是否开启优先级继承//1.TX_INHERIT 优先级继承//2.TX_NO_INHERIT 优先级不继承);/*tips 低优先级持有互斥量时高优先级想要获取互斥量的话高优先级线程会被阻塞直到低优先级释放互斥量 但是如果等低优先级使用完资源释放互斥量的这个时候高优先级还在阻塞而中优先级的要使用互斥量这个时候互斥量就会被中优先级占有 此时高优先级的反而不是最先执行的故有了优先级反转 也就是说低优先级持有互斥量时高优先级线程等待时开启优先级继承会让持有者临时继承等待者的高优先级释放之后恢复原优先级。 *///获取互斥量UINTtx_mutex_get(TX_MUTEX*mutex_ptr,ULONG wait_option//互斥量等待选项);/* 如果 Mutex 已经被当前任务拥有再次 tx_mutex_get() 会让“所有权计数”加 1,那么释放的话就要和tx_mutex_get()次数一致才算释放完 *//*注意互斥量不可用于中断互斥量之间的处理时间尽可能短 tx_mutex_get(); 只做必须保护的操作 tx_mutex_put(); */-总结Queue用于传数据Semaphore用于通知/计数Mutex用于保护共享资源软件定时器Timer时基来自于ThreadX的系统tickUINTtx_timer_create(TX_TIMER*timer_ptr,//定时器控制块CHAR*name_ptr,//定时器字符串名字VOID(*expiration_function)(ULONG),//定时器到时间之后自动执行的函数传入的是函数的地址ULONG expiration_input,//传给回调函数的参数共用一个函数的id决定要去处理什么东西参考stm32的定时器回调函数用传入参数判断是哪一个定时器到时间需要处理什么ULONG initial_ticks,//定时器第一次到期之前要等待多少个tickULONG reschedule_ticks,//第一次到期之后还要不要继续运行以及周期是多少。如果0则只执行一次如果是其他的话那么就是定时器先执行initial_ticks在执行reschedule_tickscallback再执行reschedule_ticks……UINT auto_activate//创建完马上计时还是先只创建不运行//1.TX_NO_ACTIVATE 创建了但暂时不开始计时//2.TX_AUTO_ACTIVATE 创建完直接启动);//手动开启UINTtx_timer_activate(TX_TIMER*timer_ptr);//示例代码TX_TIMER AppTimer;#defineAppTimerNameApp TimervoidTimerCallback(ULONG input);#defineAppTimerId0#defineAppTimerInitTime100#defineAppTimerScheduleTime200tx_timer_create(AppTimer,AppTimerName,TimerCallback,AppTimerId,AppTimerInitTime,AppTimerScheduleTime,TX_AUTO_ACTIVATE);voidTimerCallback(ULONG input)//这里的input就是expiration_input id{if(input1){LED1_Toggle();}elseif(input2){LED2_Toggle();}}事件标志组用一个32位整数每一个bit代表一种时间//事件标志组创建UINTtx_event_flags_create(TX_EVENT_FLAGS_GROUP*group_ptr,//事件标志组控制块CHAR*name_ptr//事件标志组字符名字);//创建之后32个标志位初始全部为0// 仅能在初始化或者线程中创建即不可在中断中创建//设置事件UINTtx_event_flags_set(TX_EVENT_FLAGS_GROUP*group_ptr,ULONG flags_to_set,//要操作的事件标志位用16进制32位也就是8个数字例如0x00000006-00000000000000000000000000000110 也可以写成1UL 5UINT set_option//1.TX_OR位或运算适合设置几个位//2.TX_AND位与运算适合清除某些位);//可以在中断中设置事件标志位也推荐这么做然后主程序获取//获取事件标志位UINTtx_event_flags_get(TX_EVENT_FLAGS_GROUP*group_ptr,ULONG requested_flags,//要等待的标志位可与flags_to_set共用宏定义UINT get_option,//1.TX_OR要等待的标志位位或只要有一个位1就算成功//2.TX_AND要等待的标志位位与全部为1才算成功//3.TX_OR_CLEAR等待标志位如果有一个为1对应的标志位就清零//4.TX_AND_CLEAR等待标志位如果全部为1对应的标志位就清零ULONG*actual_flags_ptr,//告诉哪些事件真的发生了如果前面用的是or位或模式的话这里就可以判断究竟是哪一个事件发生/*eg tx_event_flags_get( SystemEvents, ADC_DONE | NET_READY, TX_OR_CLEAR, actual_flags, TX_WAIT_FOREVER ); if(actual_flags ADC_DONE) ProcessADC(); if(actual_flags NET_READY) ProcessNetwork(); */ULONG wait_option);ThreadX 动态内存管理实时系统中申请内存容易产生内存碎片以及申请/施放时间不确定等对实时系统都比较麻烦Block Pool 大小完全一样的内存固定大小块申请/释放事件是确定性的能够避免内存碎片问题创建BlockUINTtx_block_pool_create(TX_BLOCK_POOL*pool_ptr,//内存控制块CHAR*name_ptr,//字符串名字ULONG block_size,//每一个内存块大小,单位字节VOID*pool_start,//整块底层内存首地址ULONG pool_size//整个内存池有多少字节);//内存块的数量由ThreadX自己计算不用传参//底层内存不会100%变成有效数据空间RTOS自己也需要占掉一部分//示例代码TX_BLOCK_POOL AppPoolPTR;#defineAppBlockNameapp_block#defineAppBlockSize64uint32_tAppPoolBuf[1024];UINTtx_block_pool_create(AppPoolPTR,AppBlockName,AppBlockSize,AppPoolBuf,sizeof(AppPoolBuf));申请内存块UINTtx_block_allocate(TX_BLOCK_POOL*pool_ptr,VOID**block_ptr,//二级指针ULONG wait_option//块全部用完等待选择);//示例代码uint8_t*p;//指向某一个空闲的块//一级指针p本身就是地址了p就是地址再取地址也就是二级指针statustx_block_allocate(AppPoolPTR,(VOID**)p,TX_NO_WAIT);释放内存块UINTtx_block_release(VOID*block_ptr);//某块内存释放时必须还回它原来所属的那个内存分区Byte Pool 一大块内存每次想用多少就取多少找内存是从头开始找喷到第一块足够大的空闲区域就从这里开始分配还是会产生内存碎片不过在后续搜索内存时发现可以合并的内存ThreadX提供的管理会将其合并也就是内存整理更灵活但是分配时间不确定不适合时间关键场景创建字节池//API函数UINTtx_byte_pool_create(TX_BYTE_POOL*pool_ptr,CHAR*name_ptr,VOID*pool_start,//内存池开始地址要求4字节对齐ULONG pool_size//内存池总大小);//示例代码TX_BYTE_POOL AppBytePool;#defineAppBytePoolNameapp_byteuint32_tAppPoolBuf[1024];UINTtx_byte_pool_create(AppBytePool,AppBytePoolName,AppPoolBuf,sizeof(AppPoolBuf));分配内存字节//API函数UINTtx_byte_allocate(TX_BYTE_POOL*pool_ptr,VOID**memory_ptr,ULONG memory_size,//单位为字节,指定的内存大小ULONG wait_option);//示例代码uint8_t*p;#defineAppMemorySize77tx_byte_allocate(AppBytePool,(VOID**)p,AppMemorySize,TX_NO_WAIT);释放内存UINTtx_byte_release(VOID*memory_ptr);查询字节内存还剩多少UINTtx_byte_pool_info_get(TX_BYTE_POOL*pool_ptr,CHAR**name,ULONG*available,//还剩多少字节ULONG*fragments,//当前有多少个内存片段TX_THREAD**first_suspended,ULONG*suspended_count,TX_BYTE_POOL**next_pool);//不需要的参数可以设置为TX_NULL