FreeRTOS入门(二)

目录

什么是RTOS?

嵌入式有哪些常见的RTOS?

✓ VxWorks(开源收费)

✓ UCOSII&III(开源免费)

✓ FreeRTOS(开源免费)

✓ RT_Thread(开源免费)

✓ AliOS(开源收费)

✓ LiteOS

FreeRTOS的主要功能列表

任务

任务的创建立

动态创建

静态创建

任务的状态

任务的挂起和恢复

任务的删除

系统延时函数的实现

相对延时函数 

绝对延时函数

小练习

目标

操作过程


---------------------------------------------------------------------------------------------------------------------------------

        之前看韦老师的课程出了一篇Freertos的入门教程,那个全是技术都是操作这次来个系列教程我们从理论出发。

xTaskCreateRestrictedStatic[FreeRTOS-MPU Specific] - FreeRTOS

什么是RTOS?

        Real-time operating system, 最突出的特点:“实时性”
        实时操作系统中都要包含一个实时任务调度器,这个任务调度器与其它操作系统的最大不同是强调:严格按照优先级来分配CPU时间,并且时间片轮转不是实时调度器的一个必选项。
⚫ 实时性:在固定的时间内对事件进行响应,实时并不意味着快
⚫ 操作系统:一种系统软件,提供任务管理和协调的控制功能
⚫ 嵌入式操作系统:功能可裁剪、代码可移植
 

嵌入式有哪些常见的RTOS?

✓ VxWorks(开源收费)

        VxWorks 操作系统是美国WindRiver公司于1983年设计开发的一种嵌入式实时操作系统(RTOS),是嵌入式开发环境的关键组成部分。良好的持续发展能力、高性能的内核以及友好的用户开发环境,在嵌入式实时操作系统领域占据一席之地。它以其良好的可靠性和卓越的实时性被广泛地应用在通信、军事、航空、航天等高精尖技术及实时性要求极高的领域中,如卫星通讯军事演习、弹道制导、飞机导航等。在美国的 F-16、FA-18战斗机、B-2 隐形轰炸机爱国者导弹上,甚至连1997年4月在火星表面登陆的火星探测器、2008年5月登陆的凤凰号,和2012年8月登陆的好奇号也都使用到了VxWorks。


✓ UCOSII&III(开源免费)

这个我前面学习的时候出过相关的文章,Silicon Labs收购了Micrium,以前是商业化的要花钱现在也是开源免费的了,他遵循Apache License 2.0开源协议。

uc-osⅡ入门——创建工程模板_宇努力学习的博客-CSDN博客

✓ FreeRTOS(开源免费)

        FreeRTOS是一个迷你的实时操作系统内核。作为一个轻量级的操作系统,功能包括:任务管理时间管理信号量消息队列、内存管理、记录功能、软件定时器协程等,可基本满足较小系统的需要。

        由于RTOS需占用一定的系统资源(尤其是RAM资源),只有μC/OS-II、embOS、salvo、FreeRTOS等少数实时操作系统能在小RAM单片机上运行。相对μC/OS-II、embOS等商业操作系统,FreeRTOS操作系统是完全免费的操作系统,具有源码公开、可移植、可裁减、调度策略灵活的特点,可以方便地移植到各种单片机上运行,其最新版本为10.4.4版。

        18年的时候被Amazon(亚马逊)收购,遵循MIT开源协议。

✓ RT_Thread(开源免费)

        RT-Thread 起源于上海睿赛德电子科技有限公司是一款主要由中国开源社区主导开发的开源实时操作系统(v3.1.0以及以前版本遵循GPLv2+许可协议,v3.1.0以后版本遵循 Apache License 2.0 开源许可协议)。实时线程操作系统不仅仅是一个单一的实时操作系统内核,它也是一个完整的应用系统,包含了实时、嵌入式系统相关的各个组件:TCP/IP协议栈,libc接口,图形用户界面等。


✓ AliOS(开源收费)

        AliOS(前称:阿里云OS、云OS、YunOS)是阿里巴巴集团推出的移动操作系统。 AliOS以驱动万物智能为目标,可应用于智联网汽车、智能家居、手机、Pad等智能终端,为行业提供一站式IoT解决方案,构建IoT云端一体化生态,使物联网终端更加智能。 2023年1月5日,因业务方向调整,YunOS空间服务下线。

        他是基于Linux研发的开源收费的一款嵌入式RTOS。

