FreeRTOS入门(二)

news2024/10/7 7:32:13

目录

什么是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****/

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/705959.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

微信小程序canvas层级太高,与其他非原生组件层级冲突

官网已经提出新版本以支持同层渲染&#xff0c;但是实际项目中层级还是冲突的。 最后在文档中找到这样一段话&#xff0c;用真机打开&#xff0c;层级就正常了 。所以建议大家&#xff0c;多使用真机调试去测试&#xff01;&#xff01;&#xff01;&#xff01;

redis中常用的命令

1.关于对key操作的命令 keys *: 查看redis中所有的key exists key: 判断指定的key是否存在。存在返回1 否则返回0 del key: 删除指定的key expire key seconds: 为指定的key设置过期时间 2.关于库的命令 默认redis中存在16个库 select n: 选中库 n0~15 flushdb: 清空…

C++中的exec()函数

exec()函数在C中是一个进程控制函数&#xff0c;用于创建新进程执行其他程序或命令行指令。exec()函数可以替换当前进程的代码和数据&#xff0c;创建新的进程运行其他程序。exec()函数有多个版本&#xff0c;例如execl、execv、execle、execve等&#xff0c;根据不同的参数类型…

SAP 区分工单BOM物料是手工删除 还是 Teco后自动关闭需求

SAP 区分工单BOM物料是手工删除 还是 Teco后自动关闭需求 首先 resb表删除标识XLOEK 都为 ‘X’&#xff0c;无法通过其它字段直接区分 1先从前台界面区分 手工删除的&#xff0c;组件界面颜色正常&#xff0c;状态为-REL 删除 Teco自动关闭需求的&#xff0c;颜色不一样&am…

python中调用java函数

python中调用java函数 1. 将java项目打包成jar&#xff08;IDEA&#xff09;2. 在python中调用jar 1. 将java项目打包成jar&#xff08;IDEA&#xff09; 【CtrlShiftAltS】或者“File --> Project Structure --> Project Settings” 选择Artifacts选项卡&#xff0c;点…

[Android Studio]1.2计数器

所有要改的代码如下&#xff1a; MainActivity代码&#xff1a; package com.example.code02;import androidx.appcompat.app.AppCompatActivity; import android.os.Bundle; import android.view.View; import android.widget.Button; import android.widget.TextView; imp…

FPGA第一个程序入门

1、使用正点原子的达芬奇开发板进行第一个FPGA程序设计。 2、启动vivado 2019.2。 3、 新建工程。 File--Project--New&#xff0c;选择RTL Project&#xff08;寄存器传输级&#xff09;。 4、选择器件。 add sources点击next&#xff0c; add constraints点击next&#x…

npm install安装依赖总结

node下载地址&#xff1a;https://nodejs.org/en/download/releases 。可以看到node版本、npm版本、node_module版本 1.npm的全局安装路径 查看默认值&#xff1a; npm get prefix 默认是C:\Users\你的用户名\AppData\Roaming\npm 、 可以通过 npm config prefix 更改全局…

