2010年7月30日 星期五

偷吃步

繼剛剛寫的,do while(0)的好處後,當然要找個實例來練習一下


平常最常用的就是數值飽和 和 deadband


以往程式都需要一直複製貼上,呵,我就是這樣


這樣程式會愈來愈大


利用define 的好處,改寫一下程式


以下是練習的程式


#define sa(value,sta) \
 do { \
  if (value>sta)value=sta;\
  else if(value<-sta)value=-sta;\
 }while(0)


//定義一個飽和的巨集


#define deadband(value,range) \
 do{ \
  if (value>range)value-=range; \
  else if (value<-range)value+=range; \
  else value=0; \
 }while(0)


//定義一個deadband 的巨集


int main(void){


 int c=-50,d;


 deadband(c,3); 
 sa(c,10);
 


 while(1);
}


 


喔?這樣以後要使用deadband 或是 飽和的程式時,就可以結省許多空間了,真的是太好了


那就可以寫自己專屬的lib了,好像很不賴的感覺。


do while(0)

在看範例程式的時候,又看到奇怪的用法


do{


xxx( );


XXX( );


}while(0);


心想,雖然我c語言沒有很強,但是while(0) 不成立,只做一次,幹嘛不寫成


xxx( );


XXX( );


多浪費do while(0) 的動作



網路上的文章寫得很清楚do while(0)


搞懂了為什麼之後


那麼,這樣寫code size會不會變大?


int c;
void a1(void){
 c++;
}
void b1(void){
 c++;
}


#define test() \
do { \
 a1( ); \
 b1( ); \
}while(0)


int main(void){


 test();
 
 //a1( ); 
 //b1( ); 


 while(1);
}


經過compiler的結果,test( ) ; 和 單獨使用 a1( ); b1( );,這樣的code size 並不會變大。


又偷學到了一招好用的define 技巧


2010年7月29日 星期四

變數宣告的問題

在看了別人的程式之後,看到別人的範例很愛用static 的型態


本來只是在複習static 的用法而已


意外的找到一篇還不錯的文章


之前旁邊的學弟問我,全域變數和區域變數名稱一樣,會有什麼結果……


當時我還沒有辦法理解,現在已經有了非常明確的答案了


第3章scope裡面,說得非常清楚


嗯,接下來就是需要實作了


 


int same_name;
int ans;


int  k1(void){
 int same_name=10; //區域變數  same_name
 return same_name;


//回傳區域變數same_name


//在離開副程式後,全域變數same_name 會將數值存回原本全域變數的數值
}


 


int  k2(void){
 same_name=20; //全域變數 same_name 數值為20
 return same_name;
}



int main(void){


 same_name=30;//全域變數 same_name 數值為30
 ans=k1();
 ans=k2();
 
 while(1);
}


 


一個非常有趣的問題,不過我覺得,還是不要出現這樣的問題比較好,因為在debug上我覺得會造成困擾才對。


 


那麼練習下一個問題


void a1 ( void){
 static int b=0;
 b++;
}


void a2 ( void){
 int b=0;
 b++;
}


int main(void){
 int i;
 
 for (i=0;i<10;i++)a1( ); 


//此時b值為10,因為static變數 的關係,程式使用完,還會保留資料。
 for (i=0;i<10;i++)a2( );


//此時b值為1,因為是local 變數,用過即丟。
 
 while(1);
}


 


原來,我對C語言,仍舊一知半解……


2010年7月28日 星期三

繼承與指標副程式

之前有提到過的指標副程式的用法


最近看到了一個有趣的程式寫法


至於出處,我已經不清楚了,不過是從microchip網站的範例下載來的


因為是練習,所以刪除了一些不必要使用的程式



typedef struct {     // EEPROM DATA OBJECT
 unsigned int  *buff;     
 unsigned char  n;       
 unsigned int  addr;      
 unsigned int  csel;  // chip select     
}I2CEMEM_DATA;


//資料結構


typedef struct {     // EEPROM DRIVER OBJECT
 unsigned int cmd;
 I2CEMEM_DATA *oData;        
 void (*init)(void *);                  
 void (*tick)(void *);
}I2CEMEM_DRV;


//其中oDATA 繼承了 I2CEMEM_DATA的資料結構
   
#define I2CSEMEM_DRV_DEFAULTS { 0,\
        (I2CEMEM_DATA *)0,\
  (void (*)(void *))Init_I2C,\
        (void (*)(void *))I2CEMEMdrv} 