✓ LiteOS

        2015年5月20日,在2015华为网络大会上,华为发布了敏捷网络3.0,主要包括轻量级的物联网操作系统LiteOS、敏捷物联网关、敏捷控制器三部分。华为战略Marketing总裁徐文伟介绍,LiteOS体积只有10KB级,而且实行开源,使智能硬件开发变得更加简单。 [1] 

        Huawei LiteOS是华为1+2+1物联网解决方案的组成部分,遵循BSD-3开源许可协议,自开源以来,已经和一些厂商、家电企业达成了合作,华为希望通过开源、开放将LiteOS打造成像安卓一样的物联网终端的物联网操作系统。 [2] 

        Huawei LiteOS其具备「零配置」、「自发现」和「自组网」能力,让使用 LiteOS 的物联终端能够自动接入支持的网络。Huawei LiteOS 将使得智能硬件的开发变得更加简单,从而加快实现万物的互联互通。


(阿里和华为的RTOS和上面已经不太一样了,更倾向于应用开发)

FreeRTOS的主要功能列表

        二值信号量通常用于互斥访问或同步,二值信号量和互斥信号量非常类似,但是还是有一些细微的差别,互斥信号量拥有优先级继承机制,二值信号量没有优先级继承。因此二值信号另更适合用于同步(任务与任务或任务与中断的同步),而互斥信号量适合用于简单的互斥访问。和队列一样。

        信号量API函数允许设置一个阻塞时间,阻塞时间是当任务获取信号量的时候由于信号量无效从而导致任务进入阻塞态的最大时钟节拍数。如果多个任务同时阻塞在同一个信号量上的话那么优先级最高的那个任务优先获得信号量,这样当信号量有效的时候高优先级的任务就会解除阻塞状态。

        二值信号量其实就是一个只有一个队列项的队列,这个特殊的队列要么是满的,要么是空的,这不正好就是二值的吗?任务和中断使用这个特殊队列不用在乎队列中存的是什么消息,只需要知道这个队列是满的还是空的。可以利用这个机制来完成任务与中断之间的同步。在实际应用中通常会使用一个任务来处理MCU的某个外设,比如网络应用中,一般最简单的方法就是使用一个任务去轮询的查询MCU的ETH(网络相关外设,如STM32的以太网MAC)外设是否有数据,当有数据的时候就处理这个网络数据。这样使用轮询的方式是很浪费CPU资源的,而且也阻止了其他任务的运行。最理想的方法就是当没有网络数据的时候网络任务就进入阻塞态,把CPU让给其他的任务,当有数据的时候网络任务才去执行。

        现在使用二值信号量就可以实现这样的功能,任务通过获取信号量来判断是否有网络数据,没有的话就进入阻塞态,而网络中断服务函数(大多数的网络外设都有中断功能,比如STM32的MAC专用DMA中断,通过中断可以判断是否接收到数据)通过释放信号量来通知任务以太网外设接收到了网络数据,网络任务可以去提取处理了。网络任务只是在一直的获取二值信号量,它不会释放信号量,而中断服务函数是一直在释放信号量,它不会获取信号量。在中断服务函数中发送信号量可以使用函数xSemaphoreGiveFromISR(),也可以使用任务通知功能来替代二值信号量,而且使用任务通知的话速度更快,代码量更少。使用二值信号量来完成中断与任务同步的这个机制中,任务优先级确保了外设能够得到及时的处理,这样做相当于推迟了中断处理过程。也可以使用队列来替代二值信号量,在外设事件的中断服务函数中获取相关数据,并将相关的数据通过队列发送给任务。如果队列无效的话任务就进入阻塞态,直至队列中有数据,任务接收到数据以后就开始相关的处理过程。

任务

任务的创建立

任务就是RTOS的执行单位,但是要注意所有的中断都是要比任务优先级更高的

任务的建立有两种方式动态创建和静态创建

