基于STM32F103移植华为LiteOS—信号量
对于“信号量”这个实现任务间通信机制,想必学过一些操作系统的读者应该都有所了解吧。这里我用比较通俗一点的话给大家讲解华为LiteOS操作系统里头的“信号量”。
一、信号量概念
1、基本概念从专业上的讲解 信号量(Semaphore)是一种实现任务间通信的机制,实现任务之间同步或临界资源的互斥访问。常用于协助一组相互竞争的任务来访问临界资源。 在多任务系统中,各任务之间需要同步或互斥实现临界资源的保护,信号量功能可以为用户提供这方面的支持。 通常一个信号量的计数值用于对应有效的资源数,表示剩下的可被占用的互斥资源数。其值的含义分两种情况: ? 0,表示没有积累下来的Post操作,且有可能有在此信号量上阻塞的任务。 ? 正值,表示有一个或多个Post下来的释放操作。 以同步为目的的信号量和以互斥为目的的信号量在使用有如下不同: ? 用作互斥时,信号量创建后记数是满的,在需要使用临界资源时,先取信号量,使其变空,这样其他任务需要使用临界资源时就会因为无法取到信号量而阻塞,从而保证了临界资源的安全。 ? 用作同步时,信号量在创建后被置为空,任务1取信号量而阻塞,任务2在某种条件发生后,释放信号量,于是任务1得以进入READY或RUNNING态,从而达到了两个任务间的同步。 2、通俗一点的讲解
我们可以将“LiteOS中的信号量”比作为上图所画“饭店中的饭桌”,其中“饭店”=“信号量”、“饭桌=信号量的容量”。当客户对某个饭桌预约或已经用餐了,那么这个饭桌就被标记为“被申请使用中”,也就是图中的红色X,申请使用的饭桌是不会限制使用时间的,当用餐完毕、结账后,这个饭桌是被标记为“空闲状态”,也就是图中的绿色√,此时是可以被客户马上预约或者用餐的。从上可知道LiteOS操作系统拥有“实时响应,实时处理”机制,而那么多个饭桌的管理就相当于信号量在系统中的资源管理了。
3、华为LiteOS开发指南上对信号量的运作机制讲解 4、华为LiteOS信号量的使用场景 信号量是一种非常灵活的同步方式,可以运用在多种场合中,实现锁、同步、资源计数等功能,也能方便的用于任务与任务,中断与任务的同步中。
二、华为LiteOS中的信号量分为“二值信号量”和“计数信号量”
1、二值信号量 二值信号量可以理解为一个饭店只有一张桌子,该桌子只有(被申请使用和空闲)两种状态,而二值信号量“0”表示申请信号量,“1”表示释放信号量。 在嵌入式操作系统中二值信号量是任务与任务间、 中断与任务间同步的重要手段。可以理解为事件1申请该二值信号量,将事件2阻塞,等待事件1完成,事件1完成后释放该二值信号量,事件2申请该二值信号量,等待事件2完成,事件2完成后释放该二值信号量,然后又到事件1……往返事件的运行达成任务间的同步。 2、计数信号量 计算信号量也就可以理解为一个饭店和它的所有桌子。 顾名思义,计数信号量是用于计数的,在实际的使用中,计数信号量常用于事件计数与资源管理。每当某个事件发生时,任务/中断释放一个信号量(信号量计数值加 1),当处理被事件时(一般在任务中处理),处理任务会取走该信号量(信号量计数值减 1),信号量的计数值则表示还剩余多少个事件没被处理。此外,系统还有很多资源,也可以使用计数信号量进行资源管理,信号量的计数值表示系统中可用的资源数目,任务必须先获取到信号量才能访问资源,当信号量的计数值为零时表示系统没有可用的资源,但是要注意,在使用完资源的时候必须归还信号量,否则当计数值为 0 的时候任务就无法访问该资源了。 3、关于华为LiteOS操作系统中的“二值信号量”和“计数信号量”的一些细节 ①信号量个数 上图为LiteOS操作系统中target_config.h配置文件中信号量的程序片段,图中“20”表示最大支持的信号量数量,这个信号量不是指“桌子”,它指的是“饭店”。 也就是如下图所示,能创建的信号量个数最多是LOSCFG_BASE_IPC_SEM_LIMIT个。(无论是二值信号量或计数信号都可以)
②二值信号量容量和计数信号量容量
由上图可知在los_sem.ph文件中对二值信号量容量、计数信号量容量有定义。这里二值信号量就不再多说了,在开发中大家可能会遇到申请计数信号量时会按照申请的个数申请,但是释放计数信号量会释放不会按照申请的个数释放同等的个数,而是释放不完。 这里我要说一下,不是释放不完,而是数量太多,计数信号量的最大容量个数是OS_SEM_COUNTING_MAX_COUNT=0xFFFF=65535个,太多了,给人错觉是释放不完。 这里我做了一直图给大家讲解,如下图所示。 如果大家释放信号量需要不想让它释放不完,实现一些资源管理的功能,那么大家可以修改los_sem.ph文件中定义的OS_SEM_COUNTING_MAX_COUNT变量值,要是使用到多个计数信号量,而且计数信号量创建的容量值不相同,那么我不建议这么做,大家可自行定义一些变量作为标记,防止一直释放计数信号量。
三、亮代码环节
1、计数信号量(模拟停车场)
#include "los_sys.h "
#include "los_task.ph"
#include "los_queue.h"
#include "los_sem.h"
#include "led.h"
#include "usart1.h"
#include "key.h"
UINT32 Pend_Task_Handle;
UINT32 Post_Task_Handle;
UINT32 CountSem_Handle;
UINT32 CountSem_Handle1;
UINT32 CountSem_Handle2;
static UINT32 AppTaskCreate(void);
static UINT32 Create_Pend_Task(void);
static UINT32 Create_Post_Task(void);
static void Pend_Task(void);
static void Post_Task(void);
int main(void)
{
UINT32 uwRet = LOS_OK;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
Usart1_Config();
LED_GPIO_Config();
Key_Config_parameter();
printf("\r\n正点原子战舰开发板-LiteOS-SRAM 动态创建多任务!\r\n");
uwRet = LOS_KernelInit();
if (uwRet != LOS_OK) {
printf("LiteOS核心初始化失败!任务代码0X%X\r\n",uwRet);
return LOS_NOK;
}
uwRet = AppTaskCreate();
if (uwRet != LOS_OK) {
printf("AppTaskCreate任务创建失败!任务代码0X%X\r\n",uwRet);
return LOS_NOK;
}
LOS_Start();
while(1){
GPIO_SetBits(GPIOB, GPIO_Pin_5);
GPIO_SetBits(GPIOE,GPIO_Pin_5);
}
}
static UINT32 AppTaskCreate(void)
{
UINT32 uwRet = LOS_OK;
uwRet = LOS_SemCreate(5,&CountSem_Handle);
if(uwRet != LOS_OK) {
printf("CountSem创建失败!失败代码0X%X\r\n",uwRet);
}
uwRet = Create_Pend_Task();
if(uwRet != LOS_OK) {
printf("Pend_Task create failed!\r\n");
return uwRet;
}
uwRet = Create_Post_Task();
if(uwRet != LOS_OK) {
printf("Post_Task create failed!\r\n");
return uwRet;
}
return LOS_OK;
}
static UINT32 Create_Pend_Task(void) {
UINT32 uwRet = LOS_OK;
TSK_INIT_PARAM_S task_init_param;
task_init_param.usTaskPrio = 5;
task_init_param.pcName = "Pend_Task";
task_init_param.pfnTaskEntry = (TSK_ENTRY_FUNC)Pend_Task;
task_init_param.uwStackSize = 1024;
uwRet = LOS_TaskCreate(&Pend_Task_Handle,&task_init_param);
return uwRet;
}
static UINT32 Create_Post_Task(void) {
UINT32 uwRet = LOS_OK;
TSK_INIT_PARAM_S task_init_param;
task_init_param.usTaskPrio = 4;
task_init_param.pcName = "Post_Task";
task_init_param.pfnTaskEntry = (TSK_ENTRY_FUNC)Post_Task;
task_init_param.uwStackSize = 1024;
uwRet = LOS_TaskCreate(&Post_Task_Handle,&task_init_param);
return uwRet;
}
static void Pend_Task(void) {
UINT32 uwRet = LOS_OK;
while(1) {
if(KEY_CLASS[KEY0].Key_State_Machine.Key_Event==KEY_CONFIRN_DOWN) {
uwRet = LOS_SemPend(CountSem_Handle,0);
if(LOS_OK == uwRet) {
printf("KEY0被按下,成功申请到停车位!\r\n");
} else {
printf ("KEY0 被按下,不好意思,现在停车场已满!\r\n");
}
}
LOS_TaskDelay(20);
}
}
static void Post_Task(void) {
UINT32 uwRet = LOS_OK;
while(1) {
Read_Key_State();
if(KEY_CLASS[KEYUP].Key_State_Machine.Key_Event==KEY_CONFIRN_DOWN) {
uwRet = LOS_SemPost(CountSem_Handle);
if(LOS_OK == uwRet) {
printf("KEY_UP 被按下,释放 1 个停车位!\r\n");
} else {
printf("KEY_UP 被按下,但已无车位可以释放!\r\n");
}
}
LOS_TaskDelay(20);
}
}
2、二值信号量(两个任务间进行同步操作)
#include "los_sys.h "
#include "los_task.ph"
#include "los_queue.h"
#include "los_sem.h"
#include "led.h"
#include "usart1.h"
#include "key.h"
UINT32 Read_Task_Handle;
UINT32 Write_Task_Handle;
UINT32 BinarySem_Handle;
uint8_t ucValue[2] = {0x00,0x000};
static UINT32 AppTaskCreate(void);
static UINT32 Create_Read_Task(void);
static UINT32 Create_Write_Task(void);
static void Read_Task(void);
static void Write_Task(void);
int main(void)
{
UINT32 uwRet = LOS_OK;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
Usart1_Config();
LED_GPIO_Config();
Key_Config_parameter();
printf("正点原子战舰开发板-LiteOS-SRAM 动态创建多任务!\r\n\r\n");
uwRet = LOS_KernelInit();
if (uwRet != LOS_OK) {
printf("LiteOS核心初始化失败!任务代码0X%X\r\n",uwRet);
return LOS_NOK;
}
uwRet = AppTaskCreate();
if (uwRet != LOS_OK) {
printf("AppTaskCreate任务创建失败!任务代码0X%X\r\n",uwRet);
return LOS_NOK;
}
LOS_Start();
while(1){
GPIO_SetBits(GPIOB, GPIO_Pin_5);
GPIO_SetBits(GPIOE,GPIO_Pin_5);
}
}
static UINT32 AppTaskCreate(void)
{
UINT32 uwRet = LOS_OK;
uwRet = LOS_BinarySemCreate(1,&BinarySem_Handle);
if(uwRet != LOS_OK) {
printf("BinarySem创建失败!失败代码0X%X\r\n",uwRet);
}
uwRet = Create_Read_Task();
if(uwRet != LOS_OK) {
printf("Read_Task create failed!\r\n");
return uwRet;
}
uwRet = Create_Write_Task();
if(uwRet != LOS_OK) {
printf("Write_Task create failed!\r\n");
return uwRet;
}
return LOS_OK;
}
static UINT32 Create_Read_Task(void) {
UINT32 uwRet = LOS_OK;
TSK_INIT_PARAM_S task_init_param;
task_init_param.usTaskPrio = 5;
task_init_param.pcName = "Read_Task";
task_init_param.pfnTaskEntry = (TSK_ENTRY_FUNC)Read_Task;
task_init_param.uwStackSize = 1024;
uwRet = LOS_TaskCreate(&Read_Task_Handle,&task_init_param);
return uwRet;
}
static UINT32 Create_Write_Task(void) {
UINT32 uwRet = LOS_OK;
TSK_INIT_PARAM_S task_init_param;
task_init_param.usTaskPrio = 4;
task_init_param.pcName = "Write_Task";
task_init_param.pfnTaskEntry = (TSK_ENTRY_FUNC)Write_Task;
task_init_param.uwStackSize = 1024;
uwRet = LOS_TaskCreate(&Write_Task_Handle,&task_init_param);
return uwRet;
}
static void Read_Task(void) {
UINT32 uwRet = LOS_OK;
while(1) {
uwRet = LOS_SemPend(BinarySem_Handle,LOS_WAIT_FOREVER);
if(uwRet==LOS_OK) {
printf("\r\nRead_Task Apply Sem Successful!\r\n");
}
printf("Read_Task!\r\n");
printf("ucValue[0] = %d, ucValue[1] = %d \r\n",ucValue[0],ucValue[1]);
if(ucValue[0]==ucValue[1]) {
printf("Synchronization Successful!\r\n");
} else {
printf("Fail!\r\n");
}
LOS_SemPost(BinarySem_Handle);
}
}
static void Write_Task(void) {
UINT32 uwRet = LOS_OK;
while(1) {
uwRet = LOS_SemPend(BinarySem_Handle,LOS_WAIT_FOREVER);
if(uwRet==LOS_OK) {
printf("\r\nWrite_Task Apply Sem Successful!\r\n");
}
printf("Write_Task---1!\r\n");
ucValue[0]++;
LOS_TaskDelay(1000);
ucValue[1]++;
printf("Write_Task---2!\r\n");
LOS_SemPost(BinarySem_Handle);
LOS_TaskYield();
}
}
总结
学习东西要有耐心,从根本上去分析,不要盲目追求结果 如下图,野火教程文档只是说出现这种不希望的现象,至于为什么会出现,怎么去去解决却没有提,很多人会做完这个实验就不了了之了。
有什么问题,欢迎大家一起交流探讨学习,我个人信息详情有我的联系方式,留言也可以。大家一起共同进步。
|