嵌入式RTOS编程

RTOS简介

首先我们要了解一下RTOS,也就是实时操作系统。

首先呢,单片机编程分为裸机系统和多任务系统

裸机系统

裸机系统通常分成轮询系统和前后台系统

轮询系统

轮询系统即是在裸机编程的时候,先初始化好相关的硬件,然后让主程序在一个死循环里面不断循环, 顺序地做各种事情。轮询系统是一种非常简单的软件结构, 通常只适用于那些只需要顺序执行代码且不需要外部事件来驱动的就能完成的事情。 比如LED翻转,串口输出,液晶显示等这些操作。

举个例子,就好比你需要工作人员帮你处理一件特别的事情,但是呢你排队排在后面,并不能获得工作人员的帮助。等轮到你的时候,你的那件事情已经结束了。所以这种系统实用性很低。

前后台系统

在轮询系统的基础上加入了中断,还是之前那个例子。你可以大声呼喊让工作人员提前处理你的事情,其余的人就等待。这就是我们一般用的中断,让当前执行的程序暂停保存,先去处理中断事件。这个还可以根据中断的处理时长分为在中断函数(前台)里处理和回到主程序(后台)处理。这样也可以避免中断触发情况多的情况,前台处理不过来。

多任务系统

这个和前后台差不多,但是这个里面我们运行处理的不再是一个无限循环的程序了。而是多个无限循环不反悔的函数小程序也就是任务,而且我们处理中断事件不再放到中断里面处理了,被标记优先级

后根据对应任务的优先级去选择处理,例如

int flag1 = 0;
int flag2 = 0;
int flag3 = 0;

int main(void)
{
    /* 硬件相关初始化 */
    HardWareInit();

    /* OS初始化 */
    RTOSInit();

    /* OS启动,开始多任务调度,不再返回 */
    RTOSStart();
}

void ISR1(void)
{
    /* 置位标志位 */
    flag1 = 1;
}

void ISR2(void)
{
    /* 置位标志位 */
    flag2 = 2;
}

void ISR3(void)
{
    /* 置位标志位 */
    flag3 = 1;
}

voidDoSomething1(void)
{
    /* 无限循环,不能返回 */
    for (;;)
    {
        /* 任务实体 */
        if (flag1)
        {
        }
    }
}

voidDoSomething2(void)
{
    /* 无限循环,不能返回 */
    for (;;)
    {
        /* 任务实体 */
        if (flag2)
        {
        }
    }
}

voidDoSomething3(void)
{
    /* 无限循环,不能返回 */
    for (;;)
    {
        /* 任务实体 */
        if (flag3)
        {
        }
    }
}

flag1的优先级最高,就最先处理它。至于任务怎么运行的先不解释。

这个内容很多,建议去看https://doc.embedfire.com/rtos/ucos/zh/latest/zero_to_one/multi_task.html,里面说的很详细。

接下来我主要介绍一下UOSIII实际工程上的一些应用

UOSIII移植及应用
UOSIII下载

首先要去下载UOSIII的源码,这个随便搜一下就找的到

http://micrium.com/downloadcenter/

然后下载下来应该是这样的(文件夹uC-BSP和uC-CONFIG是自己新建的两个文件夹,自行修改)
在这里插入图片描述

这里简单解释一下各个文件夹名称的含义

uC-CPU:用于存放μC/OS-III根据CPU总结的通用代码,只与CPU相关

uC-LIB:用于存放一些C语言函数库

uCOS-III: source 用于存放μC/OS-III源码,Ports用于存放接口文件

新建工程

这里建议用HAL库,提醒一下stm32 cube ide 不能改编译环境为keil,不熟悉ide添加文件建议用stm cube mx。

首先是通用配置
在这里插入图片描述

RCC和SYS大家都知道就不解释了

然后我这里需要串口发送数据,需要打开一个串口

在这里插入图片描述

然后改编译环境

在这里插入图片描述

uos移植
1.为uC-BSP文件夹新建bsp.c和bsp.h文件
2.给文件夹uC-CONFIG添加以下文件(从以下路径复制过来)