动态创建

 BaseType_t xTaskCreate(
							  TaskFunction_t pvTaskCode,
							  const char * const pcName,
							  uint16_t usStackDepth,
							  void *pvParameters,
							  UBaseType_t uxPriority,
							  TaskHandle_t *pvCreatedTask
						  );
 
 /* Create a new task and add it to the list of tasks that are ready to run.
 *
 * xTaskCreate() can only be used to create a task that has unrestricted
 * access to the entire microcontroller memory map.  Systems that include MPU
 * support can alternatively create an MPU constrained task using
 * xTaskCreateRestricted().
 *
 * @param pvTaskCode Pointer to the task entry function.  Tasks
 * must be implemented to never return (i.e. continuous loop).
 *
 * @param pcName A descriptive name for the task.  This is mainly used to
 * facilitate debugging.  Max length defined by configMAX_TASK_NAME_LEN - default
 * is 16.
 *
 * @param usStackDepth The size of the task stack specified as the number of
 * variables the stack can hold - not the number of bytes.  For example, if
 * the stack is 16 bits wide and usStackDepth is defined as 100, 200 bytes
 * will be allocated for stack storage.
 *
 * @param pvParameters Pointer that will be used as the parameter for the task
 * being created.
 *
 * @param uxPriority The priority at which the task should run.  Systems that
 * include MPU support can optionally create tasks in a privileged (system)
 * mode by setting bit portPRIVILEGE_BIT of the priority parameter.  For
 * example, to create a privileged task at priority 2 the uxPriority parameter
 * should be set to ( 2 | portPRIVILEGE_BIT ).
 *
 * @param pvCreatedTask Used to pass back a handle by which the created task
 * can be referenced.
 *
 * @return pdPASS if the task was successfully created and added to a ready
 * list, otherwise an error code defined in the file projdefs.h
 */

动态创建的内存是RTOS分配的有几率回收,但是静态创建是我们自己申请的。不能被回收。

静态创建

TaskHandle_t xTaskCreateStatic( TaskFunction_t pxTaskCode,
                                const char *pcName,
                                uint32_t ulStackDepth,
                                void *pvParameters,
                                UBaseType_t uxPriority,
                                StackType_t *puxStackBuffer,
                                StaticTask_t *pxTaskBuffer );
 
 /* Create a new task and add it to the list of tasks that are ready to run.
 *
 * Internally, within the FreeRTOS implementation, tasks use two blocks of
 * memory.  The first block is used to hold the task's data structures.  The
 * second block is used by the task as its stack.  If a task is created using
 * xTaskCreate() then both blocks of memory are automatically dynamically
 * allocated inside the xTaskCreate() function.  (see
 * https://www.FreeRTOS.org/a00111.html).  If a task is created using
 * xTaskCreateStatic() then the application writer must provide the required
 * memory.  xTaskCreateStatic() therefore allows a task to be created without
 * using any dynamic memory allocation.
 *
 * @param pxTaskCode Pointer to the task entry function.  Tasks
 * must be implemented to never return (i.e. continuous loop).
 *
 * @param pcName A descriptive name for the task.  This is mainly used to
 * facilitate debugging.  The maximum length of the string is defined by
 * configMAX_TASK_NAME_LEN in FreeRTOSConfig.h.
 *
 * @param ulStackDepth The size of the task stack specified as the number of
 * variables the stack can hold - not the number of bytes.  For example, if
 * the stack is 32-bits wide and ulStackDepth is defined as 100 then 400 bytes
 * will be allocated for stack storage.
 *
 * @param pvParameters Pointer that will be used as the parameter for the task
 * being created.
 *
 * @param uxPriority The priority at which the task will run.
 *
 * @param puxStackBuffer Must point to a StackType_t array that has at least
 * ulStackDepth indexes - the array will then be used as the task's stack,
 * removing the need for the stack to be allocated dynamically.
 *
 * @param pxTaskBuffer Must point to a variable of type StaticTask_t, which will
 * then be used to hold the task's data structures, removing the need for the
 * memory to be allocated dynamically.
 *
 * @return If neither puxStackBuffer nor pxTaskBuffer are NULL, then the task
 * will be created and a handle to the created task is returned.  If either
 * puxStackBuffer or pxTaskBuffer are NULL then the task will not be created and
 * NULL is returned.
 */

不知道是不是内核版本的问题,我发现这个硬石的源码里没有静态创建。

任务的状态

  • 运行

    当任务实际执行时,它被称为处于运行状态。 任务当前正在使用处理器。 如果运行 RTOS 的处理器只有一个内核, 那么在任何给定时间内都只能有一个任务处于运行状态。

  • 准备就绪

    准备就绪任务指那些能够执行(它们不处于阻塞或挂起状态), 但目前没有执行的任务, 因为同等或更高优先级的不同任务已经处于运行状态。

  • 阻塞

    如果任务当前正在等待时间或外部事件,则该任务被认为处于阻塞状态。 例如,如果一个任务调用vTaskDelay(),它将被阻塞(被置于阻塞状态), 直到延迟结束-一个时间事件。 任务也可以通过阻塞来等待队列、信号量、事件组、通知或信号量 事件。 处于阻塞状态的任务通常有一个"超时"期, 超时后任务将被超时,并被解除阻塞, 即使该任务所等待的事件没有发生。

    “阻塞”状态下的任务不使用任何处理时间,不能 被选择进入运行状态。

  • 挂起

    与“阻塞”状态下的任务一样, “挂起”状态下的任务不能 被选择进入运行状态,但处于挂起状态的任务 没有超时。 相反,任务只有在分别通过 vTaskSuspend() 和 xTaskResume() API 调用明确命令时 才会进入或退出挂起状态

