嵌入式系統上的簡(jiǎn)易printf

發(fā)布時(shí)間:2009-11-27 19:09    發(fā)布者:linux_Ultra
關(guān)鍵詞: printf , 嵌入式系統
嵌入式系統上的簡(jiǎn)易printf
windstorm on 2008-12-30,17:02 1,097 views Comments (3)
此文針對嵌入式軟件的業(yè)余初學(xué)者,高手請自行路過(guò)打醬油。
嵌入式中,調試手段通常有兩種,一是遠程gdb,一是直接printf。如果是調試自己玩的小板子,用gdb有點(diǎn)大張旗鼓了,大多數情況下printf就可以搞定。不過(guò)printf的問(wèn)題是stdiolib的size太大,稍微有點(diǎn)程序,加上幾個(gè)常用的庫,比如stdio和string,超過(guò)16k甚至32k(已經(jīng)大于一些低端芯片的flash容量了)是很正常的事情,而且通常比較慢,程序越多,越麻煩。道理很簡(jiǎn)單,標準C語(yǔ)言庫的規范中,Printf()必須處理大量的數據格式,包括字符串、字符、(各種長(cháng)度的有符號和無(wú)符號)數字,以及浮點(diǎn)值。而且格式字符串還要包括用于更改文本對齊、基數、間距、字段寬度和精度的調節器和指示器。符合這個(gè)規范的代碼必然會(huì )是冗長(cháng)和繁重的。一些嵌入式系統庫倒是提供了一些之針對整數的printf,但還是有問(wèn)題,首先是還是太大,其次是你沒(méi)有自己的調整權限。
其實(shí)printf也就是IO的調用包裝而已,我們完全可以自己寫(xiě)一個(gè)簡(jiǎn)易版本的printf滿(mǎn)足自己的需要,并隨時(shí)根據需要裁剪。具體來(lái)說(shuō),printf在這里要起的作用就是將調試字符串從嵌入式目標空閑的串口壓出,并在運行于宿主工作站的終端模擬器上顯示結果。下面就簡(jiǎn)單介紹一下,如何來(lái)自己寫(xiě)一個(gè)簡(jiǎn)易printf函數。


要寫(xiě)printf,首先要知道什么是可變參數傳遞,我們來(lái)看看標準庫里面,是如何定義可變參數實(shí)現的:

#define _AUPBND (sizeof (acpi_native_int) - 1)
#define _ADNBND (sizeof (acpi_native_int) - 1)
#define _bnd(X, bnd) (((sizeof (X)) + (bnd)) & (~(bnd)))
#define va_arg(ap, T) (*(T *)(((ap) += (_bnd (T,_AUPBND))) - (_bnd (T,_ADNBND))))
#define va_end(ap) (void) 0
#define va_start(ap, A) (void) ((ap) = (((char *) &(A)) +(_bnd(A,_AUPBND))))
關(guān)于可變參數的原理,網(wǎng)上有一些文章,總結來(lái)說(shuō),就是我們可以通過(guò)Intel80×86機器的對齊特性來(lái)獲得所有的參數,因為在Intel80×86機器上,每個(gè)變量的地址都要是sizeof(int)的倍數,這樣能提升CPU運行的效率。也就是說(shuō),所有參數的首地址都要是4的倍數,就算你是char型的,那浪費3個(gè)byte也要安排你占第四個(gè)坑。
好,由于C語(yǔ)言傳遞參數時(shí)是用push指令從右到左將參數逐個(gè)壓棧,因此我們通過(guò)棧指針跳4n格來(lái)訪(fǎng)問(wèn)第n個(gè)參數,不要忘了,參數的地址都是字對齊的。這里,我們用#define _bnd(X, bnd) (((sizeof (X)) + (bnd)) &(~(bnd)))來(lái)計算類(lèi)型為X的參數在棧中占據的字對齊后的字節數。bnd是sizeof (acpi_native_int) –1,acpi_native_unit在32位機的定義是:
typedef u32 acpi_native_uint;
所以( ~(bnd))就是0xfffffffc 。 因此,_bnd(X,bnd) 宏在32位機下就是
  ( (sizeof(X) + 3)&0xfffffffc )
