FreeRTOS记录(九、一个裸机工程转FreeRTOS的实例)

2023-11-09

记录一下一个实际项目由裸机程序改成FreeRTOS,以前产品的平台还是C8051单片机上面的程序,
硬件平台改成了STM32L051,同时使用STM32CubeMX生成的工程,使用FreeRTOS系统
..EEPROM数据存储读取函数修改更新											2021/12/28
..测试,修改,再测试修改...												2021/12/29

对于裸机向FreeRTOS的转变,简单可以用下图表示:

在这里插入图片描述

我们前面的文章介绍过的FreeRTOS的任务原理,调度机制,这篇文章只做移植记录。

一、裸机程序到FreeRTOS概述

裸机到FreeRTOS的转变:

  • 该写的驱动还是要写,如果平台一样是可以直接用裸机中的。
    比如工程中的按键驱动mod_button.c
    按键驱动程序源码请参考我的另一篇博文:几个实用的按键驱动

  • 以前 驱动 或 函数 中的 ”干等“”的延时函数,不是中断中调用的情况下是可以直接改成·osDelay(ms 延时函数),us 的延时函数(I2C协议中使用的),可以沿用以前的。(STM32CubeMX 下并没有现成的 us 延时函数,可以自己写一个简单的);
    .
    在这里插入图片描述.
    上图为温湿度读取的函数,可以看到修改了多种不同的延时函数,因为用在了不同的平台上面;
    .
    在这里插入图片描述
    上图为 32Mhz 主频下面的 不准确 us 延时函数。

  • 以前 驱动 或 函数 中有些也是 轮询方式设计的,如果使用了 FreeRTOS 的一些信号量,任务通知,消息队列等可以实现 唤醒触发任务方式的机制,需要稍作修改。
    .
    在这里插入图片描述
    上图中判断是否接收到报文的语句if(Read_pt != Enocean_Data)是裸机中用来判断串口是否开始接收到了数据,接收到了,立即 干等 一段时间HAL_Delay(7);但是在FreeRTOS 中不需要这样的干等。

  • 以前的全局变量,为了修改少,移植简单,可以沿用以前的全局变量,包括一些标志位
    .
    在这里插入图片描述

  • 以前的 中断 ,不直接涉及到任务的,该怎样还是怎样:
    .
    在这里插入图片描述
    如果直接和任务有关的,需要做一定的修改, 比如 定时器定时任务 与 事件组有关:
    .
    在这里插入图片描述
    再比如串口接收中断 使用 消息队列接收数据:
    .在这里插入图片描述

  • 考虑到 STM32L051 的 ram 空间只有 8KB,还需要时刻关注着内存使用情况!!!

二、移植过程

2.1 基本框架搭建

1、首先在STM32CubeMX中根据原理图把基础的东西设置好,本测试的芯片是STM32L051C8T6,可以参考博文:

STM32L051测试 (一、使用CubeMX生成工程文件)

在window下使用VScode搭建ARM开发环境——手把手教学详细版
在这里插入图片描述

2、然后在FreeRTOS配置中,新建3个任务 和 一个消息队列,可参考博文:

FreeRTOS记录(一、熟悉开发环境以及CubeMX下FreeRTOS配置)

FreeRTOS记录(六、FreeRTOS消息队列—Enocean模块串口通讯、RAM空间不足问题分析)
.
在这里插入图片描述

3、添加基本的代码,添加以前写好的驱动文件,每个驱动拿过来记得在Makefile中添加一下,然后每一步编译保证没有错误(此部分是最初的源码,后面还需要补充,还会对每个细节进行说明):

在这里插入图片描述

4、添加FreeRTOS相关基本代码,直接上源码(此部分是最初的源码,后面还需要补充,还会对每个细节进行说明):

/* Includes ------------------------------------------------------------------*/
#include "FreeRTOS.h"
#include "task.h"
#include "main.h"
#include "cmsis_os.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */     
#include "stdio.h"
#include "Enocean.h"
#include "mod_button.h"
#include <string.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 */
BTN_STRUCT	LEARN_BUTTON_2S ={ Lrn_Key_GPIO_Port,	
            Lrn_Key_Pin, // Pin
						BTN_ACTIVE,			                  // Active state
						2000,		                          // Time the button has to be in 'Active state' to be recognized as pressed
						100 };

BTN_STRUCT	LEARN_BUTTON_150mS ={ Lrn_Key_GPIO_Port,	
            Lrn_Key_Pin, // Pin
						BTN_ACTIVE,			                  // Active state
						150,		                          // Time the button has to be in 'Active state' to be recognized as pressed
						100 };		

BTN_STRUCT	CLEAR_BUTTON_2S = { Clr_Key_GPIO_Port,	
            Clr_Key_Pin, // Pin
						BTN_ACTIVE,			                  // Active state
						2000,		                          //Time the button has to be in 'Active state' to be recognized as pressed
						100	 };	
BTN_STRUCT	CLEAR_BUTTON_150mS = { Clr_Key_GPIO_Port,	
            Clr_Key_Pin, // Pin
						BTN_ACTIVE,			                  // Active state
						150,		                          //Time the button has to be in 'Active state' to be recognized as pressed
						100	 };	

BTN_STRUCT	CLEAR_BUTTON_5S = { Clr_Key_GPIO_Port,	
            Clr_Key_Pin, // Pin
						BTN_ACTIVE,			                  // Active state
						4500,		                          // Time the button has to be in 'Active state' to be recognized as pressed
						100	 };	

BTN_STRUCT	LEARN_BUTTON_5S = { Lrn_Key_GPIO_Port,	
            Lrn_Key_Pin, // Pin
						BTN_ACTIVE,			                  // Active state
						5000,		                          // Time the button has to be in 'Active state' to be recognized as pressed
						100	 };	
/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN Variables */

/* USER CODE END Variables */
osThreadId KeyTaskHandle;
osThreadId LEDTaskHandle;
osThreadId enoecanreceivedHandle;
osMessageQId EnoceanQueueHandle;

/* Private function prototypes -----------------------------------------------*/
/* USER CODE BEGIN FunctionPrototypes */
void Relay_On(uint8_t ch)
{
  if(ch == 1){
    HAL_GPIO_WritePin(Relay1_Control_GPIO_Port,Relay1_Control_Pin,GPIO_PIN_SET);
    osDelay(20);
    HAL_GPIO_WritePin(Relay1_Control_GPIO_Port,Relay1_Control_Pin,GPIO_PIN_RESET);
  }
  else if(ch == 2){
    HAL_GPIO_WritePin(Relay2_Control_GPIO_Port,Relay2_Control_Pin,GPIO_PIN_SET);
    osDelay(20);
    HAL_GPIO_WritePin(Relay2_Control_GPIO_Port,Relay2_Control_Pin,GPIO_PIN_RESET);
  }
}

void Relay_Off(uint8_t ch)
{
  if(ch == 1){
    HAL_GPIO_WritePin(Relay1_Close_GPIO_Port,Relay1_Close_Pin,GPIO_PIN_SET);
    osDelay(20);
    HAL_GPIO_WritePin(Relay1_Close_GPIO_Port,Relay1_Close_Pin,GPIO_PIN_RESET);
  }
  else if(ch == 2){
    HAL_GPIO_WritePin(Relay2_Close_GPIO_Port,Relay2_Close_Pin,GPIO_PIN_SET);
    osDelay(20);
    HAL_GPIO_WritePin(Relay2_Close_GPIO_Port,Relay2_Close_Pin,GPIO_PIN_RESET);
  }
} 

/* USER CODE END FunctionPrototypes */

void StartKeyTask(void const * argument);
void StartLEDTask(void const * argument);
void StartenoecanTask(void const * argument);

void MX_FREERTOS_Init(void); /* (MISRA C 2004 rule 8.1) */

/* GetIdleTaskMemory prototype (linked to static allocation support) */
void vApplicationGetIdleTaskMemory( StaticTask_t **ppxIdleTaskTCBBuffer, StackType_t **ppxIdleTaskStackBuffer, uint32_t *pulIdleTaskStackSize );

/* USER CODE BEGIN GET_IDLE_TASK_MEMORY */
static StaticTask_t xIdleTaskTCBBuffer;
static StackType_t xIdleStack[configMINIMAL_STACK_SIZE];
  
void vApplicationGetIdleTaskMemory( StaticTask_t **ppxIdleTaskTCBBuffer, StackType_t **ppxIdleTaskStackBuffer, uint32_t *pulIdleTaskStackSize )
{
  *ppxIdleTaskTCBBuffer = &xIdleTaskTCBBuffer;
  *ppxIdleTaskStackBuffer = &xIdleStack[0];
  *pulIdleTaskStackSize = configMINIMAL_STACK_SIZE;
  /* place for user code */
}                   
/* USER CODE END GET_IDLE_TASK_MEMORY */

/**
  * @brief  FreeRTOS initialization
  * @param  None
  * @retval None
  */
