2011年11月3日 星期四

int 2 ascii

之前寫了一個程式優化的文章


參考了BEE大大的建議,今天寫了一個int 2 ascii function


 


 


void int2ascii(BYTE *cp, WORD uquad)


{


    #define to_char(cp) (cp)+'0'


 


    memset(cp-5, '0', 5); // set all byte is ascii of '0'


   


    while (uquad >= 10) {


        *--cp = to_char(uquad % 10);


        uquad /= 10;


    }


    *--cp = to_char(uquad);


}


使用方式:


 




    BYTE CP[6]={0};


    WORD TEST = 4;


    int2ascii(&CP[5],TEST);


 


cool~  挺方便的


 


實在沒有多少時間寫文章,只能說抱歉了……


 


另外因為工作上的需求,要來做點工具了,又要停頓一陣子了,有空再來整理。



2011年11月2日 星期三

神奇的選單

如何寫一個容易維護的選單程式,當初只會寫了一堆if then else, 或者是switch case……


維護的好辛苦,今天寫了一個神奇的選單程式,維護上就簡單多了


VBITS_16 Menu_Flag_All,
         Menu_Flag_0,
         Menu_Flag_1,
         Menu_Flag_2,
         Menu_Flag_3,
         Menu_Flag_4;



void Null_Menu(void)
{
   ;
}


void Task_Menu_0(void)
{
    Menu_Flag_0.word = 0x0002;
    Menu_Service(&Menu_Flag_0, Menu_Table_0);
}


void Task_Menu_1(void)
{
    Menu_Flag_1.word = 0x0002;
    Menu_Service(&Menu_Flag_1, Menu_Table_1);
}


void Task_Menu_2(void)
{
    Menu_Flag_2.word = 0x0002;
    Menu_Service(&Menu_Flag_2, Menu_Table_2);
}


void Task_Menu_3(void)
{
    Menu_Flag_3.word = 0x0001;
    Menu_Service(&Menu_Flag_3, Menu_Table_3);
}


void Task_Menu_4(void)
{
    Menu_Flag_4.word = 0x0001;
    Menu_Service(&Menu_Flag_4, Menu_Table_4);
}


void test(void)
{
   ;
}


const FUNCT_PTR_V_V Menu_Table_0[16] =
{
    Null_Menu,
    Null_Menu,
    Null_Menu,
    Null_Menu,
    Null_Menu,
    Null_Menu,
    Null_Menu,
    Null_Menu,
    Null_Menu,
    Null_Menu,
    Null_Menu,
    Null_Menu,
    Null_Menu,
    Null_Menu,
    Null_Menu,
    Null_Menu,
};


const FUNCT_PTR_V_V Menu_Table_1[16] =
{
    Null_Menu,
    Null_Menu,
    Null_Menu,
    Null_Menu,
    Null_Menu,
    Null_Menu,
    Null_Menu,
    Null_Menu,
    Null_Menu,
    Null_Menu,
    Null_Menu,
    Null_Menu,
    Null_Menu,
    Null_Menu,
    Null_Menu,
    Null_Menu,
};
const FUNCT_PTR_V_V Menu_Table_2[16] =
{
    Null_Menu,
    Null_Menu,
    Null_Menu,
    Null_Menu,
    Null_Menu,
    Null_Menu,
    Null_Menu,
    Null_Menu,
    Null_Menu,
    Null_Menu,
    Null_Menu,
    Null_Menu,
    Null_Menu,
    Null_Menu,
    Null_Menu,
    Null_Menu,
};
const FUNCT_PTR_V_V Menu_Table_3[16] =
{
    test, //test
    Null_Menu,
    Null_Menu,
    Null_Menu,
    Null_Menu,
    Null_Menu,
    Null_Menu,
    Null_Menu,
    Null_Menu,
    Null_Menu,
    Null_Menu,
    Null_Menu,
    Null_Menu,
    Null_Menu,
    Null_Menu,
    Null_Menu,
};
const FUNCT_PTR_V_V Menu_Table_4[16] =
{
    Null_Menu,
    Null_Menu,
    Null_Menu,
    Null_Menu,
    Null_Menu,
    Null_Menu,
    Null_Menu,
    Null_Menu,
    Null_Menu,
    Null_Menu,
    Null_Menu,
    Null_Menu,
    Null_Menu,
    Null_Menu,
    Null_Menu,
    Null_Menu,
};


