FreeRTOS课程设计之临沂大学停车场车位管理系统(一)

FreeRTOS课程设计之临沂大学停车场车位管理系统(一)

先看一下效果吧

1.作品要求

模拟临大停车场车位管理系统

首页显示:

欢迎来到临沂大学

空闲停车位n个(n个灯亮)

“by姓名”

按下K1(停车位数递减),显示“欢迎来到临沂大学,停车位剩余n个”。连续按n次,显示“没有空闲停车位”。

按下K2(停车位数递增),显示“欢迎再来临沂大学,停车位空余n个”。

按下K3,挂起任务1. 灯闪烁。

按下K4,恢复任务1. 灯全亮。

加分项:
功能的扩展与创意。

2.实现思路
  • 需要用到 LED、OLED、按键等外设
  • 使用FreeRTOS实时操作系统
  • 需要创建几个任务、任务之间如何安排、数据如何传递?
  • 想好这些之后,再开始敲代码之前画个图吧!

image-20201217201146378

主要创建4个任务,这4个任务如上图所示,如何把这几个任务联系起来?

当然是通过信号量了

老师说,信号量可以在任务之间传递,但我没怎么用上,偷了个懒,定义了一个chewei(车位变量),Take与Give这两个任务就是对这个变量进行操作,使其–、++。

LED_Task则是根据chewei信息及时更新LED与OLED显示。

KEY_Task则是挂起与恢复 前两个任务。

这样就是大体的思路了。

3.主要代码
3.1 任务句柄的创建
/**************************** 任务句柄 ********************************/
/* 
 * 任务句柄是一个指针,用于指向一个任务,当任务创建好之后,它就具有了一个任务句柄
 * 以后我们要想操作这个任务都需要通过这个任务句柄,如果是自身的任务操作自己,那么
 * 这个句柄可以为NULL。
 */
static TaskHandle_t AppTaskCreate_Handle = NULL;/* 创建任务句柄 */
static TaskHandle_t Take_Task_Handle = NULL;/* Take_Task任务句柄 */
static TaskHandle_t Give_Task_Handle = NULL;/* Give_Task任务句柄 */
static TaskHandle_t KEY_Task_Handle = NULL;/* KEY任务句柄 */
static TaskHandle_t LED_Task_Handle = NULL;/* LED任务句柄 */
3.2 AppTaskCreate任务
/***********************************************************************
  * @ 函数名  : AppTaskCreate
  * @ 功能说明: 为了方便管理,所有的任务创建函数都放在这个函数里面
  * @ 参数    : 无  
  * @ 返回值  : 无
  **********************************************************************/
static void AppTaskCreate(void)
{
  BaseType_t xReturn = pdPASS;/* 定义一个创建信息返回值,默认为pdPASS */
  
  taskENTER_CRITICAL();           //进入临界区
  
  /* 创建Test_Queue */
  CountSem_Handle = xSemaphoreCreateCounting(4,4);	 
  if(NULL != CountSem_Handle)
    printf("CountSem_Handle计数信号量创建成功!\r\n");

  /* 创建Take_Task任务 */
  xReturn = xTaskCreate((TaskFunction_t )Take_Task, /* 任务入口函数 */
                        (const char*    )"Take_Task",/* 任务名字 */
                        (uint16_t       )512,   /* 任务栈大小 */
                        (void*          )NULL,	/* 任务入口函数参数 */
                        (UBaseType_t    )4,	    /* 任务的优先级 */
                        (TaskHandle_t*  )&Take_Task_Handle);/* 任务控制块指针 */
  if(pdPASS == xReturn)
    printf("创建Take_Task任务成功!\r\n");
  
  /* 创建Give_Task任务 */
  xReturn = xTaskCreate((TaskFunction_t )Give_Task,  /* 任务入口函数 */
                        (const char*    )"Give_Task",/* 任务名字 */
                        (uint16_t       )512,  /* 任务栈大小 */
                        (void*          )NULL,/* 任务入口函数参数 */
                        (UBaseType_t    )4, /* 任务的优先级 */
                        (TaskHandle_t*  )&Give_Task_Handle);/* 任务控制块指针 */ 
  if(pdPASS == xReturn)
    printf("创建Give_Task任务成功!\n\n");
	/* 创建KEY_Task任务 */
  xReturn = xTaskCreate((TaskFunction_t )KEY_Task,  /* 任务入口函数 */
                        (const char*    )"KEY_Task",/* 任务名字 */
                        (uint16_t       )512,  /* 任务栈大小 */
                        (void*          )NULL,/* 任务入口函数参数 */
                        (UBaseType_t    )4, /* 任务的优先级 */
                        (TaskHandle_t*  )&KEY_Task_Handle);/* 任务控制块指针 */ 
  if(pdPASS == xReturn)
    printf("创建KEY_Task任务成功!\n\n");
	/* 创建LED_Task任务 */
	 xReturn = xTaskCreate((TaskFunction_t )LED_Task,  /* 任务入口函数 */
                        (const char*    )"LED_Task",/* 任务名字 */
                        (uint16_t       )512,  /* 任务栈大小 */
                        (void*          )NULL,/* 任务入口函数参数 */
                        (UBaseType_t    )4, /* 任务的优先级 */
                        (TaskHandle_t*  )&LED_Task_Handle);/* 任务控制块指针 */ 
  if(pdPASS == xReturn)
    printf("创建LED_Task任务成功!\n\n");
  
  vTaskDelete(AppTaskCreate_Handle); //删除AppTaskCreate任务
  
  taskEXIT_CRITICAL();            //退出临界区
}
3.3 Take_Task
/**********************************************************************
  * @ 函数名  : Take_Task
  * @ 功能说明: Take_Task任务主体
  * @ 参数    :   
  * @ 返回值  : 无
  ********************************************************************/