void MX_FREERTOS_Init(void) {
  /* USER CODE BEGIN Init */
  printf("Free RTOS init! \r\n");  //for printf test
  /* USER CODE END Init */

  /* 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 */

  /* Create the queue(s) */
  /* definition and creation of EnoceanQueue */
  osMessageQDef(EnoceanQueue, 50, uint8_t);
  EnoceanQueueHandle = osMessageCreate(osMessageQ(EnoceanQueue), NULL);

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

  /* Create the thread(s) */
  /* definition and creation of KeyTask */
  osThreadDef(KeyTask, StartKeyTask, osPriorityAboveNormal, 0, 300);
  KeyTaskHandle = osThreadCreate(osThread(KeyTask), NULL);

  /* definition and creation of LEDTask */
  osThreadDef(LEDTask, StartLEDTask, osPriorityLow, 0, 64);
  LEDTaskHandle = osThreadCreate(osThread(LEDTask), NULL);

  /* definition and creation of enoecanreceived */
  osThreadDef(enoecanreceived, StartenoecanTask, osPriorityHigh, 0, 192);
  enoecanreceivedHandle = osThreadCreate(osThread(enoecanreceived), NULL);

  /* USER CODE BEGIN RTOS_THREADS */
  /* add threads, ... */
  __HAL_UART_ENABLE_IT(&hlpuart1,UART_IT_RXNE); //

  COMMAND_GetmoduleID(); 
  /* USER CODE END RTOS_THREADS */

}

/* USER CODE BEGIN Header_StartKeyTask */
/**
  * @brief  Function implementing the KeyTask thread.
  * @param  argument: Not used 
  * @retval None
  */
/* USER CODE END Header_StartKeyTask */
void StartKeyTask(void const * argument)
{
  /* USER CODE BEGIN StartKeyTask */
  /* Infinite loop */
  for(;;)
  {
    if(btn_getState(&LEARN_BUTTON_150mS) == BTN_EDGE2){
        taskENTER_CRITICAL();
        printf("K1 kicked !!,BIT_KEY set!\r\n");
        taskEXIT_CRITICAL();

    }

    if((btn_getState(&LEARN_BUTTON_2S) == BTN_PRESSED)){  
        taskENTER_CRITICAL();
        printf("K1 pushed 2S!!,send enocean!\r\n");
        taskEXIT_CRITICAL();
        // osThreadSuspendAll();
        // SendLrnTelegram();
        // osThreadResumeAll();
        while(btn_getState(&LEARN_BUTTON_150mS));
    }

    if(btn_getState(&CLEAR_BUTTON_150mS) == BTN_EDGE2){
        taskENTER_CRITICAL();
        printf("K2 pushed!!\r\n");        
        printf("==================================\r\n");
        printf("任务名          任务状态    优先级     剩余栈   任务序号\r\n");
        taskEXIT_CRITICAL();
        uint8_t mytaskstatebuffer[500];
        osThreadList((uint8_t *)&mytaskstatebuffer);
        taskENTER_CRITICAL();
        printf("%s\r\n",mytaskstatebuffer);
        taskEXIT_CRITICAL();
    }

    if((btn_getState(&CLEAR_BUTTON_5S) == BTN_PRESSED)){  
        taskENTER_CRITICAL();
        printf("Clear pushed 5S!!\r\n");
        taskEXIT_CRITICAL();
        // osThreadSuspendAll();
        // SendLrnTelegram();
        // osThreadResumeAll();
        while(btn_getState(&CLEAR_BUTTON_2S));
        while(btn_getState(&CLEAR_BUTTON_150mS));
    }
    osDelay(1);
  }
  /* USER CODE END StartKeyTask */
}

/* USER CODE BEGIN Header_StartLEDTask */
/**
* @brief Function implementing the LEDTask thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_StartLEDTask */
void StartLEDTask(void const * argument)
{
  /* USER CODE BEGIN StartLEDTask */
  /* Infinite loop */
  for(;;)
  {
    HAL_GPIO_TogglePin(Clr_Led_GPIO_Port,Clr_Led_Pin);
    osDelay(500);
    HAL_GPIO_TogglePin(Lrn_Led_GPIO_Port,Lrn_Led_Pin);
    osDelay(500);
  }
  /* USER CODE END StartLEDTask */
}

/* USER CODE BEGIN Header_StartenoecanTask */
/**
* @brief Function implementing the enoecanreceived thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_StartenoecanTask */
void StartenoecanTask(void const * argument)
{
  /* USER CODE BEGIN StartenoecanTask */
  TEL_RADIO_TYPE	rTel;
  TEL_PARAM_TYPE  pTel;
  /* Infinite loop */
  for(;;)
  {
    if(xQueueReceive(EnoceanQueueHandle,&USART_Enocean_BUF[Enocean_Data++],portMAX_DELAY) == pdPASS){
      while(xQueueReceive(EnoceanQueueHandle,&USART_Enocean_BUF[Enocean_Data++],10));
      Enocean_Data -= 1;
      if(Enocean_Data >= 38){
        Getmodule_ID(&u32MyId);
        taskENTER_CRITICAL();
        printf("my ID is :0x %x\r\n",(unsigned int)(long)u32MyId);
        taskEXIT_CRITICAL();
      }
      // HAL_UART_Transmit(&huart1,USART_Enocean_BUF, Enocean_Data,0xFFFF); //将串口3接收到的数据通过串口1传出 
      else if(radio_getTelegram(&rTel,&pTel) == OK){
          if((rTel.trps.u8Choice == RADIO_CHOICE_RPS)||(rTel.trps.u8Choice == RADIO_CHOICE_1BS)){
            taskENTER_CRITICAL();
            printf("rps/1bs received!!BIT_Radio set!\r\n");
            taskEXIT_CRITICAL();
          }
          else if(rTel.trps.u8Choice == RADIO_CHOICE_4BS){
            taskENTER_CRITICAL();
            printf("4bs received\r\n"); 
            taskEXIT_CRITICAL();
          }      
      }
      memset(USART_Enocean_BUF,0,sizeof(USART_Enocean_BUF));
      Enocean_Data=0;
    }
    osDelay(1);
  }
  /* USER CODE END StartenoecanTask */
}

/* Private application code --------------------------------------------------*/
/* USER CODE BEGIN Application */
     
/* USER CODE END Application */

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

2.2 代码修改

在完成上述基本移植的基础上,需要针对实际需要的功能 对 相应的代码进行修改.

2.2.1 按键任务(使用状态机)

首先修改的是按键部分,按键虽然有不同的情况,但我这里都放在同一个任务中,对以前的按键操作,基本上是实现了相同的效果,其中还有一个按键是查看所有任务的运气情况和任务栈使用情况,前面的FreeRTOS记录博文中也有如何实现:

void StartKeyTask(void const * argument)
{
  /* USER CODE BEGIN StartKeyTask */
  uint8 um;
  /* Infinite loop */
  for(;;)
  {      
    if(btn_getState(&LEARN_BUTTON_150mS) == BTN_EDGE2)
    {
      /*in normol work mode,when lrn key kicked,change the work mode (1~3 mode loop )*/
      if((bSettingmodeOn == FALSE)&&(bLearnModeOn == FALSE)&&(bCLEARModeOn == FALSE))
      {
        CurrentOpeartion_Mode++;
        if(CurrentOpeartion_Mode == 4)
          CurrentOpeartion_Mode = 1;
        LED_Flash_Times('L',CurrentOpeartion_Mode,0);
        taskENTER_CRITICAL();
        printf("lrn kicked !!,change the work mode!\r\n");
        taskEXIT_CRITICAL();
        // MyData.MODENUM = CurrentOpeartion_Mode;
        // mem_writeFlash((uint8 xdata *)&MyData,APP_FLASH_USER_TABLE,sizeof(MyData));
        // rTel_State.t4bs.u8Data0 |= 0x08;                                     
        // rTel_State.t4bs.u8Data0 &= 0x8F;
        // rTel_State.t4bs.u8Data0 |= (CurrentOpeartion_Mode<<4);              
        // radio_sendTelegram(&rTel_State, &pTel_State);   
      }
      /*in learn mode,when lrn key kicked,change the learn channal*/
      else if(Learn_Channel1_Flag == 1){       
        for(um=0;um<2;um++)
        {
          LRN_LED_OFF;
          osDelay(100);
          LRN_LED_ON;
          osDelay(100);
        }
        taskENTER_CRITICAL();
        printf("next chanle learn!\r\n");
        taskEXIT_CRITICAL();
        Learn_Channel1_Flag = 0;
        Learn_Channel2_Flag = 1; 
      }
      /*in set mode,when lrn key kicked,change the parameter(1~3 parameter loop)*/
      else if(bSettingmodeOn == TRUE){
        parameter++;
        if(parameter == 4){parameter = 1;}
        LED_Flash_Times('L',parameter,bSettingmodeOn);
        taskENTER_CRITICAL();
        printf("parameter %d set!\r\n",parameter);
        taskEXIT_CRITICAL();
      }
    }

    if((btn_getState(&LEARN_BUTTON_2S) == BTN_PRESSED)){
      /*in normol work mode,when lrn key pressed 2s,get into the learn mode*/ 
      if((clrvalue == GPIO_PIN_RESET)&&(bSettingmodeOn == FALSE)&&(bLearnModeOn == FALSE)&&(bCLEARModeOn == FALSE)){
        bSettingmodeOn = TRUE;
        for(um=0;um<6;um++)
        {
          LRN_LED_ON;CLR_LED_ON;
          osDelay(50);
          LRN_LED_OFF;CLR_LED_OFF;
          osDelay(50);
        }
        LRN_LED_ON;CLR_LED_ON;
        taskENTER_CRITICAL();
        printf("Clr and lrn pushed 5S!!,into set mode!\r\n");
        taskEXIT_CRITICAL();
      }
      /*in normol work mode,when lrn and clr key pressed 2s together,get into the set mode*/ 
      else if((clrvalue == GPIO_PIN_SET)&&(bSettingmodeOn == FALSE)){
        bLearnModeOn = !bLearnModeOn;
        if(bCLEARModeOn == TRUE)
        {
          bCLEARModeOn = FALSE;
          // Clear_Channel1_Flag = 0;  
          // Clear_Channel2_Flag = 0;  
          CLR_LED_OFF;
        }
        if (bLearnModeOn){
          ModePriority_Flag = 0;
          // radio_sendTelegram(&rTel_State, &pTel_State);
          for(um=0;um<8;um++)
          {
            LRN_LED_ON;
            osDelay(50);
            LRN_LED_OFF;
            osDelay(50);
          }
          LRN_LED_ON;
          Learn_Channel1_Flag = 1;
        }
        else
        {
          LRN_LED_OFF;
          Learn_Channel2_Flag = 0;                          
          ModePriority_Flag = 1;
        }
        taskENTER_CRITICAL();
        printf("lrn pushed 2S!!,into or out learn mode!\r\n");
        taskEXIT_CRITICAL();
      }
      while(btn_getState(&LEARN_BUTTON_150mS));
      while(btn_getState(&CLEAR_BUTTON_150mS));
    }

    if(btn_getState(&CLEAR_BUTTON_150mS) == BTN_EDGE2){
      /*in normol work mode,when clr key kicked,send one learn Telegram*/
      if(bSettingmodeOn == FALSE){
        taskENTER_CRITICAL();
        printf("K2 pushed!!,send lrn radio...\r\n");        
        printf("==================================\r\n");
        printf("任务名          任务状态    优先级     剩余栈   任务序号\r\n");
        taskEXIT_CRITICAL();
        uint8_t mytaskstatebuffer[500];
        osThreadList((uint8_t *)&mytaskstatebuffer);
        taskENTER_CRITICAL();
        printf("%s\r\n",mytaskstatebuffer);
        taskEXIT_CRITICAL();
        SendLrnTelegram();
      }
      /*in set mode,when clr key kicked,change the parameter value*/
      else if(bSettingmodeOn == TRUE){
        if(parameter == 1){
          Tcnt++;
            if(Tcnt == 7)
          Tcnt = 1;
          LED_Flash_Times('C',Tcnt,bSettingmodeOn);
          // MyData.DelayTimeParameter = Tcnt;	   //LightParameter;
          // mem_writeFlash((uint8 xdata *)&MyData,APP_FLASH_USER_TABLE,sizeof(MyData));
			
          switch(Tcnt){
          case 1: 
            ONTime_Delay = 60000;	   //delay 1min
            break;
          case 2: 
            ONTime_Delay = 300000;	   //delay 5min
            break;
          case 3: 
            ONTime_Delay = 600000;	   //delay 10min
            break;
          case 4: 
            ONTime_Delay = 900000;	   //delay 15min
            break;
          case 5: 
            ONTime_Delay = 1200000;    //delay 20min
            break;
          case 6: 
            ONTime_Delay = 1800000;    //delay 30min
            break;
          }
        }
        else if(parameter == 2){
          Lcnt++;
            if(Lcnt == 7)
          Lcnt = 1;
          LED_Flash_Times('C',Lcnt,bSettingmodeOn);
          // MyData.LightParameter = Lcnt; 
          // mem_writeFlash((uint8 xdata *)&MyData,APP_FLASH_USER_TABLE,sizeof(MyData));
	
			    switch(Lcnt){
          case 1: 
            Lux_Threshold = 50; 	 //Lighter	_  50lux 
            break;
          case 2: 
            Lux_Threshold = 100;	 //
            break;
          case 3: 
            Lux_Threshold = 150;	 //
            break;
          case 4: 
            Lux_Threshold = 200;	 //
            break;
          case 5: 
            Lux_Threshold = 300;	//
            break;
          case 6: 
            Lux_Threshold = 500;	//
            break;
          }
			  }	
			  else if(parameter == 3){
          Rcnt++;
            if(Rcnt == 4)
          Rcnt = 1;
          LED_Flash_Times('C',Rcnt,bSettingmodeOn);
          // MyData.RepeaterParameter = Rcnt; 
          // mem_writeFlash((uint8 xdata *)&MyData,APP_FLASH_USER_TABLE,sizeof(MyData));

          switch(Rcnt){
          case 1:
            REPEATER_OFF();
            break;
          case 2: 
            REPEATER_ONE_ON();
            break;
          case 3:
            REPEATER_TWO_ON();
            break;   
          }
        }
      }
    }

    if((btn_getState(&CLEAR_BUTTON_5S) == BTN_PRESSED)){
        if(bSettingmodeOn == FALSE){
			    if(bLearnModeOn == TRUE)
			    {
            bLearnModeOn = FALSE;
            LRN_LED_OFF;
			    }
				// ClearALLLearnedID(smSensors1,APP_FLASH_CHANNEL1_TABLE,MAX_SMACK_SENSORS,CHANNEL1);
				// ClearALLLearnedID(smSensors2,APP_FLASH_CHANNEL2_TABLE,MAX_SMACK_SENSORS,CHANNEL2);
          LED_Flash_Times('C',5,0);
          bCLEARModeOn = FALSE;
				}
        else if(bSettingmodeOn == TRUE){  //在参数设置模式下,按CLEAR是退出设置模式
			  	for(um=0;um<6;um++)
          {
            LRN_LED_ON;CLR_LED_ON;
            osDelay(50);
            LRN_LED_OFF;CLR_LED_OFF;
            osDelay(50);
          }
          // CLEAR_UART_Buffer(RX,RX_Buffer);
          //Globle_Int_EN(1);
          bSettingmodeOn = FALSE;
        }
        taskENTER_CRITICAL();
        printf("Clear pushed 5S!!,clear all ID or exit setting mode! ...\r\n");
        taskEXIT_CRITICAL();
        while(btn_getState(&CLEAR_BUTTON_2S));
        while(btn_getState(&CLEAR_BUTTON_150mS));
    }
    osDelay(1);
  }
  /* USER CODE END StartKeyTask */
}

时刻关注着 内存使用情况:

在这里插入图片描述

2.2.3 数据存储(使用STM32L051内置EEPROM保存数据)

因为产品在使用过程中,有一些数据是需要掉电保存的,所以需要使用到 EEPROM,当然 内部的 Flash 也是可以用来保存的,只不过对于 STM32L051 而言,有内置的 EEPROM ,使用起来更加方便,如果平台是 STM32F103系列,只能使用内部的Flash 或者外接 EEPROM 了。

对于 STM32L051 的使用说明,可以参考我的另外一篇博文:
STM32L051测试 (四、Flash和EEPROM的读写)

需要保存一些参数数据,和无线设备ID等参数数据,还考考虑2个通道,stml0_flash.h文件中一些宏定义如下:


/*
two relay_control
*/
#define CHANNEL1_ADDR_BASE    0x08080000
#define CH1_CHANNEL1_ADDR     0x08080000 + 0				
#define CH2_CHANNEL1_ADDR     0x08080000 + 10
#define CH3_CHANNEL1_ADDR     0x08080000 + 20
#define CH4_CHANNEL1_ADDR     0x08080000 + 30
#define CH5_CHANNEL1_ADDR     0x08080000 + 40
#define CH6_CHANNEL1_ADDR     0x08080000 + 50
#define CH7_CHANNEL1_ADDR     0x08080000 + 60 
#define CH8_CHANNEL1_ADDR     0x08080000 + 70 

#define CHANNEL2_ADDR_BASE    0x08080000 + EEPROM_PAGE_SIZE
#define CH1_CHANNEL2_ADDR     0x08080000 + EEPROM_PAGE_SIZE + 0				
#define CH2_CHANNEL2_ADDR     0x08080000 + EEPROM_PAGE_SIZE + 10
#define CH3_CHANNEL2_ADDR     0x08080000 + EEPROM_PAGE_SIZE + 20
#define CH4_CHANNEL2_ADDR     0x08080000 + EEPROM_PAGE_SIZE + 30
#define CH5_CHANNEL2_ADDR     0x08080000 + EEPROM_PAGE_SIZE + 40
#define CH6_CHANNEL2_ADDR     0x08080000 + EEPROM_PAGE_SIZE + 50
#define CH7_CHANNEL2_ADDR     0x08080000 + EEPROM_PAGE_SIZE + 60 
#define CH8_CHANNEL2_ADDR     0x08080000 + EEPROM_PAGE_SIZE + 70 