在这里插入图片描述

3.将uCOS相关文件复制到HAL工程的MDK-ARM文件夹下

在这里插入图片描述
在这里插入图片描述

4.添加到工程路径之中

点击Manage Project Items

在这里插入图片描述

新建文件夹

在这里插入图片描述

这里就是把之前的文件加入到工程

点击CPU–>Add Files…,选中以下文件,Add
在这里插入图片描述
在这里插入图片描述

点击LIB–>Add Files…,选中以下文件,Add

在这里插入图片描述
在这里插入图片描述

点击PORT–>Add Files…,选中以下文件,Add

在这里插入图片描述

点击SOURCE–>Add Files…,选中以下文件,Add

在这里插入图片描述

点击CONFIG–>Add Files…,选中以下文件,Add

在这里插入图片描述

点击BSP–>Add Files…,选中以下文件,Add

在这里插入图片描述

加入完成之后导入文件路径
在这里插入图片描述
这个简称为魔法棒

在这里插入图片描述

点击include Paths

在这里插入图片描述

为bsp.c和bsp.h添加代码

bsp.h

// bsp.h
#ifndef  __BSP_H__
#define  __BSP_H__

#include "stm32f1xx_hal.h"

void BSP_Init(void);

#endif

bsp.c

// bsp.c
#include "includes.h"

#define  DWT_CR      *(CPU_REG32 *)0xE0001000
#define  DWT_CYCCNT  *(CPU_REG32 *)0xE0001004
#define  DEM_CR      *(CPU_REG32 *)0xE000EDFC
#define  DBGMCU_CR   *(CPU_REG32 *)0xE0042004

#define  DEM_CR_TRCENA                   (1 << 24)
#define  DWT_CR_CYCCNTENA                (1 <<  0)

CPU_INT32U  BSP_CPU_ClkFreq (void)
{
    return HAL_RCC_GetHCLKFreq();
}

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);
}



void BSP_Init(void)
{
	BSP_Tick_Init();
	MX_GPIO_Init();
}


#if (CPU_CFG_TS_TMR_EN == DEF_ENABLED)
void  CPU_TS_TmrInit (void)
{
    CPU_INT32U  cpu_clk_freq_hz;


    DEM_CR         |= (CPU_INT32U)DEM_CR_TRCENA;                /* Enable Cortex-M3's DWT CYCCNT reg.                   */
    DWT_CYCCNT      = (CPU_INT32U)0u;
    DWT_CR         |= (CPU_INT32U)DWT_CR_CYCCNTENA;

    cpu_clk_freq_hz = BSP_CPU_ClkFreq();
    CPU_TS_TmrFreqSet(cpu_clk_freq_hz);
}
#endif


#if (CPU_CFG_TS_TMR_EN == DEF_ENABLED)
CPU_TS_TMR  CPU_TS_TmrRd (void)
{
    return ((CPU_TS_TMR)DWT_CYCCNT);
}
#endif


#if (CPU_CFG_TS_32_EN == DEF_ENABLED)
CPU_INT64U  CPU_TS32_to_uSec (CPU_TS32  ts_cnts)
{
	CPU_INT64U  ts_us;
  CPU_INT64U  fclk_freq;

 
  fclk_freq = BSP_CPU_ClkFreq();
  ts_us     = ts_cnts / (fclk_freq / DEF_TIME_NBR_uS_PER_SEC);

  return (ts_us);
}
#endif
 
 
#if (CPU_CFG_TS_64_EN == DEF_ENABLED)
CPU_INT64U  CPU_TS64_to_uSec (CPU_TS64  ts_cnts)
{
	CPU_INT64U  ts_us;
	CPU_INT64U  fclk_freq;


  fclk_freq = BSP_CPU_ClkFreq();
  ts_us     = ts_cnts / (fclk_freq / DEF_TIME_NBR_uS_PER_SEC);
	
  return (ts_us);
}
#endif

修改其余文件

在这里插入图片描述

