[Embedded open source library] The use of timeslice, a completely decoupled time slice polling framework structure

Completely decoupled time slice polling framework structure

  • Introduction
  • Project code
    • timeslice.h
    • timeslice.c
    • list.h
    • list.c
  • Create project
  • Porting code
  • experiment
  • Function description
    • timeslice_task_init
    • timeslice_task_add
    • timeslice_tak_del
    • timeslice_get_task_num
  • end

Introduction

Timeslice is a time slice polling framework. It is a completely decoupled time slice polling framework. It is very convenient to use. There are four files in the project, namely the header file and source file of tieslice and the header file of list. Source file, tieslice is responsible for polling tasks, and list is a doubly linked list responsible for task management. It is widely used and classic in the Linux kernel. This framework is implemented with reference to the intrusive linked list of rtt real-time operating system. This article is based on This framework is transplanted to the stm32 microcontroller for experimentation, and it is very easy to use. The microcontroller only needs to enable a timer as a clock;

The usage environment of this chapter:

stm32f407vet6
The code project is created using cubemx

Project code

The code of this project is an article I saw on the WeChat public account. The code is not uploaded to github. The source code is directly posted here;

timeslice.h

#ifndef _TIMESLICE_H
#define _TIMESLICE_H

#include "list.h"

typedef enum {<!-- -->
    TASK_STOP,
    TASK_RUN
} IsTaskRun;

typedef struct timesilce
{<!-- -->
    unsigned int id;
    void (*task_hdl)(void);
    IsTaskRun is_run;
    unsigned int timer;
    unsigned int timeslice_len;
    ListObj timeslice_task_list;
} TimesilceTaskObj;

void timeslice_exec(void);
void timeslice_tick(void);
void timeslice_task_init(TimesilceTaskObj* obj, void (*task_hdl)(void), unsigned int id, unsigned int timeslice_len);
void timeslice_task_add(TimesilceTaskObj* obj);
void timeslice_task_del(TimesilceTaskObj* obj);
unsigned int timeslice_get_task_timeslice_len(TimesilceTaskObj* obj);
unsigned int timeslice_get_task_num(void);
unsigned char timeslice_task_isexist(TimesilceTaskObj* obj);

#endif

timeslice.c

#include "timeslice.h"

static LIST_HEAD(timeslice_task_list);

void timeslice_exec()
{<!-- -->
    ListObj* node;
    TimesilceTaskObj* task;

    list_for_each(node, & timeslice_task_list)
    {<!-- -->

        task = list_entry(node, TimesilceTaskObj, timeslice_task_list);
        if (task->is_run == TASK_RUN)
        {<!-- -->
            task->task_hdl();
            task->is_run = TASK_STOP;
        }
    }
}

void timeslice_tick()
{<!-- -->
    ListObj* node;
    TimesilceTaskObj* task;

    list_for_each(node, & timeslice_task_list)
    {<!-- -->
        task = list_entry(node, TimesilceTaskObj, timeslice_task_list);
        if (task->timer != 0)
        {<!-- -->
            task->timer--;
            if (task->timer == 0)
            {<!-- -->
                task->is_run = TASK_RUN;
                task->timer = task->timeslice_len;
            }
        }
    }
}

unsigned int timeslice_get_task_num()
{<!-- -->
    return list_len( & timeslice_task_list);
}

void timeslice_task_init(TimesilceTaskObj* obj, void (*task_hdl)(void), unsigned int id, unsigned int timeslice_len)
{<!-- -->
    obj->id = id;
    obj->is_run = TASK_STOP;
    obj->task_hdl = task_hdl;
    obj->timer = timeslice_len;
    obj->timeslice_len = timeslice_len;
}

void timeslice_task_add(TimesilceTaskObj* obj)
{<!-- -->
    list_insert_before( & amp;timeslice_task_list, & amp;obj->timeslice_task_list);
}

