stm32配合xshell串列埠輸入

一叁五發表於2024-05-17

前言

透過xshell對stm32f103c8t6晶片進行串列埠除錯。

最近發現xshell也可以進行串列埠除錯,但是在資料的輸入上會有一些問題。
因為正常的串列埠除錯助手都是統一輸入,直接傳送,但是xshell不同,正常情況下是字元逐一輸入的。
所以在進行串列埠除錯時,需要逐個字元分析計算,最後統一處理。

用xshell除錯的優點就是軟體最佳化好,使用起來很舒服。
缺點是,沒法看16進位制資料;不能自定義資料幀格式;也沒有輸入框,也顯示不了使用者輸入資料。

整體來說,xshell用於這種串列埠除錯,其實不是很明智的選擇。

設計

思路

graph LR A(初始化) B(主函式處理) C(回撥函式處理) A --> C C <--> B C --> C

初始化之後,回撥函式接收使用者輸入,主函式處理使用者輸入的資料

同時,因為xshell不能顯示使用者的輸入資料,所以需要在主函式中列印使用者輸入。
不建議在回撥函式中列印,具體原因程式碼中會有表示。

分析

本來想做成一個類,這樣所有的串列埠都可以同時使用,但是有一說一,C語言實現類確實有點麻煩。
可以用一個結構體儲存引數,然後傳遞到函式中,這個方法要比類簡單的多,而且也能實現所有串列埠都可以使用的功能。但是目前一個串列埠就夠用,就沒有實現了,有興趣的可以自己實現一下。

初始化部分

初始化部分決定了整段程式碼的相對獨立性,所以在初始化時應該輸入一個引數,讓這個庫可以用於任何一個串列埠。

這個引數應該是串列埠的決定性引數,如&huart1這樣的。

回撥函式

回撥函式應該做最少的處理,畢竟中斷裡面做大量計算,太佔資源了。

主函式處理

這一部分可以做大量的計算和處理,畢竟主函式可以被打斷,不會太耗費資源。

實現

實現這些功能,要解決幾個問題

  1. 輸入的命令需要儲存下來,方便後面處理。
  2. 回撥函式是執行在中斷中,可以做到即時,但是主函式是在while中,不能做到即時相應。
  3. 需要一個特定的按鍵來出發判斷機制,做對應的命令處理。

特定命令判斷

先從最簡單的開始,以使用者的習慣來說,回車做為確定符號是很自然的。
退格作為命令輸入之後的取消,也是相對合理的。只是在做的時候和正常的取消有點不同。

輸入命令的儲存

儲存採用的是棧的方式,定義一個陣列,再定義一個座標指標,透過座標指標向陣列寫入資料。

不同步問題

資料無法完全實時同步,所以就需要一個輸出指標。這樣雖然輸出和輸入資料還是不同步,但可以做到延遲響應,不會讓資料在輸出時丟失。

程式碼

原始碼

H檔案

/*
 * command.h
 *
 *  Created on: May 15, 2024
 *      Author: yangg
 */

#ifndef COMMAND_H_
#define COMMAND_H_

#include "stm32f1xx_hal.h"
#include "usart.h"

#define COMMAND_STACK_MAX 15//使用者自行修改,命令的最大長度,比int16_t的最大範圍小即可

void comInit(UART_HandleTypeDef *huart);
void comPrintManage();
void comCallback();

#endif /* COMMAND_H_ */

C檔案

/*
 * command.c
 *
 *  Created on: May 15, 2024
 *      Author: yangg
 */

#include "command.h"

UART_HandleTypeDef *_huart;

static uint8_t _comBuff[COMMAND_STACK_MAX];
static uint8_t _temp;

static int16_t _comTop;
static int16_t _comPrint;

void comInit(UART_HandleTypeDef *huart) {
	_comTop = -1;
	_comPrint = -1;
	_huart = huart;
	HAL_UART_Receive_IT(_huart, &_temp, 1);
}

void comPrintManage() {
	while (_comPrint < _comTop || _comPrint == COMMAND_STACK_MAX - 1) {
		if (_comPrint < COMMAND_STACK_MAX - 1) {
			_comPrint++;
		}
		if (_comBuff[_comPrint] == 0x08) {
			_comTop = -1;
			_comPrint = -1;
			HAL_UART_Transmit(_huart, (uint8_t*) "\r\n", 2, 100);
		} else if (_comBuff[_comPrint] == 0x0D) {
			// TODO 命令處理--------------------------------------------------
			_comTop = -1;
			_comPrint = -1;
			HAL_UART_Transmit(_huart, (uint8_t*) "\r\n", 2, 100);
		} else {
			if (_comPrint < COMMAND_STACK_MAX - 1) {
				HAL_UART_Transmit(_huart, &_comBuff[_comPrint], 1, 100);
			} else {
				return;//超過最大長度不顯示,
			}
		}

	}
}

void comCallback() {
	if (_comTop < COMMAND_STACK_MAX - 1) {
		_comTop++;
	}
	_comBuff[_comTop] = _temp;
	HAL_UART_Receive_IT(_huart, &_temp, 1);
}

在C檔案中的TODO 命令處理部分可以寫自己定義的命令和處理方式。

對退格的處理不是清除一個字元,因為如果清除一個字元,需要每次都對字串進行重新整理。所以對於退格就直接清除掉所有資料了。

示例

主函式

int main(void) {

	/* USER CODE BEGIN 1 */

	/* USER CODE END 1 */

	/* MCU Configuration--------------------------------------------------------*/

	/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
	HAL_Init();

	/* USER CODE BEGIN Init */

	/* USER CODE END Init */

	/* Configure the system clock */
	SystemClock_Config();

	/* USER CODE BEGIN SysInit */

	/* USER CODE END SysInit */

	/* Initialize all configured peripherals */
	MX_GPIO_Init();
	MX_I2C1_Init();
	MX_SPI1_Init();
	MX_USART1_UART_Init();
	MX_USART2_UART_Init();
	MX_USB_PCD_Init();
	/* USER CODE BEGIN 2 */
	UART1_print("\r\n\r\n");
	UART1_print("program is ready and running");
	UART1_print("\r\n");
	ledInit(LED_GPIO_Port, LED_Pin, GPIO_PIN_RESET);
	comInit(&huart1);//-----------這裡初始化
	/* USER CODE END 2 */

	/* Infinite loop */
	/* USER CODE BEGIN WHILE */
	while (1) {
		ledToggle();
		comPrintManage();//----------這裡是主函式呼叫
		/* USER CODE END WHILE */

		/* USER CODE BEGIN 3 */
	}
	/* USER CODE END 3 */
}

回撥函式

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) {
	if (huart->Instance == USART1) {
		comCallback();
	}
}

相關文章