在以下位置处将PendSV_Handler和SysTick_Handler改为OS_CPU_PendSVHandler和OS_CPU_SysTickHandler

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述 在这里插入图片描述
在文件includes.h中添加头文件引用

在这里插入图片描述

找到lib_cfg.h

更改堆空间的大小,根据自己的芯片调整

在这里插入图片描述
有什么不清楚的看这篇博客

https://blog.csdn.net/qq_45659777/article/details/121570886

功能实现main函数

这里我要实现的是两个task分别以1s和3s周期对LED灯进行点亮-熄灭的控制;另外一个task以2s周期通过串口发送“hello uc/OS! 欢迎来到RTOS多任务环境!”

先放代码

main.c

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* 任务优先级 */
#define START_TASK_PRIO		3
#define LED0_TASK_PRIO		4
#define MSG_TASK_PRIO		5
#define LED1_TASK_PRIO		6

/* 任务堆栈大小	*/
#define START_STK_SIZE 		96
#define LED0_STK_SIZE 		64
#define MSG_STK_SIZE 		64
#define LED1_STK_SIZE 		64

/* 任务栈 */	
CPU_STK START_TASK_STK[START_STK_SIZE];
CPU_STK LED0_TASK_STK[LED0_STK_SIZE];
CPU_STK MSG_TASK_STK[MSG_STK_SIZE];
CPU_STK LED1_TASK_STK[LED1_STK_SIZE];

/* 任务控制块 */
OS_TCB StartTaskTCB;
OS_TCB Led0TaskTCB;
OS_TCB MsgTaskTCB;
OS_TCB Led1TaskTCB;

/* USER CODE END PD */

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

/* USER CODE END PM */

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

/* USER CODE BEGIN PV */

/* 任务函数定义 */
void start_task(void *p_arg);
static  void  AppTaskCreate(void);
static  void  AppObjCreate(void);
static  void  led_pc13(void *p_arg);
static  void  send_msg(void *p_arg);
static  void  led_pa3(void *p_arg);
/* 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 */
/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  /**Initializes the CPU, AHB and APB busses clocks 
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }
  /**Initializes the CPU, AHB and APB busses 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_DIV2;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

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

/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
	OS_ERR  err;
	OSInit(&err);
  HAL_Init();
	SystemClock_Config();
	//MX_GPIO_Init(); 这个在BSP的初始化里也会初始化
  MX_USART1_UART_Init();	
	/* 创建任务 */
	OSTaskCreate((OS_TCB     *)&StartTaskTCB,                /* Create the start task                                */
				 (CPU_CHAR   *)"start task",
				 (OS_TASK_PTR ) start_task,   
				 (void       *) 0,
				 (OS_PRIO     ) START_TASK_PRIO,
				 (CPU_STK    *)&START_TASK_STK[0],
				 (CPU_STK_SIZE) START_STK_SIZE/10,
				 (CPU_STK_SIZE) START_STK_SIZE,
				 (OS_MSG_QTY  ) 0,
				 (OS_TICK     ) 0,
				 (void       *) 0,
				 (OS_OPT      )(OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR),
				 (OS_ERR     *)&err);
	/* 启动多任务系统,控制权交给uC/OS-III */
	OSStart(&err);            /* Start multitasking (i.e. give control to uC/OS-III). */
               
}