const FUNCT_PTR_V_V Menu_Table_All[16] =
{
    Task_Menu_0,
    Task_Menu_1,
    Task_Menu_2,
    Task_Menu_3,
    Task_Menu_4,
    Null_Menu,
    Null_Menu,
    Null_Menu,
    Null_Menu,
    Null_Menu,
    Null_Menu,
    Null_Menu,
    Null_Menu,
    Null_Menu,
    Null_Menu,
    Null_Menu,
};


 


void Menu_Service(VBITS_16 *service_flags, const FUNCT_PTR_V_V *service_table)
{


    WORD flags;
    int index = 0;
    if ( 0 == service_flags->word) return;
    flags = service_flags->word;
    while((flags & 1) == 0)
    {
       flags >>= 1;
       index++;
    }


    service_flags->word &= ~(1<<index);  /* Clear service request. */


    (*service_table[index])();              /* Dispatch to service handler. */


}


 


使用上也不複雜


  Menu_Flag_All.word = 0x0008;
  Menu_Service(&Menu_Flag_All, Menu_Table_All);


一樣也是call 副程式來使用,應該還可以寫得更好才對。


 


今天就先這樣吧!


PID 寫法

回想以前寫的程式,和現在在公司被訓練的結果,果然還是有差


今天播空寫一下PID 的寫法,功能可能不太正常,不過大至上應該是正常的


typedef enum _PIDType_e
{
    CTR_TYPE_PID = 0,
    CTR_TYPE_P,
    CTR_TYPE_PD,
    CTR_TYPE_PI,
} PIDType_e;


 


typedef struct _PIDStruct
{
PIDType_e Type;
    int Kp;
    int Ki;
    int Kd;
    long Error;
    long Error_Differential;
    long Error_Last;
    long Error_Accumulate;
    long Error_Saturation;
    long Output;
    long Output_Saturation;
   
}PIDStruct;


#define PIDStruct_Size sizeof(PIDStruct)


void PID_Control(PIDStruct *PID_ST)
{
    PID_ST->Error_Differential = PID_ST->Error - PID_ST->Error_Last;
    PID_ST->Error_Accumulate += PID_ST->Error;
    SATURATION(PID_ST->Error_Accumulate,
        PID_ST->Error_Saturation,
        -PID_ST->Error_Saturation);
    PID_ST->Error_Last = PID_ST->Error;


    switch (PID_ST->Type)
    {
    case CTR_TYPE_PID:
        PID_ST->Output = PID_ST->Kp * PID_ST->Error +
                         PID_ST->Ki * PID_ST->Error_Accumulate +
                         PID_ST->Kd * PID_ST->Error_Differential;      
        break;
    case CTR_TYPE_P:
        PID_ST->Output = PID_ST->Kp * PID_ST->Error;
    break;
    case CTR_TYPE_PD:
        PID_ST->Output = PID_ST->Kp * PID_ST->Error +
                         PID_ST->Kd * PID_ST->Error_Differential;      
    break;
    case CTR_TYPE_PI:
        PID_ST->Output = PID_ST->Kp * PID_ST->Error +
                         PID_ST->Ki * PID_ST->Error_Accumulate;
    break;


    default:
        // Error
        break;
    }
   
    if (0 != PID_ST->Output_Saturation)
    {
        // do not judgement if saturation is zero.
        SATURATION(PID_ST->Output,
        PID_ST->Output_Saturation,
        -PID_ST->Output_Saturation);
    }


}


這樣在做應用的時後,如果整支程式重複的地方,就不需要重複的貼上PID的寫法,只要CALL 這支副程式就可以搞定了


用之前先初始化變數


PIDStruct Motor_CTR_L;


void PID_Init(void)
{
    memset(&Motor_CTR_L, 0, PIDStruct_Size);
    Motor_CTR_L.Type = CTR_TYPE_PID;
    Motor_CTR_L.Kp = 10;
    Motor_CTR_L.Ki = 1;
    Motor_CTR_L.Kd = 2;
    Motor_CTR_L.Output_Saturation = -4000;
    Motor_CTR_L.Error_Saturation = -3000;
   
}


把誤差丟進去 Motor_CTR_L.Error = -1000;


最後CALL 副程式


PID_Control(&Motor_CTR_L);


我只有貼比較重要的片段,不過也差不多是所有的程式碼了。


 


忙裡偷閒的練習寫自己有興趣的程式,還滿爽的!


2011年6月1日 星期三

keil c compiler 流呈

上班後才發現,自己的會的東西太少


這是keil compiler的流呈


其實看懂這個不難,但如果要用make file 把這個流呈寫出來,那應該功力是超強