任务的挂起和恢复

任务的挂起函数: 将任务从就绪列表删除,添加到挂起列表。

void vTaskSuspend( TaskHandle t pxTaskToSuspend );


任务的恢复函数: 将任务从挂起列表删除,添加到就绪列表.
 

void vTaskResume( TaskHandle t pxTaskToResume);

任务的删除

将任务从所在列表(可能是就绪列表、挂起列表或阻塞列表)中删除


void vTaskDelete( TaskHandle t pxTask );


如果参数为“NULL”则删除任务自身

系统延时函数的实现

相对延时函数 

令任务进入阻塞状态,并在延时的时间后解除阻塞重新运行。
可以使用函数 pdmS_TO_TICKS() 将需要的时间(单位: ms) 转换成系统时钟节拍。

void vTaskDelay( TickType t xTicksToDelay );


绝对延时函数


和 vTaskDelay() 类似,区别在于从某个时间点算起,延时到一个绝对的时间点可以使用函数 xTaskGetTickCount() 获取当前系统时钟节拍数
 

void vTaskDelayUntil( TickType t *pxPreviousWakeTimeTickType t xTimeIncrement );

小练习

        因为freeRTOS已经内置到我们的cubeMX里了,所以不需要像uc-os那样手动移植可以直接用cubeMX生成。

目标

CubeMX启动FreeRTOS的相关注意事项
CubeMX添加一个创建其他任务的任务 TaskCreat Task()
动态创建新的 Led1 Task(),实现Led1的500ms亮灭闪烁完成任务创建后,TaskCreat Task() 任务删除自己
CubeMX创建任务 Key1 Task(),实现处理按键按下的动作,切换 Led1 Task的挂起和恢复状态
实现串口打印,HAL延时2s,使用相对延时函数CubeMX创建任务 Uart1 Task()延时5s
CubeMX创建任务 Uart2 Task(),实现串口打印,HAL延时2s,使用绝对延时函数
延时5s

操作过程

配置好三个灯和一个按键以及串口1后我们配置FreeRTOS

这里选择V1版本,V2版本支持的芯片种类更多我们只用32的话V1就够了

默认节拍是1000Hz,也就是一毫秒中断一次,越大越节约资源,任务优先级设置为7(4-32),最小堆栈大小是128个字也就是 512个字节

        IDLE是系统自动创建的空闲任务,永远不允许阻塞可以在其它任务都阻塞的时候去清理他们执行任务切换,并在其它任务死掉后回收资源. 

        FreeRTOS程序在任意时刻,必须至少有一个任务处于运行状态,为了达到这个要求,FreeRTOS使用了Idle任务:当vTaskStartScheduler调用后,调度器会自动创建Idle任务,这个任务的任务函数就是一个连续性工作的任务,所以他总是可以处于就绪态(在运行态和就绪态之间转换,没有其他状态)。由于Idle任务的优先级是最低的(优先级为0),所以Idle任务不会抢占用户任务的运行。当其他高优先级的任务需要运行时,他们会抢占Idle任务。

        MUTEXES是互斥信号量这里默认使用,递归信号量和计数信号量是默认关闭的

 默认队列大小是8,任务标签默认是关闭的向下兼容这个是兼容之前的版本,版本之间其实差别还挺大的,一般就一直用一个版本,但是为了兼容性这个默认是打开的。。

USE_PORT_OPTIMISED_TASK_SELECTION此配置用于优化优先级列表中要执行的最高优先级任务的算法。对CM内核的移植文件,默认已经在文件portmacro.h文件中使能。

低功耗这个用的比较多,

内核的通知功能一般是打开的 

用不用把记录的堆栈放到高地址区

        内存的分配方式有两种,动态和静态之前那个没有静态的源码估计就是这里选择了只动态分配,我们默认是两个都打开

他的堆的大小可以在512b到64k之间,这里默认是3072其实很小 只够6个任务,我们刚刚设置任务是128个字512个字节

内存管理方案有5个我们常用第四个

下面还有钩子函数的使用,监控内存CPU的使用等等功能就不细说了

 

然后我们创建第一个任务,操作系统不能一个任务都没有所以他有个默认的任务我们在创建一个优先级比较高的任务让他来动态创建任务

 

