• STM32HAL-完全解耦面向对象思维的架构-时间轮片法使用(timeslice)


    目录

    概述 

    一、开发环境

    二、STM32CubeMx配置

    三、编码 

    四、运行结果

    五、代码解释

    六、总结


    概述 

            timeslice是一个时间片轮询框架,完全解耦的时间片轮询框架,非常适合裸机单片机引用。接下来将该框架移植到stm32单片机运行,单片机只需用1个定时器作为时钟即可。
    友情链接(项目示例):https://download.csdn.net/download/qq_36075612/88498232

    一、开发环境

    1、硬件平台
         STM32F401CEU6
         内部Flash : 512Kbytes,SARM : 96 Kbytes

    二、STM32CubeMx配置

     2.1、系统时钟配置

    2.2、下载调试配置

    2.3、TIM配置(1ms中断)

    2.4、usart1配置

    2.5、生成代码

    2.6、编译工程

    三、编码 

    1、usart.c添加打印

    1. /* USER CODE BEGIN 1 */
    2. #include "stdio.h"
    3. #ifdef __GNUC__
    4. /* With GCC/RAISONANCE, small printf (option LD Linker->Libraries->Small printf
    5. set to 'Yes') calls __io_putchar() */
    6. #define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
    7. #else
    8. #define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
    9. #endif /* __GNUC__ */
    10. /**
    11. * @brief Retargets the C library printf function to the USART.
    12. * @param None
    13. * @retval None
    14. */
    15. PUTCHAR_PROTOTYPE
    16. {
    17. /* Place your implementation of fputc here */
    18. /* e.g. write a character to the EVAL_COM1 and Loop until the end of transmission */
    19. HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xFFFF);
    20. return ch;
    21. }
    22. int fgetc(FILE * f)
    23. {
    24. uint8_t ch = 0;
    25. HAL_UART_Receive(&huart1, (uint8_t *)&ch, 1, 0xffff);
    26. return ch;
    27. }
    28. /* USER CODE END 1 */

    2、tim1.c

    1. /* USER CODE BEGIN Header */
    2. /**
    3. ******************************************************************************
    4. * @file tim.c
    5. * @brief This file provides code for the configuration
    6. * of the TIM instances.
    7. ******************************************************************************
    8. * @attention
    9. *
    10. * Copyright (c) 2023 STMicroelectronics.
    11. * All rights reserved.
    12. *
    13. * This software is licensed under terms that can be found in the LICENSE file
    14. * in the root directory of this software component.
    15. * If no LICENSE file comes with this software, it is provided AS-IS.
    16. *
    17. ******************************************************************************
    18. */
    19. /* USER CODE END Header */
    20. /* Includes ------------------------------------------------------------------*/
    21. #include "tim.h"
    22. /* USER CODE BEGIN 0 */
    23. /* USER CODE END 0 */
    24. TIM_HandleTypeDef htim1;
    25. /* TIM1 init function */
    26. void MX_TIM1_Init(void)
    27. {
    28. /* USER CODE BEGIN TIM1_Init 0 */
    29. /* USER CODE END TIM1_Init 0 */
    30. TIM_ClockConfigTypeDef sClockSourceConfig = {0};
    31. TIM_MasterConfigTypeDef sMasterConfig = {0};
    32. /* USER CODE BEGIN TIM1_Init 1 */
    33. /* USER CODE END TIM1_Init 1 */
    34. htim1.Instance = TIM1;
    35. htim1.Init.Prescaler = 84-1;
    36. htim1.Init.CounterMode = TIM_COUNTERMODE_UP;
    37. htim1.Init.Period = 1000-1;
    38. htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
    39. htim1.Init.RepetitionCounter = 0;
    40. htim1.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
    41. if (HAL_TIM_Base_Init(&htim1) != HAL_OK)
    42. {
    43. Error_Handler();
    44. }
    45. sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
    46. if (HAL_TIM_ConfigClockSource(&htim1, &sClockSourceConfig) != HAL_OK)
    47. {
    48. Error_Handler();
    49. }
    50. sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
    51. sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
    52. if (HAL_TIMEx_MasterConfigSynchronization(&htim1, &sMasterConfig) != HAL_OK)
    53. {
    54. Error_Handler();
    55. }
    56. /* USER CODE BEGIN TIM1_Init 2 */
    57. /* USER CODE END TIM1_Init 2 */
    58. }
    59. void HAL_TIM_Base_MspInit(TIM_HandleTypeDef* tim_baseHandle)
    60. {
    61. if(tim_baseHandle->Instance==TIM1)
    62. {
    63. /* USER CODE BEGIN TIM1_MspInit 0 */
    64. /* USER CODE END TIM1_MspInit 0 */
    65. /* TIM1 clock enable */
    66. __HAL_RCC_TIM1_CLK_ENABLE();
    67. /* TIM1 interrupt Init */
    68. HAL_NVIC_SetPriority(TIM1_UP_TIM10_IRQn, 0, 0);
    69. HAL_NVIC_EnableIRQ(TIM1_UP_TIM10_IRQn);
    70. /* USER CODE BEGIN TIM1_MspInit 1 */
    71. /* USER CODE END TIM1_MspInit 1 */
    72. }
    73. }
    74. void HAL_TIM_Base_MspDeInit(TIM_HandleTypeDef* tim_baseHandle)
    75. {
    76. if(tim_baseHandle->Instance==TIM1)
    77. {
    78. /* USER CODE BEGIN TIM1_MspDeInit 0 */
    79. /* USER CODE END TIM1_MspDeInit 0 */
    80. /* Peripheral clock disable */
    81. __HAL_RCC_TIM1_CLK_DISABLE();
    82. /* TIM1 interrupt Deinit */
    83. HAL_NVIC_DisableIRQ(TIM1_UP_TIM10_IRQn);
    84. /* USER CODE BEGIN TIM1_MspDeInit 1 */
    85. /* USER CODE END TIM1_MspDeInit 1 */
    86. }
    87. }
    88. /* USER CODE BEGIN 1 */
    89. #include "stdio.h"
    90. int timeCount = 0;
    91. void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim){
    92. if(htim->Instance == TIM1){
    93. timeCount++;
    94. if(timeCount==1000){
    95. timeCount = 0;
    96. printf("time + 1s\n");
    97. }
    98. }
    99. }
    100. /* USER CODE END 1 */

    3、在根目录创建timeslice文件夹,分别有list.c、list.h、thread_demo.c、thread_demo.h、timeslice.c、timeslice.h文件组成

    1)、list.c

    1. #include "list.h"
    2. void list_init(ListObj* list)
    3. {
    4. list->next = list->prev = list;
    5. }
    6. void list_insert_after(ListObj* list, ListObj* node)
    7. {
    8. list->next->prev = node;
    9. node->next = list->next;
    10. list->next = node;
    11. node->prev = list;
    12. }
    13. void list_insert_before(ListObj* list, ListObj* node)
    14. {
    15. list->prev->next = node;
    16. node->prev = list->prev;
    17. list->prev = node;
    18. node->next = list;
    19. }
    20. void list_remove(ListObj* node)
    21. {
    22. node->next->prev = node->prev;
    23. node->prev->next = node->next;
    24. node->next = node->prev = node;
    25. }
    26. int list_isempty(const ListObj* list)
    27. {
    28. return list->next == list;
    29. }
    30. unsigned int list_len(const ListObj* list)
    31. {
    32. unsigned int len = 0;
    33. const ListObj* p = list;
    34. while (p->next != list)
    35. {
    36. p = p->next;
    37. len++;
    38. }
    39. return len;
    40. }

    2)、list.h

    1. #ifndef _LIST_H
    2. #define _LIST_H
    3. #define offset_of(type, member) (unsigned long) &((type*)0)->member
    4. #define container_of(ptr, type, member) ((type *)((char *)(ptr) - offset_of(type, member)))
    5. typedef struct list_structure
    6. {
    7. struct list_structure* next;
    8. struct list_structure* prev;
    9. } ListObj;
    10. #define LIST_HEAD_INIT(name) {&(name), &(name)}
    11. #define LIST_HEAD(name) ListObj name = LIST_HEAD_INIT(name)
    12. void list_init(ListObj* list);
    13. void list_insert_after(ListObj* list, ListObj* node);
    14. void list_insert_before(ListObj* list, ListObj* node);
    15. void list_remove(ListObj* node);
    16. int list_isempty(const ListObj* list);
    17. unsigned int list_len(const ListObj* list);
    18. #define list_entry(node, type, member) \
    19. container_of(node, type, member)
    20. #define list_for_each(pos, head) \
    21. for (pos = (head)->next; pos != (head); pos = pos->next)
    22. #define list_for_each_safe(pos, n, head) \
    23. for (pos = (head)->next, n = pos->next; pos != (head); \
    24. pos = n, n = pos->next)
    25. #endif

    3)、timeslice.c

    1. #include "timeslice.h"
    2. static LIST_HEAD(timeslice_task_list);
    3. void timeslice_exec(void)
    4. {
    5. ListObj* node;
    6. TimesilceTaskObj* task;
    7. list_for_each(node, ×lice_task_list)
    8. {
    9. task = list_entry(node, TimesilceTaskObj, timeslice_task_list);
    10. if (task->is_run == TASK_RUN)
    11. {
    12. task->task_hdl();
    13. task->is_run = TASK_STOP;
    14. }
    15. }
    16. }
    17. void timeslice_tick(void)
    18. {
    19. ListObj* node;
    20. TimesilceTaskObj* task;
    21. list_for_each(node, ×lice_task_list)
    22. {
    23. task = list_entry(node, TimesilceTaskObj, timeslice_task_list);
    24. if (task->timer != 0)
    25. {
    26. task->timer--;
    27. if (task->timer == 0)
    28. {
    29. task->is_run = TASK_RUN;
    30. task->timer = task->timeslice_len;
    31. }
    32. }
    33. }
    34. }
    35. unsigned int timeslice_get_task_num(void)
    36. {
    37. return list_len(×lice_task_list);
    38. }
    39. void timeslice_task_init(TimesilceTaskObj* obj, void (*task_hdl)(void), unsigned int id, unsigned int timeslice_len)
    40. {
    41. obj->id = id;
    42. obj->is_run = TASK_STOP;
    43. obj->task_hdl = task_hdl;
    44. obj->timer = timeslice_len;
    45. obj->timeslice_len = timeslice_len;
    46. }
    47. void timeslice_task_add(TimesilceTaskObj* obj)
    48. {
    49. list_insert_before(×lice_task_list, &obj->timeslice_task_list);
    50. }
    51. void timeslice_task_del(TimesilceTaskObj* obj)
    52. {
    53. if (timeslice_task_isexist(obj))
    54. list_remove(&obj->timeslice_task_list);
    55. else
    56. return;
    57. }
    58. unsigned char timeslice_task_isexist(TimesilceTaskObj* obj)
    59. {
    60. unsigned char isexist = 0;
    61. ListObj* node;
    62. TimesilceTaskObj* task;
    63. list_for_each(node, ×lice_task_list)
    64. {
    65. task = list_entry(node, TimesilceTaskObj, timeslice_task_list);
    66. if (obj->id == task->id)
    67. isexist = 1;
    68. }
    69. return isexist;
    70. }
    71. unsigned int timeslice_get_task_timeslice_len(TimesilceTaskObj* obj)
    72. {
    73. return obj->timeslice_len;
    74. }

    4)、timeslice.h

    1. #ifndef _TIMESLICE_H
    2. #define _TIMESLICE_H
    3. #include "list.h"
    4. typedef enum {
    5. TASK_STOP,
    6. TASK_RUN
    7. } IsTaskRun;
    8. typedef struct timesilce
    9. {
    10. unsigned int id;
    11. void (*task_hdl)(void);
    12. IsTaskRun is_run;
    13. unsigned int timer;
    14. unsigned int timeslice_len;
    15. ListObj timeslice_task_list;
    16. } TimesilceTaskObj;
    17. void timeslice_exec(void);
    18. void timeslice_tick(void);
    19. void timeslice_task_init(TimesilceTaskObj* obj, void (*task_hdl)(void), unsigned int id, unsigned int timeslice_len);
    20. void timeslice_task_add(TimesilceTaskObj* obj);
    21. void timeslice_task_del(TimesilceTaskObj* obj);
    22. unsigned int timeslice_get_task_timeslice_len(TimesilceTaskObj* obj);
    23. unsigned int timeslice_get_task_num(void);
    24. unsigned char timeslice_task_isexist(TimesilceTaskObj* obj);
    25. #endif

    5)、task_demo.c

    1. #include
    2. #include "timeslice.h"
    3. #include "gpio.h"
    4. // 创建5个任务对象
    5. TimesilceTaskObj task_1, task_2, task_3, task_4, task_5, task_led;
    6. // 具体的任务函数
    7. void task1_hdl(void)
    8. {
    9. printf(">> task 1 is running ...\n");
    10. }
    11. void task2_hdl(void)
    12. {
    13. printf(">> task 2 is running ...\n");
    14. }
    15. void task3_hdl(void)
    16. {
    17. printf(">> task 3 is running ...\n");
    18. }
    19. void task4_hdl(void)
    20. {
    21. printf(">> task 4 is running ...\n");
    22. }
    23. void task5_hdl(void)
    24. {
    25. printf(">> task 5 is running ...\n");
    26. }
    27. void led_hd1(void)
    28. {
    29. HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);
    30. }
    31. // 初始化任务对象,并且将任务添加到时间片轮询调度中
    32. void task_init(void)
    33. {
    34. timeslice_task_init(&task_1, task1_hdl, 1, 10);
    35. timeslice_task_init(&task_2, task2_hdl, 2, 20);
    36. timeslice_task_init(&task_3, task3_hdl, 3, 30);
    37. timeslice_task_init(&task_4, task4_hdl, 4, 40);
    38. timeslice_task_init(&task_5, task5_hdl, 5, 50);
    39. timeslice_task_init(&task_led, led_hd1, 6, 1000);
    40. timeslice_task_add(&task_1);
    41. timeslice_task_add(&task_2);
    42. timeslice_task_add(&task_3);
    43. timeslice_task_add(&task_4);
    44. timeslice_task_add(&task_5);
    45. timeslice_task_add(&task_led);
    46. }
    47. void task_run(void)
    48. {
    49. task_init();
    50. printf(">> task num: %d\n", timeslice_get_task_num());
    51. printf(">> task len: %d\n", timeslice_get_task_timeslice_len(&task_3));
    52. timeslice_task_del(&task_2);
    53. printf(">> delet task 2\n");
    54. printf(">> task 2 is exist: %d\n", timeslice_task_isexist(&task_2));
    55. printf(">> task num: %d\n", timeslice_get_task_num());
    56. timeslice_task_del(&task_5);
    57. printf(">> delet task 5\n");
    58. printf(">> task num: %d\n", timeslice_get_task_num());
    59. printf(">> task 3 is exist: %d\n", timeslice_task_isexist(&task_3));
    60. timeslice_task_add(&task_2);
    61. printf(">> add task 2\n");
    62. printf(">> task 2 is exist: %d\n", timeslice_task_isexist(&task_2));
    63. timeslice_task_add(&task_5);
    64. printf(">> add task 5\n");
    65. printf(">> task num: %d\n", timeslice_get_task_num());
    66. printf("\n\n========timeslice running===========\n");
    67. while(1)
    68. {
    69. timeslice_exec();
    70. }
    71. }

    6)、task_demo.h

    1. #ifndef _TASK_DEMO_H
    2. #define _TASK_DEMO_H
    3. void task_run(void);
    4. #endif

    4、main.c

    1. /* USER CODE BEGIN Header */
    2. /**
    3. ******************************************************************************
    4. * @file : main.c
    5. * @brief : Main program body
    6. ******************************************************************************
    7. * @attention
    8. *
    9. * Copyright (c) 2023 STMicroelectronics.
    10. * All rights reserved.
    11. *
    12. * This software is licensed under terms that can be found in the LICENSE file
    13. * in the root directory of this software component.
    14. * If no LICENSE file comes with this software, it is provided AS-IS.
    15. *
    16. ******************************************************************************
    17. */
    18. /* USER CODE END Header */
    19. /* Includes ------------------------------------------------------------------*/
    20. #include "main.h"
    21. #include "tim.h"
    22. #include "usart.h"
    23. #include "gpio.h"
    24. /* Private includes ----------------------------------------------------------*/
    25. /* USER CODE BEGIN Includes */
    26. #include "stdio.h"
    27. #include "task_demo.h"
    28. /* USER CODE END Includes */
    29. /* Private typedef -----------------------------------------------------------*/
    30. /* USER CODE BEGIN PTD */
    31. /* USER CODE END PTD */
    32. /* Private define ------------------------------------------------------------*/
    33. /* USER CODE BEGIN PD */
    34. /* USER CODE END PD */
    35. /* Private macro -------------------------------------------------------------*/
    36. /* USER CODE BEGIN PM */
    37. /* USER CODE END PM */
    38. /* Private variables ---------------------------------------------------------*/
    39. /* USER CODE BEGIN PV */
    40. /* USER CODE END PV */
    41. /* Private function prototypes -----------------------------------------------*/
    42. void SystemClock_Config(void);
    43. /* USER CODE BEGIN PFP */
    44. /* USER CODE END PFP */
    45. /* Private user code ---------------------------------------------------------*/
    46. /* USER CODE BEGIN 0 */
    47. /* USER CODE END 0 */
    48. /**
    49. * @brief The application entry point.
    50. * @retval int
    51. */
    52. int main(void)
    53. {
    54. /* USER CODE BEGIN 1 */
    55. /* USER CODE END 1 */
    56. /* MCU Configuration--------------------------------------------------------*/
    57. /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
    58. HAL_Init();
    59. /* USER CODE BEGIN Init */
    60. /* USER CODE END Init */
    61. /* Configure the system clock */
    62. SystemClock_Config();
    63. /* USER CODE BEGIN SysInit */
    64. /* USER CODE END SysInit */
    65. /* Initialize all configured peripherals */
    66. MX_GPIO_Init();
    67. MX_USART1_UART_Init();
    68. MX_TIM1_Init();
    69. /* USER CODE BEGIN 2 */
    70. HAL_TIM_Base_Start_IT(&htim1);
    71. printf("heihei\r\n");
    72. task_run();
    73. /* USER CODE END 2 */
    74. /* Infinite loop */
    75. /* USER CODE BEGIN WHILE */
    76. while (1)
    77. {
    78. /* USER CODE END WHILE */
    79. /* USER CODE BEGIN 3 */
    80. }
    81. /* USER CODE END 3 */
    82. }
    83. /**
    84. * @brief System Clock Configuration
    85. * @retval None
    86. */
    87. void SystemClock_Config(void)
    88. {
    89. RCC_OscInitTypeDef RCC_OscInitStruct = {0};
    90. RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
    91. /** Configure the main internal regulator output voltage
    92. */
    93. __HAL_RCC_PWR_CLK_ENABLE();
    94. __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE2);
    95. /** Initializes the RCC Oscillators according to the specified parameters
    96. * in the RCC_OscInitTypeDef structure.
    97. */
    98. RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
    99. RCC_OscInitStruct.HSEState = RCC_HSE_ON;
    100. RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
    101. RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
    102. RCC_OscInitStruct.PLL.PLLM = 25;
    103. RCC_OscInitStruct.PLL.PLLN = 168;
    104. RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
    105. RCC_OscInitStruct.PLL.PLLQ = 4;
    106. if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
    107. {
    108. Error_Handler();
    109. }
    110. /** Initializes the CPU, AHB and APB buses clocks
    111. */
    112. RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
    113. |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
    114. RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
    115. RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
    116. RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
    117. RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
    118. if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
    119. {
    120. Error_Handler();
    121. }
    122. }
    123. /* USER CODE BEGIN 4 */
    124. /* USER CODE END 4 */
    125. /**
    126. * @brief This function is executed in case of error occurrence.
    127. * @retval None
    128. */
    129. void Error_Handler(void)
    130. {
    131. /* USER CODE BEGIN Error_Handler_Debug */
    132. /* User can add his own implementation to report the HAL error return state */
    133. __disable_irq();
    134. while (1)
    135. {
    136. }
    137. /* USER CODE END Error_Handler_Debug */
    138. }
    139. #ifdef USE_FULL_ASSERT
    140. /**
    141. * @brief Reports the name of the source file and the source line number
    142. * where the assert_param error has occurred.
    143. * @param file: pointer to the source file name
    144. * @param line: assert_param error line source number
    145. * @retval None
    146. */
    147. void assert_failed(uint8_t *file, uint32_t line)
    148. {
    149. /* USER CODE BEGIN 6 */
    150. /* User can add his own implementation to report the file name and line number,
    151. ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
    152. /* USER CODE END 6 */
    153. }
    154. #endif /* USE_FULL_ASSERT */

    分别添加以下6个文件:list.c、list.h、task_demo.c、task_demo.h、timeslice.c、timeslice.h。同时,把timslice目录下的.h文件包含进项目中来,如下所示:

    注意:keil上需要勾选Use MicroLIB,否则CubeMX生成的串口工程无法打印问题

    四、运行结果

    五、代码解释

    时间片轮询架构

    其实该部分主要使用了面向对象的思维,使用结构体作为对象,并使用结构体指针作为参数传递,这样作可以节省资源,并且有着极高的运行效率。

    其中最难的部分是侵入式链表的使用,这种链表在一些操作系统内核中使用十分广泛,这里是参考RT-Thread实时操作系统中的侵入式链表实现。

    底层侵入式双向链表

    该链表是linux内核中使用十分广泛,也十分经典,其原理具体可以参考文章:

    https://www.cnblogs.com/skywang12345/p/3562146.html

    六、总结

            好了,终于介绍完毕,以后裸机开发,有了此时间片论法,如虎添翼。感谢各位同仁参阅。

    参考文章:

    1、单片机面向对象思维的架构:时间轮片法_strongerHuang的博客-CSDN博客 

    2、【嵌入式开源库】timeslice的使用,完全解耦的时间片轮询框架构-CSDN博客

  • 相关阅读:
    TCP通信流程详解
    mmdetection3d SUN RGB-D数据集预处理
    动态加权平衡损失:深度神经网络的类不平衡学习和置信度校准
    HCIA网络基础10-交换网络及STP
    codeforces每日5题(均1600)-第二十八天
    我的创作纪念日
    国际市场中的量子计算软件平台研究中心与部署重心介绍
    9/7 dp练习+01背包方案数+求背包具体方案
    Object.defineProperty()
    独立开发者学习的技术栈
  • 原文地址:https://blog.csdn.net/qq_36075612/article/details/134192847