1、利用STM32CubeMX配置FreeRTOS

假设我们之前的配置引脚、配置定时器、配置时钟的步骤均已完成,现在开始导入FreeRTOS。
在这里插入图片描述
接下来为我们的程序配置四个任务,注意这里我们的三个任务的优先级是一样的。如果你设置了某一个任务的优先级高,那么程序运行时会一直运行那个任务而无法进入其他任务。
数码管的优先级设高,不然数码管的扫描如果受键码的解析的拖延会产生闪烁!(可以试着把优先级改为low,然后你就会发现按键按下的时候数码管会闪烁。)这个道理和中断优先级是一样的。具体可以看这篇文章:
关于优先级的一点问题思考
这里的function名字最好也修改成和task名字相对应,我这边是当时上课没注意。。。

图1 ledtask设置
图2 keytask设置
图3 蜂鸣器task设置
图4 数码管task设置

最后导出文件即可。

2、完成流水灯、按键、蜂鸣器数码管工作

1、在gpio.c和.h文件里面书写并声明按键扫描和led、数码管子程序

void SetLeds(uint8_t sta)
{
	HAL_GPIO_WritePin(L1_GPIO_Port,L1_Pin,(sta & 0x01) ? GPIO_PIN_RESET:GPIO_PIN_SET);
	HAL_GPIO_WritePin(L2_GPIO_Port,L2_Pin,(sta & 0x02) ? GPIO_PIN_RESET:GPIO_PIN_SET);
	HAL_GPIO_WritePin(L3_GPIO_Port,L3_Pin,(sta & 0x04) ? GPIO_PIN_RESET:GPIO_PIN_SET);
	HAL_GPIO_WritePin(L4_GPIO_Port,L4_Pin,(sta & 0x08) ? GPIO_PIN_RESET:GPIO_PIN_SET);
	HAL_GPIO_WritePin(L5_GPIO_Port,L5_Pin,(sta & 0x10) ? GPIO_PIN_RESET:GPIO_PIN_SET);
	HAL_GPIO_WritePin(L6_GPIO_Port,L6_Pin,(sta & 0x20) ? GPIO_PIN_RESET:GPIO_PIN_SET);
	HAL_GPIO_WritePin(L7_GPIO_Port,L7_Pin,(sta & 0x40) ? GPIO_PIN_RESET:GPIO_PIN_SET);
	HAL_GPIO_WritePin(L8_GPIO_Port,L8_Pin,(sta & 0x80) ? GPIO_PIN_RESET:GPIO_PIN_SET);
}

