μC/OS-II学习--使用篇(一篇就足够了)

μC/OS-II ,这个以前都读成了miu C OS two。其实应该读做“micro C O S two”,μ代表“微小”之意,字母C代表“控制器”,所以总体含义为“微控制器 操作系统版本2”。不过不知道这么读,会不会被不专业的人嘲笑,着实尴尬。
反正不要叫成“U”cOS就好。
在这里插入图片描述

这篇文章主要介绍一下如何在μC/OS-II下进行程序开发。简单点说,就是如何调用那些系统函数。少说细节和究竟,只说怎么搞。
在这里插入图片描述

主文件

一般主文件,即main.c中的main函数写法如下,以下是我在STM32上运行μC/OS-II的例子

int main(void)
{
	//芯片初始化
	STM_Init();
	//其他初始化
	RCC_Config();
	GPIO_Config();
	USART_Config();
	NVIC_Config();
	
	//系统初始化
	OSInit();
	//创建任务
	OSTaskCreate(Task_1,(void *)0,&Task_1_stk[TASK_1_STK_SIZE-1],TASK_1_PRIO);
	OSTaskCreate(Task_2,(void *)0,&Task_2_stk[TASK_2_STK_SIZE-1],TASK_2_PRIO);
	//启动任务
	OSStart();

	return 0;
}

关键函数就是OSInit和OSStart。一个是预备,一个是开始。
在这里插入图片描述

任务

操作系统,最核心的内容,就是多任务执行,所以这也是最容易切入一个操作系统的点,初始化完成,创建两个函数,看到他们交替运行,便是极好的。
在这里插入图片描述

任务的优先级越高,反映优先级的值则越低(这个和FreeRTOS是相反的)。在最新的µC/OS-Ⅱ版本中,任务的优先级数也可作为任务的标识符使用。其实就是优先级能表示该任务,因为一个任务只能选一个优先级,一个优先级只能被一个任务使用。
在这里插入图片描述

功能函数备注
建立任务OSTaskCreate()
建立任务OSTaskCreateExt()
堆栈检验OSTaskStkChk()
删除任务OSTaskDel()
请求删除任务OSTaskDelReq()
改变任务的优先级OSTaskChangePrio()
挂起任务OSTaskSuspend()
恢复任务OSTaskResume()
获得有关任务的信息OSTaskQuery()

主要介绍一下创建任务,毕竟这个是最常用最多用的。
在这里插入图片描述

INT8U OSTaskCreate(void (*task)(void *pd), void *pdata, OS_STK *ptos, INT8U prio);
参数函数
task是指向任务代码的指针。
pdata指向一个数据结构,该结构用来在建立任务时向任务传递参数。
ptos为指向任务堆栈栈顶的指针。
prio为任务的优先级。每个任务必须有一个唯一的优先级作为标识。数字越小,优先级越高
返回值OSTaskCreate()的返回值为下述之一:
OS_NO_ERR:函数调用成功。
OS_PRIO_EXIST:具有该优先级的任务已经存在。
OS_PRIO_INVALID:参数指定的优先级大于 OS_LOWEST_PRIO。 l OS_NO_MORE_TCB:系统中没有 OS_TCB 可以分配给任务了。

时间管理

有了任务,如果想多任务并行,那自然是需要任务有运行态和非运行态,这样才能让出CPU。
在这里插入图片描述

功能函数备注
任务延时函数void OSTimeDly(INT32U ticks);
按时分秒延时函数void OSTimeDlyHMSM (INT8U hours, INT8U minutes, INT8U seconds, INT16U ms)
让处在延时期的任务结束延时INT8U OSTimeDlyResume(INT8U prio);这个是主动唤醒一个处于延迟等待的任务
系统时间INT32U OSTimeGet(void);获取系统时钟的计数器值,这是一个32位的值
void OSTimeSet(INT32U ticks);

内存

用户开发,难免用到申请内存,不过这次的提供的内存管理和我们常用的malloc不太一样,系统提供一系列函数,来供我们分配内存时候调用。其实说简单点,更像是一个基于静态数组,进行的内存重新划分方式提供可以使用的内存块。
开启内存管理需要打开宏
在这里插入图片描述