#define User_Data_ADDR  DATA_EEPROM_END_ADDR - 100

#define CurChannelNums    2 
#define NO_SENSOR_ID	0x00000000  //因为L051 EEPROM 初始化是0
#define DefaultVal      0x00
#define MAX_SMACK_SENSORS  8

/* 4 + 1+ 1+ 1+ 1 = 8 考虑蓝牙 +2  10 */
typedef struct
{
	uint32 	u32SensorId;
	uint8 	RORG;
	uint8 	FUNC;
	uint8 	TYPE;
	uint8	u8LearnSN;	
}__attribute__ ((packed)) LEARNED_SENSORS;

/* 放在EEPROM 最后的地方  留 10 个字节*/
typedef struct                    
{
	uint8 	MODENUM;
	uint8	LightParameter;
	uint8   DelayTimeParameter;
	uint8	RepeaterParameter;
	uint8 	AutoAndManual_MODE[CurChannelNums];
	uint8	IDNUM[CurChannelNums];      		
}__attribute__ ((packed)) User_Data;

extern User_Data MyData;
extern LEARNED_SENSORS smSensors_ch1[8];
extern LEARNED_SENSORS smSensors_ch2[8];

typedef enum                    
{
	CHANNEL1 = 0,
	CHANNEL2,
} CHANNEL_ID;


然后在stml0_flash.c文件中,加入以下相关的函数,下面是清除和读取函数(下面的写ID函数其实有问题,每次写的都是结构体指针的第一个元素的值):

/*
  双路执行器操作
*/
/*
  HAL_FLASHEx_DATAEEPROM_Erase(uint32_t Address)
  0-80
  EEPROM_PAGE_SIZE + 80
  全部清除
*/
void EnoceanID_ErasePage()
{
  u8 i = 0;
	HAL_FLASHEx_DATAEEPROM_Unlock(); 
  for(i=0; i<21; i++){
    HAL_FLASHEx_DATAEEPROM_Erase(CH1_CHANNEL1_ADDR + 4*i); 
    HAL_FLASHEx_DATAEEPROM_Erase(CH1_CHANNEL2_ADDR + 4*i);
  }
	HAL_FLASHEx_DATAEEPROM_Lock();
}

/* 固定地址,少传参数 */
User_Data Set_User_DataRead()
{
	User_Data uservalue;
  uservalue.MODENUM                 = FLASH_Readbyte((uint32_t)User_Data_ADDR);
  uservalue.LightParameter          = FLASH_Readbyte((uint32_t)(User_Data_ADDR + 1));
  uservalue.DelayTimeParameter      = FLASH_Readbyte((uint32_t)(User_Data_ADDR + 2));
  uservalue.RepeaterParameter       = FLASH_Readbyte((uint32_t)(User_Data_ADDR + 3));
  uservalue.AutoAndManual_MODE[0]   = FLASH_Readbyte((uint32_t)(User_Data_ADDR + 4));
  uservalue.AutoAndManual_MODE[1]   = FLASH_Readbyte((uint32_t)(User_Data_ADDR + 5));
  uservalue.IDNUM[0]                = FLASH_Readbyte((uint32_t)(User_Data_ADDR + 6));
  uservalue.IDNUM[1]                = FLASH_Readbyte((uint32_t)(User_Data_ADDR + 7));

  return uservalue;
}

LEARNED_SENSORS channel_DataRead(uint32_t address)
{
  LEARNED_SENSORS channelvalue;
  channelvalue.u32SensorId = FLASH_ReadWord(address);
  channelvalue.RORG        = FLASH_Readbyte(address + 4);
  channelvalue.FUNC        = FLASH_Readbyte(address + 5);
  channelvalue.TYPE        = FLASH_Readbyte(address + 6);
  channelvalue.u8LearnSN   = FLASH_Readbyte(address + 7);
  return channelvalue;
}

void channel_all_dataRead(LEARNED_SENSORS *smSensors,CHANNEL_ID ChannelNum){
  uint8 u8Count;
  for(u8Count=0;u8Count<8;u8Count++){
    if(ChannelNum == 0){
      smSensors[u8Count] = channel_DataRead(CHANNEL1_ADDR_BASE + u8Count*10);
    }
    else if(ChannelNum == 1){
      smSensors[u8Count] = channel_DataRead(CHANNEL2_ADDR_BASE + u8Count*10);
    }
  }
}


void  FLASH_WriteParameter(User_Data * mydata)
{
	u8 i = 0;
  u8 writedata[8]={0};
  writedata[0] = mydata->MODENUM;
  writedata[1] = mydata->LightParameter;
  writedata[2] = mydata->DelayTimeParameter;
  writedata[3] = mydata->RepeaterParameter;
  writedata[4] = mydata->AutoAndManual_MODE[0];
  writedata[5] = mydata->AutoAndManual_MODE[1];
  writedata[6] = mydata->IDNUM[0];
  writedata[7] = mydata->IDNUM[1];
  HAL_FLASHEx_DATAEEPROM_Unlock(); 
  for(i=0; i<8; i++){
    while(HAL_FLASHEx_DATAEEPROM_Program(FLASH_TYPEPROGRAMDATA_BYTE, User_Data_ADDR + i, writedata[i]) != HAL_OK);
  }
	HAL_FLASHEx_DATAEEPROM_Lock();
}

/*
void  FLASH_WriteSensorID(LEARNED_SENSORS * mysensors,uint8 num, CHANNEL_ID  ChannelNum)
{
  uint32 sensorid;
  uint8  sensorvalue[4];
  u8 i = 0;
  sensorid        = mysensors->u32SensorId;
  sensorvalue[0]  = mysensors->RORG;
  sensorvalue[1]  = mysensors->FUNC;
  sensorvalue[2]  = mysensors->TYPE;
  sensorvalue[3]  = mysensors->u8LearnSN;
  HAL_FLASHEx_DATAEEPROM_Unlock(); //!!!问题所在!!!
  if(ChannelNum == 0){
    switch (num)
    {
    case 0:
      FLASH_WriteWord(CH1_CHANNEL1_ADDR,sensorid);//!!!问题所在!!!这里会锁EEPROM
      for(i=0; i<4; i++){
        while(HAL_FLASHEx_DATAEEPROM_Program(FLASH_TYPEPROGRAMDATA_BYTE, CH1_CHANNEL1_ADDR + 4 + i, sensorvalue[i]) != HAL_OK);
   */
      }