void timeslice_task_del(TimesilceTaskObj* obj)
{<!-- -->
    if (timeslice_task_isexist(obj))
        list_remove( & amp;obj->timeslice_task_list);
    else
        return;
}


unsigned char timeslice_task_isexist(TimesilceTaskObj* obj)
{<!-- -->
    unsigned char isexist = 0;
    ListObj* node;
    TimesilceTaskObj* task;

    list_for_each(node, & timeslice_task_list)
    {<!-- -->
        task = list_entry(node, TimesilceTaskObj, timeslice_task_list);
        if (obj->id == task->id)
            isexist = 1;
    }

    return isexist;
}

unsigned int timeslice_get_task_timeslice_len(TimesilceTaskObj* obj)
{<!-- -->
    return obj->timeslice_len;
}

list.h

#ifndef _LIST_H
#define _LIST_H

#define offset_of(type, member) (unsigned long) & amp;((type*)0)->member
#define container_of(ptr, type, member) ((type *)((char *)(ptr) - offset_of(type, member)))

typedef struct list_structure
{<!-- -->
    struct list_structure* next;
    struct list_structure* prev;
} ListObj;

#define LIST_HEAD_INIT(name) {<!-- --> & amp;(name), & amp;(name)}
#define LIST_HEAD(name) ListObj name = LIST_HEAD_INIT(name)

void list_init(ListObj* list);
void list_insert_after(ListObj* list, ListObj* node);
void list_insert_before(ListObj* list, ListObj* node);
void list_remove(ListObj* node);
int list_isempty(const ListObj* list);
unsigned int list_len(const ListObj* list);

#define list_entry(node, type, member) \
    container_of(node, type, member)

#define list_for_each(pos, head) \
    for (pos = (head)->next; pos != (head); pos = pos->next)

#define list_for_each_safe(pos, n, head) \
  for (pos = (head)->next, n = pos->next; pos != (head); \
    pos = n, n = pos->next)

#endif

list.c

#include "list.h"

void list_init(ListObj* list)
{<!-- -->
    list->next = list->prev = list;
}

void list_insert_after(ListObj* list, ListObj* node)
{<!-- -->
    list->next->prev = node;
    node->next = list->next;

    list->next = node;
    node->prev = list;
}

void list_insert_before(ListObj* list, ListObj* node)
{<!-- -->
    list->prev->next = node;
    node->prev = list->prev;

    list->prev = node;
    node->next = list;
}

void list_remove(ListObj* node)
{<!-- -->
    node->next->prev = node->prev;
    node->prev->next = node->next;

    node->next = node->prev = node;
}

int list_isempty(const ListObj* list)
{<!-- -->
    return list->next == list;
}

unsigned int list_len(const ListObj* list)
{<!-- -->
    unsigned int len = 0;
    const ListObj* p = list;
    while (p->next != list)
    {<!-- -->
        p = p->next;
        len + + ;
    }

    return len;
}

Create project

Configure high-speed clock and low-speed clock to provide external crystal oscillator

Configure the debugging mode to sw debugging mode

Configure clock frequency
Configure LEDs. Here on my board, these three IOs are connected to LEDs.

Configure a 10ms timer (1000000hz / 1000ms == 1ms = 1khz to get 10ms and need to count 10000 reloads) remember to turn on interrupts

Configure the project and generate it, set the project name, and generate a separate .c.h file to copy the library file


Let’s start the code transplantation work;

Porting code

First we need to add the code of the open source project to the project
Add timeslice polling function in the timer interrupt service function

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{<!-- -->
if(htim->Instance == TIM3)
{<!-- -->
timeslice_tick();
}
}

Then we need to start the timer in the main function and call the exec function in while1 to schedule the time slice

 HAL_TIM_Base_Start_IT( & amp;htim3);
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {<!-- -->
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
\t  
timeslice_exec();
  }

At this point, our transplantation work is completed. The decoupling effect of this project is really good, and the transplantation is quite simple. Then we create several tasks to experiment with the effect;