再来一个普通优先级的按键任务

然后再创建一个串口的任务比按键任务优先级低一点

任务都创建完了现在生成工程

这时候会出现一个报错,这个Systick是由M3或者M4内核提供的,我们把他作为FreeRTOs的基准时钟,但是这个时钟默认被HAL库的这个延时函数占用了。

 这时我们选择No然后改下时钟

改成一个不是Systick的就行

 新建一个LED1handle

进行串口重定向

/* USER CODE BEGIN Header_StartTaskCreate */
/**
* @brief Function implementing the TaskCreate thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_StartTaskCreate */
void StartTaskCreate(void const * argument)
{
  /* USER CODE BEGIN StartTaskCreate */
  /* Infinite loop */
  for(;;)
  {
    osDelay(1);
  }
  /* USER CODE END StartTaskCreate */
}

找到自动生成的任务函数,添加我们LED任务的创建

 我们自己的代码不能随便写要在规定的位置

再这连个注释中间

让灯一直闪烁

那这个osDelay是什么呢它长得和前面说到相对延时绝对延时都不一样

/*********************** Generic Wait Functions *******************************/
/**
* @brief   Wait for Timeout (Time Delay)
* @param   millisec      time delay value
* @retval  status code that indicates the execution status of the function.
*/
osStatus osDelay (uint32_t millisec)
{
#if INCLUDE_vTaskDelay
  TickType_t ticks = millisec / portTICK_PERIOD_MS;
  
  vTaskDelay(ticks ? ticks : 1);          /* Minimum delay = 1 tick */
  
  return osOK;
#else
  (void) millisec;
  
  return osErrorResource;
#endif
}

然后我们的函数就变成了这个样子

/* USER CODE BEGIN Header_StartTaskCreate */
/**
* @brief Function implementing the TaskCreate thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_StartTaskCreate */
void StartTaskCreate(void const * argument)
{
  /* USER CODE BEGIN StartTaskCreate */
	if(xTaskCreate ((TaskFunction_t )StartLed1Task , "CreatTask", 128, NULL, 2, (TaskHandle_t )LED1TaskHandle) == pdTRUE){
		vTaskDelete(NULL);
	}
  /* Infinite loop */
  for(;;)
  {
    osDelay(1);
  }
  /* USER CODE END StartTaskCreate */
}
void StartLed1Task(void)
{
	for(;;)
	{
		HAL_GPIO_WritePin (LED1_GPIO_Port, LED1_Pin, GPIO_PIN_RESET);
		osDelay(500);
		HAL_GPIO_WritePin (LED1_GPIO_Port, LED1_Pin, GPIO_PIN_SET);
		osDelay(500);
	}
}

可恶啊,改了一个多小时,发现这个创建任务来创建LED1任务一直失败,

最后发现是这里设置太小了,之前是3072,之前就觉得会暴雷真的是这里,然后我改了一个很大的就没问题了。

 