void start_task(void *p_arg)
{
	OS_ERR err;
	CPU_SR_ALLOC();
	p_arg = p_arg;
	
	/* YangJie add 2021.05.20*/
  BSP_Init();                                                   /* Initialize BSP functions */
  //CPU_Init();
  //Mem_Init();                                                 /* Initialize Memory Management Module */

#if OS_CFG_STAT_TASK_EN > 0u
   OSStatTaskCPUUsageInit(&err);  		//统计任务                
#endif
	
#ifdef CPU_CFG_INT_DIS_MEAS_EN			//如果使能了测量中断关闭时间
    CPU_IntDisMeasMaxCurReset();	
#endif

#if	OS_CFG_SCHED_ROUND_ROBIN_EN  		//当使用时间片轮转的时候
	 //使能时间片轮转调度功能,时间片长度为1个系统时钟节拍,既1*5=5ms
	OSSchedRoundRobinCfg(DEF_ENABLED,1,&err);  
#endif		
	
	OS_CRITICAL_ENTER();	//进入临界区
	/* 创建LED0任务 */
	OSTaskCreate((OS_TCB 	* )&Led0TaskTCB,		
				 (CPU_CHAR	* )"led_pc13", 		
                 (OS_TASK_PTR )led_pc13, 			
                 (void		* )0,					
                 (OS_PRIO	  )LED0_TASK_PRIO,     
                 (CPU_STK   * )&LED0_TASK_STK[0],	
                 (CPU_STK_SIZE)LED0_STK_SIZE/10,	
                 (CPU_STK_SIZE)LED0_STK_SIZE,		
                 (OS_MSG_QTY  )0,					
                 (OS_TICK	  )0,					
                 (void   	* )0,					
                 (OS_OPT      )OS_OPT_TASK_STK_CHK|OS_OPT_TASK_STK_CLR,
                 (OS_ERR 	* )&err);		

/* 创建LED1任务 */
	OSTaskCreate((OS_TCB 	* )&Led1TaskTCB,		
				 (CPU_CHAR	* )"led_pa3", 		
                 (OS_TASK_PTR )led_pa3, 			
                 (void		* )0,					
                 (OS_PRIO	  )LED1_TASK_PRIO,     
                 (CPU_STK   * )&LED1_TASK_STK[0],	
                 (CPU_STK_SIZE)LED1_STK_SIZE/10,	
                 (CPU_STK_SIZE)LED1_STK_SIZE,		
                 (OS_MSG_QTY  )0,					
                 (OS_TICK	  )0,					
                 (void   	* )0,					
                 (OS_OPT      )OS_OPT_TASK_STK_CHK|OS_OPT_TASK_STK_CLR,
                 (OS_ERR 	* )&err);										 
				 
	/* 创建MSG任务 */
	OSTaskCreate((OS_TCB 	* )&MsgTaskTCB,		
				 (CPU_CHAR	* )"send_msg", 		
                 (OS_TASK_PTR )send_msg, 			
                 (void		* )0,					
                 (OS_PRIO	  )MSG_TASK_PRIO,     	
                 (CPU_STK   * )&MSG_TASK_STK[0],	
                 (CPU_STK_SIZE)MSG_STK_SIZE/10,	
                 (CPU_STK_SIZE)MSG_STK_SIZE,		
                 (OS_MSG_QTY  )0,					
                 (OS_TICK	  )0,					
                 (void   	* )0,				
                 (OS_OPT      )OS_OPT_TASK_STK_CHK|OS_OPT_TASK_STK_CLR, 
                 (OS_ERR 	* )&err);
				 
	OS_TaskSuspend((OS_TCB*)&StartTaskTCB,&err);		//挂起开始任务			 
	OS_CRITICAL_EXIT();	//进入临界区
}
/**
  * 函数功能: 启动任务函数体。
  * 输入参数: p_arg 是在创建该任务时传递的形参
  * 返 回 值: 无
  * 说    明:无
  */
