stm32實現printf列印log的幾種辦法
在stm32微控制器下,改一些bug的時候,光靠除錯還不行,有時候需要列印log來檢視某些變數在一段時間內的變化趨勢,但是板卡又沒有接串列埠,沒辦法重定向到串列埠列印,上網查資料研究了一下,發現以下幾種辦法可實現微控制器像在pc終端一樣列印log:
方法1:使用串列埠重定向,將printf列印的資訊輸出到串列埠,再將串列埠連線pc端串列埠接收終端,在終端上檢視log.主要2個步驟:修改printf函式底層呼叫到的fputc函式和避免使用semihos TIng(半主機模式)。
a.如果使用mdk作為編譯工具,在Target選項框裡選Use MicroLib 選項,即為使用微庫模式,不會使用半主機模式;
b.工程中新增串列埠配置程式碼,方便後續使用串列埠傳送資料
void UART1_Configuration(void) { USART_InitTypeDef USART_InitStructure; USART_ClockInitTypeDef USART_ClockInitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1 |RCC_APB2Periph_USART1, ENABLE ); USART_ClockInitStructure.USART_Clock = USART_Clock_Disable; // 時鐘低電平活動 USART_ClockInitStructure.USART_CPOL = USART_CPOL_Low; // 時鐘低電平 USART_ClockInitStructure.USART_CPHA = USART_CPHA_2Edge; // 時鐘第二個邊沿進行資料捕獲 USART_ClockInitStructure.USART_LastBit = USART_LastBit_Disable; // 最後一位資料的時鐘脈衝不從SCLK輸出 /* Configure the USART1 synchronous paramters */ USART_ClockInit(USART1, &USART_ClockInitStructure); // 時鐘引數初始化設定 USART_InitStructure.USART_BaudRate = 115200; // 波特率為:115200 USART_InitStructure.USART_WordLength = USART_WordLength_8b; // 8位資料 USART_InitStructure.USART_StopBits = USART_StopBits_1; // 在幀結尾傳輸1個停止位 USART_InitStructure.USART_Parity = USART_Parity_No ; // 奇偶失能 USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; // 硬體流控制失能 USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; // 傳送使能+接收使能 /* Configure USART1 basic and asynchronous paramters */ USART_Init(USART1, &USART_InitStructure); /* Enable USART1 */ USART_ClearFlag(USART1, USART_IT_RXNE); //清中斷,以免一啟用中斷後立即產生中斷 USART_ITConfig(USART1,USART_IT_RXNE, ENABLE); //使能USART1中斷源 USART_Cmd(USART1, ENABLE); //USART1總開關:開啟 }
c.新增串列埠中斷程式
#define TxBufferSize (countof(TxBuffer) - 1) #define RxBufferSize 0x20 /* Private macro -------------------------------------------------------------*/ #define countof(a) (sizeof(a) / sizeof(*(a))) /* Private variables ---------------------------------------------------------*/ u8 TxBuffer[] = "\n\rUSART Hyperterminal Interrupts Example: USART-Hyperterminal\ communication using Interrupt\n\r"; u8 RxBuffer[RxBufferSize]; u8 NbrOfDataToTransfer = TxBufferSize; u8 NbrOfDataToRead = RxBufferSize; u8 TxCounter = 0; u16 RxCounter = 0; /******************************************************************************* * Function Name : USART1_IRQHandler * Description : This function handles USART1 global interrupt request. * Input : None * Output : None * Return : None *******************************************************************************/ void USART1_IRQHandler(void) { if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) { /* Read one byte from the receive data register */ RxBuffer[RxCounter++] = (USART_ReceiveData(USART1) & 0x7F); if(RxCounter == NbrOfDataToRead) { /* Disable the USART Receive interrupt */ RxCounter = 0; //USART_ITConfig(USART1, USART_IT_RXNE, DISABLE); } } if(USART_GetITStatus(USART1, USART_IT_TXE) != RESET) { /* Write one byte to the transmit data register */ USART_SendData(USART1, TxBuffer[TxCounter++]); if(TxCounter == NbrOfDataToTransfer) { /* Disable the USART1 Transmit interrupt */ USART_ITConfig(USART1, USART_IT_TXE, DISABLE); } } }
d.修改fputc和fgetc函式
int fputc(int ch, FILE *f)
{
/* 傳送一個位元組資料到USART1 */
USART_SendData(USART1, (u8) ch);
/* 等待發送完畢 */
while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
return (ch);
}
// 重定向c庫函式scanf到USART1
int fgetc(FILE *f)
{
/* 等待串列埠1輸入資料 */
while (USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == RESET);
return (int)USART_ReceiveData(USART1);
}
d.現在就可以直接在程式碼中使用printf就可以在串列埠連線的pc端看到列印資訊了
方法3:使用Jlink自帶的RTT工具列印。相比前2種,速度更快,也不依賴編譯工具,只要有JLink即可。步驟如下:
b.從官網下載的JLINK程式安裝後,再找到包裡以下4個個檔案新增到工程中
c.這就可以直接使用SEGGER_RTT_printf函式列印,函式使用格式如下:
SEGGER_RTT_printf((0,"hello test %d times\r\n",++u32Counter);
d.先啟動除錯程式,然後可以在安裝的Jlink目錄找到J-link RTT Client.exe(還有其他檢視log工具),開啟此程式,列印的資訊即可顯示在該視窗
e.檢視RTT輸出的工具有三個:
- RTTViewer:不支援中文。至少要進入一次Debugger才能正常顯示輸出。建議進入Debugger之後再開啟,否則經常不能正常顯示輸出
- RTTLogger:支援中文,並且可以儲存為log檔案。使用具體的正確使用方法不清楚。根據手冊說明,log只接收RTT通道1的輸出,即
SEGGER_RTT_printf(1,"字串",輸出格式)
。但是實測,只能輸出RTT通道0的資訊,並且要求程式碼中要有使用到通道1的語句。否則收不到資料。 - RTTClient:必須配合RTTLogger或者keil的Debugger來使用,而RTTLogger也必須配合Debugger使用。Client、Logger和Debugger三個視窗都開啟的時候,Client和Logger只有其中一個能正常顯示,另外一個會嚴重丟失資料。
方法4:使用圖形化顯示資料的除錯法寶—JScope。
J-Scope是SEGGER公司推出的,可以在目標MCU執行時,實時分析資料並圖形化顯示的軟體。它不需要像SWO那樣需要MCU上面額外的引腳,而是使用標準的除錯介面。J-Link驅動4.90之後的版本都有這個軟體哦。具體操作筆者未測試過,可以參考該文章https://blog.csdn.net/qlexcel/article/details/55668322