#if( configSUPPORT_DYNAMIC_ALLOCATION == 1 )

	BaseType_t xTaskCreate(	TaskFunction_t pxTaskCode,
							const char * const pcName,		/*lint !e971 Unqualified char types are allowed for strings and single characters only. */
							const configSTACK_DEPTH_TYPE usStackDepth,
							void * const pvParameters,
							UBaseType_t uxPriority,
							TaskHandle_t * const pxCreatedTask )
	{
	TCB_t *pxNewTCB;
	BaseType_t xReturn;

		/* If the stack grows down then allocate the stack then the TCB so the stack
		does not grow into the TCB.  Likewise if the stack grows up then allocate
		the TCB then the stack. */
		#if( portSTACK_GROWTH > 0 )
		{
			/* Allocate space for the TCB.  Where the memory comes from depends on
			the implementation of the port malloc function and whether or not static
			allocation is being used. */
			pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) );

			if( pxNewTCB != NULL )
			{
				/* Allocate space for the stack used by the task being created.
				The base of the stack memory stored in the TCB so the task can
				be deleted later if required. */
				pxNewTCB->pxStack = ( StackType_t * ) pvPortMalloc( ( ( ( size_t ) usStackDepth ) * sizeof( StackType_t ) ) ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */

				if( pxNewTCB->pxStack == NULL )
				{
					/* Could not allocate the stack.  Delete the allocated TCB. */
					vPortFree( pxNewTCB );
					pxNewTCB = NULL;
				}
			}
		}
		#else /* portSTACK_GROWTH */
		{
		StackType_t *pxStack;

			/* Allocate space for the stack used by the task being created. */
			pxStack = ( StackType_t * ) pvPortMalloc( ( ( ( size_t ) usStackDepth ) * sizeof( StackType_t ) ) ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */

			if( pxStack != NULL )
			{
				/* Allocate space for the TCB. */
				pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) ); /*lint !e961 MISRA exception as the casts are only redundant for some paths. */

				if( pxNewTCB != NULL )
				{
					/* Store the stack location in the TCB. */
					pxNewTCB->pxStack = pxStack;
				}
				else
				{
					/* The stack cannot be used as the TCB was not created.  Free
					it again. */
					vPortFree( pxStack );
				}
			}
			else
			{
				pxNewTCB = NULL;
			}
		}
		#endif /* portSTACK_GROWTH */

		if( pxNewTCB != NULL )
		{
			#if( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 ) /*lint !e731 Macro has been consolidated for readability reasons. */
			{
				/* Tasks can be created statically or dynamically, so note this
				task was created dynamically in case it is later deleted. */
				pxNewTCB->ucStaticallyAllocated = tskDYNAMICALLY_ALLOCATED_STACK_AND_TCB;
			}
			#endif /* configSUPPORT_STATIC_ALLOCATION */

			prvInitialiseNewTask( pxTaskCode, pcName, ( uint32_t ) usStackDepth, pvParameters, uxPriority, pxCreatedTask, pxNewTCB, NULL );
			prvAddNewTaskToReadyList( pxNewTCB );
			xReturn = pdPASS;
		}
		else
		{
			xReturn = errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY;
		}

		return xReturn;
	}

看了下源码这个正确创建返回值是pdPASS

主打一个丝滑,完整的主函数放这里了,然后这个怎么说捏就是创建了一个创建任务,动态创建一个LED1闪烁的任务,如果创建成功了就把自己删掉,然后有一个按键任务,是外部中断触发的,如果按下就会挂起LED任务,再按一下就恢复任务,再按在挂起。有一个串口输出的任务打印一些信息

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  ******************************************************************************
  * @attention
  *
  * <h2><center>&copy; Copyright (c) 2023 STMicroelectronics.
  * All rights reserved.</center></h2>
  *
  * This software component is licensed by ST under BSD 3-Clause license,
  * the "License"; You may not use this file except in compliance with the
  * License. You may obtain a copy of the License at:
  *                        opensource.org/licenses/BSD-3-Clause
  *
  ******************************************************************************
  */
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "cmsis_os.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include <stdio.h>
#include <stdbool.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 ---------------------------------------------------------*/
UART_HandleTypeDef huart1;

osThreadId defaultTaskHandle;
osThreadId TaskCreateHandle;
osThreadId Key1TaskHandle;
osThreadId UartTaskHandle;
/* USER CODE BEGIN PV */
osThreadId Led1TaskHandle;
/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_USART1_UART_Init(void);
void StartDefaultTask(void const * argument);
void StartTaskCreat(void const * argument);
void StartKey1Task(void const * argument);
void StartUartTask(void const * argument);
void StartLed1Task(void const * argument);

/* USER CODE BEGIN PFP */
bool Key1PressedFlag = false;
/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
int fputc(int ch, FILE *fp)
{
	HAL_UART_Transmit(&huart1,  (uint8_t *)&ch, 1, 1000);
	return ch;
}
/* 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_USART1_UART_Init();
  /* USER CODE BEGIN 2 */
	printf("FreeRTOS Demo1 @ %s %s\n",__DATE__,__TIME__);//打印系统日期和时间
  /* USER CODE END 2 */

  /* USER CODE BEGIN RTOS_MUTEX */
  /* add mutexes, ... */
  /* USER CODE END RTOS_MUTEX */

  /* USER CODE BEGIN RTOS_SEMAPHORES */
  /* add semaphores, ... */
  /* USER CODE END RTOS_SEMAPHORES */

  /* USER CODE BEGIN RTOS_TIMERS */
  /* start timers, add new ones, ... */
  /* USER CODE END RTOS_TIMERS */

  /* USER CODE BEGIN RTOS_QUEUES */
  /* add queues, ... */
  /* USER CODE END RTOS_QUEUES */

  /* Create the thread(s) */
  /* definition and creation of defaultTask */
  osThreadDef(defaultTask, StartDefaultTask, osPriorityNormal, 0, 128);
  defaultTaskHandle = osThreadCreate(osThread(defaultTask), NULL);

  /* definition and creation of TaskCreate */
  osThreadDef(TaskCreate, StartTaskCreat, osPriorityHigh, 0, 128);
  TaskCreateHandle = osThreadCreate(osThread(TaskCreate), NULL);

  /* definition and creation of Key1Task */
  osThreadDef(Key1Task, StartKey1Task, osPriorityNormal, 0, 128);
  Key1TaskHandle = osThreadCreate(osThread(Key1Task), NULL);

  /* definition and creation of UartTask */
  osThreadDef(UartTask, StartUartTask, osPriorityBelowNormal, 0, 128);
  UartTaskHandle = osThreadCreate(osThread(UartTask), NULL);


  /* USER CODE BEGIN RTOS_THREADS */
  /* add threads, ... */
  /* USER CODE END RTOS_THREADS */

  /* Start scheduler */
  osKernelStart();

  /* We should never get here as control is now taken by the scheduler */
  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

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

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

  /** 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.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 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_DIV2;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

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

/**
  * @brief USART1 Initialization Function
  * @param None
  * @retval None
  */
