0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

cola_os的分析及使用

我快闭嘴 来源:嵌入式大杂烩 作者:嵌入式大杂烩 2022-08-12 09:37 次阅读

正文


本期主角:cola_os

cola_os是一个300多行代码实现的多任务管理的OS,在很多MCU开发中,功能很简单,实时性要求不强,任务多了管理不当又很乱。

如果使用RTOS显得太浪费,这时候可以尝试使用使用cola_os这类基于软件定时器实现的时间片轮询框架。

仓库链接:

https://gitee.com/schuck/cola_os

license:MulanPSL-1.0(木兰宽松许可证, 第1版)。

cola_os是一份简洁明了的代码,包含很多有用的编程思想,值得通读。下面我们一起来学习一下:

cola_os的分析及使用

其实关于cola_os其实我们前几天的推文中也有做介绍。今天我们再一起来完整地梳理一遍。

cola_os目前的内容如:

d72d7110-1971-11ed-ba43-dac502259ad0.pngd73c46fe-1971-11ed-ba43-dac502259ad0.png

1、cola_os

cola_os就是cola_os的任务管理模块。任务使用链表进行管理,其数据结构如:

typedefvoid(*cbFunc)(uint32_tevent);

typedefstructtask_s
{
uint8_ttimerNum;//定时编号
uint32_tperiod;//定时周期
booloneShot;//true只执行一次
boolstart;//开始启动
uint32_ttimerTick;//定时计数
boolrun;//任务运行标志
booltaskFlag;//任务标志是主任务还是定时任务
uint32_tevent;//驱动事件
cbFuncfunc;//回调函数
structtask_s*next;
}task_t;

每创建一个任务吗,就是往任务链表中插入一个任务节点。

其创建任务的方法有两种:

  • 创建主循环任务
  • 创建定时任务

两种方式创建,都是会在while(1)循环中调度执行任务函数。

我们可以看看cola_task_loop任务遍历函数,这个函数最终是要放在主函数while(1)中调用的。其内容如:

voidcola_task_loop(void)
{
uint32_tevents;
task_t*cur=task_list;
OS_CPU_SRcpu_sr;

while(cur!=NULL)
{
if(cur->run)
{
if(NULL!=cur->func)
{
events=cur->event;
if(events)
{
enter_critical();
cur->event=0;
exit_critical();
}
cur->func(events);
}
if(TASK_TIMER==cur->taskFlag)
{
enter_critical();
cur->run=false;
exit_critical();
}
if((cur->oneShot)&&(TASK_TIMER==cur->taskFlag))
{
cur->start=false;
}
}
cur=cur->next;
}
}

两种方式创建的任务都会在cur->func(events);被调用。不同的就是:遍历执行到定时任务时,需要清掉定时相关标志。

其中,events作为任务函数的参数传入。从cola_task_loop可以看到,事件并未使用到,events无论真还是假,在执行任务函数前,都被清零了。events的功能应该是作者预留的。

创建任务很简单,比如创建一个定时任务:

statictask_ttimer_500ms;

