2012年11月18日 星期日

如何在VS2010下編譯64位元組合語言

查了一些資料,自己試了一下。簡單記錄以免又忘記。
1. 在VS2010下面開一個新Project,選用Empty Project,這點和開純C的程式一樣。
2. Platform改為x64
3. 點專案名右鍵,找到Build Customization,將其masm選項打勾
4. 在Source File下加入 *.asm。若為新檔,可以先選用C++,再將副檔名改為asm。
5. 修改Proprety內的設定:
   a. 在Advanced下的Entry Point寫上啟始函式名
   b. 在System下的SubSystem選一個型別

看起來容易,問題是不好查。
除錯時只要將Disassembly打開就可以了。
有了除錯器,再來就簡單了。


2012年11月11日 星期日

64位元組合語言讀書心得

除了介紹X64組合語言外,也介紹了Win64 ABI。
看了Win64 ABI終於了解為何Win32的程式在Win64下是無法使用的。
因為改了基本的C函式呼叫的動作。
為了增加C函式呼叫的速度,將原先參數堆放在Stack的動作,部分改到暫存器中。
這確實會增加執行效率,而Bee在ARM處理器也看到是用這樣的動作。
Win64 ABI是使用四個暫存器 RCX, RDX, R8, R9。
一時好奇去查了一下Linux ABI用了六個RDI, RSI, RDX, RCX, R8, R9。
看來大家都是用同一招做C函式呼叫加速。

其他的,就和一般處理器差不多。
再下來就是我自己的作業了:寫一個程式。


2012年10月24日 星期三

程式寫好,非但沒有讚美,反而被罵!

這是以前公司真實事件。


公司機器從我進公司以來,一直有生產調精準度問題。


機器程式到我手上已有四年,產品必須改善功能才能追上市場腳步。


在新加功能中,多了一個檢知器,可以看到工件上標記,並進行加工。


開發過程中,發現系統使用3*3矩陣做映射對位。這塊程式古老到超過十年未有人去動。


為了符合新觀察到的現象,我使用4*4矩陣來做映射對位。


效果出奇的好,精度提高4倍。且生技工程師也稱讚比以前好調很多。


這次修改只動用到軟體,沒有增加任何硬體,等於未加入任何成本。


我認為這是件不錯的事。


 


主管列入績效,卻沒有下文。


去追結果,此項似乎被老闆刪除了。


 


半年後,主管提出我升職申請,再次列入績效內。


很怪的是,我升不了。要不是主管拼了命一再申請,我根本升不了。


 


既然升了,總要報告我的工作績效。


一提到此項功能,我總算了解我升不了的原因。


在研發會議上,老闆指著我這個項目,對著四十幾位工程師面前說:


"這明顯就是一個軟體錯誤,只是修正錯誤,根本不是功能。你根本不夠格談這件事。"


好吧!這件事老闆如此說,我也不回嘴。


接下來,我便報告目前我手上維護機器數目及名稱。大約有五十多機種。


 


三個月後,我離職了。還好接手工程師程度好,沒有問我太多問題。


公司機種大約一百多種,在我手上快一半,銷售數量佔產出機器70%


 


真不知老闆到底在想什麼?



2012年8月12日 星期日

新工作一年感想

一句話:突飛猛進。

工作量大,新技術不斷出現。部門人口暴增三倍。
所以就沒有多少時間寫文章了。

一邊工作,一邊找人。只是,人難找。
找嵌入式系統的人,都是做手機,不想來碰硬體。
找單晶片的人,不懂RTOS。
找馬達控制的人,幾乎沒有工作經驗。
找影像處理的人,不幸的目前無空缺。
有合的真的不多。



簡單多工3.1版


Bee用第3版發展到程式大了,使用的Task數目一多,整個反應時間就慢了。
但有不少Task其實作用時間很短,且工作量很低。
所以就加入了暫時性的工作。
其實考量了可以使用暫時性工作可以用,大部分都是暫時性工作的使用機會高。
這樣就不會一下子增加太多的工作數目,可以保持掃描率,反應不會太慢。

只是暫時性工作要除錯完成再改為暫時性管理方式。因為TaskID一直變的話是不好追蹤的。

