天天看點

FreeRTOS-Plus-CLI移植

FreeRTOS+CLI 是一個可擴充的架構,允許應用程式編寫器定義和注冊自己的指令行輸入指令,相當于RTOS的指令解析器,在實際項目中很有用。本文介紹如何移植CLI。

FreeRTOS+ CLI(指令行界面)提供了一種簡單,小巧,可擴充且RAM高效的方法,使您的FreeRTOS應用程式能夠處理指令行輸入。

硬體平台:stm32f407zgt6

軟體庫:HAL

本文測試工程下載下傳連結:https://download.csdn.net/download/qq_26972441/85351855

包括了Free RTOS源碼

一. 需要準備FreeRTOS源碼,并且找到以下檔案放到一個檔案夾下

FreeRTOS-Plus-CLI移植

二. 将檔案夾下的c檔案添加到keil目錄

FreeRTOS-Plus-CLI移植

三. 使能序列槽空閑中斷和DMA接收

void bsp_usart_USART2_Init(void)
{
  /* USER CODE END USART2_Init 1 */
  huart2.Instance = USART2;
  huart2.Init.BaudRate = 115200;
  huart2.Init.WordLength = UART_WORDLENGTH_8B;
  huart2.Init.StopBits = UART_STOPBITS_1;
  huart2.Init.Parity = UART_PARITY_NONE;
  huart2.Init.Mode = UART_MODE_TX_RX;
  huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  huart2.Init.OverSampling = UART_OVERSAMPLING_16;
  if (HAL_UART_Init(&huart2) != HAL_OK)
  {
    Error_Handler();
  }
	__HAL_UART_ENABLE_IT(&huart2, UART_IT_IDLE); //使能序列槽空閑中斷
  HAL_UART_Receive_DMA(&huart2, usart2_rx_buffer.data, USART_RX_BUFFER_SIZE); //開啟序列槽DMA接收
}
           

四. 修改serial.c 的xSerialPortInitMinimal函數調用序列槽初始化函數

xComPortHandle xSerialPortInitMinimal( unsigned long ulWantedBaud, unsigned portBASE_TYPE uxQueueLength )
{
	xComPortHandle xReturn;
	/* Create the queues used to hold Rx/Tx characters. */
	xRxedChars = xQueueCreate( uxQueueLength, ( unsigned portBASE_TYPE ) sizeof( signed char ) );
	xCharsForTx = xQueueCreate( uxQueueLength + 1, ( unsigned portBASE_TYPE ) sizeof( signed char ) );
	
	/* If the queue/semaphore was created correctly then setup the serial port
	hardware. */
	if( ( xRxedChars != serINVALID_QUEUE ) && ( xCharsForTx != serINVALID_QUEUE ) )
	{
    /* 初始化序列槽1 */
    bsp_usart_USART2_Init();
	}
	else
	{
		xReturn = ( xComPortHandle ) 0;
	}

	/* This demo file only supports a single port but we have to return
	something to comply with the standard demo header file. */
	return xReturn;
}
           

五. 修改serial.c 的xSerialPutChar函數為輪詢方式

signed portBASE_TYPE xSerialPutChar( xComPortHandle pxPort, signed char cOutChar, TickType_t xBlockTime )
{
signed portBASE_TYPE xReturn;

	if( xQueueSend( xCharsForTx, &cOutChar, xBlockTime ) == pdPASS )
	{
		xReturn = pdPASS;
    uint8_t cChar;
    /* 發送隊列中有資料,通過輪詢方式發送出去 */
    if(xQueueReceive(xCharsForTx, &cChar, 0) == pdTRUE) {
      if((HAL_UART_GetState(&huart2) & HAL_UART_STATE_BUSY_TX) != HAL_UART_STATE_BUSY_TX) {
        HAL_UART_Transmit(&huart2, &cChar, 1, 1000);
      }
    }
	}
	else
	{
		xReturn = pdFAIL;
	}

	return xReturn;
}
           

六. FreeRTOSConfig.h 增加宏定義

七. 完善序列槽中斷服務函數