//每500ms执行一次
staticvoidtimer_500ms_cb(uint32_tevent)
{
printf("task0running...
");
}

cola_timer_create(&timer_500ms,timer_500ms_cb);
cola_timer_start(&timer_500ms,TIMER_ALWAYS,500);

cola_os是基于软件定时器来进行任务调度管理的,需要一个硬件定时器提供时基。比如使用系统滴答定时器,配置为1ms中断一次。

在1ms中断中不断轮询判断定时计数是否到达定时时间:

voidSysTick_Handler(void)
{
cola_timer_ticker();
}

voidcola_timer_ticker(void)
{
task_t*cur=task_list;
OS_CPU_SRcpu_sr;
while(cur!=NULL)
{
if((TASK_TIMER==cur->taskFlag)&&cur->start)
{
if(++cur->timerTick>=cur->period)
{
cur->timerTick=0;
if(cur->func!=NULL)
{
enter_critical();
cur->run=true;
exit_critical();
}
}
}
cur=cur->next;
}
}

如果到了则将标志cur->run置位,在while大循环中的cola_task_loop函数中如果检测到该标志就执行该任务函数。

2、cola_device

cola_device是硬件抽象层,使用链表来管理各个设备。其借鉴了RT-ThreadLinux相关驱动框架思想。大致内容如:

数据结构如:

typedefstructcola_devicecola_device_t;

structcola_device_ops
{
int(*init)(cola_device_t*dev);
int(*open)(cola_device_t*dev,intoflag);
int(*close)(cola_device_t*dev);
int(*read)(cola_device_t*dev,intpos,void*buffer,intsize);
int(*write)(cola_device_t*dev,intpos,constvoid*buffer,intsize);
int(*control)(cola_device_t*dev,intcmd,void*args);

};

structcola_device
{
constchar*name;
structcola_device_ops*dops;
structcola_device*next;
};

硬件抽象层的接口如:

/*
驱动注册
*/
intcola_device_register(cola_device_t*dev);
/*
驱动查找
*/
cola_device_t*cola_device_find(constchar*name);
/*
驱动读
*/
intcola_device_read(cola_device_t*dev,intpos,void*buffer,intsize);
/*
驱动写
*/
intcola_device_write(cola_device_t*dev,intpos,constvoid*buffer,intsize);
/*
驱动控制
*/
intcola_device_ctrl(cola_device_t*dev,intcmd,void*arg);

首先,在驱动层注册好设备,把操作设备的函数指针及设备名称插入到设备链表中:

staticcola_device_tled_dev;

staticvoidled_gpio_init(void)
{
GPIO_InitTypeDefGPIO_InitStructure;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOC,ENABLE);
GPIO_InitStructure.GPIO_Pin=PIN_GREENLED;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_OUT;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_OType=GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd=GPIO_PuPd_NOPULL;
GPIO_Init(PORT_GREEN_LED,&GPIO_InitStructure);
LED_GREEN_OFF;
}

staticintled_ctrl(cola_device_t*dev,intcmd,void*args)
{
if(LED_TOGGLE==cmd)
{
LED_GREEN_TOGGLE;
}
else
{

}
return1;
}


staticstructcola_device_opsops=
{
.control=led_ctrl,
};

staticvoidled_register(void)
{
led_gpio_init();
led_dev.dops=&ops;
led_dev.name="led";
cola_device_register(&led_dev);
}

cola_device_register函数如:

intcola_device_register(cola_device_t*dev)
{
if((NULL==dev)||(cola_device_is_exists(dev)))
{
return0;
}

if((NULL==dev->name)||(NULL==dev->dops))
{
return0;
}
returndevice_list_inster(dev);
}

驱动注册好设备之后,应用层就可以根据设备名称来查找设备是否被注册,如果已经注册则可以调用设备操作接口操控设备。比如创建一个定时任务定时反转led:

voidapp_init(void)
{
app_led_dev=cola_device_find("led");
assert(app_led_dev);
cola_timer_create(&timer_500ms,timer_500ms_cb);
cola_timer_start(&timer_500ms,TIMER_ALWAYS,500);
}

staticvoidtimer_500ms_cb(uint32_tevent)
{
cola_device_ctrl(app_led_dev,LED_TOGGLE,0);
}

3、cola_init

cola_init是一个自动初始化模块,模仿Linux的initcall机制。RT-Thread也有实现这个功能:

d74db22c-1971-11ed-ba43-dac502259ad0.png

一般的,我们的初始化在主函数中调用,如:

d761ebac-1971-11ed-ba43-dac502259ad0.png

有了自动初始化模块,可以不在主函数中调用,例如:

voidSystemClock_Config(void)
{
}
pure_initcall(SystemClock_Config);

这样也可以调用SystemClock_Config。pure_initcall如:

#define__used__attribute__((__used__))

typedefvoid(*initcall_t)(void);