static void Take_Task(void* parameter)
{	
  BaseType_t xReturn = pdTRUE;/* 定义一个创建信息返回值,默认为pdPASS */
  /* 任务都是一个无限循环,不能返回 */
  while (1)
  {
    //如果KEY1被单击	
		if( 0 == HAL_GPIO_ReadPin(KEY1_GPIO_Port, KEY1_Pin)  )       
		{  
			delay_ms(80);
			if( 0 == HAL_GPIO_ReadPin(KEY1_GPIO_Port, KEY1_Pin)  )
			{
			/* 获取一个计数信号量 */
				xReturn = xSemaphoreTake(CountSem_Handle,	/* 计数信号量句柄 */
                             0); 	/* 等待时间:0 */
				if(chewei!=0){
					chewei--;
				
				}
					
				
				if ( pdTRUE == xReturn ) 
					printf( "KEY1被按下,成功申请到停车位。\n" );
				else
					printf( "KEY1被按下,不好意思,现在停车场已满!\n" );	
		  }			
		}
		vTaskDelay(20);     //每20ms扫描一次		
  }
}
3.4 Give_Task
static void Give_Task(void* parameter)
{	 
  BaseType_t xReturn = pdTRUE;/* 定义一个创建信息返回值,默认为pdPASS */
  /* 任务都是一个无限循环,不能返回 */
  while (1)
  {
    //如果KEY2被单击
		if( 0 == HAL_GPIO_ReadPin(KEY2_GPIO_Port, KEY2_Pin) )  			
		{
			delay_ms(80);
			if( 0 == HAL_GPIO_ReadPin(KEY2_GPIO_Port, KEY2_Pin) )
			{
			/* 获取一个计数信号量 */
      xReturn = xSemaphoreGive(CountSem_Handle);//给出计数信号量  
      chewei++;
			
				if ( pdTRUE == xReturn ) 
					printf( "KEY2被按下,成功释放车位!\r\n" );
				else
					printf( "KEY2被按下,不好意思,无可释放车位!\r\n" );	
				
		  }		
				
	
	
		}
		vTaskDelay(20);     //每20ms扫描一次	
  }
}
3.5 Key_Task
static void KEY_Task(void* parameter)
{	
  while (1)
  {
    if( 0 == HAL_GPIO_ReadPin(KEY3_GPIO_Port, KEY3_Pin))
			{
				delay_ms(80);
			if( 1 == HAL_GPIO_ReadPin(KEY3_GPIO_Port, KEY3_Pin) )
			{
				/* K3 被按下 */
				printf("挂起任务\n");
				vTaskSuspend(Take_Task_Handle);/* 挂起Take任务 */
				vTaskSuspend(Give_Task_Handle);/* 挂起Give任务 */
				printf("挂起任务成功!\n");
				shining = 1;
			}
		}			
    if( 0 == HAL_GPIO_ReadPin(KEY4_GPIO_Port, KEY4_Pin) )
    {/* K4 被按下 */
			delay_ms(80);
			if( 0 == HAL_GPIO_ReadPin(KEY4_GPIO_Port, KEY4_Pin) )
			{/* K4 被按下 */
				printf("恢复任务!\n");
				vTaskResume(Take_Task_Handle);/* 恢复Take任务! */
				vTaskResume(Give_Task_Handle);/* 恢复Give任务! */
				printf("恢复任务成功!\n");
				shining = 0; 
			}
		}

    vTaskDelay(20);/* 延时20个tick */
  }
}