static void MX_USART1_UART_Init(void)
{

  /* USER CODE BEGIN USART1_Init 0 */

  /* USER CODE END USART1_Init 0 */

  /* USER CODE BEGIN USART1_Init 1 */

  /* USER CODE END USART1_Init 1 */
  huart1.Instance = USART1;
  huart1.Init.BaudRate = 115200;
  huart1.Init.WordLength = UART_WORDLENGTH_8B;
  huart1.Init.StopBits = UART_STOPBITS_1;
  huart1.Init.Parity = UART_PARITY_NONE;
  huart1.Init.Mode = UART_MODE_TX_RX;
  huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  huart1.Init.OverSampling = UART_OVERSAMPLING_16;
  if (HAL_UART_Init(&huart1) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN USART1_Init 2 */

  /* USER CODE END USART1_Init 2 */

}

/**
  * @brief GPIO Initialization Function
  * @param None
  * @retval None
  */
static void MX_GPIO_Init(void)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};

  /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOC_CLK_ENABLE();
  __HAL_RCC_GPIOA_CLK_ENABLE();
  __HAL_RCC_GPIOB_CLK_ENABLE();
  __HAL_RCC_GPIOG_CLK_ENABLE();

  /*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, GPIO_PIN_RESET);

  /*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(GPIOG, LED2_Pin|LED3_Pin, GPIO_PIN_RESET);

  /*Configure GPIO pin : KEY1_Pin */
  GPIO_InitStruct.Pin = KEY1_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING_FALLING;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  HAL_GPIO_Init(KEY1_GPIO_Port, &GPIO_InitStruct);

  /*Configure GPIO pin : LED1_Pin */
  GPIO_InitStruct.Pin = LED1_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(LED1_GPIO_Port, &GPIO_InitStruct);

  /*Configure GPIO pins : LED2_Pin LED3_Pin */
  GPIO_InitStruct.Pin = LED2_Pin|LED3_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(GPIOG, &GPIO_InitStruct);

  /* EXTI interrupt init*/
  HAL_NVIC_SetPriority(EXTI0_IRQn, 5, 0);
  HAL_NVIC_EnableIRQ(EXTI0_IRQn);

}

/* USER CODE BEGIN 4 */
void StartLed1Task(void const * argument)
{
	for(;;)
	{
		HAL_GPIO_WritePin (LED1_GPIO_Port, LED1_Pin, GPIO_PIN_RESET);
		osDelay(500);
		HAL_GPIO_WritePin (LED1_GPIO_Port, LED1_Pin, GPIO_PIN_SET);
		osDelay(500);
	}
}

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
	if(GPIO_Pin == GPIO_PIN_0){
		Key1PressedFlag = true;
	}

}
/* USER CODE END 4 */

/* USER CODE BEGIN Header_StartDefaultTask */
/**
  * @brief  Function implementing the defaultTask thread.
  * @param  argument: Not used
  * @retval None
  */
/* USER CODE END Header_StartDefaultTask */
void StartDefaultTask(void const * argument)
{
  /* USER CODE BEGIN 5 */
  /* Infinite loop */
  for(;;)
  {
    osDelay(1);
  }
  /* USER CODE END 5 */
}

