一,前后台系统和RTOS
1,前后台系统
早期嵌入式开发没有嵌入式操作系统的概念 ,直接操作裸机,在裸机上写程序,比如用51单片机基本就没有操作系统的概念。通常把程序分为两部分:前台系统和后台系统。
简单的小系统通常是前后台系统,这样的程序包括一个死循环和若干个中断服务程序:
应用程序是一个无限循环,循环中调用API函数完成所需的操作,这个大循环就叫做后台系统。
中断服务程序用于处理系统的异步事件,也就是前台系统。前台是中断级,后台是任务级。
2,RTOS
RTOS全称为: Real Time OS,就是实时操作系统,强调的是: 实时性。实时操作系统又分为硬实时和软实时。
硬实时要求在规定的时间内必须完成操作 ,硬实时系统不允许超时,在软实时里面处理过程超时的后果就没有那么严格。
在实时操作系统中,我们可以把要实现的功能划分为多个任务,每个任务负责实现其中的一部分,每个任务都是一个很简单的程序,通常是一个死循环
。
RTOS操作系统: UCOS, FreeRTOS, RTX, RT-Thread, DJYOS等。
RTOS操作系统的核心内容在于: 实时内核。
3,可剥夺型内核
RTOS的内核负责管理所有的任务,内核决定了运行哪个任务,何时停止当前任务切换到其他任务,这个是内核的多任务管理能力。
多任务管理给人的感觉就好像芯片有多个CPU,多任务管理实现了CPU资源的最大化利用,多任务管理有助于实现程序的模块化开发,能够实现复杂的实时应用。
可剥夺内核顾名思义就是可以剥夺其他任务的CPU使用权,它总是运行就绪任务中的优先级最高的那个任务
UCOS系统简介
UCOS是Micrium公司出品的RTOS类实时操作系统, UCOS目前有两个版本:
UCOSII和UCOSIII。
UCOSIII是一个可裁剪、可剥夺型的多任务内核,而且没有任务数限制。
UCOSIII提供了实时操作系统所需的所有功能,包括资源管理、同步、任务通信等。
UCOSIII是用C和汇编来写的,其中绝大部分都是用C语言编写的,只有极少数的与处理器密切相关的部分代码才是用汇编写的, UCOSIII结构简洁,可读性很强!最主
要的是非常适合初次接触嵌入式实时操作系统学生、嵌入式系统开发人员和爱好者学习。
#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"
#include "includes.h"
#include "dht.h"
#include "infra_red.h"
//任务1控制块
OS_TCB Task1_TCB;
void task1(void *parg);
CPU_STK task1_stk[128]; //任务1的任务堆栈,大小为128字,也就是512字节
//任务2控制块
OS_TCB Task2_TCB;
void task2(void *parg);
CPU_STK task2_stk[128]; //任务2的任务堆栈,大小为128字,也就是512字节
//主函数
int main(void)
{
OS_ERR err;
delay_init(168); //时钟初始化
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //中断分组配置
uart_init(9600); //串口初始化
LED_Init(); //LED初始化
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOG, ENABLE); //打开温湿度模块控制GPIOG的时钟
//OS初始化,它是第一个运行的函数,初始化各种的全局变量,例如中断嵌套计数器、优先级、存储器
OSInit(&err);
//创建任务1
OSTaskCreate( (OS_TCB *)&Task1_TCB, //任务控制块
(CPU_CHAR *)"Task1", //任务的名字
(OS_TASK_PTR)task1, //任务函数
(void *)0, //传递参数
(OS_PRIO)3, //任务的优先级
(CPU_STK *)task1_stk, //任务堆栈基地址
(CPU_STK_SIZE)128/10, //任务堆栈深度限位,用到这个位置,任务不能再继续使用
(CPU_STK_SIZE)128, //任务堆栈大小
(OS_MSG_QTY)0, //禁止任务消息队列
(OS_TICK)0, //默认时间片长度
(void *)0, //不需要补充用户存储区
(OS_OPT)OS_OPT_TASK_NONE, //没有任何选项
&err //返回的错误码
);
//创建任务2
OSTaskCreate( (OS_TCB *)&Task2_TCB, //任务控制块
(CPU_CHAR *)"Task2", //任务的名字
(OS_TASK_PTR)task2, //任务函数
(void *)0, //传递参数
(OS_PRIO)4, //任务的优先级
(CPU_STK *)task2_stk, //任务堆栈基地址
(CPU_STK_SIZE)128/10, //任务堆栈深度限位,用到这个位置,任务不能再继续使用
(CPU_STK_SIZE)128, //任务堆栈大小
(OS_MSG_QTY)0, //禁止任务消息队列
(OS_TICK)0, //默认时间片长度
(void *)0, //不需要补充用户存储区
(OS_OPT)OS_OPT_TASK_NONE, //没有任何选项
&err //返回的错误码
);
//启动OS,进行任务调度
OSStart(&err);
}
void task1(void *parg)
{
OS_ERR err;
uint8_t dht_data[5]={0};
printf("task1 is create ok\r\n");
//红外初始化
ir_init();
while(1)
{
if(dht11_read_data(dht_data)==0)
{
printf("temp=%d.%d\r\n",dht_data[2],dht_data[3]);
printf("humi=%d.%d\r\n",dht_data[0],dht_data[1]);
printf("\r\n");
}
delay_ms(500);
delay_ms(500);
OSTimeDlyHMSM(0,0,1,0,OS_OPT_TIME_HMSM_STRICT,&err); //延时1s
}
}
void task2(void *parg)
{
OS_ERR err;
printf("task2 is create ok\r\n");
while(1)
{
//添加LED2闪烁
printf("task2 is running ...\r\n");
OSTimeDlyHMSM(0,0,1,0,OS_OPT_TIME_HMSM_STRICT,&err); //延时1s
}
}
(1)假如任务1和任务2两个任务的优先级都是一样的,而且任务1比任务2创建更早,会出现什么结果?例如任务1和任务2的运行代码是一样的。如下:
void task1(void *parg)
{
OS_ERR err;
printf("task1 is create ok\r\n");
while(1)
{
printf("task1 is running ...\r\n");
}
}
void task2(void *parg)
{
OS_ERR err;
printf("task2 is create ok\r\n");
while(1)
{
printf("task2 is running ...\r\n");
}
}
结果
task1 is running ...
task1 is running ...
task1 is running ...
task1 is running ...
task1 is running ...
......
当两个任务的优先级都是一样且最高,这个两个任务都没有任何的让出CPU的函数,在执行的时候,只执行创建最早的任务,任务1是比任务2创建更早,只能执行任务1.
(2)任务1的优先级是比任务2高,代码如下:
void task1(void *parg)
{
OS_ERR err;
printf("task1 is create ok\r\n");
while(1)
{
printf("task1 is running ...\r\n");
OSTimeDlyHMSM(0,0,0,10,OS_OPT_TIME_HMSM_STRICT,&err); //延时10ms
}
}
void task2(void *parg)
{
OS_ERR err;
printf("task2 is create ok\r\n");
while(1)
{
printf("task2 is running ...\r\n");
OSTimeDlyHMSM(0,0,0,10,OS_OPT_TIME_HMSM_STRICT,&err); //延时10ms
}
}
结果:
task2task1 is running ...
is task1 is running ...
runntask1 is running ...
ing task1 is running ...
...
task1 is running ...
task1 is running ...
task2task1 is running ...
在任务2在打印的过程当中,打印数据时间超过10ms,所以在打印的中途ucos发现任务1已经就绪了,任务1就会抢夺CPU的使用权,任务2就停止执行,等任务1执行完之后让出CPU,任务2才继续执行。
(三)临界区代码用于资源保护,以下有两种的编写方法,哪一种才是正确的?
1,
//进入临界区,保护以下的代码,关闭总中断,停止了ucos的任务调度,其他任务已经停止执行
OS_CRITICAL_ENTER();
printf("task1 is running ...\r\n");
//退出临界区,开启总中断,允许ucos的任务调度
OS_CRITICAL_EXIT();
OSTimeDlyHMSM(0,0,1,0,OS_OPT_TIME_HMSM_STRICT,&err); //延时1000ms
2,
//进入临界区,保护以下的代码,关闭总中断,停止了ucos的任务调度,其他任务已经停止执行
OS_CRITICAL_ENTER();
printf("task1 is running ...\r\n");
OSTimeDlyHMSM(0,0,1,0,OS_OPT_TIME_HMSM_STRICT,&err); //延时1000ms
//退出临界区,开启总中断,允许ucos的任务调度
OS_CRITICAL_EXIT();
只有例子1才是正确的,能够进行准确的延时,唯独只有例子2延时是失效的。
在进入临界区代码,因为它是关闭了总中断,所以跟阻塞相关、时间管理函数都会失效。
在ucos3,提供的最小毫米级别的延时是多少,依据是什么?
在os_cfg_app.h当中有关于设置ucos3时钟频率,时钟频率如下:
#define OS_CFG_TICK_RATE_HZ 200u /* Tick rate in Hertz (10 to 1000 Hz) */
因此,延时最小只能是5ms。
如果要设置最小的延时为1ms,可以调整当前该宏定义OS_CFG_TICK_RATE_HZ为1000。
OS_CFG_TICK_RATE_HZ的值越小,ucos3能够实现更低的功耗,但是高优先级任务的执行不会太及时。反过来说,该值越大,功耗就越高,同时高优先级的任务被执行的延迟时间会更及时!
应用场景:如果是电池供电且任务数特别少,可以将OS_CFG_TICK_RATE_HZ调小。如果是普通电源供电且任务数特别多,将OS_CFG_TICK_RATE_HZ调大!
延时函数:delay_us与delay_ms能够正常地使用!
中断服务函数,如果在ucos3操作系统使用下,还得添加这两个函数,示例如下:
void USART1_IRQHandler(void) //串口1中断服务程序
{
//进入中断,告诉UCOS3停止任务调度,并进行中断嵌套计数
OSIntEnter();
//添加代码
//退出中断,告诉UCOS3已经完成中断处理,并更新中断嵌套计数,可以进行任务调度
OSIntExit();
}
信号量
#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"
#include "includes.h"
//任务1控制块
OS_TCB Task1_TCB;
void task1(void *parg);
CPU_STK task1_stk[128]; //任务1的任务堆栈,大小为128字,也就是512字节
//任务2控制块
OS_TCB Task2_TCB;
void task2(void *parg);
CPU_STK task2_stk[128]; //任务2的任务堆栈,大小为128字,也就是512字节
OS_SEM SYNC_SEM; //定义一个信号量,用于任务同步
//主函数
int main(void)
{
OS_ERR err;
delay_init(168); //时钟初始化
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //中断分组配置
uart_init(9600); //串口初始化
LED_Init(); //LED初始化
//OS初始化,它是第一个运行的函数,初始化各种的全局变量,例如中断嵌套计数器、优先级、存储器
OSInit(&err);
//创建任务1
OSTaskCreate( (OS_TCB *)&Task1_TCB, //任务控制块
(CPU_CHAR *)"Task1", //任务的名字
(OS_TASK_PTR)task1, //任务函数
(void *)0, //传递参数
(OS_PRIO)3, //任务的优先级
(CPU_STK *)task1_stk, //任务堆栈基地址
(CPU_STK_SIZE)128/10, //任务堆栈深度限位,用到这个位置,任务不能再继续使用
(CPU_STK_SIZE)128, //任务堆栈大小
(OS_MSG_QTY)0, //禁止任务消息队列
(OS_TICK)0, //默认时间片长度
(void *)0, //不需要补充用户存储区
(OS_OPT)OS_OPT_TASK_NONE, //没有任何选项
&err //返回的错误码
);
//创建任务2
OSTaskCreate( (OS_TCB *)&Task2_TCB, //任务控制块
(CPU_CHAR *)"Task2", //任务的名字
(OS_TASK_PTR)task2, //任务函数
(void *)0, //传递参数
(OS_PRIO)2, //任务的优先级
(CPU_STK *)task2_stk, //任务堆栈基地址
(CPU_STK_SIZE)128/10, //任务堆栈深度限位,用到这个位置,任务不能再继续使用
(CPU_STK_SIZE)128, //任务堆栈大小
(OS_MSG_QTY)0, //禁止任务消息队列
(OS_TICK)0, //默认时间片长度
(void *)0, //不需要补充用户存储区
(OS_OPT)OS_OPT_TASK_NONE, //没有任何选项
&err //返回的错误码
);
//创建一个信号量
OSSemCreate ((OS_SEM* )&SYNC_SEM,
(CPU_CHAR* )"SYNC_SEM",
(OS_SEM_CTR)0,
(OS_ERR* )&err);
//启动OS,进行任务调度
OSStart(&err);
}
void task1(void *parg)
{
OS_ERR err;
printf("task1 is create ok\r\n");
while(1)
{
OSSemPend(&SYNC_SEM,0,OS_OPT_PEND_BLOCKING,0,&err); //请求信号量
printf("task1 is running ...\r\n");
}
}
void task2(void *parg)
{
OS_ERR err;
printf("task2 is create ok\r\n");
while(1)
{
//连续发送3个信号量
OSSemPost(&SYNC_SEM,OS_OPT_POST_1,&err); //发送信号量
OSSemPost(&SYNC_SEM,OS_OPT_POST_1,&err); //发送信号量
OSSemPost(&SYNC_SEM,OS_OPT_POST_1,&err); //发送信号量
printf("task2 is running ...\r\n");
OSTimeDlyHMSM(0,0,1,0,OS_OPT_TIME_HMSM_STRICT,&err); //延时10ms
}
}
事件
#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"
#include "includes.h"
//任务1控制块
OS_TCB Task1_TCB;
void task1(void *parg);
CPU_STK task1_stk[128]; //任务1的任务堆栈,大小为128字,也就是512字节
//任务2控制块
OS_TCB Task2_TCB;
void task2(void *parg);
CPU_STK task2_stk[128]; //任务2的任务堆栈,大小为128字,也就是512字节
//任务3控制块
OS_TCB Task3_TCB;
void task3(void *parg);
CPU_STK task3_stk[128]; //任务2的任务堆栈,大小为128字,也就是512字节
OS_FLAG_GRP g_os_flag; //定义一个事件标志组
//按键初始化函数
void KEY_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA|RCC_AHB1Periph_GPIOE,ENABLE); //使能GPIOA GPIOE时钟
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_2|GPIO_Pin_3|GPIO_Pin_4; //PE2,3,4引脚
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN; //输入
GPIO_InitStructure.GPIO_PuPd=GPIO_PuPd_UP; //上拉输入
GPIO_Init(GPIOE,&GPIO_InitStructure); //初始化GPIOE
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0; //PA0引脚
GPIO_InitStructure.GPIO_PuPd=GPIO_PuPd_DOWN; //下拉输入
GPIO_Init(GPIOA,&GPIO_InitStructure); //初始化GPIOA
}
//主函数
int main(void)
{
OS_ERR err;
delay_init(168); //时钟初始化
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //中断分组配置
uart_init(115200); //串口初始化
LED_Init(); //LED初始化
//OS初始化,它是第一个运行的函数,初始化各种的全局变量,例如中断嵌套计数器、优先级、存储器
OSInit(&err);
//创建任务1
OSTaskCreate( (OS_TCB *)&Task1_TCB, //任务控制块
(CPU_CHAR *)"Task1", //任务的名字
(OS_TASK_PTR)task1, //任务函数
(void *)0, //传递参数
(OS_PRIO)3, //任务的优先级
(CPU_STK *)task1_stk, //任务堆栈基地址
(CPU_STK_SIZE)128/10, //任务堆栈深度限位,用到这个位置,任务不能再继续使用
(CPU_STK_SIZE)128, //任务堆栈大小
(OS_MSG_QTY)0, //禁止任务消息队列
(OS_TICK)0, //默认时间片长度
(void *)0, //不需要补充用户存储区
(OS_OPT)OS_OPT_TASK_NONE, //没有任何选项
&err //返回的错误码
);
//创建任务2
OSTaskCreate( (OS_TCB *)&Task2_TCB, //任务控制块
(CPU_CHAR *)"Task2", //任务的名字
(OS_TASK_PTR)task2, //任务函数
(void *)0, //传递参数
(OS_PRIO)2, //任务的优先级
(CPU_STK *)task2_stk, //任务堆栈基地址
(CPU_STK_SIZE)128/10, //任务堆栈深度限位,用到这个位置,任务不能再继续使用
(CPU_STK_SIZE)128, //任务堆栈大小
(OS_MSG_QTY)0, //禁止任务消息队列
(OS_TICK)0, //默认时间片长度
(void *)0, //不需要补充用户存储区
(OS_OPT)OS_OPT_TASK_NONE, //没有任何选项
&err //返回的错误码
);
//创建任务3
OSTaskCreate( (OS_TCB *)&Task3_TCB, //任务控制块
(CPU_CHAR *)"Task3", //任务的名字
(OS_TASK_PTR)task3, //任务函数
(void *)0, //传递参数
(OS_PRIO)2, //任务的优先级
(CPU_STK *)task3_stk, //任务堆栈基地址
(CPU_STK_SIZE)128/10, //任务堆栈深度限位,用到这个位置,任务不能再继续使用
(CPU_STK_SIZE)128, //任务堆栈大小
(OS_MSG_QTY)0, //禁止任务消息队列
(OS_TICK)0, //默认时间片长度
(void *)0, //不需要补充用户存储区
(OS_OPT)OS_OPT_TASK_NONE, //没有任何选项
&err //返回的错误码
);
//创建一个事件标志组
OSFlagCreate( (OS_FLAG_GRP* )&g_os_flag,
(CPU_CHAR*)"os_flag",
(OS_FLAGS)0,
(OS_ERR* )&err);
//按键初始化
KEY_Init();
//启动OS,进行任务调度
OSStart(&err);
}
void task1(void *parg)
{
OS_ERR err;
OS_FLAGS os_flag;
printf("task1 is create ok\r\n");
while(1)
{
if(PAin(0) == 0)
{
//去抖动
OSTimeDlyHMSM(0,0,0,20,OS_OPT_TIME_HMSM_STRICT,&err); //延时20ms
if(PAin(0) == 0)
{
//发送事件标志,标志值为0x1,设置g_os_flag的bit0为1
os_flag= OSFlagPost((OS_FLAG_GRP* )&g_os_flag,
(OS_FLAGS)0x1,
(OS_OPT)OS_OPT_POST_FLAG_SET,
&err);
printf("task1,os_flag=%02X\r\n",os_flag);
}
}
OSTimeDlyHMSM(0,0,0,20,OS_OPT_TIME_HMSM_STRICT,&err); //延时20ms
}
}
void task2(void *parg)
{
OS_ERR err;
OS_FLAGS os_flag;
printf("task2 is create ok\r\n");
while(1)
{
if(PEin(2) == 0)
{
OSTimeDlyHMSM(0,0,0,20,OS_OPT_TIME_HMSM_STRICT,&err); //延时20ms
if(PEin(2) == 0)
{
//发送事件标志,标志值为0x2,设置g_os_flag的bit1为1
os_flag= OSFlagPost((OS_FLAG_GRP* )&g_os_flag,
(OS_FLAGS)0x2,
(OS_OPT)OS_OPT_POST_FLAG_SET,
&err);
printf("task2,os_flag=%02X\r\n",os_flag);
}
}
OSTimeDlyHMSM(0,0,0,20,OS_OPT_TIME_HMSM_STRICT,&err); //延时20ms
}
}
void task3(void *parg)
{
OS_ERR err;
OS_FLAGS os_flag;
printf("task3 is create ok\r\n");
while(1)
{
//OS_OPT_PEND_FLAG_SET_ANY 和OS_OPT_PEND_FLAG_SET_ALL是有区别的
//前者是等待任意一个事件,后者要确保所有事件成立
os_flag= OSFlagPend((OS_FLAG_GRP* )&g_os_flag,
(OS_FLAGS)0x3, //等待bit1和bit0置位
(OS_TICK)0, //0代表是默认一直阻塞等待
(OS_OPT)OS_OPT_PEND_FLAG_SET_ANY+OS_OPT_PEND_FLAG_CONSUME, //等待事件标志组任何一位置位
(CPU_TS *)0, //默认不记录时间戳
&err);
printf("task3,os_flag=%02X\r\n",os_flag);
}
}
消息队列
#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"
#include "includes.h"
//任务1控制块
OS_TCB Task1_TCB;
void task1(void *parg);
CPU_STK task1_stk[128]; //任务1的任务堆栈,大小为128字,也就是512字节
//任务2控制块
OS_TCB Task2_TCB;
void task2(void *parg);
CPU_STK task2_stk[128]; //任务2的任务堆栈,大小为128字,也就是512字节
#define TASK_Q_NUM 64 //发任务内建消息队列的长度
//主函数
int main(void)
{
OS_ERR err;
delay_init(168); //时钟初始化
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //中断分组配置
uart_init(9600); //串口初始化
LED_Init(); //LED初始化
//OS初始化,它是第一个运行的函数,初始化各种的全局变量,例如中断嵌套计数器、优先级、存储器
OSInit(&err);
//创建任务1
OSTaskCreate( (OS_TCB *)&Task1_TCB, //任务控制块
(CPU_CHAR *)"Task1", //任务的名字
(OS_TASK_PTR)task1, //任务函数
(void *)0, //传递参数
(OS_PRIO)3, //任务的优先级
(CPU_STK *)task1_stk, //任务堆栈基地址
(CPU_STK_SIZE)128/10, //任务堆栈深度限位,用到这个位置,任务不能再继续使用
(CPU_STK_SIZE)128, //任务堆栈大小
(OS_MSG_QTY)0, //禁止任务消息队列
(OS_TICK)0, //默认时间片长度
(void *)0, //不需要补充用户存储区
(OS_OPT)OS_OPT_TASK_NONE, //没有任何选项
&err //返回的错误码
);
//创建任务2
OSTaskCreate( (OS_TCB *)&Task2_TCB, //任务控制块
(CPU_CHAR *)"Task2", //任务的名字
(OS_TASK_PTR)task2, //任务函数
(void *)0, //传递参数
(OS_PRIO)2, //任务的优先级
(CPU_STK *)task2_stk, //任务堆栈基地址
(CPU_STK_SIZE)128/10, //任务堆栈深度限位,用到这个位置,任务不能再继续使用
(CPU_STK_SIZE)128, //任务堆栈大小
(OS_MSG_QTY)TASK_Q_NUM, //任务2需要使用内建消息队列,消息队列长度为64个字
(OS_TICK)0, //默认时间片长度
(void *)0, //不需要补充用户存储区
(OS_OPT)OS_OPT_TASK_NONE, //没有任何选项
&err //返回的错误码
);
//启动OS,进行任务调度
OSStart(&err);
}
void task1(void *parg)
{
uint32_t msg_cnt=0;
char pbuf[64];
OS_ERR err;
printf("task1 is create ok\r\n");
while(1)
{
msg_cnt++;
sprintf((char*)pbuf,"msg_cnt=%d\r\n",msg_cnt);
//发送消息
OSTaskQPost((OS_TCB* )&Task2_TCB, //向任务task2发送消息
(void* )pbuf,
(OS_MSG_SIZE)strlen(pbuf),
(OS_OPT )OS_OPT_POST_FIFO,
(OS_ERR* )&err);
printf("task1 is running ...\r\n");
OSTimeDlyHMSM(0,0,1,0,OS_OPT_TIME_HMSM_STRICT,&err); //延时1S
}
}
void task2(void *parg)
{
char *p;
OS_MSG_SIZE size;
OS_ERR err;
printf("task2 is create ok\r\n");
while(1)
{
//请求消息
p=OSTaskQPend((OS_TICK )0,
(OS_OPT )OS_OPT_PEND_BLOCKING,
(OS_MSG_SIZE* )&size,
(CPU_TS* )0,
(OS_ERR* )&err );
printf("task2 is running ...\r\n");
printf(p);
}
}
软定时器
/*
描述:
1.当前创建了软件定时器1,工作频率为100Hz,优先级为2,可在os_cfg_app.h进行设置
#define OS_CFG_TMR_TASK_PRIO 2u
#define OS_CFG_TMR_TASK_RATE_HZ 100u
2.创建任务1,优先级为3,;创建任务2,优先级为4
*/
#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"
#include "includes.h"
//任务1控制块
OS_TCB Task1_TCB;
void task1(void *parg);
CPU_STK task1_stk[128]; //任务1的任务堆栈,大小为128字,也就是512字节
//任务2控制块
OS_TCB Task2_TCB;
void task2(void *parg);
CPU_STK task2_stk[128]; //任务2的任务堆栈,大小为128字,也就是512字节
OS_TMR tmr1; //定时器1
//软件定时器1回调函数
void tmr1_callback(void *ptmr,void *p_arg);
//主函数
int main(void)
{
OS_ERR err;
delay_init(168); //时钟初始化
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //中断分组配置
uart_init(9600); //串口初始化
LED_Init(); //LED初始化
//OS初始化,它是第一个运行的函数,初始化各种的全局变量,例如中断嵌套计数器、优先级、存储器
OSInit(&err);
//创建任务1
OSTaskCreate( (OS_TCB *)&Task1_TCB, //任务控制块
(CPU_CHAR *)"Task1", //任务的名字
(OS_TASK_PTR)task1, //任务函数
(void *)0, //传递参数
(OS_PRIO)3, //任务的优先级
(CPU_STK *)task1_stk, //任务堆栈基地址
(CPU_STK_SIZE)128/10, //任务堆栈深度限位,用到这个位置,任务不能再继续使用
(CPU_STK_SIZE)128, //任务堆栈大小
(OS_MSG_QTY)0, //禁止任务消息队列
(OS_TICK)0, //默认时间片长度
(void *)0, //不需要补充用户存储区
(OS_OPT)OS_OPT_TASK_NONE, //没有任何选项
&err //返回的错误码
);
//创建任务2
OSTaskCreate( (OS_TCB *)&Task2_TCB, //任务控制块
(CPU_CHAR *)"Task2", //任务的名字
(OS_TASK_PTR)task2, //任务函数
(void *)0, //传递参数
(OS_PRIO)4, //任务的优先级
(CPU_STK *)task2_stk, //任务堆栈基地址
(CPU_STK_SIZE)128/10, //任务堆栈深度限位,用到这个位置,任务不能再继续使用
(CPU_STK_SIZE)128, //任务堆栈大小
(OS_MSG_QTY)0, //禁止任务消息队列
(OS_TICK)0, //默认时间片长度
(void *)0, //不需要补充用户存储区
(OS_OPT)OS_OPT_TASK_NONE, //没有任何选项
&err //返回的错误码
);
//创建软件定时器1
OSTmrCreate( (OS_TMR *)&tmr1, //软件定时器1
(CPU_CHAR *)"tmr1", //定时器名字
(OS_TICK )20, //20*10=200ms,延迟200ms执行
(OS_TICK )100, //100*10=1000ms,执行周期为1000ms
(OS_OPT )OS_OPT_TMR_PERIODIC, //周期模式
(OS_TMR_CALLBACK_PTR )tmr1_callback, //软件定时器1回调函数
(void *)0, //参数为0
(OS_ERR *)&err); //返回的错误码
//启动软件定时器1
OSTmrStart(&tmr1,&err);
//启动OS,进行任务调度
OSStart(&err);
}
void task1(void *parg)
{
OS_ERR err;
printf("task1 is create ok\r\n");
while(1)
{
printf("task1 is running ...\r\n");
OSTimeDlyHMSM(0,0,1,0,OS_OPT_TIME_HMSM_STRICT,&err); //延时1S
}
}
void task2(void *parg)
{
OS_ERR err;
printf("task2 is create ok\r\n");
while(1)
{
printf("task2 is running ...\r\n");
OSTimeDlyHMSM(0,0,1,0,OS_OPT_TIME_HMSM_STRICT,&err); //延时1S
}
}
void tmr1_callback(void *ptmr,void *p_arg)
{
//软件定时器回调函数禁止使用任何的阻塞或者删除定时器函数
printf("tmr1_callback\r\n");
}
完整任务代码
/*
描述:
1.当前创建了软件定时器1,工作频率为100Hz,优先级为2,可在os_cfg_app.h进行设置
#define OS_CFG_TMR_TASK_PRIO 2u
#define OS_CFG_TMR_TASK_RATE_HZ 100u
2.创建任务1,优先级为3,;创建任务2,优先级为4
3.测试结果: 任务1、任务2、软件定时器回调函数通过临界区代码保护了共享资源,各任务之间很好地执行
*/
#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"
#include "includes.h"
//任务1控制块
OS_TCB Task1_TCB;
void task1(void *parg);
CPU_STK task1_stk[128]; //任务1的任务堆栈,大小为128字,也就是512字节
//任务2控制块
OS_TCB Task2_TCB;
void task2(void *parg);
CPU_STK task2_stk[128]; //任务2的任务堆栈,大小为128字,也就是512字节
OS_TMR tmr1; //定时器1
//软件定时器1回调函数
void tmr1_callback(void *ptmr,void *p_arg);
OS_Q queue; //声明消息队列
OS_SEM sem; //声明多值信号量
OS_PEND_DATA mul_pend_array [ 2 ]; //声明等待对象数组
//主函数
int main(void)
{
OS_ERR err;
delay_init(168); //时钟初始化
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //中断分组配置
uart_init(9600); //串口初始化
LED_Init(); //LED初始化
//OS初始化,它是第一个运行的函数,初始化各种的全局变量,例如中断嵌套计数器、优先级、存储器
OSInit(&err);
//创建任务1
OSTaskCreate( (OS_TCB *)&Task1_TCB, //任务控制块
(CPU_CHAR *)"Task1", //任务的名字
(OS_TASK_PTR)task1, //任务函数
(void *)0, //传递参数
(OS_PRIO)3, //任务的优先级
(CPU_STK *)task1_stk, //任务堆栈基地址
(CPU_STK_SIZE)128/10, //任务堆栈深度限位,用到这个位置,任务不能再继续使用
(CPU_STK_SIZE)128, //任务堆栈大小
(OS_MSG_QTY)0, //禁止任务消息队列
(OS_TICK)0, //默认时间片长度
(void *)0, //不需要补充用户存储区
(OS_OPT)OS_OPT_TASK_NONE, //没有任何选项
&err //返回的错误码
);
//创建任务2
OSTaskCreate( (OS_TCB *)&Task2_TCB, //任务控制块
(CPU_CHAR *)"Task2", //任务的名字
(OS_TASK_PTR)task2, //任务函数
(void *)0, //传递参数
(OS_PRIO)4, //任务的优先级
(CPU_STK *)task2_stk, //任务堆栈基地址
(CPU_STK_SIZE)128/10, //任务堆栈深度限位,用到这个位置,任务不能再继续使用
(CPU_STK_SIZE)128, //任务堆栈大小
(OS_MSG_QTY)64, //任务消息队列64字
(OS_TICK)0, //默认时间片长度
(void *)0, //不需要补充用户存储区
(OS_OPT)OS_OPT_TASK_NONE, //没有任何选项
&err //返回的错误码
);
//创建软件定时器1
OSTmrCreate( (OS_TMR *)&tmr1,
(CPU_CHAR *)"tmr1",
(OS_TICK )20,
(OS_TICK )100,
(OS_OPT )OS_OPT_TMR_PERIODIC,
(OS_TMR_CALLBACK_PTR )tmr1_callback,
(void *)0,
(OS_ERR *)&err);
//启动软件定时器1
OSTmrStart(&tmr1,&err);
/* 创建多值信号量 sem */
OSSemCreate ( (OS_SEM *)&sem, //指向要创建的多值信号量
(CPU_CHAR *)"Sem For Test", //多值信号量的名字
(OS_SEM_CTR )0, //多值信号量初始不可用
(OS_ERR *)&err); //返回错误类型
/* 创建消息队列 queue */
OSQCreate ( (OS_Q *)&queue, //指向要创建的消息队列
(CPU_CHAR *)"Queue For Test", //消息队列的名字
(OS_MSG_QTY )20, //最多可容20条消息
(OS_ERR *)&err); //返回错误类型
/* 初始化要等待的多个内核对象 */
mul_pend_array [ 0 ] .PendObjPtr = ( OS_PEND_OBJ * ) & sem;
mul_pend_array [ 1 ] .PendObjPtr = ( OS_PEND_OBJ * ) & queue;
//启动OS,进行任务调度
OSStart(&err);
}
void task1(void *parg)
{
static uint32_t task_cnt=0;
char buf[64]={0};
OS_ERR err;
printf("task1 is create ok\r\n");
while(1)
{
task_cnt++;
sprintf(buf,"task1 run cnt=%d",task_cnt);
/* 发布消息到消息队列 queue */
OSQPost ( (OS_Q *)&queue, //消息队列
(void *)buf, //消息内容
(OS_MSG_SIZE )strlen(buf), //消息长度
(OS_OPT )OS_OPT_POST_FIFO|OS_OPT_POST_ALL, //发到队列且发送给所有任务
(OS_ERR *)&err); //返回错误类型
OSTimeDlyHMSM(0,0,3,0,OS_OPT_TIME_HMSM_STRICT,&err); //延时3S
}
}
void task2(void *parg)
{
OS_ERR err;
printf("task2 is create ok\r\n");
while(1)
{
/* 等待多个内核对象 */
OSPendMulti((OS_PEND_DATA *)mul_pend_array, //等待对象数组
(OS_OBJ_QTY )2, //等待对象数目
(OS_TICK )0, //无期限等待
(OS_OPT )OS_OPT_PEND_BLOCKING, //阻塞任务
(OS_ERR *)&err); //返回错误类型
/* 查看哪些等待对象可用 */
if ( mul_pend_array [0] .RdyObjPtr == mul_pend_array [0] .PendObjPtr ) //如果 sem 可用
{
printf("find tmr1 running...\r\n");
}
if ( mul_pend_array [1] .RdyObjPtr == mul_pend_array [1] .PendObjPtr )//如果 queue 可用
{
printf("recv msg:%s,len:%d\r\n",(char *) mul_pend_array[1] .RdyMsgPtr, mul_pend_array [1] .RdyMsgSize );
}
}
}
//软件定时器回调函数禁止使用任何的阻塞或者删除定时器函数
void tmr1_callback(void *ptmr,void *p_arg)
{
OS_ERR err;
OSSemPost(&sem,OS_OPT_POST_1,&err); //释放信号量
}