void  FLASH_WriteSensorID(LEARNED_SENSORS *mysensors,uint8 num, CHANNEL_ID  ChannelNum)
{

  uint32 sensorid;
  uint8  sensorvalue[4] = {0};
  u8 i = 0;
  sensorid        = mysensors->u32SensorId;
  sensorvalue[0]  = mysensors->RORG;
  sensorvalue[1]  = mysensors->FUNC;
  sensorvalue[2]  = mysensors->TYPE;
  sensorvalue[3]  = mysensors->u8LearnSN;

  if(ChannelNum == 0){
    switch (num)
    {
    case 0:
      FLASH_WriteWord(CH1_CHANNEL1_ADDR,sensorid);
      HAL_FLASHEx_DATAEEPROM_Unlock(); 
      for(i=0; i<4; i++){        
        while(HAL_FLASHEx_DATAEEPROM_Program(FLASH_TYPEPROGRAMDATA_BYTE, CH1_CHANNEL1_ADDR + 4 + i, sensorvalue[i]) != HAL_OK);
      }
      break;
    case 1:
      FLASH_WriteWord(CH2_CHANNEL1_ADDR,sensorid);
      HAL_FLASHEx_DATAEEPROM_Unlock(); 
      for(i=0; i<4; i++){
        while(HAL_FLASHEx_DATAEEPROM_Program(FLASH_TYPEPROGRAMDATA_BYTE, CH2_CHANNEL1_ADDR + 4 + i, sensorvalue[i]) != HAL_OK);
      } 
      break;
    case 2:
      FLASH_WriteWord(CH3_CHANNEL1_ADDR,sensorid);
      HAL_FLASHEx_DATAEEPROM_Unlock(); 
      for(i=0; i<4; i++){
        while(HAL_FLASHEx_DATAEEPROM_Program(FLASH_TYPEPROGRAMDATA_BYTE, CH3_CHANNEL1_ADDR + 4 + i, sensorvalue[i]) != HAL_OK);
      } 
      break;
    case 3:
      FLASH_WriteWord(CH4_CHANNEL1_ADDR,sensorid);
      HAL_FLASHEx_DATAEEPROM_Unlock(); 
      for(i=0; i<4; i++){
        while(HAL_FLASHEx_DATAEEPROM_Program(FLASH_TYPEPROGRAMDATA_BYTE, CH4_CHANNEL1_ADDR + 4 + i, sensorvalue[i]) != HAL_OK);
      }
      break;
    case 4:
      FLASH_WriteWord(CH5_CHANNEL1_ADDR,sensorid);
      HAL_FLASHEx_DATAEEPROM_Unlock(); 
      for(i=0; i<4; i++){
        while(HAL_FLASHEx_DATAEEPROM_Program(FLASH_TYPEPROGRAMDATA_BYTE, CH5_CHANNEL1_ADDR + 4 + i, sensorvalue[i]) != HAL_OK);
      }
      break;
    case 5:
      FLASH_WriteWord(CH6_CHANNEL1_ADDR,sensorid);
      HAL_FLASHEx_DATAEEPROM_Unlock(); 
      for(i=0; i<4; i++){
        while(HAL_FLASHEx_DATAEEPROM_Program(FLASH_TYPEPROGRAMDATA_BYTE, CH6_CHANNEL1_ADDR + 4 + i, sensorvalue[i]) != HAL_OK);
      }
      break;
    case 6:
      FLASH_WriteWord(CH7_CHANNEL1_ADDR,sensorid);
      HAL_FLASHEx_DATAEEPROM_Unlock(); 
      for(i=0; i<4; i++){
        while(HAL_FLASHEx_DATAEEPROM_Program(FLASH_TYPEPROGRAMDATA_BYTE, CH7_CHANNEL1_ADDR + 4 + i, sensorvalue[i]) != HAL_OK);
      }
      break;
    case 7:
      FLASH_WriteWord(CH8_CHANNEL1_ADDR,sensorid);
      HAL_FLASHEx_DATAEEPROM_Unlock(); 
      for(i=0; i<4; i++){
        while(HAL_FLASHEx_DATAEEPROM_Program(FLASH_TYPEPROGRAMDATA_BYTE, CH8_CHANNEL1_ADDR + 4 + i, sensorvalue[i]) != HAL_OK);
      }
      break; 
    default:
      break;
    }
  }
  else if(ChannelNum == 1){
    switch (num)
    {
    case 0:
      FLASH_WriteWord(CH1_CHANNEL2_ADDR,sensorid);
      HAL_FLASHEx_DATAEEPROM_Unlock(); 
      for(i=0; i<4; i++){
        while(HAL_FLASHEx_DATAEEPROM_Program(FLASH_TYPEPROGRAMDATA_BYTE, CH1_CHANNEL2_ADDR + 4 + i, sensorvalue[i]) != HAL_OK);
      }
      break;
    case 1:
      FLASH_WriteWord(CH2_CHANNEL2_ADDR,sensorid);
      HAL_FLASHEx_DATAEEPROM_Unlock(); 
      for(i=0; i<4; i++){
        while(HAL_FLASHEx_DATAEEPROM_Program(FLASH_TYPEPROGRAMDATA_BYTE, CH2_CHANNEL2_ADDR + 4 + i, sensorvalue[i]) != HAL_OK);
      }
      break;
    case 2:
      FLASH_WriteWord(CH3_CHANNEL2_ADDR,sensorid);
      HAL_FLASHEx_DATAEEPROM_Unlock(); 
      for(i=0; i<4; i++){
        while(HAL_FLASHEx_DATAEEPROM_Program(FLASH_TYPEPROGRAMDATA_BYTE, CH3_CHANNEL2_ADDR + 4 + i, sensorvalue[i]) != HAL_OK);
      }
      break;
    case 3:
      FLASH_WriteWord(CH4_CHANNEL2_ADDR,sensorid);
      HAL_FLASHEx_DATAEEPROM_Unlock(); 
      for(i=0; i<4; i++){
        while(HAL_FLASHEx_DATAEEPROM_Program(FLASH_TYPEPROGRAMDATA_BYTE, CH4_CHANNEL2_ADDR + 4 + i, sensorvalue[i]) != HAL_OK);
      }
      break;
    case 4:
      FLASH_WriteWord(CH5_CHANNEL2_ADDR,sensorid);
      HAL_FLASHEx_DATAEEPROM_Unlock(); 
      for(i=0; i<4; i++){
        while(HAL_FLASHEx_DATAEEPROM_Program(FLASH_TYPEPROGRAMDATA_BYTE, CH5_CHANNEL2_ADDR + 4 + i, sensorvalue[i]) != HAL_OK);
      }
      break;
    case 5:
      FLASH_WriteWord(CH6_CHANNEL2_ADDR,sensorid);
      HAL_FLASHEx_DATAEEPROM_Unlock(); 
      for(i=0; i<4; i++){
        while(HAL_FLASHEx_DATAEEPROM_Program(FLASH_TYPEPROGRAMDATA_BYTE, CH6_CHANNEL2_ADDR + 4 + i, sensorvalue[i]) != HAL_OK);
      }
      break;
    case 6:
      FLASH_WriteWord(CH7_CHANNEL2_ADDR,sensorid);
      HAL_FLASHEx_DATAEEPROM_Unlock(); 
      for(i=0; i<4; i++){
        while(HAL_FLASHEx_DATAEEPROM_Program(FLASH_TYPEPROGRAMDATA_BYTE, CH7_CHANNEL2_ADDR + 4 + i, sensorvalue[i]) != HAL_OK);
      }
      break;
    case 7:
      FLASH_WriteWord(CH8_CHANNEL2_ADDR,sensorid);
      HAL_FLASHEx_DATAEEPROM_Unlock(); 
      for(i=0; i<4; i++){
        while(HAL_FLASHEx_DATAEEPROM_Program(FLASH_TYPEPROGRAMDATA_BYTE, CH8_CHANNEL2_ADDR + 4 + i, sensorvalue[i]) != HAL_OK);
      }
      break; 
    default:
      break;
    }
  }
  HAL_FLASHEx_DATAEEPROM_Lock();
}


需要定义3个全局变量,保存 参数配置 和 ID数据,虽然数据存放在 EEPROM 中,但是并不需要 每次比较都从 EEPROM 中读取(如果是Flash读取相对来说,次数少,时间长,影响效率),
所以只需要上电读取出来一次数据,放到全局变量中,然后比较的时候直接和全局变量比较,如果进行了参数设计,修改了 EERPOM 的数据,随时更新一下 全局变量的数值,这几个全局变量 无法避免的需要占用 RAM 空间:

在这里插入图片描述

修改到现在,还是感慨一下,本来是为了减少代码修改量,直接复制以前的代码,尽可能的不修改框架,但是以前是基于 51 单片机,对于数据存储和 STM32 大不一样,所以对于数据存储,包括后面的学习来说和以前代码的兼容性,太费心了,还不如直接按照自己的思路重新设计一下。

时刻关注着 内存使用情况:

在这里插入图片描述

对于上面的写ID函数
void FLASH_WriteSensorID(LEARNED_SENSORS * mysensors,uint8 num, CHANNEL_ID ChannelNum)
因为程序中定义的其实是一个结构体数组,所以上面的函数在写的时候永远写的是第一个数组第一个结构体的值:

在这里插入图片描述
函数没有问题,是在调用的时候,调用出错,正确方式应该是如下方式:

在这里插入图片描述

2.2.3 报文接收任务(使用消息队列接收)

报文接收使用消息队列,具体的框架在另一篇博文有提到:
FreeRTOS记录(六、FreeRTOS消息队列—Enocean模块串口通讯、RAM空间不足问题分析)