新增程式功能說明:

#define MIN_FREE_NUM    3
設定從那一個ID數往後找

unsigned char Get_Temp_Task(void (*p_func)(void));
設定暫時性工作,傳入啟動函式,回傳可用的TaskID。
若回傳為0xFF,則是無空的Task可用。

void Task_Retire(void);
暫時性工作結束

unsigned char Check_Task_Free(unsigned char TaskID);
看工作是否為空閒,主要是用來檢查暫時性工作是否結束。


整體程式:
#include "mt.h"
#define TASK_NUM        4
#define MIN_FREE_NUM    3
extern void SimInit(void);
extern void OS_ENTER_CRITICAL(void);
extern void OS_EXIT_CRITICAL(void);

#define INIT_DEVICE()        SimInit()
#define DIS_INTR()            OS_ENTER_CRITICAL()
#define ENA_INTR()            OS_EXIT_CRITICAL()
// O.S function declaim
void Dummy(void);
unsigned char FuncID;
volatile unsigned char Task_State[TASK_NUM];
unsigned short Task_Delay_Count[TASK_NUM];
void (*TaskFunc[TASK_NUM])(void);

// O.S functions
void InitSystem(void)
{
    extern void Task1(void);
    extern void Task2(void);
    extern void Task3(void);
    unsigned char i;

    FuncID = 0;
    for ( i= 0; i< TASK_NUM ; i++)
    {
        Task_State[i] = TASK_RUNNING;
        Task_Delay_Count[i] = 0;
        TaskFunc[i] = Dummy;
    }
    TaskFunc[0] = Task1;
    TaskFunc[1] = Task2;
    TaskFunc[2] = Task3;
}

void Sleep_Next(void (*p_func)(void))
{
    TaskFunc[FuncID] = p_func;
    Task_State[FuncID] = TASK_WAIT;
}

void WakeUp(unsigned char n)
{
    Task_State[n] = TASK_RUNNING;
}

void Task_Set_Next(void (*p_func)(void))
{
    TaskFunc[FuncID]=p_func;
}

void Dummy(void)
{
    Sleep_Next(Dummy);
}

void Task_Delay_Ms_Next(unsigned int n, void (*p_func)(void))
{
    if (! n ) n = 1;
    TaskFunc[FuncID] = p_func;
    Task_Delay_Count[FuncID] = n;
    Task_State[FuncID] = TASK_DELAY;
}

void Task_Set_Skip_Next(unsigned int n, void (*p_func)(void))
{
    if (! n ) n = 1;
    TaskFunc[FuncID] = p_func;
    Task_Delay_Count[FuncID] = n;
    Task_State[FuncID] = TASK_SKIP;
}

unsigned char Get_Temp_Task(void (*p_func)(void))
{
    int i = MIN_FREE_NUM;
    while(i < TASK_NUM )
    {
        if( TaskFunc[i] == Dummy )
        {
            TaskFunc[i] = p_func;
            return i;
        }
        i++;
    }
    return 0xff;
}

void Task_Retire(void)
{
    Sleep_Next(Dummy);
}

unsigned char Check_Task_Free(unsigned char TaskID)
{
    return ( TaskFunc[TaskID] == Dummy )? 1: 0;
}

// O.S functions end

// main loop
void main(void)
{
    INIT_DEVICE();
    InitSystem();
    while (1)
    {
        switch (Task_State[FuncID])
        {
        case TASK_RUNNING :
            TaskFunc[FuncID]();
            break;
        case TASK_SKIP :
            if ( Task_Delay_Count[FuncID] )
            {
                Task_Delay_Count[FuncID]--;
                if (! Task_Delay_Count[FuncID]) WakeUp(FuncID);
            }
            break;
        default:
            break;
        }
        if ( ++FuncID >= TASK_NUM ) FuncID = 0;
    }
}

void TimeTick(void)  // This is timer interrupt
{
    unsigned char i;

    for ( i=0; i< TASK_NUM; i++)
    {
        if ( Task_State[i] == TASK_DELAY )
        {
            if ( Task_Delay_Count[i] )
            {
                Task_Delay_Count[i]--;
                if (! Task_Delay_Count[i]) WakeUp(i);
            }
        }
    }
}