static  void  led_pc13 (void *p_arg)
{
  OS_ERR      err;

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

  while (DEF_TRUE)
  {
		HAL_GPIO_WritePin(GPIOC,GPIO_PIN_13,GPIO_PIN_RESET);
		OSTimeDlyHMSM(0, 0, 1, 0,OS_OPT_TIME_HMSM_STRICT,&err);
		HAL_GPIO_WritePin(GPIOC,GPIO_PIN_13,GPIO_PIN_SET);
		OSTimeDlyHMSM(0, 0, 1, 0,OS_OPT_TIME_HMSM_STRICT,&err);
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

static  void  led_pa3 (void *p_arg)
{
  OS_ERR      err;

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

  while (DEF_TRUE)
  {
		HAL_GPIO_WritePin(GPIOA,GPIO_PIN_3,GPIO_PIN_RESET);
		OSTimeDlyHMSM(0, 0, 3, 0,OS_OPT_TIME_HMSM_STRICT,&err);
		HAL_GPIO_WritePin(GPIOA,GPIO_PIN_3,GPIO_PIN_SET);
		OSTimeDlyHMSM(0, 0, 3, 0,OS_OPT_TIME_HMSM_STRICT,&err);
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

static  void  send_msg (void *p_arg)
{
  OS_ERR      err;

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

  while (DEF_TRUE)
  {
			printf("hello uc/OS \r\n");
		OSTimeDlyHMSM(0, 0, 2, 0,OS_OPT_TIME_HMSM_STRICT,&err);
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}


/* USER CODE BEGIN 4 */
/**
  * 函数功能: 创建应用任务
  * 输入参数: p_arg 是在创建该任务时传递的形参
  * 返 回 值: 无
  * 说    明:无
  */
static  void  AppTaskCreate (void)
{
  
}


/**
  * 函数功能: uCOSIII内核对象创建
  * 输入参数: 无
  * 返 回 值: 无
  * 说    明:无
  */
static  void  AppObjCreate (void)
{

}

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

  /* 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,
     tex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
  /* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */

/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

这里简单解释一下这个程序,帮助大家读懂这个程序

首先我们要理解一个任务会在它申请的一块地址运行,多个任务互不干扰。cpu会不停的切换任务(怎么切换取决于你的配置)这就导致你看起来,他们像是在一起运行一样。所以我们创建任务就需要申请空间OSTaskCreate函数就是实现这个功能的,其他的东西我们先不管,一个空间需要一个地址,一个大小

(CPU_STK *)就是取地地址,CPU_STK_SIZE就是申请的空间大小。然后我们需要一个函数来作为这边空间的任务入口OS_TASK_PTR定义的就是函数入口。一般任务都有以下结构

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aIAlM9Zl-1667805180172)(…/AppData/Roaming/Typora/typora-user-images/image-20221107144714579.png)]

然后再本程序当中先创建了一个任务,然后在这个任务中再次创建了三个任务,这个最开始的任务为起始任务。然后里面的三个任务我们就很清楚了,是我们要实现的三个功能。

点灯,串口这些都不难,主要的是控制每个任务的时间,大家可以选择用延时函数来实现

OSTimeDlyHMSM这个函数就是实现这个功能的,其实这里调用的是一个空闲任务。可以去学习一下UOSIII中的那些任务。

e file name and line number,
tex: printf(“Wrong parameters value: file %s on line %d\r\n”, file, line) /
/
USER CODE END 6 /
}
#endif /
USE_FULL_ASSERT */

/************************ © COPYRIGHT STMicroelectronics *END OF FILE/


这里简单解释一下这个程序,帮助大家读懂这个程序

首先我们要理解一个任务会在它申请的一块地址运行,多个任务互不干扰。cpu会不停的切换任务(怎么切换取决于你的配置)这就导致你看起来,他们像是在一起运行一样。所以我们创建任务就需要申请空间OSTaskCreate函数就是实现这个功能的,其他的东西我们先不管,一个空间需要一个地址,一个大小

(CPU_STK    *)就是取地地址,CPU_STK_SIZE就是申请的空间大小。然后我们需要一个函数来作为这边空间的任务入口OS_TASK_PTR定义的就是函数入口。一般任务都有以下结构

[外链图片转存中...(img-aIAlM9Zl-1667805180172)]	

然后再本程序当中先创建了一个任务,然后在这个任务中再次创建了三个任务,这个最开始的任务为起始任务。然后里面的三个任务我们就很清楚了,是我们要实现的三个功能。

点灯,串口这些都不难,主要的是控制每个任务的时间,大家可以选择用延时函数来实现

OSTimeDlyHMSM这个函数就是实现这个功能的,其实这里调用的是一个空闲任务。可以去学习一下UOSIII中的那些任务。

<img src="QQ图片20221107150836.gif" alt="QQ图片20221107150836" style="zoom:67%;" />	
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值