void StartenoecanTask(void const * argument)
{
  /* USER CODE BEGIN StartenoecanTask */
  uint8 ACK_Val;
  uint32 u32GatewayID = 0;
  /* Infinite loop */
  for(;;)
  {
    if(xQueueReceive(EnoceanQueueHandle,&USART_Enocean_BUF[Enocean_Data++],portMAX_DELAY) == pdPASS){
      while(xQueueReceive(EnoceanQueueHandle,&USART_Enocean_BUF[Enocean_Data++],7));
      if(Enocean_Data > 1)Enocean_Data -= 1;
        // printf("%d\r\n",Enocean_Data);
      if((Enocean_Data >= 38)&&(u32MyId == 0)){
        Getmodule_ID(&u32MyId);
        taskENTER_CRITICAL();
        printf("my ID is :0x %x\r\n",(unsigned int)(long)u32MyId);
        taskEXIT_CRITICAL();
        if(MyData.RepeaterParameter == 0x00);
        else{
          Rcnt = MyData.RepeaterParameter; 
          switch(Rcnt){
          case 1:
            REPEATER_OFF();
            break;
          case 2: 
            REPEATER_ONE_ON();
            break;
          case 3:
            REPEATER_TWO_ON();
            break;
          }
        }
      }  
      else if ((bLearnModeOn == FALSE)&&(bCLEARModeOn == FALSE)&&(bSettingmodeOn == FALSE)){
        if(radio_getTelegram(&rTel,&pTel) == OK){
          if(pTel.p_rx.u32DestinationId != u32MyId){
            taskENTER_CRITICAL();
            printf("receive radio need analysis!!!\r\n");
            taskEXIT_CRITICAL();
            if(MyData.IDNUM[0]){
              ACK_Val = RadioTelegram_PROCESS(smSensors_ch1,MAX_SMACK_SENSORS);
              if(ACK_Val)
              {
                ChannelAct_Process(ACK_Val,CurrentOpeartion_Mode,CHANNEL1);
                CH1_Find = 1;
              }
            }
            if(MyData.IDNUM[1]){
              ACK_Val = RadioTelegram_PROCESS(smSensors_ch2,MAX_SMACK_SENSORS);
              if(ACK_Val)
              {
                ChannelAct_Process(ACK_Val,CurrentOpeartion_Mode,CHANNEL2);
                CH2_Find = 1;
              }
            }

            if((CH1_Find == 1)&&(CH2_Find == 0))
            {
              LED_Flash_Times('L',CHANNEL1+1,bLearnModeOn);
              CH1_Find = 0;
            }
            else if((CH1_Find == 0)&&(CH2_Find == 1))
            {
              LED_Flash_Times('L',CHANNEL2+1,bLearnModeOn);
              CH2_Find = 0;
            }
            else if((CH1_Find == 1)&&(CH2_Find == 1))
            {
              LRN_LED_ON;CLR_LED_ON;
              osDelay(150);
              LRN_LED_OFF;CLR_LED_OFF;
              CH1_Find = 0;
              CH2_Find = 0;
            }
            else
            {
              CH1_Find = 0;
              CH2_Find = 0;
            }      
          }
          else{ //u32GatewayID = rTel.trps.u32Id; 指定地址发送的报文
            taskENTER_CRITICAL();
            printf("this is a radio send to me!! don't care!\r\n");
            taskEXIT_CRITICAL();
            if((rTel.trps.u8Choice == RADIO_CHOICE_RPS)||(rTel.t1bs.u8Choice == RADIO_CHOICE_1BS)){
              
              u32GatewayID = rTel.trps.u32Id;
              if((u32GatewayID >= 0xFF800000)&&(u32GatewayID <= 0xFFFFFF80)){
                ChannelAct_Process(rTel.trps.u8Data,CurrentOpeartion_Mode,CHANNEL1);
                LED_Flash_Times('L',1,bLearnModeOn);
              }
              //4bs的不考虑
            }
          }
        }
      }
      else if(bLearnModeOn == TRUE){ //into learn mode
          taskENTER_CRITICAL();
          printf("ready to learn!!!\r\n");
          taskEXIT_CRITICAL();
        if(radio_getTelegram(&rTel,&pTel) == OK){
          
          if(pTel.p_rx.u32DestinationId != u32MyId){
            if((rTel.t1bs.u8Choice == RADIO_CHOICE_1BS)||(rTel.trps.u8Choice == RADIO_CHOICE_RPS))
						  u32GatewayID = rTel.trps.u32Id;
					  else if(rTel.t4bs.u8Choice == RADIO_CHOICE_4BS)
						  u32GatewayID = rTel.t4bs.u32Id; 
            else {
              taskENTER_CRITICAL();
              printf("cann't learn!!!\r\n");
              taskEXIT_CRITICAL();
              break;
            }
            if((u32GatewayID < 0xFF800000)&&(bLearnModeOn == TRUE)){
              if(Learn_Channel1_Flag == 1){
                taskENTER_CRITICAL();
                printf("ch111 learn!!!\r\n");
                taskEXIT_CRITICAL();
                // osSignalSet(LearnTaskHandle,channel1_learn);
                executeLearn(smSensors_ch1,MAX_SMACK_SENSORS,CHANNEL1);
              }
              else if(Learn_Channel2_Flag == 1){
                taskENTER_CRITICAL();
                printf("ch222 learn!!!\r\n");
                taskEXIT_CRITICAL();
                // osSignalSet(LearnTaskHandle,channel2_learn);
                executeLearn(smSensors_ch2,MAX_SMACK_SENSORS,CHANNEL2); 
              }
            } 
          }   
        }
      }
      memset(USART_Enocean_BUF,0,sizeof(USART_Enocean_BUF));
      Enocean_Data=0;
      // taskENTER_CRITICAL();
      // printf("end!\r\n");
      // taskEXIT_CRITICAL();
    }
    osDelay(5);
  }
  /* USER CODE END StartenoecanTask */
}

使用消息队列接收 串口信息的任务,最初的时候定义的是 100 的缓存,消息队列也是100的大小,在压力测试下面,可能会使得 任务卡死。
现象就是任务永远处理阻塞状态,但是收不到消息了,按键任务正常,所以能够查看此任务的状态。
当然这与程序测试的时候加了printf有关,最终是改成了 200 的缓存,去掉printf,测试还是正常的。

2.2.4 学习清除任务(使用任务通知接收执行)

学习和清除使用任务通知,虽然清除只留下全部清除,简单一个函数可以实现,为了后期扩展,也使用任务通知实现。

说明:最初是计划用任务通知实现 学习清除操作,可实际上最后还是直接在按键任务,和报文接收任务中 直接调用函数的方式实现了 学习清除操作:

在这里插入图片描述
在这里插入图片描述

如果使用任务通知就得确定一个事情,发送任务通知是否会发生任务调度?(因为任务后续代码可能对缓存进行修改,比如清除之类的)

这个我从任务通知的源码里面好像没有找到(也可能是我没有仔细),但是是可以通过测试来确定是否发送任务通知会有任务调度的,具体的方式可以参照:

FreeRTOS记录(七、FreeRTOS信号量、事件标志组、邮箱和消息队列、任务通知的关系)

上面博文中 2.2 邮箱测试 ------ 2.21 问题测试,不添加临界区与添加临界区来观察是否发送任务通知会和邮箱一样发生任务调度,当然测试的时候需要注意 任务 与 任务之间的优先级问题。

这里就暂时先不做测试,这个移植已经画了太多时间了= =!

这样的话,我就去掉了准备好的这个任务,只留下了2个任务:

在这里插入图片描述

时刻关注着 内存使用情况(去掉一个任务后空间多了):

在这里插入图片描述

2.2.5 数据处理函数

因为前面的所有框架,基本是按照以前逻辑的逻辑,以少修改以前代码为前提的,所以数据处理函数依然可以沿用以前的,基本不变,这里直接放一下代码:

uint8 RadioTelegram_PROCESS(LEARNED_SENSORS *smSensors,uint8 MAX_SENSORS_NUMS){
  uint8 u8Count;
	uint16 luxValue = 0;
	for(u8Count=0;u8Count<MAX_SENSORS_NUMS;u8Count++)
  {
		if((smSensors[u8Count].u32SensorId == rTel.trps.u32Id)||(smSensors[u8Count].u32SensorId == rTel.t4bs.u32Id)){
			if((rTel.trps.u8Choice == RADIO_CHOICE_RPS)||(rTel.t1bs.u8Choice == RADIO_CHOICE_1BS)){
				if((CurrentOpeartion_Mode == 1)||(CurrentOpeartion_Mode == 3))
				{
					if((smSensors[u8Count].u8LearnSN == 0x50)||(smSensors[u8Count].u8LearnSN == 0x70))
						Left_Right_Flag = 0;    
					else if((smSensors[u8Count].u8LearnSN == 0x10)||(smSensors[u8Count].u8LearnSN == 0x30))
						Left_Right_Flag = 1;
				}
				return rTel.trps.u8Data;
			}
			if(rTel.t4bs.u8Choice == RADIO_CHOICE_4BS){
				if((smSensors[u8Count].RORG == 0xA5)&&(smSensors[u8Count].FUNC == 0x07)&&(smSensors[u8Count].TYPE == 0x03))
				{
					luxValue = rTel.t4bs.u8Data2;
			     	luxValue = (luxValue<<2)|(rTel.t4bs.u8Data1>>6);
				 	if((luxValue <= Lux_Threshold)&&(rTel.t4bs.u8Data0 &0x80))
					  return  1; 
					else
						return  0;
				}

				if((smSensors[u8Count].RORG == 0xA5)&&(smSensors[u8Count].FUNC == 0x08)&&(smSensors[u8Count].TYPE == 0x02))//A5-08-02
				{
					//luxValue = rTel.t4bs.u8Data2;
					luxValue = ((uint16)rTel.t4bs.u8Data2)*1020/255;								
					if((luxValue <= Lux_Threshold)&&(!(rTel.t4bs.u8Data0 &0x02)))
						return  1;                 
					else                          
						return  0;
		    	}
				
				if((smSensors[u8Count].RORG == 0xA5)&&(smSensors[u8Count].FUNC == 0x07)&&(smSensors[u8Count].TYPE == 0x02))//A5-07-02
				{
					if(rTel.t4bs.u8Data0 &0x80)
					 	return  1;                 
					else                           
						return  0;
		    	}
				
				if((smSensors[u8Count].RORG == 0xA5)&&(smSensors[u8Count].FUNC == 0x08)&&(smSensors[u8Count].TYPE == 0x01))//A5-08-01
				{
					luxValue = ((uint16)rTel.t4bs.u8Data2)*510/255;
					if((luxValue <= Lux_Threshold)&&(!(rTel.t4bs.u8Data0 &0x02)))
					 	return  1;              
					else                  
						return  0;
		    	}
				
				if((smSensors[u8Count].RORG == 0xA5)&&(smSensors[u8Count].FUNC == 0x08)&&(smSensors[u8Count].TYPE == 0x03))//A5-08-03
				{
					luxValue = ((uint16)rTel.t4bs.u8Data2)*1530/255;
					if((luxValue <= Lux_Threshold)&&(!(rTel.t4bs.u8Data0 &0x02)))
					 	return  1;             
					else                          
						return  0;
		    	}
			}
		}
	}
	return 0;
}