開頭檔內容:
void Sleep_Next(void (*p_func)(void));
void WakeUp(unsigned char n);
void Task_Set_Next(void (*p_func)(void));
void Task_Delay_Ms_Next(unsigned int n, void (*p_func)(void));
void Task_Set_Skip_Next(unsigned int n, void (*p_func)(void));
unsigned char Get_Temp_Task(void (*p_func)(void));
void Task_Retire(void);
unsigned char Check_Task_Free(unsigned char TaskID);

//  Task State
#define        TASK_RUNNING  0
#define        TASK_SKIP     1
#define        TASK_DELAY    2
#define        TASK_WAIT     3

// O.S. Macro
#define     SET_TASK(id,p_func)     {TaskFunc[id]=p_func;}
#define     SET_STATE(id,state)     {Task_State[id]=state;}
#define     SET_DELAY(id,n)         {Task_Delay_Count[id]=n;}

// Time Constant
#define     SKIP_CYCLE_TIME_US      (0.083)
#define     SKIP_US(x)              ((int)((x)*(1.0/SKIP_CYCLE_TIME_US)))


測試程式:
#include < stdio.h >
#include "mt.h"

unsigned char temp_count;
void TempTask(void)
{
    printf("T");
    temp_count++;
    if(temp_count < 10)
    {
        Task_Set_Skip_Next(SKIP_US(500),TempTask);
    }
    else
    {
        Task_Retire();
    }
}

void Task1(void)
{
    unsigned char task_handle;
  
    printf("G");
    temp_count = 0;
    task_handle = Get_Temp_Task(TempTask);
    WakeUp(task_handle);
    Task_Delay_Ms_Next(10,Task1);
}

void Task2(void)
{
    printf("z");
    Task_Delay_Ms_Next(1,Task2);
}

void Task3(void)
{
    printf(".");
    Task_Set_Skip_Next(SKIP_US(1000),Task3);
}


測試結果:



2012年8月5日 星期日

Cortex-M3 : 遇到HardFault該如何處理

寫Config週邊時最常遇到的是,週邊不動,不然就是HardFault。

若是發展中的程式,還知道現在正在加入的是那一個功能。

但拿到的是舊程式,出現HardFault就糟了,因為從那裏產生的都不知。

其實沒有如此糟,只要去看堆疊內容就行了。

SP+24的位置,就是產生HardFault的位址,一般會是標準函式庫。
就可以查出是那一個呼叫弄錯了。

另外SP+20則是Link Register的內容,可以進一步告知前一個呼叫函式位置。
往前追就可以看到是那一個應用程式弄錯了。

不過大概要會組合語言的人才會知道怎麼回事,原理不多說了。


2012年7月29日 星期日

機器視覺的計算硬體

目前可以使用的計算硬體有:

1. CPU
  這是最傳統的,但計算效能較低。就算是影像用的DSP,效能仍輸以下介紹的硬體。
  但它有一個好處是其他硬體沒有的:良好的除錯及程式修改環境。
 
2. FPGA
  Bee有一段時間是想要做這個,才開始要做就出現了另一個硬體。
  使用FPGA有最快的執行效能,但是要包含記憶體控制才能做得好。
  所以寫程式的難度算是最高的。相對的價格也是高。
  開發慢且修改不易是FPGA影像處理發展上的門檻。
 
3. GPU
  不錯的執行效能及相對便宜的價格。程式開發也還可以,也不需擔心記憶體管理上的瑣事。
  只是起步較晚資料相對少。
 
之所以提這個問題,是看到有人想往機器視覺發展,選擇的路徑有些奇怪。
可能是限制於知識背景,所以使用單一方法去做。

Bee則是因為三種方法都會並無此問題。
問題反而是:正確可以計算的方法為何?
Bee採用的方法是:
先以CPU的架構先去取得資料,採用背景分析的方式去找運算方法。
然後採用GPU的方式去實現,因為程式相近,所以轉換較快速。
再不行的話再將前處理以FPGA去處理,不過還沒走到這一步過。

對於FPGA還不是很熟的人要去做機器視覺,Bee只能說:請您自求多福。