功能函数备注
建立一个内存分区OS_MEM *OSMemCreate(void *addr, INT32U nblks, INT32U blksize, INT8U *perr);
分配一个内存块void *OSMemGet(OS_MEM *pmem, INT8U *perr);
释放一个内存块INT8U OSMemPut(OS_MEM *pmem, void *pblk);
查询一个内存分区的状态INT8U OSMemQuery(OS_MEM *pmem, OS_MEM_DATA *p_mem_data);

举个例子吧,看一下就明白了。demo才是真正有价值的代码。

OS_MEM *CommMem;
//一共16个块,每块32个int大小
INT32U CommBuf[16][32];

static OS_STK Task_mem_stk[80];

void Task_mem (void *p_arg) 
{
	INT8U err;
	INT8U *pmsg;
	(void)p_arg;

	
	for (;;) 
	{
		//申请一块内存,这个大小就是之前注册的时候的大小,32个int的大小
		pmsg = OSMemGet(CommMem, &err);
		if (pmsg != (INT8U *)0) 
		{
			printf("get mem success\n");
			strcpy(pmsg,"test");
			printf("content:%s\n",pmsg);
			err = OSMemPut(CommMem, (void *)pmsg);
			
			if (err == OS_ERR_NONE) 
			{
				printf("put mem success\n");
			}
			else	
			{
				printf("put mem fail\n");
			}
		}
		else
		{
			printf("get mem fail\n");
		}
		OSTimeDlyHMSM(0, 0, 2, 0);
	}
}

int main(void)
{
	INT8U err;
	
	STM_Init();
	RCC_Config();
	GPIO_Config();
	USART_Config();
	NVIC_Config();
	
	OSInit();
	//创建内存块组
	CommMem = OSMemCreate(&CommBuf[0][0], 16, 32 * sizeof(INT32U), &err);
	OSTaskCreate(Task_mem,(void *)0,&Task_mem_stk[79],5);
	OSStart();

	return 0;
}

输出结果
在这里插入图片描述

通讯

这里也是操作系统的标志内容,任务之间的通讯,也是稍微复杂一点的东西,不过一通百通,操作系统都会有类似的东西,信号量,消息队列等的。
在这里插入图片描述

信号量

µC/OS-II 中的信号量由两部分组成:一个是信号量的计数值,它是一个 16 位的无符号整数(0 到 65,535 之间);另一个是由等待该信号量的任务组成的等待任务表。

功能函数备注
建立一个信号量OS_EVENT *OSSemCreate(INT16U value);
删除一个信号量OS_EVENT *OSSemDel(OS_EVENT *pevent, INT8U opt, INT8U *perr);
等待一个信号量void OSSemPend(OS_EVENT *pevent, INT32U timeout, INT8U *perr);
发送一个信号量INT8U OSSemPost(OS_EVENT *pevent);
无等待地请求一个信号量INT16U OSSemAccept(OS_EVENT *pevent);用于中断ISR中调用
查询一个信号量的当前状态INT8U OSSemQuery(OS_EVENT *pevent, OS_SEM_DATA *p_sem_data);

还是以一个demo例子说明

OS_EVENT *DispSem;
static OS_STK Task_1_stk[TASK_1_STK_SIZE];
static OS_STK Task_2_stk[TASK_2_STK_SIZE];

/*****************************************************
 *任务等待信号量的任务
*****************************************************/
void Task_pend(void *p_arg)
{
	INT8U err;

	(void)p_arg;
	
	while(1)
	{
		printf("wait signal!\r\n");
		OSSemPend(DispSem, 0, &err);
		printf("get signal!\r\n");
	}
}

/******************************************************
 *发送信号量的任务
******************************************************/
void Task_post(void *p_arg)
{	
	INT8U err;
	(void)p_arg;
	while(1)
	{
		err = OSSemPost(DispSem);
		switch (err) 
		{
			case OS_ERR_NONE:
				printf("post signal!\r\n");
			break;
			case OS_ERR_SEM_OVF:
			/* Semaphore has overflowed */
				printf("overflowed signal!\r\n");
			break;
		}
		OSTimeDlyHMSM(0, 0, 2, 0);
	}
}