2.2.6 延时执行操作(使用软件定时器)

因为有些操作需要用到定时器,所以最后还得使用一下软件定时器,软件定时器的使用参考博文:

FreeRTOS记录(八、FreeRTOS软件定时器)

在STM32cubeMX 中设置:

在这里插入图片描述

在这里插入图片描述

然后在程序中需要的地方开启软件定时器:

在这里插入图片描述

完善一下 callback 函数(还是为了兼容以前代码,但是实际上可能需要维护一下):

/* myTimerCallback01 function */
void myTimerCallback01(void const * argument)
{
  /* USER CODE BEGIN myTimerCallback01 */
  uint8_t myTimerID;
  uint8_t CH;
  myTimerID = (uint8_t)pvTimerGetTimerID(argument);

  if (myTimerID == time_one){
    for(CH=0;CH<CurChannelNums;CH++){
      if(FirstStart_Flag[CH] == 1){
        if(CH == 0) {
          Relay_Off(1);
          Switch_Flag = 1;
            t4bsu8Data0 &= ~Relay1_OnState;
        }
        if(CH == 1) {
          Relay_Off(2);
          Switch_Flag = 1;
            t4bsu8Data0 &= ~Relay2_OnState;                                                                                                                                                                                                                                                                   
        }
        FirstStart_Flag[CH] = 0;
      }
    }
  }

  /* USER CODE END myTimerCallback01 */
}

时刻关注着 内存使用情况:

在这里插入图片描述

2.2.7 设备状态上报任务(使用任务通知接收执行)

在以前的代码中,有一个全局变量 Switch_Flag ,标志着设备的状态发生了变化,如果状态改变,需要发送一次状态报文:

if(Switch_Flag == 1){
	SendConfigInfoTelegram();
 	Switch_Flag = 0;
}

所以新建了一个任务,单独作为发送状态报文:

在这里插入图片描述

但是最终以这种方式测试下来,有bug,就是影响接受任务中间的执行操作。

简单来说就是无线控制本来是很丝滑,很立即的,加了这个全局变量的判断,就变得卡卡的!

期初是觉得 Switch_Flag 改变的地方太多了,加任务通知不太适合,但是如果直接这样判断全局变量,显然没法使用,所以还是改为任务通知的方式。

void StartSendconfigTask(void const * argument)
{
  /* USER CODE BEGIN StartSendconfigTask */
  osEvent gettask;
  /* Infinite loop */
  for(;;)
  {
    gettask = osSignalWait(sendconfig,osWaitForever);
    if(gettask.value.v == sendconfig){
      SendConfigInfoTelegram();
    }
    osDelay(1);
  }
  /* USER CODE END StartSendconfigTask */
}

接着修改数据处理函数,因为里面涉及到设备状态的修改:

在这里插入图片描述

最后在接收任务中执行完了数据数据函数以后,再进行任务通知的发送才测试正常:

在这里插入图片描述

时刻关注着 内存使用情况:

在这里插入图片描述

2.2.8 延时函数!!!

延时函数为什么标红色单独拿出来说呢:

对于使用操作系统来说,延时函数使用操作系统的延时函数,可以避免裸机中一个很大的问题,就是干等,所以,在某些情况下不需要考虑,因为延时时间导致其他程序不能够无及时运行的问题!

但是!! 习惯了裸机的逻辑,操作系统的延时函数带来好处的同时,也使得使用起来必须要充分考虑! 延时的时候是会发生任务调度的,如果在某些想不被打断的操作中,千万不要使用延时函数 和 带有延时函数的函数。

在程序中有一个LED灯闪烁的函数:

在这里插入图片描述

其中使用了osDelay,然后在学习函数中
void executeLearn(LEARNED_SENSORS *smSensors,uint8 MAX_SENSORS_NUMS,CHANNEL_ID ChannelNum)

在这里插入图片描述

最后修改了一下,把做指示作用的闪灯放在最后:

/*
freertos 进入学习有个问题,就比如一个开关的连续2个报文,在学习第一条的时候,其实第二条已经过来了,第一条没有来得及学完的话,会直接学习第二条
已经解决,因为调用了闪灯函数,里面有延时函数
*/
void executeLearn(LEARNED_SENSORS *smSensors,uint8 MAX_SENSORS_NUMS,CHANNEL_ID ChannelNum)
{
	uint8 u8Count,TypeNums;	
  uint8 FUNC,TYPE,LrnBit_Flag;
  uint16 Manufacturer_ID; 
  for(u8Count=0;u8Count<MAX_SENSORS_NUMS;u8Count++)
  {
    if((smSensors[u8Count].u32SensorId == rTel.trps.u32Id)||(smSensors[u8Count].u32SensorId == rTel.t4bs.u32Id))
    {	
      // printf("%d learned!\r\n",u8Count);
      return;
    }
  }
	//Sensor ID not found so the Application needs to learn it in, proove if it is a correct result, 
	//search for free place in the APP learn table
  for(u8Count=0;u8Count<MAX_SENSORS_NUMS;u8Count++){
    if(smSensors[u8Count].u32SensorId == NO_SENSOR_ID)
      break;	
  }
		//if no free place in APP learn table, discard the learn request, no response time is needed
  if(u8Count == MAX_SENSORS_NUMS){
    LED_Flash_Times('L',4,bLearnModeOn);
    return;
  }

	//there is a free place, learn in the sensor		
  if(rTel.trps.u8Choice == RADIO_CHOICE_RPS){
    smSensors[u8Count].u32SensorId = rTel.trps.u32Id;
    smSensors[u8Count].u8LearnSN = rTel.trps.u8Data;
    // printf("%x\r\n",smSensors[u8Count].u8LearnSN);
  }
  else if(rTel.t1bs.u8Choice == RADIO_CHOICE_1BS)//if 1BS
  {
    if((rTel.t1bs.u8Data&0x08) == 0)
      smSensors[u8Count].u32SensorId = rTel.t1bs.u32Id;
    else
        return;

  }
	else if(rTel.t4bs.u8Choice == RADIO_CHOICE_4BS)    //if 4BS
  {
    if((CurrentOpeartion_Mode == 1)||(CurrentOpeartion_Mode == 3))
      return;
    else{
      FUNC = (rTel.t4bs.u8Data3&0xFC)>>2;
      TYPE = ((rTel.t4bs.u8Data3&0x03)<<5)|((rTel.t4bs.u8Data2&0xF8)>>3);
      Manufacturer_ID = rTel.t4bs.u8Data2&0x07;
      Manufacturer_ID <<= 8;
      Manufacturer_ID |= rTel.t4bs.u8Data1;
      LrnBit_Flag = rTel.t4bs.u8Data0&0x08;
      for(TypeNums=0;TypeNums<EEP_TYPE_Num;TypeNums++){
        if((FUNC == EEP_Info[TypeNums][0])&&(TYPE == EEP_Info[TypeNums][1])&&(LrnBit_Flag == 0))
        {
          smSensors[u8Count].u32SensorId = rTel.t4bs.u32Id;
          smSensors[u8Count].RORG = RADIO_CHOICE_4BS;
          smSensors[u8Count].FUNC = FUNC;
          smSensors[u8Count].TYPE = TYPE;
          break;
        }
      }
      if(TypeNums == EEP_TYPE_Num)
        return;
    }
  }

  MyData.IDNUM[ChannelNum] += 1;
  if(ChannelNum == CHANNEL1)
     t4bsu8Data1 = MyData.IDNUM[ChannelNum];
  if(ChannelNum == CHANNEL2)
     t4bsu8Data2 = MyData.IDNUM[ChannelNum];

  //保存数据需要进入临界区
  taskENTER_CRITICAL(); 
  FLASH_WriteParameter(&MyData);
  // printf("is %d\r\n",u8Count); 
  FLASH_WriteSensorID(&smSensors[u8Count],u8Count,ChannelNum); //保存数据
  channel_dataRead(&smSensors[u8Count],u8Count,ChannelNum); //学习完了更新一下数据,函数用指针操作可能不需要更新直接已经修改了smSensors_ch1
  taskEXIT_CRITICAL();
  memset(USART_Enocean_BUF,0,sizeof(USART_Enocean_BUF)); //串口缓存清除
  Enocean_Data=0;

  LED_Flash_Times('L',2,bLearnModeOn); //问题在于这句话,闪灯会导致任务调度,因为里面有延时函数 !!!

  // Send4BSDataTelegram(&t4bsu8Data3,&t4bsu8Data2,&t4bsu8Data1,&t4bsu8Data0); 
  // mem_writeFlash((uint8 xdata *)&MyData,APP_FLASH_USER_TABLE,sizeof(MyData));//keep
  // mem_writeFlash((uint8 xdata *)smSensors,u16DstAddress,sizeof(LEARNED_SENSORS)*MAX_SMACK_SENSORS);	
  return;
}	