//巨集,定義I2CEMEM_DRV的資料 



void Init_I2C( I2CEMEM_DRV *i2cMem );
void I2CEMEMdrv(I2CEMEM_DRV *i2cMem);


int main (void){
 unsigned int wBuff[32];
 // Instantiate Drive and Data objects
 I2CEMEM_DRV i2cmem= I2CSEMEM_DRV_DEFAULTS;


//初始化i2cmem ,使用I2CSEMEM_DRV_DEFAULTS 巨集
                                 
 I2CEMEM_DATA wData={0,0,0,0}, rData={0,0,0,0};


 i2cmem.init(&i2cmem); //Initialize I2C periphral device;


//執行void Init_I2C( I2CEMEM_DRV *i2cMem )
//等同執行 Init_I2C( )這支副程式;


 wData.buff=wBuff;


//將wData.buff 指向 WBuff 的位址
 i2cmem.oData=&wData;


//將i2cmem.oData 指向wData 的位址
 i2cmem.tick(&i2cmem);  


//執行void I2CEMEMdrv(I2CEMEM_DRV *i2cMem)
//等同執行 I2CEMEMdrv( )這支副程式



 
 while(1);
}



void Init_I2C( I2CEMEM_DRV *i2cMem )
{
 i2cMem->cmd=10;
    i2cMem->oData=0; 
}


void I2CEMEMdrv(I2CEMEM_DRV *i2cMem)
{
 int  cntr=0;
 i2cMem->oData->addr=11;
 i2cMem->oData->buff;
 i2cMem->oData->n=12;
 i2cMem->oData->csel=13;


 for(cntr=0;cntr<32;cntr++)
 *(i2cMem->oData->buff+cntr)=cntr;
}


其中裡面比較有趣的是(void (*)(void *)) 這樣的轉態指令,一開始的時候,我還搞不是很懂為什麼要這樣做,原因是在程式設定時,因為型態的不同,所以要強制轉態,這就好像是變數一樣:


int a=10;


char b;


b=( char )a;


有點類似這樣的感覺


這樣的程式感覺好像是多此一舉,用定址副程式就可以搞定了,還特定寫了指標副程式,搞得程式很複雜,但我猜這是為了讓程式可以模組化,只要找到程式類型相近的,那麼,就可以直接套進去使用了,或許,只需要修改


#define I2CSEMEM_DRV_DEFAULTS { 0,\
        (I2CEMEM_DATA *)0,\
  (void (*)(void *))Init_I2C,\
        (void (*)(void *))I2CEMEMdrv} 


這段巨集程式,這樣就可以輕鬆修改程式了


ps:好久沒有看這樣的程式,真的會生疏,還好有寫blog的習慣,不然應該都忘光了


2010年7月21日 星期三

呼吸燈

昨天花了一點時間在做爽度的東西


上一篇做爽度的機構搞定後,另一個學弟layout了一塊藍光LED,配合原本繼有的TIMER,製作一個簡單的呼吸燈


關燈的樣子



開燈的樣子



在寫這段程式的時候,不想浪費硬體資源,所以就將呼吸燈的程式寫在1ms 的sampling time 裡


為了調一個看起來比較順眼的呼吸燈,我竟然調了好久@@


以下是我的呼吸燈程式


s16 ho_x,ho_y,ho_z;
#define ku 18
void hosi(void)  //1ms
{
  ho_x++;
  if (ho_x>ku){
    ho_x=0;
    if (ho_z){
     ho_y++;
     if (ho_y>((ku+8)<<1))ho_z=0;
    }
    else {
     ho_y--;
     if (ho_y<-8)ho_z=1;
    }
   }
 
 if   (ho_x>ho_y>>1)  GPIO_ResetBits(GPIOA, GPIO_Pin_12) ;
 else  GPIO_SetBits(GPIOA, GPIO_Pin_12) ;
}


好啦,做爽度的東西做好了,會不會比賽的時候,結果失敗了,大家都會笑說:「哈哈,那組很騷包的也不怎麼樣嘛!!!」,哈哈


2010年7月19日 星期一

感冒了

話說這幾個月都是大熱天,蛤?


就是熱死人不嘗命的日子


我竟然感冒了


哈哈


有句話是


「只有笨蛋才會在夏天感冒」


所以


我是笨蛋


2010年7月18日 星期日

做爽度的機構

這星期其實我去花蓮玩,回來後,學弟搞一個很屌的機構出來


話說,這真的是在做爽度的


還把隊名刻在上面= =