int main(void)
{
	STM_Init();
	OSInit();
	
	RCC_Config();
	GPIO_Config();
	USART_Config();
	NVIC_Config();
	DispSem = OSSemCreate(1);
	
	OSTaskCreate(Task_pend,(void *)0,&Task_1_stk[TASK_1_STK_SIZE-1],TASK_1_PRIO);
	OSTaskCreate(Task_post,(void *)0,&Task_2_stk[TASK_2_STK_SIZE-1],TASK_2_PRIO);

	OSStart();

	return 0;
}

任务1负责等待信号,任务2负责发送信号,那么每两秒,任务1就能等到信号,然后再等下一次任务2发送信号。结果如下
在这里插入图片描述

邮箱

邮箱是µC/OS-II 中另一种通讯机制,它可以使一个任务或者中断服务子程序向另一个任务发送一个指针型的变量。该指针指向一个包含了特定“消息”的数据结构。
在这里插入图片描述

需要打开宏定义才能开启邮箱
在这里插入图片描述

功能函数备注
建立一个邮箱OS_EVENT *OSMboxCreate(void *pmsg);
删除一个邮箱OS_EVENT *OSMboxDel(OS_EVENT *pevent, INT8U opt, INT8U *perr);
等待一个邮箱中的消息void *OSMboxPend(OS_EVENT *pevent, INT32U timeout, INT8U *perr);
发送一个消息到邮箱中INT8U OSMboxPost(OS_EVENT *pevent, void *pmsg);
无等待地从邮箱中得到一个消息void *OSMboxAccept(OS_EVENT *pevent);这个不会挂起任务,所以用于ISRs
查询一个邮箱的状态

INT8U OSMboxQuery(OS_EVENT *pevent, OS_MBOX_DATA *p_mbox_data);

还是demo程序进入

OS_EVENT *CommMbox;

static OS_STK Task_1_stk[TASK_1_STK_SIZE];
static OS_STK Task_2_stk[TASK_2_STK_SIZE];
INT8U CommRxBuf[100];

/*****************************************************
 *任务等待邮箱消息的任务
*****************************************************/
void Task_mbox_pend(void *p_arg)
{
	INT8U err;
	void *pmsg;

	(void)p_arg;
	
	while(1)
	{
		printf("wait mbox!\r\n");
		pmsg = OSMboxPend(CommMbox, 0, &err);
		if (err == OS_ERR_NONE) 
		{
			printf("get mbox[%s]!\r\n",(char*)pmsg);
		} 
		else 
		{
		 /* Code for message not received within timeout */
		}
	}
}

/******************************************************
 *发送邮箱消息的任务
******************************************************/
void Task_mbox_send(void *p_arg)
{	
	INT8U err;
	(void)p_arg;
	while(1)
	{
		err = OSMboxPost(CommMbox, (void *)&CommRxBuf[0]);
		switch (err) 
		{
			case OS_ERR_NONE:
				printf("post mbox!\r\n");
			break;
			default:
				printf("err[%d]\r\n",err);
			break;
		}
		OSTimeDlyHMSM(0, 0, 2, 0);
	}
}


int main(void)
{
	STM_Init();
	OSInit();
	
	RCC_Config();
	GPIO_Config();
	USART_Config();
	NVIC_Config();
	strcpy((char*)CommRxBuf,"msg content");
	CommMbox = OSMboxCreate((void *)0);
	
	OSTaskCreate(Task_mbox_pend,(void *)0,&Task_1_stk[TASK_1_STK_SIZE-1],TASK_1_PRIO);
	OSTaskCreate(Task_mbox_send,(void *)0,&Task_2_stk[TASK_2_STK_SIZE-1],TASK_2_PRIO);

	OSStart();

	return 0;
}

显示结果
在这里插入图片描述
这里还有一个以邮箱作为二值信号的方法,用来进行资源访问的保护

//使用邮箱作为二值信号量 
OS_EVENT *MboxSem; 
void Task1 (void *pdata) 
{ 
	INT8U err; 
	for (;;) 
	{ 
		OSMboxPend(MboxSem, 0, &err); /* 获得对资源的访问权 */ 
		/* 任务获得信号量,对资源进行访问 */ 
		OSMboxPost(MboxSem, (void*)1); /* 释放对资源的访问权 */ 
	} 
}

消息队列

消息队列是µC/OS-II 中另一种通讯机制,它可以使一个任务或者中断服务子程序向另一个任务发送以指针方式定义的变量。与邮箱不同的地方就是这个消息可以发多个,有队首和队尾可供插入。
在这里插入图片描述