3.5LED_Task

/**********************************************************************
  * @ 函数名  : LED_Task
  * @ 功能说明: LED_Task任务主体
  * @ 参数    :   
  * @ 返回值  : 无
  ********************************************************************/
static void LED_Task(void* parameter)
{	 
	OLED_Clear();
	OLED_ShowChinese_Row(0,0,*huanying); 
	OLED_ShowChinese_Row(0,2,*shengyu); 
  while (1)
  {
		pub++;
				switch ( chewei ) 
					{	
						case 0:
							LED1_OFF;
							LED2_OFF;
							LED3_OFF;
							LED4_OFF;
							OLED_ShowChinese_Row(0,0,*wukongxain);
						  OLED_ShowString(0,2,"no free parking ");
							OLED_ShowNum(64,4, chewei,1,15);
							break; 
						
					  case 1:
							LED1_OFF;
						  LED2_OFF;
						  LED3_OFF;
						  LED4_ON;
							if(shining==1){
							delay_ms(500);
							LED4_OFF;
							delay_ms(500);
						}

              OLED_ShowNum(64,4, chewei,1,15);
							break;
					
						case 2:
							LED1_OFF;
						  LED2_OFF;
						  LED3_ON;
						  LED4_ON;
							if(shining==1){
							delay_ms(500);
							LED3_OFF;
							LED4_OFF;
							delay_ms(500);
						}

              OLED_ShowNum(64,4, chewei,1,15);

							break;
					
						case 3:
							LED1_OFF;
						  LED2_ON;
						  LED3_ON;
						  LED4_ON;
							if(shining==1){
							delay_ms(500);
							LED2_OFF;
							LED3_OFF;
							LED4_OFF;
							delay_ms(500);
						}

              OLED_ShowNum(64,4, chewei,1,15);

							break;
					
						case 4:
							LED1_ON;
						  LED2_ON;
						  LED3_ON;
						  LED4_ON;

							if(shining==1)
						{
							delay_ms(500);
							LED1_OFF;
							LED2_OFF;
							LED3_OFF;
							LED4_OFF;
							delay_ms(500);
						}
              OLED_ShowNum(64,4, chewei,1,15);

							break;
					
						default: 
						break;
					}
					//大概有1分钟上传一次数据
			if(pub%2000==0){
				pub = 0;
			STM32DHT11_StatusReport();
		}
  
				
		
		vTaskDelay(20);     //每20ms扫描一次	
	}
}
4.遇到一些问题
4.1 按键不灵敏

按键有些不灵敏,所要进行消抖

我用的是软件延时消抖,效果还不错,延时时间为80ms

 if( 0 == HAL_GPIO_ReadPin(KEY3_GPIO_Port, KEY3_Pin))
			{
				delay_ms(80);
			if( 1 == HAL_GPIO_ReadPin(KEY3_GPIO_Port, KEY3_Pin) )
			{
				/* K3 被按下 */
			}
		}

HAL_GPIO_ReadPin(KEY3_GPIO_Port, KEY3_Pin)

至于为什么用这个函数?

贴一张cubeMX配置图

image-20201217203620724

一般来说,需要区分这个GPIO口用于输入还是输出。

如果是output,那个一般选择no pull,这样,引脚才能根据你的output数据,进行正确输出。

如果是input,那么需要看具体应用的默认输入值是0还是1. 如果默认是输入0,则最好配置为pull down,反之则配置为pull up.

一个接有上拉电阻的端口设为输入状态时,他的常态就为高电平,用于检测低电平的输入

  • 9
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 22
    评论
评论 22
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值