很明顯,其作用是–倘若sizeof(X)不是4的整數倍,將其變?yōu)?的整數倍。
va_start(ap,A) 負責初始化參數指針ap,將函數參數A右邊第一個(gè)參數的地址賦給ap,這個(gè)第一個(gè)參數通常就是printf里面的”%x%d%f%d”。
va_arg(ap,T) 可以獲得ap指向參數的值,并使ap指向下一個(gè)參數,T用來(lái)指明當前參數類(lèi)型。
在這里,上述代碼還是麻煩,而且sizeof我們也不能直接用,所以我們不如干脆直接寫(xiě)一個(gè)不那么麻煩而有針對性的可變參數操作定義:
#define sizeof(x) ((char *)(&x+1) - (char *)(&x))
#define va_start(ap,v)  ( ap = (char *)&v + sizeof(v) )
#define va_arg(ap,t)    ( *(t *)((ap += sizeof(t)) - sizeof(t)) )
#define va_end(ap)      ( ap = (char *)0 )
有了這幾個(gè)定義,print函數就好寫(xiě)了,為了節省空間,這個(gè)簡(jiǎn)單的print()只支持“%s”,“%d”和”%c”格式的分類(lèi)符,暫時(shí)不需要其他功能,比如格式對齊之類(lèi)的,當然,可以根據自己的需要擴展這個(gè)函數。
int print( const char *fmt, ... )
{
  const char *s;
  char c;
  int d;
  va_list ap;
  va_start(ap, fmt);
  while( *fmt != '\0' )
  {
    if( *fmt != '%' )
    {
      uart_putc(*fmt++);
      continue;
    }
    switch(*++fmt)
    {
      case 's':
      {
        s = va_arg(ap, const char *);
        uart_puts(s);
        break;
      }
      case 'd':
      {
        d = va_arg(ap, int);
        uart_putints(d, 10);
        break;
      }
      case 'c':
      {
        c = va_arg(ap, char);
        uart_putc(c);
        break;
      }
      default:
        uart_putc(*fmt);
    }
    fmt++;
  }
  va_end(ap);
  return 1;
}
這里面有一些函數,uart_putc是串口驅動(dòng)程序,給串口送東西的,uart_puts是簡(jiǎn)單的多重putc包裝。uart_putints則需要做一些atoi的轉換,一個(gè)比較簡(jiǎn)單但是有效的atoi程序宏定義如下:
#define ATOI(X, result) \
do{ \
char *lptr = X; \
result = 0; \
while (1) \
{ \
if ((*lptr >= '0') && (*lptr <= '9')) \
{ \
result *= 10; \
result += *lptr - '0'; \
lptr++; \
} \
else \
{ \
break; \
} \
} \
}while(0)
本文地址:http://selenalain.com/thread-5878-1-1.html     【打印本頁(yè)】

本站部分文章為轉載或網(wǎng)友發(fā)布,目的在于傳遞和分享信息,并不代表本網(wǎng)贊同其觀(guān)點(diǎn)和對其真實(shí)性負責;文章版權歸原作者及原出處所有,如涉及作品內容、版權和其它問(wèn)題,我們將根據著(zhù)作權人的要求,第一時(shí)間更正或刪除。
您需要登錄后才可以發(fā)表評論 登錄 | 立即注冊

相關(guān)視頻

關(guān)于我們  -  服務(wù)條款  -  使用指南  -  站點(diǎn)地圖  -  友情鏈接  -  聯(lián)系我們
電子工程網(wǎng) © 版權所有   京ICP備16069177號 | 京公網(wǎng)安備11010502021702
快速回復 返回頂部 返回列表
午夜高清国产拍精品福利|亚洲色精品88色婷婷七月丁香|91久久精品无码一区|99久久国语露脸精品|动漫卡通亚洲综合专区48页