学完平衡车继续跟着铁头山羊老师学习freertos实时操作系统

跳过对freertos的介绍直接开始学习相关知识。

1.4 初次使用freertos

首先完成一个小例子

不加操作系统的情况下,用hal库编写代码如下:

因为两个灯闪烁的周期一致,可以写在同一个while循环里。

先设置调试接口,还有开发板上两个led的GPIO模式,设置成输出模式(原理图略)。

  /* USER CODE BEGIN WHILE */
  while (1)
  {
	  HAL_GPIO_WritePin(led1_GPIO_Port,led1_Pin,GPIO_PIN_SET);
	  HAL_GPIO_WritePin(led3_GPIO_Port,led3_Pin,GPIO_PIN_SET);
	  HAL_Delay(500);
	  HAL_GPIO_WritePin(led3_GPIO_Port,led3_Pin,GPIO_PIN_RESET);
	  HAL_Delay(500);
	  HAL_GPIO_WritePin(led1_GPIO_Port,led1_Pin,GPIO_PIN_RESET);
	  HAL_Delay(1000);
    /* USER CODE END WHILE */

代码如上,因为非常简单就不多解释了。

因为闪灯周期一样,所以可以写在同一个while循环里,如果两个周期不一样,情况会发生变化。

解决的方法是建立状态机

需要声明一个全局变量用来记录灯的状态以及一个全局变量用来记录上次状态转换的时间。

代码如下

/* USER CODE BEGIN PV */
uint64_t led1lasttime=0;
uint8_t led1state=0;

uint64_t led3lasttime=0;
uint8_t led3state=0;
/* USER CODE END PV */

  /* USER CODE BEGIN WHILE */
  while (1)
  {
	  uint64_t now=HAL_GetTick();
	  if((led1state==0) && (now-led1lasttime)>=1000)
	  {
		  HAL_GPIO_WritePin(led1_GPIO_Port,led1_Pin,GPIO_PIN_SET);
		  led1state=1;
		  led1lasttime=now;
	  }
	  if((led1state==1) && (now-led3lasttime)>=1000)
	  {
		  HAL_GPIO_WritePin(led1_GPIO_Port,led1_Pin,GPIO_PIN_RESET);
		  led1state=0;
		  led1lasttime=now;
	  }

	  if((led3state==0) && (now-led3lasttime)>=1500)
	  {
		  HAL_GPIO_WritePin(led1_GPIO_Port,led3_Pin,GPIO_PIN_SET);
		  led3state=1;
		  led3lasttime=now;
	  }
	  if((led3state==1) && (now-led3lasttime)>=300)
	  {
		  HAL_GPIO_WritePin(led1_GPIO_Port,led3_Pin,GPIO_PIN_RESET);
		  led3state=0;
		  led3lasttime=now;
	  }
    /* USER CODE END WHILE */

也可以声明两个函数,结果是一样的。

整个过程非常繁琐,仅仅是闪灯功能,就需要很多步骤。

如果用freertos来做,就可以分别编写两个main函数

用freertos的写法如下:

然后将代码交给调度器生成

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
void vled1Task (void *pvParameters)
{
	while(1)
	{
		HAL_GPIO_WritePin(LED1_GPIO_Port,LED1_Pin,GPIO_PIN_SET);
		vTaskDelay(pdMS_TO_TICKS(1000));
		HAL_GPIO_WritePin(LED1_GPIO_Port,LED1_Pin,GPIO_PIN_RESET);
		vTaskDelay(pdMS_TO_TICKS(1000));
	}
}

void vled3Task (void *pvParameters)
{
	while(1)
	{
		HAL_GPIO_WritePin(LED3_GPIO_Port,LED3_Pin,GPIO_PIN_SET);
		vTaskDelay(pdMS_TO_TICKS(300));
		HAL_GPIO_WritePin(LED3_GPIO_Port,LED3_Pin,GPIO_PIN_RESET);
		vTaskDelay(pdMS_TO_TICKS(1500));
	}
}
/* USER CODE END 0 */

  /* USER CODE BEGIN 2 */
  xTaskCreate(vled1Task,"led1",128,NULL,1,NULL);
  xTaskCreate(vled3Task,"led3",128,NULL,1,NULL);

  vTaskStartScheduler();
  /* USER CODE END 2 */

总结:freertos程序的一般写法

2.1 freertos总图

2.2 内核的文件结构

一般把配置文件认为是用户代码,需要粘贴在用户代码的文件夹的头文件夹下面。

下表是使用freertos需要的相关头文件

2.3 配置文件

修改时钟树配置

这个类型是用来存储系统节拍的

类比hal库,freertos的获取系统时间的函数和延迟函数的语法如下:

所以我们一般选择uint32_t作为stm32单片机的数据类型的大小

一般情况下,我们使用的是带时间片的,给这个宏赋值1

2.4 安装中断服务程序

上面的中断函数替换掉下面的中断向量表的函数

因为中断向量表的函数被替换成freertos专用的函数了,所以相关的函数也要替换。

需要让hal库让出Systick定时器使用新的定时器,给freertos用Systick定时器。

经过上述移植操作后,我们来检验一下是否能在移植好的文件里运行一段代码。

首先配置led1和led3的GPIO口,均为输出推挽模式

接着引用freertos相关的头文件

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
void vled1Task(void *pvParameters)
{
	for(;;)
	{
		HAL_GPIO_WritePin(led1_GPIO_Port,led1_Pin,GPIO_PIN_SET);
		vTaskDelay(500);
		HAL_GPIO_WritePin(led1_GPIO_Port,led1_Pin,GPIO_PIN_RESET);
		vTaskDelay(500);
	}
}

void vled3Task(void *pvParameters)
{
	for(;;)
	{
		HAL_GPIO_WritePin(led3_GPIO_Port,led3_Pin,GPIO_PIN_SET);
		vTaskDelay(100);
		HAL_GPIO_WritePin(led3_GPIO_Port,led3_Pin,GPIO_PIN_RESET);
		vTaskDelay(100);
	}
}

  /* USER CODE BEGIN 2 */
  xTaskCreate(vled1Task,"led1",128,NULL,1,NULL);
  xTaskCreate(vled3Task,"led3",128,NULL,1,NULL);
  
  vTaskStartScheduler();
  /* USER CODE END 2 */

Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