开启消息队列需要打开下面的宏定义
在这里插入图片描述

功能函数备注
建立一个消息队列OS_EVENT *OSQCreate(void **start, INT8U size);
删除一个消息队列OS_EVENT *OSQDel(OS_EVENT *pevent, INT8U opt, INT8U *perr);
等待一个消息队列中的消息void *OSQPend(OS_EVENT *pevent, INT32U timeout, INT8U *perr);
向消息队列发送一个消息(FIFO)INT8U OSQPost(OS_EVENT *pevent, void *pmsg);
向消息队列发送一个消息(LIFO)INT8U OSQPostFront(OS_EVENT *pevent, void *pmsg);
无等待地从一个消息队列中取得消息void *OSQAccept(OS_EVENT *pevent, INT8U *perr);这个不会挂起任务,所以用于ISRs
清空一个消息队列INT8U *OSQFlush(OS_EVENT *pevent);
查询一个消息队列的状态INT8U OSQQuery(OS_EVENT *pevent, OS_Q_DATA *p_q_data);

还是demo进入

OS_EVENT *CommQ;
void *CommMsg[10];
INT8U CommRxBuf1[64];
INT8U CommRxBuf2[64];

static OS_STK Task_1_stk[TASK_1_STK_SIZE];
static OS_STK Task_2_stk[TASK_2_STK_SIZE];


/*****************************************************
 *任务等待消息队列的任务
*****************************************************/
void Task_mq_pend(void *p_arg)
{
	INT8U err;
	void *pmsg;

	(void)p_arg;
	
	while(1)
	{
		printf("wait mbox!\r\n");
		pmsg = OSQPend(CommQ, 0, &err);
		if (err == OS_ERR_NONE) 
		{
			printf("get mbox[%s]!\r\n",(char*)pmsg);
		} 
		else 
		{
			/* Message not received, must have timed out */
		}
	}
}

/******************************************************
 *发送消息队列的任务
******************************************************/
void Task_mq_post(void *p_arg)
{	
	INT8U err;
	(void)p_arg;
	while(1)
	{
		err = OSQPostFront(CommQ, (void *)&CommRxBuf2[0]);
		switch (err) 
		{
			case OS_ERR_NONE:
				printf("post msg2!\r\n");
			break;
			default:
				printf("err[%d]\r\n",err);
			break;
		}
		err = OSQPost(CommQ, (void *)&CommRxBuf1[0]);
		switch (err) 
		{
			case OS_ERR_NONE:
				printf("post msg1!\r\n");
			break;
			default:
				printf("err[%d]\r\n",err);
			break;
		}
		OSTimeDlyHMSM(0, 0, 2, 0);
	}
}


int main(void)
{
	STM_Init();
	OSInit();
	
	RCC_Config();
	GPIO_Config();
	USART_Config();
	NVIC_Config();
	strcpy((char*)CommRxBuf1,"msg 1");
	strcpy((char*)CommRxBuf2,"msg 2");
	CommQ = OSQCreate(&CommMsg[0], 10);
	
	OSTaskCreate(Task_mq_pend,(void *)0,&Task_1_stk[TASK_1_STK_SIZE-1],TASK_1_PRIO);
	OSTaskCreate(Task_mq_post,(void *)0,&Task_2_stk[TASK_2_STK_SIZE-1],TASK_2_PRIO);

	OSStart();

	return 0;
}

这里使用了两种发送,一种是后进先出的OSQPostFront,还有一种先进先出OSQPost。
在这里插入图片描述

结束语

这篇主要介绍了一下如何在μC/OS-II下进行开发,把主要的功能函数怎么调用介绍了一下。这些资料在代码的Doc下面能找到更具体的说明,demo基本都是我自己写出来测试的。
最近的疫情又开始爆发,六朝古都到十三朝古都,最近又转向了九朝古都,为啥都在这些古都爆发也不得而知,看来古都的气运已然消逝殆尽。
疫情当前,其实除了防患病毒,更重要的是保持理性,不要被一些人一些势力递过来的刀子所撩拨到自己的神经,也不要上头去过度的指责某些点,我们并不是生活在一个安全的星球,而只是生活在一个安全的国家,时刻保持警惕,免得被人利用。
在这里插入图片描述

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

胖哥王老师

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值