#define__define_initcall(fn,id)
staticconstinitcall_t__initcall_##fn##id__used
__attribute__((__section__("initcall"#id"init")))=fn;

#definepure_initcall(fn)__define_initcall(fn,0)//可用作系统时钟初始化
#definefs_initcall(fn)__define_initcall(fn,1)//tick和调试接口初始化
#definedevice_initcall(fn)__define_initcall(fn,2)//驱动初始化
#definelate_initcall(fn)__define_initcall(fn,3)//其他初始化

在cola_init中,首先是调用不同顺序级别的__define_initcall宏来把函数指针fn放入到自定义的指定的段中。各个需要自动初始化的函数放到指定的段中,形成一张初始化函数表。

__ attribute __ (( __ section __)) 关键字就是用来指定数据存放段。

do_init_call函数在我们程序起始时调用,比如在bsp_init中调用:

voidbsp_init(void)
{
do_init_call();
}

do_init_call里做的事情就是遍历初始化函数表里的函数:

voiddo_init_call(void)
{
externinitcall_tinitcall0init$$Base[];
externinitcall_tinitcall0init$$Limit[];
externinitcall_tinitcall1init$$Base[];
externinitcall_tinitcall1init$$Limit[];
externinitcall_tinitcall2init$$Base[];
externinitcall_tinitcall2init$$Limit[];
externinitcall_tinitcall3init$$Base[];
externinitcall_tinitcall3init$$Limit[];

initcall_t*fn;

for(fn=initcall0init$$Base;
fn< initcall0init$$Limit;
            fn++)
    {
        if(fn)
(*fn)();
}

for(fn=initcall1init$$Base;
fn< initcall1init$$Limit;
            fn++)
    {
        if(fn)
(*fn)();
}

for(fn=initcall2init$$Base;
fn< initcall2init$$Limit;
            fn++)
    {
        if(fn)
(*fn)();
}

for(fn=initcall3init$$Base;
fn< initcall3init$$Limit;
            fn++)
    {
        if(fn)
(*fn)();
}
}

这里有 initcall0init $$ Baseinitcall0init Limit这几个initcall_t类型的函数指针数组的声明。它们事先是调用__define_initcall把函数指针fn放入到自定义的指定的段.initcall0init、.initcall1init、.initcall2init、.initcall3init。

initcall0init$$Baseinitcall0init$$Limit按照我的理解就是各个初始化函数表的开始及结束地址。从而实现遍历:

for(fn=initcall0init$$Base;
fn< initcall0init$$Limit;
            fn++)
    {
        if(fn)
(*fn)();
}

例如RT-Thread里的实现也是类似的:

volatileconstinit_fn_t*fn_ptr;

for(fn_ptr=&__rt_init_rti_board_start;fn_ptr< &__rt_init_rti_board_end; fn_ptr++)
    {
        (*fn_ptr)();
    }

关于init自动初始化机制大致就分析这些。

cola_os包含有cola_os任务管理、cola_device硬件抽象层及cola_init自动初始化三大块,这三块内容其实可以单独抽出来学习、使用。

4、cola_os的使用

下面我们基于小熊派IOT开发板来简单实践实践。

d7732c0a-1971-11ed-ba43-dac502259ad0.png

我们创建两个定时任务:

  • task0任务:定时500ms打印一次。
  • task1任务:定时1000ms打印一次。

main.c:

/*Privatevariables---------------------------------------------------------*/
statictask_ttimer_500ms;
statictask_ttimer_1000ms;
/*USERCODEENDPV*/

/*Privatefunctionprototypes-----------------------------------------------*/
voidSystemClock_Config(void);

/*USERCODEBEGINPFP*/
/*Privatefunctionprototypes-----------------------------------------------*/

/*USERCODEENDPFP*/

/*USERCODEBEGIN0*/

//每500ms执行一次
staticvoidtimer_500ms_cb(uint32_tevent)
{
printf("task0running...
");
}

//每1000ms执行一次
staticvoidtimer_1000ms_cb(uint32_tevent)
{
printf("task1running...
");
}

intmain(void)
{
/*USERCODEBEGIN1*/

/*USERCODEEND1*/

/*MCUConfiguration----------------------------------------------------------*/

/*Resetofallperipherals,InitializestheFlashinterfaceandtheSystick.*/
HAL_Init();

/*USERCODEBEGINInit*/

/*USERCODEENDInit*/

/*Configurethesystemclock*/
//SystemClock_Config();

/*USERCODEBEGINSysInit*/

/*USERCODEENDSysInit*/

/*Initializeallconfiguredperipherals*/
MX_GPIO_Init();
MX_DMA_Init();
MX_USART1_UART_Init();
/*USERCODEBEGIN2*/
printf("微信公众号:嵌入式大杂烩
");
printf("cola_ostest!
");

cola_timer_create(&timer_500ms,timer_500ms_cb);
cola_timer_start(&timer_500ms,TIMER_ALWAYS,500);
cola_timer_create(&timer_1000ms,timer_1000ms_cb);
cola_timer_start(&timer_1000ms,TIMER_ALWAYS,1000);

/*USERCODEEND2*/

/*Infiniteloop*/
/*USERCODEBEGINWHILE*/
while(1)
{

/*USERCODEENDWHILE*/

/*USERCODEBEGIN3*/
cola_task_loop();
}
/*USERCODEEND3*/

}

/**
*@briefSystemClockConfiguration
*@retvalNone
*/
voidSystemClock_Config(void)
{

RCC_OscInitTypeDefRCC_OscInitStruct;
RCC_ClkInitTypeDefRCC_ClkInitStruct;
RCC_PeriphCLKInitTypeDefPeriphClkInit;

/**InitializestheCPU,AHBandAPBbussesclocks
*/
RCC_OscInitStruct.OscillatorType=RCC_OSCILLATORTYPE_MSI;
RCC_OscInitStruct.MSIState=RCC_MSI_ON;
RCC_OscInitStruct.MSICalibrationValue=0;
RCC_OscInitStruct.MSIClockRange=RCC_MSIRANGE_6;
RCC_OscInitStruct.PLL.PLLState=RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource=RCC_PLLSOURCE_MSI;
RCC_OscInitStruct.PLL.PLLM=1;
RCC_OscInitStruct.PLL.PLLN=40;
RCC_OscInitStruct.PLL.PLLP=RCC_PLLP_DIV7;
RCC_OscInitStruct.PLL.PLLQ=RCC_PLLQ_DIV2;
RCC_OscInitStruct.PLL.PLLR=RCC_PLLR_DIV2;
if(HAL_RCC_OscConfig(&RCC_OscInitStruct)!=HAL_OK)
{
_Error_Handler(__FILE__,__LINE__);
}

/**InitializestheCPU,AHBandAPBbussesclocks
*/
RCC_ClkInitStruct.ClockType=RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource=RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider=RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider=RCC_HCLK_DIV1;
RCC_ClkInitStruct.APB2CLKDivider=RCC_HCLK_DIV1;

if(HAL_RCC_ClockConfig(&RCC_ClkInitStruct,FLASH_LATENCY_4)!=HAL_OK)
{
_Error_Handler(__FILE__,__LINE__);
}

PeriphClkInit.PeriphClockSelection=RCC_PERIPHCLK_USART1;
PeriphClkInit.Usart1ClockSelection=RCC_USART1CLKSOURCE_PCLK2;
if(HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit)!=HAL_OK)
{
_Error_Handler(__FILE__,__LINE__);
}

/**Configurethemaininternalregulatoroutputvoltage
*/
if(HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1)!=HAL_OK)
{
_Error_Handler(__FILE__,__LINE__);
}

/**ConfiguretheSystickinterrupttime
*/
HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/1000);

/**ConfiguretheSystick
*/
HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);

/*SysTick_IRQninterruptconfiguration*/
HAL_NVIC_SetPriority(SysTick_IRQn,0,0);
}
pure_initcall(SystemClock_Config);

SysTick_Handler:

voidSysTick_Handler(void)
{
/*USERCODEBEGINSysTick_IRQn0*/

/*USERCODEENDSysTick_IRQn0*/
cola_timer_ticker();
HAL_IncTick();
HAL_SYSTICK_IRQHandler();
/*USERCODEBEGINSysTick_IRQn1*/

/*USERCODEENDSysTick_IRQn1*/
}

编译、下载、运行:

d79175ca-1971-11ed-ba43-dac502259ad0.png

从运行结果可以看到,task1的定时周期是task0的两倍,符合预期。

审核编辑:汤梓红


声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • mcu
    mcu
    +关注

    关注

    146

    文章

    16965

    浏览量

    350180
  • RTOS
    +关注

    关注

    21

    文章

    809

    浏览量

    119408
  • 代码
    +关注

    关注

    30

    文章

    4741

    浏览量

    68314

原文标题:300多行代码实现的一个多任务OS

文章出处:【微信号:最后一个bug,微信公众号:最后一个bug】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    IOT OS比较,分析IOT OS的发展方向

    OS、AWorksOS、HybridOS等8个IOT OS的特点。智能物联网(AIOT,AI+IOT)是人工智能与物联网的协同应用,据此分析IOT OS的发展方向。 关键字:物联网操
    的头像 发表于 08-16 17:12 4548次阅读

    使用cola_os软件定时器实现时间片轮询框架

    如果使用RTOS显得太浪费,这时候可以尝试使用使用cola_os这类基于软件定时器实现的时间片轮询框架。
    的头像 发表于 09-22 09:03 1376次阅读
    使用<b class='flag-5'>cola_os</b>软件定时器实现时间片轮询框架

    OS/400 PASE 入门

    iSeries 服务器时,OS/400 PASE 提供很高的灵活性。当然,OS/400 PASE 只是您可以选择的多个选项之一。 API 分析 在确定应用程序是否适合 OS/400
    发表于 11-13 21:47

    【干货分享】μC/OS-II 软件定时器的分析与测试

    个好的软件定时器实现要求有较高的精度、较小的处理器开销,且占用较少的存储器资源。本文在对 μC/OS-II 定时器算法分析的基础上,对定时精度和处理器占用情况进行了分析与测试,其结果在实时系统的设计与应用中具有借鉴意义。
    发表于 02-21 11:58

    常用的嵌入式操作系统是什么?Linux OS/palm OS与Windows CE对比分析哪个好?

    嵌入式操作系统EOS具有什么特点常用的嵌入式操作系统是什么?Linux OS与Windows Ce对比分析哪个好palm OS与Windows CE对比分析哪个好
    发表于 04-27 07:06

    cola os程序框架介绍

    系列文章目录嵌入式开发|嵌入式软件框架《一》常用的软件框架介绍与选择文章目录系列文章目录前言一、cola os程序框架1.cola_init2.cola_device3.cola_os task任务
    发表于 11-08 08:17

    嵌入式系统OS应用层分析

    代码结构,一般嵌入式系统都会有个OS。下面只分析应用层:OS会给功能任务分配时间片,例如10ms、2ms、5ms,这个时间片对于stateflow的tick,也就是stateflow生成的函数
    发表于 12-17 06:35

    μC/OS-II的多任务系统实时性分析与优先级分配

    μC/OS-II的多任务系统实时性分析与优先级分配 从产品研发的角度,针对小资源系统中使用μC/OS-II的实时性和优先级关系进行了分析,提出了可删除
    发表于 03-29 15:12 985次阅读
    μC/<b class='flag-5'>OS</b>-II的多任务系统实时性<b class='flag-5'>分析</b>与优先级分配

    μC/OS-II实时性能测试与分析

    μC/OS-II实时性能测试与分析 任务切换时间和中断响应时间是嵌入式实时操作系统实时性能的重要指标。本文对μC/OS-II的上述指标进行
    发表于 03-29 15:14 1522次阅读
    μC/<b class='flag-5'>OS</b>-II实时性能测试与<b class='flag-5'>分析</b>

    μC/OS-II在EP7312上的移植

    μC/OS-II在EP7312上的移植首先介绍μC/OS-II操作系统的特点,重点分析μC/OS-II在EP7312上的移植方法,介绍μC/OS
    发表于 06-16 11:24 1131次阅读
    μC/<b class='flag-5'>OS</b>-II在EP7312上的移植

    UCOS-III OS_CPU_PendSVHandler源码分析

    UCOS-III OS_CPU_PendSVHandler源码分析
    发表于 08-28 10:48 13次下载

    嵌入式开发|嵌入式软件框架《二》前后台任务框架-cola os系统

    系列文章目录嵌入式开发|嵌入式软件框架《一》常用的软件框架介绍与选择文章目录系列文章目录前言一、cola os程序框架1.cola_init2.cola_device3.cola_os task任务
    发表于 11-03 13:51 18次下载
    嵌入式开发|嵌入式软件框架《二》前后台任务框架-<b class='flag-5'>cola</b> <b class='flag-5'>os</b>系统

    COLA Architecture整洁面向对象分层架构

    ./oschina_soft/COLA.zip
    发表于 06-14 09:41 0次下载
    <b class='flag-5'>COLA</b> Architecture整洁面向对象分层架构

    慢下来&amp;快起来,看Coca-Cola高效转型路径

    事半功倍 | 聚沙成塔, 打造 Coca-Cola 速度! 企业的数字化转型并非“一蹴而就”,而是一个持续迭代的过程,从初期确认需求、汇集资源到有效利用资源再到具体实践,其中的每一个环节对于最终效果
    的头像 发表于 01-06 06:50 432次阅读

    什么是cola_os

    cola_os是一个300多行代码实现的多任务管理的OS,在很多MCU开发中,功能很简单,实时性要求不强,任务多了管理不当又很乱。
    的头像 发表于 02-09 17:00 1525次阅读
    什么是<b class='flag-5'>cola_os</b>?