聽說,還要做個LED在下面打光……


贏了!!!



這東西是他自己劃solidworks,自己拿CNC做的,我真的不知道該說什麼


這個爽度100分!!!


     


裝上sensor的樣子,功能滿分



整個就是量身訂做,和這種人做事,不管結果如何,就是爽


雖然這個比賽的東西還沒做完,但慢慢已經到了尾聲的時候了,該好好整合一下了!


2010年7月17日 星期六

解長度不一的字串

學弟定了一個有趣的字串給我


*Y,xxx,yyy,@


其中,有效的資料為xxx與yyy,而xxx與yyy是會因為數值大小,而變長變短


所以它給我的封包有可能是:


*Y,xxx,yyy,@


*Y,xx,yy,@


*Y,x,y,@


或者是類似的變化


因為以前在寫通訊協定時,都是使用固定長度,也就是數值太小時,補0


一開始的想法,就是利用找出「,」的位置,計算2者「,」中間的長度,進而判斷使用何者解碼方式


就是下面那個「比較笨的做法」


但是有了上一次的程式優化的技巧,想到了一個比較簡單的解法,就是「比較聰明的做法」


 


//const char string_data1[]={"*Y,123,456,@"};
//const char string_data1[]={"*Y,23,45,@"};
const char string_data1[]={"*Y,3,6,@"};


int x,y;
int i,j,k;


int main (void){


 //比較笨的做法
 j = 0;
 for (i=3;i<6;i++){


  if(string_data1[i] == ',')break;
  j++;


 }
 switch (j){
  case 1:
   x=string_data1[3]-0x30;
  break;
  case 2:
   x=(string_data1[3]-0x30)*10;  
   x+=string_data1[4]-0x30;
  break;
  case 3:
   x=(string_data1[3]-0x30)*100;
   x+=(string_data1[4]-0x30)*10;    
   x+=string_data1[5]-0x30;
  break;
 }
 
 k=0;
 j = 4+j;
 for (i=j;i<j+3;i++){


  if(string_data1[i] == ',')break;
  k++;
 }


 switch (k){
  case 1:
   y=string_data1[j]-0x30;
  break;
  case 2:
   y=(string_data1[j]-0x30)*10;  
   y+=string_data1[j+1]-0x30;
  break;
  case 3:
   y=(string_data1[j]-0x30)*100;
   y+=(string_data1[j+1]-0x30)*10;    
   y+=string_data1[j+2]-0x30;
  break;
 }


 asm("Nop");


 //比較聰明的做法
 i = 3;
 x = string_data1[i++] - 0x30 ;
 while(string_data1[i] != ','){
  x *= 10 ;
  x += string_data1[i++] - 0x30 ;
 }
 i++;
 y = string_data1[i++] - 0x30 ;
 while(string_data1[i] != ','){
  y *= 10 ;
  y += string_data1[i++] - 0x30 ;
 }
 
 asm("Nop");
}


 


透過這樣的小實驗做練習,真的會提升程式上的技巧,雖然程式的功能是一樣的,但是技巧不同,可能會導致成本的提高,程式閱讀上的困難


2010年7月16日 星期五

我要改運

最近好像很雖,做什麼事都不是很順利


連出去玩都會受傷orz


今天練開車也是問題一堆= =


撞到s型跑道的安全島= =


差點別人的車撞到我的車@@


穿紅色的衣服看能不能改運好了



哈,這樣穿看起來就是胖……


希望之後的運氣可以好一點


2010年7月7日 星期三

學開車

以後上班後應該沒有什麼時間,所以在當兵前,去報考汽車駕訓班


原來開車還滿難的


坐在別人車上看別人開車都好像很輕鬆的樣子


自己開就是一回事了


不知道是否可以順利拿到駕照


或許,我要學葉問:「我要考十次!」


哈哈


 


所以沒有考過的話,不要笑我了


 


2010年7月3日 星期六

轉接板升級

終於新光保全機器人的硬體與感測器都已經定案了@@


花了點時間,重新設計了一塊轉接板


降低了一點線路,不然整台車的配線就只有一個「亂」字可言


 


這是原本的配線



 


 


新板的轉接板比起舊的轉接板大了一點,原因是把感測器也放在上面了


背面多了幾顆IC


重新配線的結果


有比較整齊一點了


原來配線是一件超辛苦的事,錯怪學弟了,難怪他每配一次都和我抱怨一次@@


好累


我剩大概一個月的時間可以製作了,希望接下來不要出現太多問題才是