三、总结体会

3.1 样例最终情况

最后整体测试看来没问题版本,编译结果:

在这里插入图片描述

在STM32CubeMX中的配置,给与FreeRTOS的内存空间为 3K:

在这里插入图片描述

最终剩余情况:

在这里插入图片描述

当然,这个内存占用还可以减少差不多 0.5k,因为我 打印了任务运行状态,最终在内存够的情况,没有把打印任务状态去掉。

最终修改下来,实际上我只用了3个任务,如果不是为了适配以前的代码框架,重新构建逻辑,可能可以更加合理的分配任务:
在这里插入图片描述

最后整个源码我上传了一份: Relay_Control.zip

3.2 一些体会

1、每个任务的栈大小要根据实际情况调整,任务调用函数深度和变量的多少都直接影响任务栈需要的大小,所以移植过程需要使用 vTaskList 观察任务运行占用内存情况,以便及时调整。 具体可参考博文:

FreeRTOS记录(四、FreeRTOS任务堆栈溢出问题和临界区)

2、对于全局变量,有些数组可以使用 const 修饰,使其存放至 flash 上,节约 ram 空间。

3、其实最终修改下来,一味的追求最小程序的修改代码,反而适得其反,还是自己按照现在的思路,重新设计一下会来得简单一些

4、对于 EEPROM 的读写函数设计修改,在 结构体,结构体数组 的运用于处理方面,花了一些时间!

EEPROM的读写操作需要添加临界区保护,有可能在写过程发生任务调度导致写读失败!!

5、延时函数要特别注意!!!除了使用临界区保护不想被打断的代码,延时函数和 带有延时的函数 使用考虑周全!

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

FreeRTOS记录(九、一个裸机工程转FreeRTOS的实例) 的相关文章

随机推荐

  • Linux下vi命令编辑器,编辑 ,保存和退出

    1 vi 文件名 vi后面有空格 接着按回车即可打开对应的文件 如果没有对应的文件 那么vi命令就会自动创建一个新的 2 vi打开文件后是命令模式状态 要用i或者a命令或Insert键才可进入可编辑的状态 最下面会出现 INSERT 3 保
  • python 列表操作方法详解及例子

    原文链接 https www cnblogs com wj 1314 p 8433116 html 列表是Python中最基本的数据结构 列表是最常用的Python数据类型 列表是一个数据的集合 集合内可以放任何数据类型 可对集合方便的增删
  • 云服务器安装操作系统后如何连接,服务器如何安装操作系统

    服务器如何安装操作系统 内容精选 换一换 如果您需要使用毕昇编译器 则需要先在服务端安装毕昇编译器 毕昇编译器基于开源LLVM开发 并进行了优化和改进 同时将flang作为默认的Fortran语言前端编译器 是针对鲲鹏平台的高性能编译器 当
  • 华为OD机考-模拟消息队列(C,python)

    题目描述 让我们来模拟一个消息队列的运作 有一个发布者和若干消费者 发布者会在给定的时刻向消息队列发送消息 若此时消息队列有消费者订阅 这个消息会被发送到订阅的消费者中优先级最高 输入中消费者按优先级升序排列 的一个 若此时没有订阅的消费者
  • urldecode 报错 Malformed UTF-8 characters, possibly incorrectly encoded

    使用urlencode 编码了一段字符串写入数据库 读取的时候使用urldecode 解码报错 Malformed UTF 8 characters possibly incorrectly encoded 解决方案 检查一下是否保存到数据
  • ajax不弹出新页面问题

    ajax默认是异步请求 做局部刷新的 指的是当前页数据渲染的 如果后台是转发或者重定向了 如果用ajax的话是不会弹出新的页面的 from提交的话 如果后台是转发或者重定向了 是可以打开新的页面的
  • 【人脸识别】【python】Object arrays cannot be loaded when allow_pickle=False解决方案

    2020年2月11日 0次阅读 共1625个字 0条评论 0人点赞 QueenDekimZ mtcnn debug 用mtcnn对LFW人脸数据集进行人脸检测与关键点对齐 并裁剪到160 160维 为后续facenet训练作training
  • wx.login wx.getUserProfile 获取登录凭证

    wx login 调用接口获取登录凭证 code 通过凭证进而换取用户登录态信息 包括用户在当前小程序的唯一标识 openid 微信开放平台帐号下的唯一标识 unionid 若当前小程序已绑定到微信开放平台帐号 及本次登录的会话密钥 ses
  • 通过hexo快速搭建个人博客

    个人博客预览点击这里 菜卷的博客 快速搭建一个博客 一 需要安装的工具 二 开始安装Hexo 三 安装完成后 初始化项目 四 在项目根目录下执行命令 五 启动项目 六 部署到github 七 配置文件 八 安装next主题 九 优化next
  • C语言程序实训--实验设备管理系统

    之前学校c语言程序实训课要求写的 如果程序有错误或可以改进的地方 希望各位指出 开发环境 IDE Visual Studio Code Dev C 处理器 AMD Ryzen 7 PRO 6850HS with Radeon Graphic
  • 73家!华为鸿蒙OS合作伙伴汇总

    6月2日 华为发布了最新版的鸿蒙操作系统 HarmonyOS 2 0 以及一系列搭载鸿蒙的硬件产品 比如手机 手表 平板 耳机 显示器等等 如今的智能终端越来越多 厂商不可能为每个设备单独准备一个系统 因为这不仅让开发者工作量倍增 消费者用
  • Flask网站中使用Keras时报错“Tensor Tensor(*) is not an element of this graph”

    HyperLPR车牌识别程序本地中能进行正常识别 但将其放到flask搭建的网站中进行识别 不能运行 并报错 Tensor Tensor is not an element of this graph HyperLPR中的识别模型采用的是K
  • Mask掩码

    Python中Mask的用法 引例 Numpy的MaskedArray模块 小于 或小于等于 给定数值 大于 或大于等于 给定数值 在给定范围内 超出给定范围 在算术运算期间忽略NaN和 或infinite值 All men are scu
  • Count Color

    http poj org problem id 2777 Description Chosen Problem Solving and Program design as an optional course you are require
  • 【QT】——布局

    目录 1 在UI窗口中布局 2 API设置布局 2 1 QLayout 2 2 QHBoxLayout 2 3 QVBoxLayout 2 4 QGirdLayout 注意 示例 Qt 窗口布局是指将多个子窗口按照某种排列方式将其全部展示到
  • Apifox—诠释国产接口管理工具新高度

    揭开Apifox的神秘面纱 曾经在对于接口管理和调试工作上 大量的开发者往往会选择使用Swagger做接口文档管理 用Postman做接口调试工具 然而这样使用的痛处其实也不言而喻 原本同一类型的工作却被放置在不同的软件工具上 并且对于接口
  • 图像二值化方法--OTSU(最大类间方差法)

    前面学习了直方图双峰法 图像二值化方法中的阈值法 最大类间方差法 OTSU 是找到自适应阈值的常用方法 原理参考了冈萨雷斯的 数字图像处理 以下是自己写的函数 获取灰度图in的OTSU阈值 int Segment otsuMat Mat i
  • [译] Scratch 平台的神经网络实现(R 语言)

    原文地址 Neural Networks from Scratch in R 原文作者 Ilia Karmanov 译文出自 掘金翻译计划 本文永久链接 github com xitu gold m 译者 CACppuccino 校对者 I
  • 【通信协议】笔记之Redis协议抓取分析

    RESP Redis序列化协议 概念 Redis底层使用的通信协议是RESP Redis Serialization Protocol的缩写 RESP协议可以序列化多种类型 比如Simple Strings 简单字符串 Errors 错误类
  • FreeRTOS记录(九、一个裸机工程转FreeRTOS的实例)

    记录一下一个实际项目由裸机程序改成FreeRTOS 以前产品的平台还是C8051单片机上面的程序 硬件平台改成了STM32L051 同时使用STM32CubeMX生成的工程 使用FreeRTOS系统 EEPROM数据存储读取函数修改更新 2