void USART2_IRQHandler(void)
{
  BaseType_t pxHigherPriorityTaskWoken = pdFALSE;
  if(__HAL_UART_GET_FLAG(&huart2, UART_FLAG_IDLE) != RESET) { //IDLE中斷(說明接收到了一幀資料)
    __HAL_UART_CLEAR_IDLEFLAG(&huart2); //清除IDLE中斷标志位
    HAL_UART_DMAStop(&huart2);
    usart2_rx_buffer.finish_flag = 1;
    usart2_rx_buffer.len = USART_RX_BUFFER_SIZE - __HAL_DMA_GET_COUNTER(huart2.hdmarx);//hdma_usart1_rx.Instance->NDTR;
    
    /* usart1 rx data process */ 
    for(uint16_t i = 0; i < usart2_rx_buffer.len; ++i) {
      xQueueSendFromISR(xRxedChars, &usart2_rx_buffer.data[i], &pxHigherPriorityTaskWoken);
    }  
    HAL_UART_Receive_DMA(&huart2, usart2_rx_buffer.data, USART_RX_BUFFER_SIZE);
  }
  HAL_UART_IRQHandler(&huart2);
	portYIELD_FROM_ISR(pxHigherPriorityTaskWoken);
}
           

八 . 主函數調用如下函數

vRegisterSampleCLICommands();
vUARTCommandConsoleStart( 512, 1 );
           

九 . 注冊測試函數

void vRegisterSampleCLICommands( void )
{
	/* Register all the command line commands defined immediately above. */
	FreeRTOS_CLIRegisterCommand( &xTaskStats );	
	FreeRTOS_CLIRegisterCommand( &xThreeParameterEcho );
	FreeRTOS_CLIRegisterCommand( &xParameterEcho );
	FreeRTOS_CLIRegisterCommand( &xTestEcho );
	#if( configGENERATE_RUN_TIME_STATS == 1 )
	{
		FreeRTOS_CLIRegisterCommand( &xRunTimeStats );
	}
	#endif
	
	#if( configINCLUDE_QUERY_HEAP_COMMAND == 1 )
	{
		FreeRTOS_CLIRegisterCommand( &xQueryHeap );
	}
	#endif

	#if( configINCLUDE_TRACE_RELATED_CLI_COMMANDS == 1 )
	{
		FreeRTOS_CLIRegisterCommand( &xStartStopTrace );
	}
	#endif
}

static const CLI_Command_Definition_t xTestEcho =
{
	"task-test",
	"\r\ntask-test:\r\n Test Function\r\n",
	prvEchoTestCommand, /* The function to run. */
	-1 /* The user can enter any number of commands. */
};
           

十 .函數指針指向的回調函數

static int test[5] = {1, 2, 3, 4 ,5};
static BaseType_t prvEchoTestCommand( char *pcWriteBuffer, size_t xWriteBufferLen, const char *pcCommandString )
{
  const char * const pcHeader = "  Test data (uint: mm)  \r\n****************************************\r\n";

		/* Remove compile time warnings about unused parameters, and check the
		write buffer is not NULL.  NOTE - for simplicity, this example assumes the
		write buffer length is adequate, so does not check for buffer overflows. */
		( void ) pcCommandString;
		( void ) xWriteBufferLen;
		configASSERT( pcWriteBuffer );

    strcpy( pcWriteBuffer, pcHeader );
    pcWriteBuffer += strlen( pcWriteBuffer );
  
    for(uint8_t i = 0; i < 5; ++i) {
      sprintf( pcWriteBuffer, "test[%d] distance: %d\t\t\r\n", i, test[i]);
      pcWriteBuffer += strlen( pcWriteBuffer );
    }

		/* There is no more data to return after this single string, so return
		pdFALSE. */
		return pdFALSE;
}
           

十一. 建立CLI任務

void vUARTCommandConsoleStart( uint16_t usStackSize, UBaseType_t uxPriority )
{
	/* Create the semaphore used to access the UART Tx. */
	xTxMutex = xSemaphoreCreateMutex();
	configASSERT( xTxMutex );

	/* Create that task that handles the console itself. */
	xTaskCreate( 	prvUARTCommandConsoleTask,	/* The task that implements the command console. */
					"CLI",						/* Text name assigned to the task.  This is just to assist debugging.  The kernel does not use this name itself. */
					usStackSize,				/* The size of the stack allocated to the task. */
					NULL,						/* The parameter is not used, so NULL is passed. */
					uxPriority,					/* The priority allocated to the task. */
					NULL );						/* A handle is not required, so just pass NULL. */
}
           

十二. CLI任務具體内容及實作過程

