我们常说的单片机编程通常都是指裸机编程,即不加入任何RTOS(Real Time OperationSystem,实时操作系统)的编程。μC/OS是目前广泛使用的RTOS之一,引入RTOS可实现实现多任务管理 。
uC/OS-III 支持以多线程的方式,同时运行多个任务,各个任务之间互不影响。以下两个任务同时进入无限循环,但是两个任务可以同时执行,然而需要在循环中主动释放CPU:
void Task_A(void *p_arg){
while(1){
OSTimeDly(10); //任务里必须要有类似的主动释放CPU的函数
}
}
void Task_B(void *p_arg){
while(1){
OSTimeDly(10);
}
}
操作系统通过 "时钟节拍" 来实现监控任务,并且主动调度和切换任务执行。
OSTimeDly() 函数通过延迟当前任务指定的系统滴答时钟数来释放CPU。
void OSTimeDly(OS_TICK dly,//时钟滴答数
OS_OPT opt,//绝对时间还是相对时间,OS_OPT_TIME_DLY指定相对于当前的时间,OS_OPT_TIME_PERIODIC指定时钟滴答必须在任务恢复前达到的周期数
RTOS_ERR *p_err);//接收错误代码的指针
创建启动外设的函数 bsp_Init(),在里面初始化GPIO和LED
void BSP_Init(void)
{
BSP_Tick_Init();
// MX_GPIO_Init()是由STM32Cubemx创建的函数
MX_GPIO_Init();
}
void BSP_Tick_Init(void)
{
CPU_INT32U cpu_clk_freq;
CPU_INT32U cnts;
cpu_clk_freq = BSP_CPU_ClkFreq();
#if(OS_VERSION>=3000u)
cnts = cpu_clk_freq/(CPU_INT32U)OSCfg_TickRate_Hz;
#else
cnts = cpu_clk_freq/(CPU_INT32U)OS_TICKS_PER_SEC;
#endif
OS_CPU_SysTickInit(cnts);
}
创建新任务的函数OSTaskCreate(),执行这个函数将新建一个任务AppTaskStart():
OSTaskCreate((OS_TCB *)&AppTaskStartTCB, (1)
(CPU_CHAR *)"App Task Start", (2)
(OS_TASK_PTR ) AppTaskStart, (3)
(void *) 0, (4)
(OS_PRIO ) APP_TASK_START_PRIO, (5)
(CPU_STK *)&AppTaskStartStk[0], (6)
(CPU_STK_SIZE) APP_TASK_START_STK_SIZE / 10, (7)
(CPU_STK_SIZE) APP_TASK_START_STK_SIZE, (8)
(OS_MSG_QTY ) 5u, (9)
(OS_TICK ) 0u, (10)
(void *) 0, (11)
(OS_OPT )(OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR), (12)
(OS_ERR *)&err); (13)
在起始任务AppTaskStart()中再次建立多个任务,然后删除起始任务:
/**
* 函数功能: 启动任务函数体。
* 输入参数: p_arg 是在创建该任务时传递的形参
* 返 回 值: 无
* 说 明:无
*/
static void AppTaskStart (void *p_arg)
{
OS_ERR err;
MX_USART1_UART_Init();
(void)p_arg;
BSP_Init(); /* Initialize BSP functions */
CPU_Init();
Mem_Init(); /* Initialize Memory Management Module */
#if OS_CFG_STAT_TASK_EN > 0u
OSStatTaskCPUUsageInit(&err); /* Compute CPU capacity with no task running */
#endif
CPU_IntDisMeasMaxCurReset();
AppTaskCreate(); /* Create Application Tasks */
AppObjCreate(); /* Create Application Objects */
OSTaskCreate((OS_TCB *)&AppTaskATCB, //(1)任务控制块,由用户自己定义。
(CPU_CHAR *)"TaskA Start", //(2)任务名称,字符串形式,这里任务名称最好要与任务函数入口名字一致,方便进行调试。
(OS_TASK_PTR ) Task_A, //(3)任务入口函数,即任务函数的名称,需要我们自己定义并且实现。
(void *) 0, //(4)任务入口函数形参,不用的时候配置为0或者NULL即可,p_arg是指向可选数据区域的指针, 用于将参数传递给任务,因为任务一旦执行,那必须是在一个死循环中,所以传参只在首次执行时有效。
(OS_PRIO ) 2, //(5)任务的优先级,由用户自己定义。
(CPU_STK *)&AppTaskAStk[0], //(6)指向栈基址的指针(即栈的起始地址)
(CPU_STK_SIZE) APP_TASK_A_STK_SIZE / 10, //(7)设置栈深度的限制位置。这个值表示任务的栈满溢之前剩余的栈容量。 例如,指定stk_size值的10%表示将达到栈限制,当栈达到90%满就表示任务的栈已满。
(CPU_STK_SIZE) APP_TASK_A_STK_SIZE, //(8)任务栈大小,单位由用户决定,如果CPU_STK 被设置为CPU_INT08U, 则单位为字节,而如果CPU_STK 被设置为CPU_INT16U,则单位为半字,同理,如果CPU_STK被设置为CPU_INT32U, 单位为字。在32位的处理器下(STM32),一个字等于4个字节,那么任务大小就为APP_TASK_START_STK_SIZE * 4字节。
(OS_MSG_QTY ) 5u, //(9)设置可以发送到任务的最大消息数,按需设置即可。
(OS_TICK ) 0u, //(10)在任务之间循环时的时间片的时间量(以滴答为单位)。指定0则使用默认值。
(void *) 0, // (11)是指向用户提供的内存位置的指针,用作TCB扩展。例如, 该用户存储器可以保存浮点寄存器的内容在上下文切换期间,每个任务执行的时间,次数、任务已经切换等。
(OS_OPT )(OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR),// (12)用户可选的任务特定选项,具体见 代码清单:创建任务-7。
(OS_ERR *)&err); // (13)
// 删除起始任务
OSTaskDel ( & AppTaskStartTCB, & err );
}
同时启用三个任务,其中一个任务以1s为周期反转LED灯,另一个任务以3s为周期反转LED灯,第三任务朝串口发送数据:
源码下载:源码
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。