2013-05-08 54 views
10

我在arm微處理器上編程,並試圖通過UART使用打印語句進行調試。我不想添加stdlibs只是爲了調試。有沒有辦法打印到控制檯沒有stdio.h/iostream.h?我可以寫我自己的printf()嗎?是否可以在沒有stdlibs的情況下寫入控制檯? c/C++

或者我可以使用DMA控制器直接寫入UART。不過,我想避免這是可能的。使用內置測試功能「回聲」或 「遠程環回」我知道我已正確配置了UART。

+2

是的,它是可能的 - 你可以編寫自己的輸出程序,找到一個小的獨立部分的printf()實現,或編寫必要的後端支持,以使從minimimal嵌入的libc這些功能(可能包含在您的工具鏈中)在您的平臺上運行。 – 2013-05-08 21:40:39

+0

謝謝。我聽說newlib作爲嵌入式libc工作正常。我會首先查找部分printf()。 – Sam 2013-05-08 21:47:21

+0

@ChrisStratton:它有效地依賴於操作系統。本地操作系統例程可能是標準庫。 – 2013-05-08 21:54:02

回答

9

簡答:是的,完全可以做到這兩種解決方案。

如果要支持所有數據類型和格式,printf函數相當複雜。但是寫一些可以在幾個不同的基礎上輸出字符串或整數的東西並不難(大多數人只需要十進制和十六進制,但八進制可能只會在你有十進制和十六進制後再增加3-4行代碼)。

通常情況下,printf的是這樣寫的:

int printf(const char *fmt, ...) 
{ 
    int ret; 
    va_list args; 

    va_start(args, fmt) 
    ret = do_xprintf(outputfunc, NULL, fmt, args); 
    va_end(args); 
    return ret; 
} 

然後是do_xprintf()做所有的所有變體(的printf,sprintf的,fprintf中,等)

int do_xprintf(void (*outputfunc)(void *extra, char c), void *extra, const char *fmt, va_list args) 
{ 
    char *ptr = fmt; 
    while(1) 
    { 
     char c = *ptr++; 

     if (c == '%') 
     { 
      c = *ptr++; // Get next character from format string. 
      switch(c) 
      { 
       case 's': 
        char *str = va_arg(args, const char *); 
        while(*str) 
        { 
         count++; 
         outputfunc(extra, *str); 
         str++; 
        } 
        break; 
       case 'x': 
        base = 16; 
        goto output_number; 

       case 'd': 
        base = 10; 
     output_number: 
        int i = va_arg(args, int); 
        // magical code to output 'i' in 'base'. 
        break; 

       default: 
        count++; 
        outputfunc(extra, c); 
        break; 
     } 
     else 
      count++; 
      outputfunc(extra, c); 
    } 
    return count; 
}     

的辛苦現在,所有你需要做的是填入上述代碼的幾個位,然後寫一個outputfunc()輸出到你的串口。

請注意,這是粗略的草圖,我敢肯定代碼中有一些錯誤 - 如果你想支持浮點或「寬度」,你將不得不多做一些工作......

(上額外的參數注 - 輸出到FILE *這將是文件指針,爲sprintf,你可以通過一個結構緩衝和位置在緩衝區中,或類似的東西)

+0

非常感謝。我是少數只需要​​十進制/十六進制的人之一。我正在使用定點符號,並希望驗證我的結果。這會讓事情變得更容易。我感謝您的幫助! – Sam 2013-05-08 22:03:13

+0

爲什麼'output_number'是case語句中的標籤,而不是普通函數?的任何原因。 – Lundin 2013-05-21 14:47:08

+0

@Lundin:只是輸入的時間更短。除非編譯器非常聰明,並且意識到兩個函數調用之間的唯一區別是輸入參數,否則它可能會生成較短的代碼。 – 2013-05-21 21:24:50

2

概念「控制檯」在你使用的特定系統的上下文之外並沒有太多意義。通常在嵌入式程序中沒有真正的控制檯概念。

您正在尋找的是一種從系統中獲取數據的方法。如果你想使用UART,並且你沒有使用像GNU/linux這樣的高級操作系統,你需要編寫你自己的I/O驅動程序。通常這意味着首先通過寄存器寫入將UART配置爲期望的低速/奇偶校驗/流量控制。對於任何類型的健壯的IO,您都希望它是中斷驅動的,因此您需要爲利用循環緩衝區的tx和rx編寫ISR。

完成之後,您可以編寫自己的printf,如Mats所示。

+0

謝謝!我意識到這一點。我最初試圖找出如何解決這個問題,因爲我遇到了問題。但是我發現用我的處理器後,每次寫入tx緩衝區後都必須訪問狀態寄存器。如果我不這樣做,它會停止。我現在正在處理波特率問題,但是一旦出現問題,我一定會使用Mats的東西。謝謝你的幫助 – Sam 2013-05-08 23:31:01

1

我發現後臺調試,將字符排入循環緩衝區,然後通過uart發送寄存器上的輪詢例程排空,這是我選擇的方法。

入隊例程基於字符,字符串和可變大小(以十六進制或固定寬度小數)。而一個豪華的緩衝區例程可以指示一個保留字符的溢出。這個方法對目標操作的開銷/影響最小,可以在中斷例程中使用(小心),並且這個想法很容易轉移,所以我忽略了我使用的所有系統上的調試器。

1

由於在嵌入式系統中通過串口打印信息會修改主程序的時間,所以我發現的最佳解決方案是發送一個以2字節編碼的小消息(有時1字節可以正常工作),然後使用PC上的一個程序來解碼這些消息並提供必要的信息,其中可能包含統計信息和您可能需要的任何信息。 通過這種方式,我爲主程序添加了一點點開銷,讓PC在處理消息方面付出了艱辛的努力。 也許是這樣的:

  • 1字節消息:比特7:4 =模塊ID,位3:0 =調試信息。

  • 2字節消息:位15:12 =模塊ID,位11:8 =調試信息,位7:0 =數據。

然後,在PC軟件,您必須聲明一個表映射到一個給定的模塊ID /調試信息對郵件,並用它們在屏幕上打印出來。

也許它不像pseudo-printf函數那麼靈活,因爲你需要在PC機上固定的一組消息進行解碼,但它不會增加太多開銷,正如我之前提到的。

希望它有幫助。

費爾南多

相關問題