之前看不懂以為就算了,現在看懂他在寫什麼,真的覺得他超強。


 




2011年5月31日 星期二

版本控制 - Mercurial

之前使用的版本控制是SVN,不過最近同事在使用Mercurial,今天花了1天玩了一下,發現確實是有些優點。


可以先去mercurial 下載軟體並看介紹:mercurial 官方網址

mercurial 介紹





這個作者寫的非常好懂


這是作者內文中的2張圖,非常的清楚了解到中控式和分散分的版本控制軟體的差別。


 


其中他寫了一段


Even if you’re working by yourself, you should use Mercurial to get the benefits of version control. This tutorial shows how easy it is to check a directory into Mercurial so you can track old versions easily.


即使程式只有自己在譔寫,他還是建議使用mercurial 來當版本控制。




 

以下是一些mercurial 的教學,有興趣的看看



mercurial 教學




mercurial coommand line




 


還有一個也是分散式的Git,不過大家可以參考這篇文



http://zoomquiet.org/res/scrapbook/ZqFLOSS/data/20100927101141/



2011年5月27日 星期五

2011 開戰了

2011  開戰了(誤,逃~~


不是啦


是:競賽網頁開站了



2011人工智慧單晶片電腦鼠暨機器人國內及國際邀請賽



給有興趣的人參考一下


剛剛看了一下比賽規則,好像今年除了有電腦鼠的國際賽之外,另外自走車也會有國際賽,我想這應該會是一個非常有趣的競賽。


看來又要感謝教育部支持這樣的比賽了


2011年5月4日 星期三

第一次與Python 接觸

雖然不是第一次看過python的語言,但這是第一次利用python寫一段程式


這次寫的是1個sin wave generator,不過似乎用得很不習慣,就先將就一下吧


for i in range(101):


   


    test = int(math.sin(float(i)/100*(2*math.pi))*0x80+0x80)


    if test > 0xFF:


        test = 0xFF


    print "0x%02x," %test,


    if i%10 == 0:


        print '\n'


 


我覺得應該還有更好的寫法,本來想把資料存到陣列裡,最一次列印出來,不過我還不會陣列的用法,就先這樣吧!


 


PS:Python 拿來寫小工具感覺還不賴,有空再來練習吧


2011年4月5日 星期二

版本控制

相信有在寫程式的一定知道,備份程式是一件很重要的事


但人工備份真的是很花時間,又很佔空間,不過沒關係,可以在網路上找版本控制軟體


在此介紹的是subversion,以下簡稱SVN


這是一套免費的軟體,而且還滿好用的


網路上也可以找到很多相關的教學


不過因為在用的時候,還是有一些手續要做,所以就寫了一篇當給自己參考的文章嚕


 


首先先建立一個「資料庫(repository)」,在此我是建在database的資料夾內



接著建立三個空的資料夾,分別為「trunk」、「branches」、「tags」


並執行匯入的動作



檢查自己的資料庫是否有加入這三個資料夾,如果有的話就代表完成了



這三個資料的名稱是為了之後版本圖revision graph要用的,這些動作非常重要,沒做這些動作的話,以後可能會非常的痛苦,因為SVN可能會誤認為所有的資料夾都是trunk,要操作branch 或是tag就會變得非常的麻煩了。



接著在要更改的專案內,check out,通常第一次都是trunk,相信大家有先看過前一篇的SVN教學,在此就不多加解說了。



因為只是要練習用,所以我只有建立一個test的文字檔



選擇此文字檔為要commit的對象。



習慣上,最好是要加新功能的話,建一個branch來做編輯測試比較好


所以我們要建立一個branch出來



因為trunk只有一個,但branch和tag可以有很多個,所以這裡我先以branches/a1來建立


雖然名稱不是很重要,但最好建立的時候是取一個比較有意義的名稱,以免brance建多了,就會造成困擾了。



建完後,SVN不會自動幫你產生這個資料夾,所以需要自己手動從資料庫把branches/a1的專案check out出來。



這樣就可以針對a1做編輯測試的動作了



我們可以先看一下版本圖,我們可以發現,現在已經建出branch了。



如果需要把專案複制給別人的話,建議不要直接用「複制」、「貼上」的方式,因為這樣會把svn相關的檔案也一起複制、貼上了,所以比較好的方式是用「匯出」,將SVN資料庫內的專案匯出



這裡要注意一下,匯出的時候記得檔名要改一下,不然可能會有檔案名稱相同的衝突



我們可以發現匯出後,就多了一個my_a1的資料夾了,裡面就是一個不含SVN的專案了




題外話,最好是建立資料庫後,裡面再建立一個不同專案的branch,這樣才不會每建一個專案,就要再多一個資料庫了;另外雖然SVN很好用,但別忘了還是要固定的備份資料庫,以免資料庫壞了,所有的專案也跟本出問題了,這樣辛苦的結晶就都白費了


2011年2月3日 星期四

宣告絕對位址的變數

以前不熟悉指標,當然也不熟悉memory的意思


感謝這個工作,讓我對memory有了進一步的了解


難怪指標在C語言無處不見,因為真的很好用


不過到底那裡好用,這個我一時也說不上來


不過最近研究了一下我之前常用的MCU,絕對位址的宣告如何使用


1、以dsPIC 為例


int var __attribute__ ((address(0x860)));


意思就是將 var 這個變數定義在address 0x860的位址上


2、以stm32  MDK 為例


#include <absacc.h> 
 
int var __at (0x20008000);


 


其實這只是一個簡單的操作手法而已


那麼要讀出絕對位址的方法就很多種了


int ans;


var = 10;


以往的做法


ans = var;


那定義成絕對位址的話


1、以dspic為例


ans = *(volatile int *)0x860;


2、以stm32 為例


ans = *(volatile int *)0x20008000;


 


或許有人可以想到這樣做有什麼好處了


不過不知道的也沒關係


有時候,真的就是"了解它,玩它",就對了


2011年1月1日 星期六

union 和 struct 的差異

我想union 和 struct 是一個很好用的語法


不過小細節最好還是要弄清楚一點比較好


以下做了一下點這方面的比較


以C30的compiler 為例


 


ex1:


以這樣的範例來說,應該不會有人有問題吧


union {
 unsigned int word;
 struct _field_1
 {
  unsigned int a:5;
  unsigned int b:4;
  unsigned int c:7; 
 }field_1; 
}union_int_1;


假設


union_int_1.field_1.a=0x1;
union_int_1.field_1.b=0x1;
union_int_1.field_1.c=0x1;


那麼


union_int_1.word 會等於0x0221;


沒錯


這是我們要的答案


 


ex2:


union {
 unsigned int word;
 struct _field_2
 {
  unsigned char a:5;
  unsigned char b:4;
  unsigned char c:7; 
 }field_2; 
}union_int_2;


 


假設


union_int_2.field_2.a=0x1;
union_int_2.field_2.b=0x1;
union_int_2.field_2.c=0x1;


那麼


union_int_1.word 會等於0x0101;


沒錯


這不是我們想要的答案,這樣就會出現問題了


問題出在,因為結構的關系,當要做bit field 的設定時,field a 與field b 2者超過8個bit (1個byte),那麼compiler 會自動利用16個bit(1個word)做處理。


所以,這次compiler 對於這個struct 會產生 3個byte 出來,對於union 來說,只能mapping 到2個byte,但多出來的1個byte 就獨自使用了


如果今天不使用到union_int_1.word 這樣的用法時,是不會產生bug,但如果不注意的話,肯定會花一段時間找bug (因為我就是過來人= =)


 


ex3:


union {
 unsigned int word;
 struct _field_3
 {
  unsigned char a:4;
  unsigned char b:4;
  unsigned char c:8; 
 }field_3; 
}union_int_3;


假設


union_int_3.field_3.a=0x1;
union_int_3.field_3.b=0x1;
union_int_3.field_3.c=0x1; 


那麼


union_int_1.word 會等於0x0111;


這也會是我們要的答案


 


有了上面的實驗結果,那麼我們是不是可以把bit field 的宣告都設為unsigned int (16bit) 這樣就好了


當然沒有錯,是可以都這樣使用,但如果你要做聯結的動作只有unsigned char (8bit) 那就會有另一個課題了


 


ex4:


union {
 unsigned char byte;
 struct _field_4
 {
  unsigned char a:4;
  unsigned char b:4;
 }field_4; 
}union_int_4;


ex5:
union {
 unsigned char byte;
 struct _field_5
 {
  unsigned int a:4;
  unsigned int b:4;
 }field_5; 
}union_int_5;


 


有興趣的可以比較2者所做的結果,這裡所做的結果都會一樣,但是在compiler的時後 ex5 會比 ex4  還要多浪費1個byte ,如果當老闆在和你要求code size 和 ram size 時,你就知道又要準備被釘了= =


PS:小細節不可馬虎,太重要了