static void prvUARTCommandConsoleTask( void *pvParameters )
{
signed char cRxedChar;
uint8_t ucInputIndex = 0;
char *pcOutputString;
static char cInputString[ cmdMAX_INPUT_SIZE ], cLastInputString[ cmdMAX_INPUT_SIZE ];
BaseType_t xReturned;
xComPortHandle xPort;

	( void ) pvParameters;

	/* Obtain the address of the output buffer.  Note there is no mutual
	exclusion on this buffer as it is assumed only one command console interface
	will be used at any one time. */
	pcOutputString = FreeRTOS_CLIGetOutputBuffer();

	/* Initialise the UART. */
	xPort = xSerialPortInitMinimal( configCLI_BAUD_RATE, cmdQUEUE_LENGTH );

	/* Send the welcome message. */
	vSerialPutString( xPort, ( signed char * ) pcWelcomeMessage, ( unsigned short ) strlen( pcWelcomeMessage ) );

	for( ;; )
	{
		/* Wait for the next character.  The while loop is used in case
		INCLUDE_vTaskSuspend is not set to 1 - in which case portMAX_DELAY will
		be a genuine block time rather than an infinite block time. */
		while( xSerialGetChar( xPort, &cRxedChar, portMAX_DELAY ) != pdPASS );

		/* Ensure exclusive access to the UART Tx. */
		if( xSemaphoreTake( xTxMutex, cmdMAX_MUTEX_WAIT ) == pdPASS )
		{
			/* Echo the character back. */
			xSerialPutChar( xPort, cRxedChar, portMAX_DELAY );

			/* Was it the end of the line? */
			if( cRxedChar == '\n' || cRxedChar == '\r' )
			{
				/* Just to space the output from the input. */
				vSerialPutString( xPort, ( signed char * ) pcNewLine, ( unsigned short ) strlen( pcNewLine ) );

				/* See if the command is empty, indicating that the last command
				is to be executed again. */
				if( ucInputIndex == 0 )
				{
					/* Copy the last command back into the input string. */
					strcpy( cInputString, cLastInputString );
				}

				/* Pass the received command to the command interpreter.  The
				command interpreter is called repeatedly until it returns
				pdFALSE	(indicating there is no more output) as it might
				generate more than one string. */
				do
				{
					/* Get the next output string from the command interpreter. */
					xReturned = FreeRTOS_CLIProcessCommand( cInputString, pcOutputString, configCOMMAND_INT_MAX_OUTPUT_SIZE );

					/* Write the generated string to the UART. */
					vSerialPutString( xPort, ( signed char * ) pcOutputString, ( unsigned short ) strlen( pcOutputString ) );

				} while( xReturned != pdFALSE );

				/* All the strings generated by the input command have been
				sent.  Clear the input string ready to receive the next command.
				Remember the command that was just processed first in case it is
				to be processed again. */
				//strcpy( cLastInputString, cInputString ); //該語句在輸入字元數超過cmdMAX_INPUT_SIZE時會導緻程式進入hardfault
        strncpy(cLastInputString, cInputString, cmdMAX_INPUT_SIZE); //複制指定個數的指令
				ucInputIndex = 0;
				memset( cInputString, 0x00, cmdMAX_INPUT_SIZE );

				vSerialPutString( xPort, ( signed char * ) pcEndOfOutputMessage, ( unsigned short ) strlen( pcEndOfOutputMessage ) );
			}
			else
			{
				if( cRxedChar == '\r' )
				{
					/* Ignore the character. */
				}
				else if( ( cRxedChar == '\b' ) || ( cRxedChar == cmdASCII_DEL ) )
				{
					/* Backspace was pressed.  Erase the last character in the
					string - if any. */
					if( ucInputIndex > 0 )
					{
						ucInputIndex--;
						cInputString[ ucInputIndex ] = '\0';
					}
				}
				else
				{
					/* A character was entered.  Add it to the string entered so
					far.  When a \n is entered the complete	string will be
					passed to the command interpreter. */
					if( ( cRxedChar >= ' ' ) && ( cRxedChar <= '~' ) )
					{
						if( ucInputIndex < cmdMAX_INPUT_SIZE )
						{
							cInputString[ ucInputIndex ] = cRxedChar;
							ucInputIndex++;
						}
					}
				}
			}

			/* Must ensure to give the mutex back. */
			xSemaphoreGive( xTxMutex );
		}
	}
}
           
FreeRTOS-Plus-CLI移植
FreeRTOS-Plus-CLI移植
FreeRTOS-Plus-CLI移植

原理:

1、通過結構體建構回調函數

2、将實體函數通過函數指針注冊

3、将CLI指令結構體添加到任務隊列

4、通過CLI任務進行指令比對

十三. 測試效果

1、輸入help會将所添加的command列出來。

FreeRTOS-Plus-CLI移植

2、輸入task-stats 會将RTOS所有任務列出來。

FreeRTOS-Plus-CLI移植

3、輸入我們自己建立的command task-test

會将結果傳回。

FreeRTOS-Plus-CLI移植

繼續閱讀