在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端看到打印信息了
方法2:利用mdk自帶的ITM功能查看log打印,這種辦法優點是不佔用串口,速度快,但還是依賴mdk來顯示,在keil官網有相關的詳細介紹http://www.keil.com/support/man/docs/uv4/uv4_db_dbg_printf_viewer.htm,有興趣的朋友可以試試
方法3:使用Jlink自帶的RTT工具打印。相比前2種,速度更快,也不依賴編譯工具,只要有JLink即可。步驟如下:
a.需要使用新版的JLINK軟件,最低要V4.90版本以上,舊版的jlink沒有該功能,這是官方下載鏈接https://www.segger.com/downloads/jlink/#J-LinkSoftwareAndDocumentationPack、
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