前言
透過xshell對stm32f103c8t6晶片進行串列埠除錯。
最近發現xshell也可以進行串列埠除錯,但是在資料的輸入上會有一些問題。
因為正常的串列埠除錯助手都是統一輸入,直接傳送,但是xshell不同,正常情況下是字元逐一輸入的。
所以在進行串列埠除錯時,需要逐個字元分析計算,最後統一處理。
用xshell除錯的優點就是軟體最佳化好,使用起來很舒服。
缺點是,沒法看16進位制資料;不能自定義資料幀格式;也沒有輸入框,也顯示不了使用者輸入資料。
整體來說,xshell用於這種串列埠除錯,其實不是很明智的選擇。
設計
思路
初始化之後,回撥函式接收使用者輸入,主函式處理使用者輸入的資料
同時,因為xshell不能顯示使用者的輸入資料,所以需要在主函式中列印使用者輸入。
不建議在回撥函式中列印,具體原因程式碼中會有表示。
分析
本來想做成一個類,這樣所有的串列埠都可以同時使用,但是有一說一,C語言實現類確實有點麻煩。
可以用一個結構體儲存引數,然後傳遞到函式中,這個方法要比類簡單的多,而且也能實現所有串列埠都可以使用的功能。但是目前一個串列埠就夠用,就沒有實現了,有興趣的可以自己實現一下。
初始化部分
初始化部分決定了整段程式碼的相對獨立性,所以在初始化時應該輸入一個引數,讓這個庫可以用於任何一個串列埠。
這個引數應該是串列埠的決定性引數,如&huart1這樣的。
回撥函式
回撥函式應該做最少的處理,畢竟中斷裡面做大量計算,太佔資源了。
主函式處理
這一部分可以做大量的計算和處理,畢竟主函式可以被打斷,不會太耗費資源。
實現
實現這些功能,要解決幾個問題
- 輸入的命令需要儲存下來,方便後面處理。
- 回撥函式是執行在中斷中,可以做到即時,但是主函式是在while中,不能做到即時相應。
- 需要一個特定的按鍵來出發判斷機制,做對應的命令處理。
特定命令判斷
先從最簡單的開始,以使用者的習慣來說,回車做為確定符號是很自然的。
退格作為命令輸入之後的取消,也是相對合理的。只是在做的時候和正常的取消有點不同。
輸入命令的儲存
儲存採用的是棧的方式,定義一個陣列,再定義一個座標指標,透過座標指標向陣列寫入資料。
不同步問題
資料無法完全實時同步,所以就需要一個輸出指標。這樣雖然輸出和輸入資料還是不同步,但可以做到延遲響應,不會讓資料在輸出時丟失。
程式碼
原始碼
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();
}
}