uint8_t KeyScan()
{
	uint8_t key = 0;
	if(HAL_GPIO_ReadPin(SW5_GPIO_Port,SW5_Pin) == GPIO_PIN_SET)
	{
		key = 5;
		HAL_Delay(50);
		if(HAL_GPIO_ReadPin(SW5_GPIO_Port,SW5_Pin) == GPIO_PIN_SET)
			return key;
		else
			return 0;
	}
	if(HAL_GPIO_ReadPin(SW6_GPIO_Port,SW6_Pin) == GPIO_PIN_SET)
	{
		key = 6;
		HAL_Delay(50);
		if(HAL_GPIO_ReadPin(SW6_GPIO_Port,SW6_Pin) == GPIO_PIN_SET)
			return key;
		else
			return 0;
	}
	if(HAL_GPIO_ReadPin(SW1_GPIO_Port,SW1_Pin) == GPIO_PIN_RESET)
	{
		key = 1;
		HAL_Delay(50);
		if(HAL_GPIO_ReadPin(SW1_GPIO_Port,SW1_Pin) == GPIO_PIN_RESET)
			return key;
		else
			return 0;
	}
	if(HAL_GPIO_ReadPin(SW2_GPIO_Port,SW2_Pin) == GPIO_PIN_RESET)
	{
		key = 2;
		HAL_Delay(50);
		if(HAL_GPIO_ReadPin(SW2_GPIO_Port,SW2_Pin) == GPIO_PIN_RESET)
			return key;
		else
			return 0;
	}
	if(HAL_GPIO_ReadPin(SW3_GPIO_Port,SW3_Pin) == GPIO_PIN_RESET)
	{
		key = 3;
		HAL_Delay(50);
		if(HAL_GPIO_ReadPin(SW3_GPIO_Port,SW3_Pin) == GPIO_PIN_RESET)
			return key;
		else
			return 0;
	}
	if(HAL_GPIO_ReadPin(SW4_GPIO_Port,SW4_Pin) == GPIO_PIN_RESET)
	{
		key = 4;
		HAL_Delay(50);
		if(HAL_GPIO_ReadPin(SW4_GPIO_Port,SW4_Pin) == GPIO_PIN_RESET)
			return key;
		else
			return 0;
	}
	return 0;
}
void Write595(uint8_t sel,uint8_t num,uint8_t bdot)	//共阴数码管 '0'~'7' , '8'~'F'
{
	static const uint8_t TAB[16] = {0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,
																	0x7F,0x6F,0x77,0x7C,0x39,0x5E,0x79,0x71};
	//71HC138输出高电平,关掉数码管显示
	HAL_GPIO_WritePin(A3_GPIO_Port,A3_Pin,GPIO_PIN_RESET);
	uint8_t dat = TAB[num & 0x0F] |(bdot?0x80:0x00);	
	//74HC595锁存8段数据
	for(uint8_t i=0;i<8;++i)
	{
		HAL_GPIO_WritePin(SCK_GPIO_Port,SCK_Pin,GPIO_PIN_RESET);
		HAL_GPIO_WritePin(SER_GPIO_Port,SER_Pin,(dat & 0x80)? GPIO_PIN_SET:GPIO_PIN_RESET);
		dat<<=1;
		HAL_GPIO_WritePin(SCK_GPIO_Port,SCK_Pin,GPIO_PIN_SET);
	}
	HAL_GPIO_WritePin(DISLK_GPIO_Port,DISLK_Pin,GPIO_PIN_RESET);
	HAL_GPIO_WritePin(DISLK_GPIO_Port,DISLK_Pin,GPIO_PIN_SET);
	//74HC138译码,sel(0~3)对应4位片选信号输出
	HAL_GPIO_WritePin(A0_GPIO_Port,A0_Pin,(sel & 0x01) ? GPIO_PIN_SET:GPIO_PIN_RESET);
	HAL_GPIO_WritePin(A1_GPIO_Port,A1_Pin,(sel & 0x02) ? GPIO_PIN_SET:GPIO_PIN_RESET);
	HAL_GPIO_WritePin(A2_GPIO_Port,A2_Pin,GPIO_PIN_RESET);
	HAL_GPIO_WritePin(A3_GPIO_Port,A3_Pin,GPIO_PIN_SET);
}

2、在freertos.c文件里面设置全局变量并且在各自任务中载入程序

1、定义全局变量
在这里插入图片描述
2、四个任务依次对应着我们当初配置
在这里插入图片描述

1、流水灯任务

void StartDefaultTask(void *argument)
{
  /* USER CODE BEGIN StartDefaultTask */
  /* Infinite loop */
  for(;;)
  {
		SetLeds(sta);
		osDelay(interval);
		if(run)
		{
			if(sta < 0x80) sta<<=1;
			else sta = 0x01;
		}
    osDelay(100);
  }
  /* USER CODE END StartDefaultTask */
}

2、按键任务

void StartTask02(void *argument)
{
  /* USER CODE BEGIN StartTask02 */
  /* Infinite loop */
  for(;;)
  {
		key=KeyScan();
		if(key!=0) seg[0]=key;
		if(key == 1)	//start
		{
			run = 1;
		}
		if(key == 2)
		{
			run = 0;
		}
		if(key == 3)	//speed up
		{
			interval/=2;
			if(interval <= 100) interval=100;
		}
		if(key == 4)	//speed down
		{
			interval*=2;
			if(interval >= 2000) interval=2000;
		}
    osDelay(100);
  }
  /* USER CODE END StartTask02 */
}

3、蜂鸣器任务

void StartTask03(void *argument)
{
  /* USER CODE BEGIN StartTask03 */
  /* Infinite loop */
  for(;;)
  {
		beep_duty = key*100;
		TIM3->CCR1 = beep_duty;
    	osDelay(100);
  }
  /* USER CODE END StartTask03 */
}

4、数码管任务

void StartTaskSEG(void *argument)
{
  /* USER CODE BEGIN StartTaskSEG */
  /* Infinite loop */
  for(;;)
  {
		for(uint8_t i =0;i<4;i++)
		{
			Write595(i,seg[i],0);
			osDelay(5);
		}
    osDelay(1);
  }
  /* USER CODE END StartTaskSEG */
}

3、关于FreeRTOS的注意事项

注意点:

1、每个任务函数中都要有延时delay
2、osdelay使当前任务挂起,当延时结束,又唤醒当前任务,相当于释放了cpu。
而HALdelay在延时中还是会占用cpu资源的。
3、时间戳可以在不使用delay的情况下得到当前时间
4、os函数都需要在任务函数中调用