/* USER CODE BEGIN Header_StartTaskCreat */
/**
* @brief Function implementing the TaskCreate thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_StartTaskCreat */
void StartTaskCreat(void const * argument)
{
  /* USER CODE BEGIN StartTaskCreat */
	if(xTaskCreate ((TaskFunction_t )StartLed1Task , "Led1Task", 128, NULL, 2, &Led1TaskHandle) == pdPASS){
		printf("Led1 success!!1\n");
		vTaskDelete(NULL);
	}
	//printf("%ld\n",xTaskCreate ((TaskFunction_t )StartLed1Task , "Led1Task", 128, NULL, 2, &Led1TaskHandle));
  /* Infinite loop */
  for(;;)
  {
	  //printf("Led1 default!!1\n");
    osDelay(1);
  }
  /* USER CODE END StartTaskCreat */
}

/* USER CODE BEGIN Header_StartKey1Task */
/**
* @brief Function implementing the Key1Task thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_StartKey1Task */
void StartKey1Task(void const * argument)
{
  /* USER CODE BEGIN StartKey1Task */
	static bool myFlag = false;
  /* Infinite loop */
  for(;;)
  {
	  if(Key1PressedFlag == true) {
		Key1PressedFlag = false;
		  osDelay(10);//消抖
		  if(HAL_GPIO_ReadPin (KEY1_GPIO_Port, KEY1_Pin) == GPIO_PIN_RESET)
		  {
			  printf("Key1 Pressed!");
			if(myFlag == false)
			{
				myFlag = true;
				printf("LED Task Suspend\n");
				vTaskSuspend(Led1TaskHandle);
			}
			else{
				myFlag = false;
				printf("LED Task Resume\n");
				vTaskResume (Led1TaskHandle);
			}
		  }
	  }
    osDelay(1);
  }
  /* USER CODE END StartKey1Task */
}

/* USER CODE BEGIN Header_StartUartTask */
/**
* @brief Function implementing the UartTask thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_StartUartTask */
void StartUartTask(void const * argument)
{
  /* USER CODE BEGIN StartUartTask */
	uint32_t times = 0;
  /* Infinite loop */
  for(;;)
  {
	  printf("UartTaskHandle Task %d times\n", times++);
      osDelay(2000);
  }
  /* USER CODE END StartUartTask */
}

 /**
  * @brief  Period elapsed callback in non blocking mode
  * @note   This function is called  when TIM7 interrupt took place, inside
  * HAL_TIM_IRQHandler(). It makes a direct call to HAL_IncTick() to increment
  * a global variable "uwTick" used as application time base.
  * @param  htim : TIM handle
  * @retval None
  */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
  /* USER CODE BEGIN Callback 0 */

  /* USER CODE END Callback 0 */
  if (htim->Instance == TIM7) {
    HAL_IncTick();
  }
  /* USER CODE BEGIN Callback 1 */

  /* USER CODE END Callback 1 */
}

/**
  * @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\n", file, line) */
  /* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */

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

 

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
你好!关于 FreeRTOS入门,我可以给你一些基本的指导。 FreeRTOS 是一个开源的实时操作系统(RTOS),它被广泛用于嵌入式系统开发。下面是一些入门的步骤: 1. 下载和安装 FreeRTOS:首先,你需要从 FreeRTOS 的官方网站(https://www.freertos.org/)下载 FreeRTOS 的最新版本。根据你的需求选择合适的版本,可能是源代码或者预编译好的进制文件。安装方法可以参考官方提供的文档。 2. 阅读文档:FreeRTOS 提供了详细的文档,包括参考手册、编程指南和示例代码等。你应该仔细阅读这些文档,了解 FreeRTOS 的基本概念、API 接口和使用方法。 3. 配置 FreeRTOS:在开始使用 FreeRTOS 之前,你需要根据你的硬件平台和应用需求进行配置。这包括选择适当的内核配置选项、任务调度策略、堆栈大小等。你可以根据文档中的指导进行配置。 4. 创建任务:在 FreeRTOS 中,任务是系统的基本执行单位。你可以使用 FreeRTOS 提供的 API 创建和管理任务。首先,你需要定义任务函数,然后使用 xTaskCreate() 函数创建任务。 5. 运行 FreeRTOS:一旦你创建了任务,你可以使用 vTaskStartScheduler() 函数启动 FreeRTOS 内核,并开始任务调度。FreeRTOS 内核会按照你配置的调度策略和优先级来调度任务执行。 6. 调试和优化:在开发过程中,你可能会遇到一些问题,比如任务调度错误、内存泄漏等。你可以使用 FreeRTOS 提供的调试工具和技术来诊断和解决这些问题。同时,你也可以根据应用的需求对系统进行优化。 以上是 FreeRTOS 入门的基本步骤。希望对你有所帮助!如果你有更多的问题,请随时提问。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

宇努力学习

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值