mysql-数据迁移 及报错解决(ERROR 1290 (HY000)

文章目录 1. 物理迁移1. 迁移前&#xff0c;配置mysql的输出目录1. 查看mysql的输出目录2. 修改mysql的输出目录 2. 文件迁移 1. 物理迁移 1. 迁移前&#xff0c;配置mysql的输出目录 1. 查看mysql的输出目录 在安装MySQL的会限制了导入与导出的目录权限。只允许在规定的目录…

量子 能源,节能减排还是另有“端倪”?

光子盒研究院 前言&#xff1a;如今&#xff0c;量子技术早已走出实验室、广泛赋能电力、化学、医学等各个领域&#xff1b;创新赛道上&#xff0c;加速奔跑的量子产业&#xff0c;将带来无限可能。现在&#xff0c;光子盒特开启「量子」专栏&#xff0c;解读量子技术将为下游应…

基于matlab从3D医学图像中对脑肿瘤进行语义分割(附源码)

一、前言 此示例演示如何从 3D 医学图像中对脑肿瘤进行语义分割。 语义分割涉及用类标记 3-D 体积的图像或体素中的每个像素。此示例说明了如何使用 3-D U-Net 深度学习网络在磁共振成像 &#xff08;MRI&#xff09; 扫描中对脑肿瘤进行二进制语义分割。U-Net是一个快速&…

【MySQL学习笔记】(五) 表的约束

表的约束 1 什么是约束&#xff1f;2 空属性3 默认值4 列描述5 zerofill6 主键7 自增长8 唯一键9 外键 1 什么是约束&#xff1f; 约束是一种限制&#xff0c;它通过对表的行或列的数据做出限制&#xff0c;来确保数据的完整性、一致性。 真正约束字段的是数据类型&#xff0…

Electron快速入门

目录 前言 一、安装需知 二、安装electron 三、开始 3.1 修改package.json文件 3.2 创建main.js文件 3.3 启动预览窗口 3.4 显示内容 四、 热加载 五、主进程和渲染进程概念介绍 六、自定义原生菜单 6.1 自定义菜单 6.2 给菜单添加点击事件 6.3 抽离菜单定义 6.…

基于SpringBoot+vue的社区维修平台设计与实现

博主介绍&#xff1a; 大家好&#xff0c;我是一名在Java圈混迹十余年的程序员&#xff0c;精通Java编程语言&#xff0c;同时也熟练掌握微信小程序、Python和Android等技术&#xff0c;能够为大家提供全方位的技术支持和交流。 我擅长在JavaWeb、SSH、SSM、SpringBoot等框架…

信号链噪声分析19

文章目录 概要整体架构流程技术名词解释技术细节小结 概要 提示&#xff1a;这里可以添加技术概要 用于定量表示 ADC 动态性能的常用指标有六个&#xff0c;分别是&#xff1a;SINAD&#xff08;信纳比&#xff09;、ENOB &#xff08;有效位数&#xff09;、SNR&#xff08;信…

keil_arm 大端小端 寄存器 栈

.text .global _start _start: /* 单寄存器ldr r0,0x40000800ldr r1,0x12345678将r1寄存器中的值&#xff0c;写到r0指向的地址空间中[0x40000800]0x12345678str r1,[r0]将r0指向地址空间中的内容&#xff0c;读到目标寄存器r2中&#xff0c;r20x12345678 ldr r2,[r0] */ /*ldr…

前端设计模式学习

UML类图 1.工厂模式 设计原则&#xff1a;最重要的就是开放封闭原则&#xff0c;对扩展开放&#xff0c;对修改封闭。 1.工厂和类分离&#xff0c;解耦 2.可以扩展多个类 3.工厂的创建逻辑也可以自由扩展 工厂模式可以拆分成三个&#xff0c;分别是工厂方法模式、抽象工厂…

C/C++图形库EasyX保姆级使用教程(二) 图形化窗口设置以及简单图形的绘制

C/C图形库EasyX保姆级使用教程 第一章 Microsoft Visual Studio 2022和EasyX的下载及安装使用 第二章 图形化窗口设置以及简单图形的绘制 文章目录 C/C图形库EasyX保姆级使用教程前言一、窗口&#xff01;1.如何生成一个图形化窗口&#xff1f;1.头文件2.初始化一个图形化窗口…

基于Java+Swing+Mysql人口普查登记系统

基于JavaSwingMysql人口普查登记系统 一、系统介绍二、功能展示1.主页2.新增人口信息3.查询人口信息 三、数据库四、其他系统实现五、获取源码 一、系统介绍 该系统实现了查看列表、新增人口信息、删除人口信息 运行环境&#xff1a;eclipse、idea、jdk1.8 二、功能展示 1.…

常州工学院数字图像处理及应用2022-2023第二学期实验报告 + 期末

《数字图像处理及应用》 课程 实验报告书 专业班级&#xff1a; 21计二 姓 名&#xff1a; 王梓权 学 号&#xff1a; 21030228 指导教师&#xff1a; 徐则中 计算机信息工程学院 《数字图像处理》实验 实验教学目的和要求 《数字图像处理》课程内容是一门综合…