精品国产人成在线_亚洲高清无码在线观看_国产在线视频国产永久2021_国产AV综合第一页一个的一区免费影院黑人_最近中文字幕MV高清在线视频

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

使用cola_os軟件定時器實現時間片輪詢框架

strongerHuang ? 來源:嵌入式大雜燴 ? 作者:ZhengNL ? 2022-09-22 09:03 ? 次閱讀

本期主角:cola_os,它是一個300多行代碼實現的多任務管理的OS,在很多MCU開發中,功能很簡單,實時性要求不強,任務多了管理不當又很亂。

如果使用RTOS顯得太浪費,這時候可以嘗試使用使用cola_os這類基于軟件定時器實現的時間片輪詢框架。

license:MulanPSL-1.0(木蘭寬松許可證, 第1版)。

cola_os是一份簡潔明了的代碼,包含很多有用的編程思想,值得通讀。下面我們一起來學習一下:

cola_os的分析及使用

其實關于cola_os其實我們前幾天的推文中也有做介紹。今天我們再一起來完整地梳理一遍。

cola_os目前的內容如:

pYYBAGMrtI-AE725AABERup36qY907.jpg
poYBAGMrtJaALfLPAAFpT0ztrrg484.jpg

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也有實現這個功能:

pYYBAGMrtMCAMJrRAADnBqz4k_0865.jpg
一般的,我們的初始化在主函數中調用,如:

pYYBAGMrtNOAJlH6AACKvIqVFWg416.jpg

有了自動初始化模塊,可以不在主函數中調用,例如:

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 $$ Baseinitcall0init Limit這幾個initcall_t類型的函數指針數組的聲明。它們事先是調用__define_initcall把函數指針fn放入到自定義的指定的段.initcall0init、.initcall1init、.initcall2init、.initcall3init。

initcall0init$$Baseinitcall0init$$Limit按照我的理解就是各個初始化函數表的開始及結束地址。從而實現遍歷:

for(fn=initcall0init$$Base;
fn

例如RT-Thread里的實現也是類似的:

volatileconstinit_fn_t*fn_ptr;

for(fn_ptr=&__rt_init_rti_board_start;fn_ptr

關于init自動初始化機制大致就分析這些。

cola_os包含有cola_os任務管理、cola_device硬件抽象層及cola_init自動初始化三大塊,這三塊內容其實可以單獨抽出來學習、使用。

4、cola_os的使用

下面我們基于小熊派IOT開發板來簡單實踐實踐。

我們創建兩個定時任務:

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*/
}

編譯、下載、運行:

pYYBAGMrtPaAefY3AAHE0NwfKjE570.jpg

從運行結果可以看到,task1的定時周期是task0的兩倍,符合預期。



審核編輯:劉清

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • 軟件定時器
    +關注

    關注

    0

    文章

    18

    瀏覽量

    6730
  • RTOS
    +關注

    關注

    22

    文章

    809

    瀏覽量

    119451
  • MCU芯片
    +關注

    關注

    3

    文章

    248

    瀏覽量

    11394

原文標題:300行代碼實現一個多任務OS

文章出處:【微信號:strongerHuang,微信公眾號:strongerHuang】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏

    評論

    相關推薦

    請問怎么修改軟件定時器時間

    ;tmr1",&err);就可以了,定時時間是35*10=350ms。在程序運行過程中需要多次改變軟件定時器時間,請問如何實現啊?總不能每
    發表于 04-08 09:35

    利用STM32的定時器中斷去實現時間的顯示

    前言利用STM32的定時器中斷,實現時間的顯示。我們知道利用定時器中斷只能進行tick的計算,然而用來顯示時間我們應該怎么辦呢?經過項目的實際運用,我發現利用
    發表于 07-16 06:42

    cola os程序框架介紹

    創建4.cola_os stime定時器創建前言在嵌入式軟件開發,包括單片機開發中,軟件架構對于開發人員是一個必須認真考慮的問題。軟件架構對
    發表于 11-08 08:17

    μC/OS-II的軟件定時器的改進的方法

    任務間的操作同步等,因此,對軟件定時器的高效實現對提升系統的響應效率是至關重要的。 作為一種基礎的軟件措施,C/OS-II[1]的 V2.8
    發表于 10-31 16:57 ?0次下載
    μC/<b class='flag-5'>OS</b>-II的<b class='flag-5'>軟件</b><b class='flag-5'>定時器</b>的改進的方法

    單片機的時間輪詢的思路介紹和架構說明

    。下面本人將詳細介紹本人模式,并參考別人的代碼建立的一個時間輪詢架構程序的方法,我想將給初學者有一定的借鑒性。使用1個定時器,可以是任意的定時器
    發表于 09-18 17:21 ?8次下載
    單片機的<b class='flag-5'>時間</b><b class='flag-5'>片</b><b class='flag-5'>輪詢</b>的思路介紹和架構說明

    嵌入式開發|嵌入式軟件框架《二》前后臺任務框架-cola os系統

    創建4.cola_os stime定時器創建前言在嵌入式軟件開發,包括單片機開發中,軟件架構對于開發人員是一個必須認真考慮的問題。軟件架構對
    發表于 11-03 13:51 ?18次下載
    嵌入式開發|嵌入式<b class='flag-5'>軟件</b><b class='flag-5'>框架</b>《二》前后臺任務<b class='flag-5'>框架</b>-<b class='flag-5'>cola</b> <b class='flag-5'>os</b>系統

    單片機的程序架構——時間輪詢【學習筆記】

    單片機的程序架構1.順序執行2.時間輪尋:==2.0 一個定時器的復用:====2.1 時間輪詢
    發表于 11-14 18:06 ?47次下載
    單片機的程序架構——<b class='flag-5'>時間</b><b class='flag-5'>片</b><b class='flag-5'>輪詢</b>【學習筆記】

    UCOSIII- 軟件定時器的使用

    首先打開宏(使能所有軟件定時器os_cfg.h文件: #define OS_CFG_TMR_EN 11.創建定時器+綁定回調函數
    發表于 12-23 19:55 ?0次下載
    UCOSIII- <b class='flag-5'>軟件</b><b class='flag-5'>定時器</b>的使用

    STM32定時器中斷顯示時間

    前言利用STM32的定時器中斷,實現時間的顯示。我們知道利用定時器中斷只能進行tick的計算,然而用來顯示時間我們應該怎么辦呢?經過項目的實際運用,我發現利用
    發表于 12-24 18:56 ?21次下載
    STM32<b class='flag-5'>定時器</b>中斷顯示<b class='flag-5'>時間</b>

    cola_os的分析及使用

    cola_os是一個300多行代碼實現的多任務管理的OS,在很多MCU開發中,功能很簡單,實時性要求不強,任務多了管理不當又很亂。
    的頭像 發表于 08-12 09:37 ?1264次閱讀

    什么是cola_os

    cola_os是一個300多行代碼實現的多任務管理的OS,在很多MCU開發中,功能很簡單,實時性要求不強,任務多了管理不當又很亂。
    的頭像 發表于 02-09 17:00 ?1535次閱讀
    什么是<b class='flag-5'>cola_os</b>?

    什么是軟件定時器軟件定時器實現原理

    軟件定時器是用程序模擬出來的定時器,可以由一個硬件定時器模擬出成千上萬個軟件定時器,這樣程序在需
    的頭像 發表于 05-23 17:05 ?2687次閱讀

    軟件架構之時間

    首先來個demo,該demo是使用電腦開兩個線程:一個線程模擬單片機的定時器中斷產生時間輪詢個時鐘,另一個線程則模擬主函數中一直運行的時間
    的頭像 發表于 01-18 09:29 ?529次閱讀
    <b class='flag-5'>軟件</b>架構之<b class='flag-5'>時間</b>輪<b class='flag-5'>片</b>法

    如何實現一個軟件定時器

    在Linux,uC/OS,FreeRTOS等操作系統中,都帶有軟件定時器,原理大同小異。典型的實現方法是:通過一個硬件定時器產生固定的時鐘節
    的頭像 發表于 04-29 11:00 ?591次閱讀

    長持續時間定時器電路圖 時間定時器的工作原理和功能

    的處理,都離不開定時器的精確控制。時間定時器通常由硬件和軟件兩部分組成,硬件部分通過計時芯片或計數
    的頭像 發表于 06-24 17:34 ?1598次閱讀
    長持續<b class='flag-5'>時間</b><b class='flag-5'>定時器</b>電路圖 <b class='flag-5'>時間</b><b class='flag-5'>定時器</b>的工作原理和功能