這個簡易延時,就是跳過數次執行後再回復所要執行的函式。可以做為微量延時用,也是Bee用來控制LCM的主要方法。
但它並不精準,只能保證大於某一個時間。
它新加了以下程式:
unsigned char Task_Skip_Count[TASK_NUM]={0,0,0,0};
void (*TaskResumeFunc[TASK_NUM])(void) = {Dummy,Dummy,TestSkip0,LED_Light};
void Task_Skip(void)
{
if ( Task_Skip_Count[TaskID] ){
Task_Skip_Count[TaskID]--;
if (! Task_Skip_Count[TaskID]) TaskFunc[TaskID]=TaskResumeFunc[TaskID];
}
}
void Task_Skip_Next(void (*p_func)(void))
{
TaskResumeFunc[TaskID]=p_func;
}
void Task_Set_Skip(unsigned char n)
{
if (! n ) n = 1;
TaskFunc[TaskID] = Task_Skip; // to sleep
Task_Skip_Count[TaskID] = n;
}
各函式功能說明:
TaskSkip()就是實際執行計數用函式,在各Task未回復到使用者的程式前,就是先用它頂進去做計數用。
Task_Skip_Next()設定延時結束時,要回復的函式。先存於TaskResumeFunc[TASK_NUM]變數中。
Task_Set_Skip()設定跳過執行的次數。
因為後二個太常用一起用,所以可以用一個巨集取代。
#define TASK_SKIP_N_NEXT(n,p_func) Task_Skip_Next(p_func);Task_Set_Skip(n);
在使用上因為涉及時間控制,但各處理器速度不同。所以只能實際量測。
Bee是先用一次簡單延時再觸發8051的腳位,量出時間,才確定要給多少值。
要是那次有別的工作用了比較長的時間,那就不保證,所以只能確定至少有多長。
像Bee用的LCM其命令之間需要73us的狀況,差不多設定跳過9次就足夠了。好在多的時間不太會影響。
另外程式開發完成後最好再量一次,因為工作多了,會和開發中的延時狀況不同,Bee的LCM程式在開發完成後發現延時多了,結果又降了一次。
有人會問,使用這種延時有什麼好處?
我只能說,可以讓多組這樣的程式一起跑,不會因一組延時就卡死。這就是一種多工的方法。
2010年2月18日 星期四
8051簡單多工2:基本設計規格及原理
在8051上想做多工,安裝RTOS又太大,大的不是程式本身,而是使用於Task registers記憶體太多了。
為了省下使用記憶體及加速切換,改用函式指標的方式做為多工切換的方法。
在多組程式運行的系統上,必須有FuncID做為各別task區分用。
所以核心的程式樣貌是這樣:
idata unsigned char FuncID = 0;
idata void (*TaskFunc[TASK_NUM])(void) = {Dummy,Dummy,TestSkip0,LED_Light};
void main(void)
{
Init_Device();
while (1)
{
register void (*Current_Func)(void);
EA = 0;
Current_Func = TaskFunc[FuncID]; // may break with interrupt
EA = 1;
Current_Func();
FuncID = (++FuncID) % TASK_NUM;
}
}
其實這段程式原是想要這樣執行:
while (1)
{
TaskFunc[0]();
TaskFunc[1]();
TaskFunc[2]();
TaskFunc[3]();
}
而TaskFunc[]內容是可以由程式改變,而原先初值設定為Dummy(),就會執行Dummy()。
如果要做程式移轉,只要做TaskFunc[0]=Dummy2;就會改為執行Dummy2()。
兩個程式若是要對跳,可以這樣寫:
void Test1(void)
{
// do 1
TaskFunc[0] = Test2;
}
void Test2(void)
{
// do 2
TaskFunc[0] = Test1;
}
然後執行時就可以看到TaskFunc[0]()執行動作為,一次執行Test1(),一次執行Test2()不停循環下去。
為了省去寫程式去記目前Task使用的FuncID,所以加入一組函式來做。
void Task_Set_Next(void (*p_func)(void))
{
TaskFunc[FuncID]=p_func;
}
程式就可以簡單寫為
void Test1(void)
{
// do 1
Task_Set_Next(Test2);
}
void Test2(void)
{
// do 2
Task_Set_Next(Test1);
}
但各程式塊不想看到其他程式,也就是要有管理程式來分隔。
所要加入的管理功能,首要是時間控制。有二種時間控制是Bee需要的。
1.短暫時間控制
定義為不使用中斷方式,以程式跳過執行權方式做延時。跳過次數可以設定。
2.ms級時間控制
定義為使用中斷方式,在執行多少ms後回復執行權。
其實並不是真正的放棄執行權,而是函式指標暫時以Dummy填入,待延時結束後才填回使用者的程式。
這時FuncID的用處出來了,因為它可以知道是那一組可以做回復動作。
最後,主程式要去開關中斷又是如何?就是EA=0後取得TaskFunc[FuncID]內容後,然後才執行。因為就結果來看來多此一舉。
如果您認為多此一舉,那代表您在中斷上使用還不夠熟。
因為若是直接寫
TaskFunc[FuncID]();
會發生死當,但機率不高。以組合語言追蹤當機的指令時,會發現一個很少見的狀況。
因為要取得TaskFunc[FuncID]的內容,8051要取用二次資料,這是因為8051是8位元,但指標是16位元。
就在取出第一個byte內容時,剛好中斷發生,跑去執行中斷程式,若又剛好是回復執行的動作,會回填新的函式指標值,然後退出中斷。
接下來取出第二個byte,但這個是回復的指標值,和原先的不同,接下來去執行就跑去怪地方死當了。
所以在取出函式指標時要加入關閉中斷,取完後回復中斷。
取出後,為了不浪費記憶體及加速,所以使用暫存器宣告register告知編譯器放在暫存器即可。
所以
register void (*Current_Func)(void);
這行是宣告一個暫存器變數,型態為函式指標。這可不是函式原型宣告哦!
為了省下使用記憶體及加速切換,改用函式指標的方式做為多工切換的方法。
在多組程式運行的系統上,必須有FuncID做為各別task區分用。
所以核心的程式樣貌是這樣:
idata unsigned char FuncID = 0;
idata void (*TaskFunc[TASK_NUM])(void) = {Dummy,Dummy,TestSkip0,LED_Light};
void main(void)
{
Init_Device();
while (1)
{
register void (*Current_Func)(void);
EA = 0;
Current_Func = TaskFunc[FuncID]; // may break with interrupt
EA = 1;
Current_Func();
FuncID = (++FuncID) % TASK_NUM;
}
}
其實這段程式原是想要這樣執行:
while (1)
{
TaskFunc[0]();
TaskFunc[1]();
TaskFunc[2]();
TaskFunc[3]();
}
而TaskFunc[]內容是可以由程式改變,而原先初值設定為Dummy(),就會執行Dummy()。
如果要做程式移轉,只要做TaskFunc[0]=Dummy2;就會改為執行Dummy2()。
兩個程式若是要對跳,可以這樣寫:
void Test1(void)
{
// do 1
TaskFunc[0] = Test2;
}
void Test2(void)
{
// do 2
TaskFunc[0] = Test1;
}
然後執行時就可以看到TaskFunc[0]()執行動作為,一次執行Test1(),一次執行Test2()不停循環下去。
為了省去寫程式去記目前Task使用的FuncID,所以加入一組函式來做。
void Task_Set_Next(void (*p_func)(void))
{
TaskFunc[FuncID]=p_func;
}
程式就可以簡單寫為
void Test1(void)
{
// do 1
Task_Set_Next(Test2);
}
void Test2(void)
{
// do 2
Task_Set_Next(Test1);
}
但各程式塊不想看到其他程式,也就是要有管理程式來分隔。
所要加入的管理功能,首要是時間控制。有二種時間控制是Bee需要的。
1.短暫時間控制
定義為不使用中斷方式,以程式跳過執行權方式做延時。跳過次數可以設定。
2.ms級時間控制
定義為使用中斷方式,在執行多少ms後回復執行權。
其實並不是真正的放棄執行權,而是函式指標暫時以Dummy填入,待延時結束後才填回使用者的程式。
這時FuncID的用處出來了,因為它可以知道是那一組可以做回復動作。
最後,主程式要去開關中斷又是如何?就是EA=0後取得TaskFunc[FuncID]內容後,然後才執行。因為就結果來看來多此一舉。
如果您認為多此一舉,那代表您在中斷上使用還不夠熟。
因為若是直接寫
TaskFunc[FuncID]();
會發生死當,但機率不高。以組合語言追蹤當機的指令時,會發現一個很少見的狀況。
因為要取得TaskFunc[FuncID]的內容,8051要取用二次資料,這是因為8051是8位元,但指標是16位元。
就在取出第一個byte內容時,剛好中斷發生,跑去執行中斷程式,若又剛好是回復執行的動作,會回填新的函式指標值,然後退出中斷。
接下來取出第二個byte,但這個是回復的指標值,和原先的不同,接下來去執行就跑去怪地方死當了。
所以在取出函式指標時要加入關閉中斷,取完後回復中斷。
取出後,為了不浪費記憶體及加速,所以使用暫存器宣告register告知編譯器放在暫存器即可。
所以
register void (*Current_Func)(void);
這行是宣告一個暫存器變數,型態為函式指標。這可不是函式原型宣告哦!
8051簡單多工1:整體程式
以前在雷兒電子網發表的文章,重貼在這裏。目前有使用在商品上,穩定性還不錯。分成幾篇來貼。
這是為了對付8051這種沒有多大堆疊的MCU發開出來的。
我懶的用組合語言寫,實際上是說我對8051組合語言不太熟,所以用純粹C寫。因為簡單,所以拿來教學應是不錯。
整個系統可以說是為了對付LCM這個對時間有點敏感的裝置設計出來的。
LCM的規格上每次下指令或資料的時間間隔為73us。說大不大,又說小不小,拿來做單純delay不做事也很浪費。所以就想出這種多工方法。
因有對時間有點敏感的Task存在,故採用固定輪詢法(Polling)來保障單一Task微量延時的時間。
基本程式如下:
#include "c8051f340.h" // SFR declarations
#include < stdio.h >
#define LCM_DB P2 /* LCM Data Bus */
#include "F340Init.h"
sbit LED1 = 0xB4;
sbit LED2 = 0xB5;
sbit LED3 = 0xB6;
sbit LED4 = 0xB7;
// task start function
void LED_Light(void);
void LED_Black(void);
// measure skip time
void TestSkip0(void);
void TestSkip1(void);
// O.S function declaim
#define TASK_NUM 4
void Dummy(void);
idata unsigned char FuncID = 0;
idata unsigned char Task_Delay_Count[TASK_NUM]={0,0,0,0};
unsigned char Task_Skip_Count[TASK_NUM]={0,0,0,0};
idata void (*TaskFunc[TASK_NUM])(void) = {Dummy,Dummy,TestSkip0,LED_Light};
void (*TaskResumeFunc[TASK_NUM])(void) = {Dummy,Dummy,TestSkip0,LED_Light};
// O.S functions
void Dummy(void)
{
return;
}
void Task_Set_Next(void (*p_func)(void))
{
TaskFunc[FuncID]=p_func;
}
void Task_Delay_Next(void (*p_func)(void))
{
TaskResumeFunc[FuncID]=p_func;
}
void Task_Delay_Ms(unsigned char n)
{
if (! n ) n = 1;
TaskFunc[FuncID] = Dummy; // to sleep
Task_Delay_Count[FuncID] = n;
}
void Task_Skip_Next(void (*p_func)(void))
{
TaskResumeFunc[FuncID]=p_func;
}
void Task_Skip(void)
{
if ( Task_Skip_Count[FuncID] ) {
Task_Skip_Count[FuncID]--;
if (! Task_Skip_Count[FuncID]) TaskFunc[FuncID]=TaskResumeFunc[FuncID];
}
}
void Task_Set_Skip(unsigned char n)
{
if (! n ) n = 1;
TaskFunc[FuncID] = Task_Skip; // to sleep
Task_Skip_Count[FuncID] = n;
}
#define TASK_DELAY_MS_NEXT(n,s) Task_Delay_Next(s);Task_Delay_Ms(n);
#define TASK_SKIP_N_NEXT(n,s) Task_Skip_Next(s);Task_Set_Skip(n);
// O.S functions end
// main loop
void main(void)
{
PCA0MD &= ~0x40; // Disable Watchdog timer
Init_Device();
while (1)
{
register void (*Current_Func)(void);
EA = 0;
Current_Func = TaskFunc[FuncID]; // may break with interrupt
EA = 1;
Current_Func();
FuncID = (++FuncID) % TASK_NUM;
}
}
void Timer0_ISR (void) interrupt INTERRUPT_TIMER0
{
unsigned char i;
TL0 = 0x80;
TH0 = 0x44; // set delay for 1 ms
for ( i=0; i < TASK_NUM ; i++ )
{
if ( Task_Delay_Count[i] )
{
Task_Delay_Count[i]--;
if (! Task_Delay_Count[i]) TaskFunc[i]=TaskResumeFunc[i]; // wake up function
}
}
}
// Begin User Tasks
// Task 0
// Task 1
// Task 2 : measure skip time
void TestSkip0(void)
{
LED4 = 0;
TASK_SKIP_N_NEXT(10,TestSkip1);
}
void TestSkip1(void)
{
LED4 = 1;
TASK_SKIP_N_NEXT(20,TestSkip0);
}
// Task 3 : function for LED
void LED_Light(void)
{
LED1 = 0;
TASK_DELAY_MS_NEXT(250,LED_Black);
}
void LED_Black(void)
{
LED1 = 1;
TASK_DELAY_MS_NEXT(250,LED_Light);
}
這是為了對付8051這種沒有多大堆疊的MCU發開出來的。
我懶的用組合語言寫,實際上是說我對8051組合語言不太熟,所以用純粹C寫。因為簡單,所以拿來教學應是不錯。
整個系統可以說是為了對付LCM這個對時間有點敏感的裝置設計出來的。
LCM的規格上每次下指令或資料的時間間隔為73us。說大不大,又說小不小,拿來做單純delay不做事也很浪費。所以就想出這種多工方法。
因有對時間有點敏感的Task存在,故採用固定輪詢法(Polling)來保障單一Task微量延時的時間。
基本程式如下:
#include "c8051f340.h" // SFR declarations
#include < stdio.h >
#define LCM_DB P2 /* LCM Data Bus */
#include "F340Init.h"
sbit LED1 = 0xB4;
sbit LED2 = 0xB5;
sbit LED3 = 0xB6;
sbit LED4 = 0xB7;
// task start function
void LED_Light(void);
void LED_Black(void);
// measure skip time
void TestSkip0(void);
void TestSkip1(void);
// O.S function declaim
#define TASK_NUM 4
void Dummy(void);
idata unsigned char FuncID = 0;
idata unsigned char Task_Delay_Count[TASK_NUM]={0,0,0,0};
unsigned char Task_Skip_Count[TASK_NUM]={0,0,0,0};
idata void (*TaskFunc[TASK_NUM])(void) = {Dummy,Dummy,TestSkip0,LED_Light};
void (*TaskResumeFunc[TASK_NUM])(void) = {Dummy,Dummy,TestSkip0,LED_Light};
// O.S functions
void Dummy(void)
{
return;
}
void Task_Set_Next(void (*p_func)(void))
{
TaskFunc[FuncID]=p_func;
}
void Task_Delay_Next(void (*p_func)(void))
{
TaskResumeFunc[FuncID]=p_func;
}
void Task_Delay_Ms(unsigned char n)
{
if (! n ) n = 1;
TaskFunc[FuncID] = Dummy; // to sleep
Task_Delay_Count[FuncID] = n;
}
void Task_Skip_Next(void (*p_func)(void))
{
TaskResumeFunc[FuncID]=p_func;
}
void Task_Skip(void)
{
if ( Task_Skip_Count[FuncID] ) {
Task_Skip_Count[FuncID]--;
if (! Task_Skip_Count[FuncID]) TaskFunc[FuncID]=TaskResumeFunc[FuncID];
}
}
void Task_Set_Skip(unsigned char n)
{
if (! n ) n = 1;
TaskFunc[FuncID] = Task_Skip; // to sleep
Task_Skip_Count[FuncID] = n;
}
#define TASK_DELAY_MS_NEXT(n,s) Task_Delay_Next(s);Task_Delay_Ms(n);
#define TASK_SKIP_N_NEXT(n,s) Task_Skip_Next(s);Task_Set_Skip(n);
// O.S functions end
// main loop
void main(void)
{
PCA0MD &= ~0x40; // Disable Watchdog timer
Init_Device();
while (1)
{
register void (*Current_Func)(void);
EA = 0;
Current_Func = TaskFunc[FuncID]; // may break with interrupt
EA = 1;
Current_Func();
FuncID = (++FuncID) % TASK_NUM;
}
}
void Timer0_ISR (void) interrupt INTERRUPT_TIMER0
{
unsigned char i;
TL0 = 0x80;
TH0 = 0x44; // set delay for 1 ms
for ( i=0; i < TASK_NUM ; i++ )
{
if ( Task_Delay_Count[i] )
{
Task_Delay_Count[i]--;
if (! Task_Delay_Count[i]) TaskFunc[i]=TaskResumeFunc[i]; // wake up function
}
}
}
// Begin User Tasks
// Task 0
// Task 1
// Task 2 : measure skip time
void TestSkip0(void)
{
LED4 = 0;
TASK_SKIP_N_NEXT(10,TestSkip1);
}
void TestSkip1(void)
{
LED4 = 1;
TASK_SKIP_N_NEXT(20,TestSkip0);
}
// Task 3 : function for LED
void LED_Light(void)
{
LED1 = 0;
TASK_DELAY_MS_NEXT(250,LED_Black);
}
void LED_Black(void)
{
LED1 = 1;
TASK_DELAY_MS_NEXT(250,LED_Light);
}
2010年2月9日 星期二
如何讀書:速讀
這是老掉牙的話題了。
不過這樣說,還是很多人把書拿起來看就可以和周公報到了。
我現在其實也沒好到那裡去,很少撐得過半小時。上班後更是如此,不可能有完整的一小時可以好好看。
既然可以看書的時間有限,所以後來改用壓縮吸收力的方式來補強,於是去學速讀。
速讀對我最大的改善是對資訊的抽取訓練,也就是一眼就要取得關鍵訊息。這個能力可以快速過濾大量訊息,並取得有用的。
另一個是對於記憶力的訓練,從聲音記憶改以影像記憶。多一種記憶方式,就可以記更多,同時反應也加快。
就是因為速讀有效,使得我開始有興趣對於頭腦的使用。
也就是用更有效率的方式來使用頭腦,我之前都沒想過腦的效率是可以訓練來改善。
這也許是我想追上別人的唯一方法吧。
不過這樣說,還是很多人把書拿起來看就可以和周公報到了。
我現在其實也沒好到那裡去,很少撐得過半小時。上班後更是如此,不可能有完整的一小時可以好好看。
既然可以看書的時間有限,所以後來改用壓縮吸收力的方式來補強,於是去學速讀。
速讀對我最大的改善是對資訊的抽取訓練,也就是一眼就要取得關鍵訊息。這個能力可以快速過濾大量訊息,並取得有用的。
另一個是對於記憶力的訓練,從聲音記憶改以影像記憶。多一種記憶方式,就可以記更多,同時反應也加快。
就是因為速讀有效,使得我開始有興趣對於頭腦的使用。
也就是用更有效率的方式來使用頭腦,我之前都沒想過腦的效率是可以訓練來改善。
這也許是我想追上別人的唯一方法吧。
2010年2月3日 星期三
C語言寫法及效能差異
這是Bee的舊文章,貼出來讓大家參考。
這是Bee見過最好的文章,來解釋指標效能及寫法的影響。
取自"Pointer on C"由Kenneth A. Reek所著。
簡體書為"C和指針",人民郵電出版社出版。
內容的一段我簡單說明。
範例是使用68000處理器
程式碼共用此段宣告
#define SIZE 50
int x[SIZE];
int y[SIZE];
int i;
int *p1, *p2;
第一個程式
void try1()
{
for( i =0; i< SIZE; i++ )
x[i] = y[i];
}
很直覺的寫法。
組合語言碼為
_try1: clrl _i
jra L20
L20001: movl _i,d0
asll #2,d0
movl #_y,a0
movl _i,d1
asll #2,d1
movl #_x,a1
movl a0@(0,d0:L),a1@(0,d1:L)
addql #1,_i
L20: moveq #50,d0
cmpl _i,d0
jgt L20001
第二個程式
void try2()
{
for( p1 = x, p2 = y; p1 - x < SIZE; )
*p1++ = *p2++;
}
改使用指標。
組合語言碼為
_try2: movl #_x,_p1
movl #_y,_p2
jra L25
L20003: movl _p2,a0
movl _p1,a1
movl a0@,a1@
addql #4,_p2
addql #4,_p1
L25: moveq #4,d0
movl d0,sp@-
movl _p1,d0
subl #_x,d0
movl d0,sp@-
jbsr ldiv
addql #8,sp
moveq #50,d1
cmpl d0,d1
jgt L20003
組合語言碼變長了,而且因為指標相減調整(除4)而去呼叫ldiv函式。增加許多執行時間。
第三個程式
void try3()
{
for( i = 0, p1 = x, p2 = y; i < SIZE; i++ )
*p1++ = *p2++;
}
計數改用變數,不讓指標調整。
組合語言碼為
_try3: clrl _i
movl #_x,_p1
movl #_y,_p2
jra L30
L20005: movl _p2,a0
movl _p1,a1
movl a0@,a1@
addql #4,_p2
addql #4,_p1
addql #1,_i
L30: moveq #50,d0
cmpl _i,d0
jgt L20005
似乎不錯。但還有更好。
第四個程式
void try4()
{
register int *p1, *p2;
register int i;
for( i = 0, p1 = x, p 2= y; i < SIZE; i++ )
*p1++ = *p2++;
}
使用暫存器變數宣告,將區域變數使用暫存器。
組合語言碼為
_try4: moveq #0,d7
movl #_x,a5
movl #_y,a4
jra L35
L20007: movl a4@+,a5@+
addql #1,d7
L35: moveq #50,d0
cmpl d7,d0
jgt L20007
如果夠了解編譯器及語言特性,就會找到更好的調整。
第五個程式
void try5()
{
register int *p1, *p2;
for( p1 = x, p2 = y; p1 < &x[SIZE]; )
*p1++ = *p2++;
}
移除計數。
組合語言碼為
_try5: movl #_x,a5
movl #_y,a4
jra L40
L20009: movl a4@+,a5@+
L40: cmpl #_x+200,a5
jcs L20009
最精簡。但C原始碼可讀性極差。
這個例子可以看出效能及可讀性之間的移轉。
也可以看出如何依組合語言碼而調整C程式碼。
到底是要好讀,還是效能,端看使用場合而定。
這是Bee見過最好的文章,來解釋指標效能及寫法的影響。
取自"Pointer on C"由Kenneth A. Reek所著。
簡體書為"C和指針",人民郵電出版社出版。
內容的一段我簡單說明。
範例是使用68000處理器
程式碼共用此段宣告
#define SIZE 50
int x[SIZE];
int y[SIZE];
int i;
int *p1, *p2;
第一個程式
void try1()
{
for( i =0; i< SIZE; i++ )
x[i] = y[i];
}
很直覺的寫法。
組合語言碼為
_try1: clrl _i
jra L20
L20001: movl _i,d0
asll #2,d0
movl #_y,a0
movl _i,d1
asll #2,d1
movl #_x,a1
movl a0@(0,d0:L),a1@(0,d1:L)
addql #1,_i
L20: moveq #50,d0
cmpl _i,d0
jgt L20001
第二個程式
void try2()
{
for( p1 = x, p2 = y; p1 - x < SIZE; )
*p1++ = *p2++;
}
改使用指標。
組合語言碼為
_try2: movl #_x,_p1
movl #_y,_p2
jra L25
L20003: movl _p2,a0
movl _p1,a1
movl a0@,a1@
addql #4,_p2
addql #4,_p1
L25: moveq #4,d0
movl d0,sp@-
movl _p1,d0
subl #_x,d0
movl d0,sp@-
jbsr ldiv
addql #8,sp
moveq #50,d1
cmpl d0,d1
jgt L20003
組合語言碼變長了,而且因為指標相減調整(除4)而去呼叫ldiv函式。增加許多執行時間。
第三個程式
void try3()
{
for( i = 0, p1 = x, p2 = y; i < SIZE; i++ )
*p1++ = *p2++;
}
計數改用變數,不讓指標調整。
組合語言碼為
_try3: clrl _i
movl #_x,_p1
movl #_y,_p2
jra L30
L20005: movl _p2,a0
movl _p1,a1
movl a0@,a1@
addql #4,_p2
addql #4,_p1
addql #1,_i
L30: moveq #50,d0
cmpl _i,d0
jgt L20005
似乎不錯。但還有更好。
第四個程式
void try4()
{
register int *p1, *p2;
register int i;
for( i = 0, p1 = x, p 2= y; i < SIZE; i++ )
*p1++ = *p2++;
}
使用暫存器變數宣告,將區域變數使用暫存器。
組合語言碼為
_try4: moveq #0,d7
movl #_x,a5
movl #_y,a4
jra L35
L20007: movl a4@+,a5@+
addql #1,d7
L35: moveq #50,d0
cmpl d7,d0
jgt L20007
如果夠了解編譯器及語言特性,就會找到更好的調整。
第五個程式
void try5()
{
register int *p1, *p2;
for( p1 = x, p2 = y; p1 < &x[SIZE]; )
*p1++ = *p2++;
}
移除計數。
組合語言碼為
_try5: movl #_x,a5
movl #_y,a4
jra L40
L20009: movl a4@+,a5@+
L40: cmpl #_x+200,a5
jcs L20009
最精簡。但C原始碼可讀性極差。
這個例子可以看出效能及可讀性之間的移轉。
也可以看出如何依組合語言碼而調整C程式碼。
到底是要好讀,還是效能,端看使用場合而定。
C語言:浮點數通信傳輸
這是Bee的舊文章,只是每隔一兩年就會有人提。那就改貼在這裡吧!
這是Bee在做控制系統時發生的一種狀況:必須將浮點數經通信傳輸到遠端控制器上,如使用DSP做為控制器等裝置,或是網路應用等。
但通信裝置資料多半是使用串列通信的方式,如一次傳輸一個Btye的方式做傳輸,而浮點數至少是32位元的資料。
Bee用C語言資料結構解決了此問題,可是我在網路上DSP討論區也發現有人也有相同問題,只不過是用指標來解決。
我整理以此兩種方法的解決方式:
1.指標方法:
void put_float(float a)
{
char *cp=(char*)&a;
Output(*cp++);
Output(*cp++);
Output(*cp++);
Output(*cp);
}
float get_float()
{
float f;
char *c = (char*)&f;
c[0] = Input();
c[1] = Input();
c[2] = Input();
c[3] = Input();
return f;
}
使用指標鑄型將要用的float資料或char資料取出及存入。
2.資料結構方法:
void put_float(float a)
{
union {
float f;
char c[4];
}var;
var.f = a;
Output(var.c[0]);
Output(var.c[1]);
Output(var.c[2]);
Output(var.c[3]);
}
float get_float(void)
{
union {
float f;
char c[4];
}var;
var.c[0] = Input();
var.c[1] = Input();
var.c[2] = Input();
var.c[3] = Input();
return (var.f);
}
使用共用資料結構union,做資料交換的中間資料存放。
結論:
要注意浮點數存入記憶體的結構,在不同CPU上可能使用不同順序,一端為Big Endian資料格式的CPU,而另一端為Little Endian的CPU,則必須注意存放及取用順序的不同。
就結果論,兩種方法沒有好壞。但以維護性來說使用資料結構方式因可讀性高,會比較好。而我也建議使用此方法。
這是Bee在做控制系統時發生的一種狀況:必須將浮點數經通信傳輸到遠端控制器上,如使用DSP做為控制器等裝置,或是網路應用等。
但通信裝置資料多半是使用串列通信的方式,如一次傳輸一個Btye的方式做傳輸,而浮點數至少是32位元的資料。
Bee用C語言資料結構解決了此問題,可是我在網路上DSP討論區也發現有人也有相同問題,只不過是用指標來解決。
我整理以此兩種方法的解決方式:
1.指標方法:
void put_float(float a)
{
char *cp=(char*)&a;
Output(*cp++);
Output(*cp++);
Output(*cp++);
Output(*cp);
}
float get_float()
{
float f;
char *c = (char*)&f;
c[0] = Input();
c[1] = Input();
c[2] = Input();
c[3] = Input();
return f;
}
使用指標鑄型將要用的float資料或char資料取出及存入。
2.資料結構方法:
void put_float(float a)
{
union {
float f;
char c[4];
}var;
var.f = a;
Output(var.c[0]);
Output(var.c[1]);
Output(var.c[2]);
Output(var.c[3]);
}
float get_float(void)
{
union {
float f;
char c[4];
}var;
var.c[0] = Input();
var.c[1] = Input();
var.c[2] = Input();
var.c[3] = Input();
return (var.f);
}
使用共用資料結構union,做資料交換的中間資料存放。
結論:
要注意浮點數存入記憶體的結構,在不同CPU上可能使用不同順序,一端為Big Endian資料格式的CPU,而另一端為Little Endian的CPU,則必須注意存放及取用順序的不同。
就結果論,兩種方法沒有好壞。但以維護性來說使用資料結構方式因可讀性高,會比較好。而我也建議使用此方法。
2010年2月2日 星期二
GTX275基本測試資料
發現沒有放GTX275的測試資料,補一下。
PC配備:
CPU
AMD Phenom(tm) 9350e
Quad-Core Processor 2.00 GHz
O.S.
Windows XP SP3
RAM
DDR2-800 2G*4(但O.S.只有顯示使用3.25G)
測試圖:
不過GTX275現在買不太到了,可能也要絶版了。現在GTX260以上的卡都不好買。
也許是缺貨加上世代交替的影響。再來玩的CUDA都會換新卡了吧!
PC配備:
CPU
AMD Phenom(tm) 9350e
Quad-Core Processor 2.00 GHz
O.S.
Windows XP SP3
RAM
DDR2-800 2G*4(但O.S.只有顯示使用3.25G)
測試圖:
不過GTX275現在買不太到了,可能也要絶版了。現在GTX260以上的卡都不好買。
也許是缺貨加上世代交替的影響。再來玩的CUDA都會換新卡了吧!
訂閱:
文章 (Atom)