Experiment

/* USER CODE BEGIN Header */
/**
  *************************************************** ****************************
  * @file: main.c
  * @brief: Main program body
  *************************************************** ****************************
  *@attention
  *
  * Copyright (c) 2023 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  *************************************************** ****************************
  */
/* USER CODE END Header */
/* Includes ----------------------------------------------- ------------------*/
#include "main.h"
#include "tim.h"
#include "gpio.h"

/* Private includes -------------------------------------------------- ----------*/
/* USER CODE BEGIN Includes */
#include "timeslice.h"
/* USER CODE END Includes */

/* Private typedef ----------------------------------------------- -------------*/
/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define -------------------------------------------------- ---------------*/
/* USER CODE BEGIN PD */

/* USER CODE END PD */

/* Private macro ----------------------------------------------- ---------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables -------------------------------------------------- -----------*/

/* USER CODE BEGIN PV */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{<!-- -->
if(htim->Instance == TIM3)
{<!-- -->
timeslice_tick();
}
}


//Create 3 task objects
TimesilceTaskObj task_1, task_2, task_3;

//Specific task function
void task1_hdl()
{
    HAL_GPIO_TogglePin(GPIOE, GPIO_PIN_13);
}

void task2_hdl()
{
    HAL_GPIO_TogglePin(GPIOE, GPIO_PIN_14 );
}

void task3_hdl()
{
    HAL_GPIO_TogglePin(GPIOE, GPIO_PIN_15);
}

//Initialize the task object and add the task to the time slice polling schedule
void task_init()
{
    timeslice_task_init( & amp;task_1, task1_hdl, 1, 1);
    timeslice_task_init( & amp;task_2, task2_hdl, 2, 1);
    timeslice_task_init( & amp;task_3, task3_hdl, 3, 1);

    timeslice_task_add( & amp;task_1);
    timeslice_task_add( & amp;task_2);
    timeslice_task_add( & amp;task_3);
}
/* USER CODE END PV */

/* Private function prototypes ------------------------------------------------ --*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/*Private user code------------------------------------------------ ----------*/
/* USER CODE BEGIN 0 */

/* USER CODE END 0 */

/**
  * @brief The application entry point.
  * @retval int
  */
int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration------------------------------------------------- ----------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_TIM3_Init();
  /* USER CODE BEGIN 2 */
  HAL_TIM_Base_Start_IT( & amp;htim3);
task_init();
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
\t  
timeslice_exec();
  }
  /* USER CODE END 3 */
}

/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  /** Configure the main internal regulator output voltage
  */
  __HAL_RCC_PWR_CLK_ENABLE();
  __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);

  /** Initializes the RCC Oscillators according to the specified parameters
  * in the RCC_OscInitTypeDef structure.
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLM = 25;
  RCC_OscInitStruct.PLL.PLLN = 336;
  RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
  RCC_OscInitStruct.PLL.PLLQ = 4;
  if (HAL_RCC_OscConfig( & amp;RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }

  /** Initializes the CPU, AHB and APB buses clocks
  */
  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_DIV4;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;

  if (HAL_RCC_ClockConfig( & amp;RCC_ClkInitStruct, FLASH_LATENCY_5) != HAL_OK)
  {
    Error_Handler();
  }
}

/* USER CODE BEGIN 4 */

/* USER CODE END 4 */

/**
  * @brief This function is executed in case of error occurrence.
  * @retval None
  */
void Error_Handler(void)
{
  /* USER CODE BEGIN Error_Handler_Debug */
  /* User can add his own implementation to report the HAL error return state */
  __disable_irq();
  while (1)
  {
  }
  /* USER CODE END Error_Handler_Debug */
}

#ifdef USE_FULL_ASSERT
/**
  * @brief Reports the name of the source file and the source line number
  * where the assert_param error has occurred.
  * @param file: pointer to the source file name
  * @param line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t *file, uint32_t line)
{
  /* USER CODE BEGIN 6 */
  /* User can add his own implementation to report the file name and line number,
     ex: printf("Wrong parameters value: file %s on line %d\r\
", file, line) */
  /* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */

core part

//Create 3 task objects
TimesilceTaskObj task_1, task_2, task_3;

//Specific task function
void task1_hdl()
{<!-- -->
    HAL_GPIO_TogglePin(GPIOE, GPIO_PIN_13);
}

void task2_hdl()
{<!-- -->
    HAL_GPIO_TogglePin(GPIOE, GPIO_PIN_14 );
}

void task3_hdl()
{<!-- -->
    HAL_GPIO_TogglePin(GPIOE, GPIO_PIN_15);
}

//Initialize the task object and add the task to the time slice polling schedule
void task_init()
{<!-- -->
    timeslice_task_init( & amp;task_1, task1_hdl, 1, 1);
    timeslice_task_init( & amp;task_2, task2_hdl, 2, 2);
    timeslice_task_init( & amp;task_3, task3_hdl, 3, 3);

    timeslice_task_add( & amp;task_1);
    timeslice_task_add( & amp;task_2);
    timeslice_task_add( & amp;task_3);
}

It should be noted that there must be a task that needs to be created before exec to ensure running. Other tasks can be created in this task. The above experiment is to implement three tasks, and the three tasks are triggered once for each interrupt. , the second task is triggered once every two interrupts, and the third task is triggered once every three interrupts;

Function description

timeslice_task_init

Initialize task function

void timeslice_task_init(TimesilceTaskObj* obj, void (*task_hdl)(void), unsigned int id, unsigned int timeslice_len)
{<!-- -->
    obj->id = id;
    obj->is_run = TASK_STOP;
    obj->task_hdl = task_hdl;
    obj->timer = timeslice_len;
    obj->timeslice_len = timeslice_len;
}

In this function, the task structure parameters are initialized. The id is similar to the task name to distinguish the task. is_run is a flag used to determine whether the task needs to be executed during this interrupt. task_hd1 represents the function pointer, which is our task function. timer indicates how many interrupts trigger a count, timeslice_len indicates how many interrupts trigger a count initial value, in timeslice_tick, when the timer value decreases to 0, the task will be triggered and the timer value will be reset to timeslice_len;

timeslice_task_add

Add tasks to a doubly linked list

void timeslice_task_add(TimesilceTaskObj* obj)
{<!-- -->
    list_insert_before( & amp;timeslice_task_list, & amp;obj->timeslice_task_list);
}

void list_insert_before(ListObj* list, ListObj* node)
{<!-- -->
    list->prev->next = node;
    node->prev = list->prev;

    list->prev = node;
    node->next = list;
}

The linked list (timeslice_task_list) will be traversed in timeslice_tick polling

timeslice_tak_del

Delete the running task list

void timeslice_task_del(TimesilceTaskObj* obj)
{<!-- -->
    if (timeslice_task_isexist(obj))
        list_remove( & amp;obj->timeslice_task_list);
    else
        return;
}

In this function, the timeslice_task_isexist function will be used to determine whether the ID of the task exists in the linked list. If it exists, exit permission will be returned. This involves a function list_entry->container_of in Linux. This function is obtained through a variable in the structure. The pointer position of the entire structure. If you are interested, you can learn about the implementation of the project code;

timeslice_get_task_num

Get the current number of tasks, which is the length of the linked list

unsigned int timeslice_get_task_num()
{<!-- -->
    return list_len( & timeslice_task_list);
}

unsigned int list_len(const ListObj* list)
{<!-- -->
    unsigned int len = 0;
    const ListObj* p = list;
    while (p->next != list)
    {<!-- -->
        p = p->next;
        len + + ;
    }

    return len;
}

End

The overall code is not complicated but it is a project worth learning. I am Liangboshuibaicai and wish all programmers a happy holiday~ See you below~