RoBoard魔人的機器人日誌

2012/2/29

[RoBoRC第三回] 介紹程式碼roborc.cpp

終於進入程式碼的部分囉
我會帶大家看程式碼的內容
以及功用~!!

roborc.cpp
主要是用於讀取檔案以及整體架構
下面依序會有註解

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#ifdef WINCE
#include <tchar.h>
#endif

#ifdef USE_RBDLL
 #define  USE_COMMON
 #include <roboard_dll.h>
#else
 #define  USE_COMMON
 #include <roboard.h>
#endif

#if defined(RB_LINUX)
    #define _stricmp  strcasecmp
    #define _strnicmp strncasecmp
#elif defined(RB_BC_DOS) || defined(RB_DJGPP)
    #define _stricmp  stricmp
    #define _strnicmp strnicmp
#endif

#include "const.h"
#include "userio.h"
#include "errcode.h"
#include "demo.h"
#include "parse.h"

//#if defined(RB_MSVC_WIN32) || defined(RB_MSVC_WINCE)
//    #include <windows.h>
//#endif

#if defined(RB_MSVC_WIN32)
    //#define USE_SOUND
#endif

#ifdef USE_SOUND                    //聲音用
 #include "fileio.h"
    #include <irrKlang.h>
    using namespace irrklang;

 class CMyFileFactory : public irrklang::IFileFactory
 {
 public:
  virtual irrklang::IFileReader* createFileReader(const ik_c8* filename)
  {
   FILEIO* file = fileio_FileOpen((char*)filename, "rb");
   if (!file) return 0;

   return new CMyReadFile(file, filename);
  }

 protected:
  class CMyReadFile : public irrklang::IFileReader
  {
  public:
   CMyReadFile(FILEIO* openedFile, const ik_c8* filename)
   {
    File = openedFile;
    strcpy(Filename, filename);
    FileSize = (ik_s32)fileio_GetSize(openedFile);
   }

   ~CMyReadFile()
   {
    fileio_FileClose(File);
   }

   ik_s32 read(void* buffer, ik_u32 sizeToRead)
   {
    return (ik_s32)fileio_Read(buffer, 1, sizeToRead, File);
   }

   bool seek(ik_s32 finalPos, bool relativeMovement)
   {
    return fileio_Seek(File, finalPos, relativeMovement ? SEEK_CUR : SEEK_SET) == 0;
   }

   ik_s32 getSize()
   {
    return FileSize;
   }

   ik_s32 getPos()
   {
    return fileio_GetPos(File);
   }

   const ik_c8* getFileName()
   {
    return Filename;
   }

   FILEIO* File;
   char Filename[1024];
   ik_s32 FileSize;

  }; //end class CMyReadFile
 }; //end class CMyFileFactory
#endif

#define MODE_CAPTURES (1)
#define MODE_REPLAYS  (2)
#define MODE_DEMO     (100)

static bool overwrite = false;
static bool realtime = false;
static bool showmessage = true;
static unsigned long replaytime = 1000L;

typedef struct _PLAY{
 int rbidx;
 int mode_idx;
 int frameno;
 char* filename;
 unsigned long usedchannels;
}PLAY;

static void showhelp(void);
static bool init_rcservo(CH_t *ch, unsigned long channels, int rbidx);
static void close_rcservo(void);
static const char* cmdtostr(unsigned cmd);
static unsigned long htol(char* str);

static void writefile_frame(FILE* fp, unsigned long* width, int frameno);
static void mode_captureframes(PLAY *play);

static void mode_replayframes(PLAY *play);

static void captureinitfrm(DEF_t *def, unsigned long *frm);
static bool FindHomeFRM(DEF_t *def, ACT_NODE *act, unsigned long *frm);
static void init_play(DEF_t *def, ACT_NODE *act);
static void mode_playdemo(char* dirname, unsigned long idletime);

/*================ function for capture frame =====================*/
static void writefile_frame(FILE* fp, unsigned long* width, int frameno)      //用於寫入檔案用
{
 int i;

 fprintf(fp, "[Frame %d]\n", frameno);              //寫入到fp指到的檔案
 for (i=0; i<32; i++)
  fprintf(fp, "channel%d = %lu\n", i, width[i]);
 fprintf(fp, "\n");
}

static void mode_captureframes(PLAY *play)
{
 unsigned long width[32], nowtime;
 unsigned c = 0;
 int i;
 FILE* fp;
 CH_t ch[32];

 showmsg("Open %s to write...", play->filename);
 if (overwrite == false) //&&                //如果option複寫模式開啟 若是沒開啟
 if ((fp = fopen(play->filename, "r")) != NULL)            //判斷能否讀取fp指到的檔案  可以的話
 {
  fclose(fp);                    //關閉剛剛開啟的檔
  showmsg(" file already exists. overwirte? (Y/N) ");
  c = keyboard_waitkey();                 //決定是否要複寫已存在的檔案
  if ((c != 'y') && (c != 'Y'))               //如果回答不是Y  則結束程式
  {
   showmsg("...abort!\n");
   return;
  }
 }

 if ((fp = fopen(play->filename, "w")) == NULL)            //判斷寫入檔案失敗的話
 {
  showmsg("...fail!\n");
  return;
 }

 showmsg("...success\n");                
 if(tool_InitCapture(ch, play->usedchannels, play->mode_idx) != true)      //判斷tool_InitCapture()是否成立  主要是將ch的結構初始化  在demo.cpp
  return;                     //不成立就結束

 if (init_rcservo(ch, play->usedchannels, play->rbidx) == false) return; else showmsg("\n"); //init rcservo

 for (i=0; i<32; i++)
  rcservo_SetServoType(i, RCSERVO_SV_FEEDBACK, RCSERVO_FB_FASTMODE + RCSERVO_FB_DENOISE); //設定Servo的型態

 rcservo_EnterCaptureMode();                 //設定為CaptureMode
 nowtime = timer_nowtime();                 //將時間紀錄至nowtime
 showmsg("Start Frame number : %d\n", play->frameno);

 while (1)
 {
//  if ((realtime != true) || (showmessage == true))
//   showmsg("Pose the KONDO robot, and press any key to capture [ESC to quit] ...\n");
  
  if(showmessage == true){                //判斷是否要顯示訊息
   if(realtime == true)
    showmsg("Pose the KONDO robot, or press ESC to quit ...\n");
   else
    showmsg("Pose the KONDO robot, and press any key to capture [ESC to quit] ...\n");
  }

  if (realtime == true)                 //如果有realtime的話
  {
      do
            {
                c = keyboard_getkey();               
            } while (((timer_nowtime() - nowtime) < replaytime) && (c != KBESC));    //執行到按ESC或時間超過replaytime為止
   nowtime = timer_nowtime();
  }
  else                     //沒有realtime的話
   c = keyboard_waitkey();                //等待按鍵

  if (c == KBESC) break;                 //當按鍵為ESC時離開這while迴圈

  rcservo_ReadPositions(play->usedchannels, RCSERVO_CMD_POWEROFF, width);     //讀取馬達的值   放入width[32]內

  for (i=0; i<32; i++)                 //跑所有的pin腳   1~32
  {
   if (width[i] == 0xffffffffL)              //如果讀取到0xffffffff  即為讀取失敗
   {
    showmsg("ERROR: fail to read the position of channel %d (PIN S%d) !\n", i, i + 1);
    width[i] = 0L;                 //將值改為0
   }
   else                    //如果讀取成功
   {
          if ((realtime == false) || (showmessage == true))        //依設定決定是否輸出
        showmsg("The position of channel %d (PIN S%d) is %lu\n", i, i + 1, width[i]);
   }
  }//end for i...

  writefile_frame(fp, width, play->frameno);            //呼叫writefile_frame()   即為寫入檔案

  if ((realtime != true) || (showmessage == true))
   showmsg("[Frame %d] captured\n\n", play->frameno);
  play->frameno += 1;                  //當前frame編號累加1
 }

 fclose(fp);                     //結束時關閉開啟的檔案
 close_rcservo();                   //關閉rcservo
}

/*================ function for replay frame =====================*/
static void mode_replayframes(PLAY *play)              //為第二個模式  重播模式
{
 DEF_t def;
 ACT_NODE *act;
 int actno = 0;
 
 if(tool_InitReplay(&def, play->filename, replaytime, play->mode_idx, actno, play->usedchannels) != true){ //主要是初始化設定  在demo.cpp
  showmsg("...fail to replay file : %s\n", play->filename);
  return;
 }
 showmsg("Open %s to load...\n", play->filename);
 if(init_rcservo(def.channel, play->usedchannels, play->rbidx) != true)      //init rcservo
  return;

 act = tool_FindACTNode(def.acttable, actno, def.actsize);         //用actno尋找act的節點  在demo.cpp
 if(act == NULL){                   //如果沒找到   即為actno有誤
  showmsg("...fail to find act number: %d\n", actno);
  return;
 }

 if(Servo[play->mode_idx].feedback == true)             //如果是有feedback功能的馬達
  rcservo_EnterPlayMode();                //那就可以直接進入PlayMode
 else{                      //如果無此功能
  showmsg("Move to Home : [Frame %d]...", act->data.frmno);        //就必須要設定起始frame
  rcservo_EnterPlayMode_HOME(act->frm);
  if ((realtime != true) || (showmessage == true))          //依設定決定是否輸出
   showmsg("...finish\n");
  act += 1;                    
 }
 for(; act->type != ACT_START; act += 1){
  if ((realtime != true) || (showmessage == true))          //依設定決定是否輸出
   showmsg("Playing [Frame %d]...", act->data.frmno);
  rcservo_MoveTo(act->frm, act->time);             //移動馬達

  if ((realtime != true) || (showmessage == true))          //依設定決定是否輸出
   showmsg("...finish\n");

  if(showmessage == true){                //依設定決定是否輸出
   if(realtime == true)
    showmsg("press ESC to quit\n\n");
   else
    showmsg("press any key to continue [ESC to quit]\n\n");
  }
//  if ((realtime != true) || (showmessage == true))
//   showmsg("press any key to continue [ESC to quit]\n\n");
  if (realtime == true){
   if (keyboard_getkey() == KBESC) break;            //ESC離開程式
  }
  else{
   if (keyboard_waitkey() == KBESC) break;            //ESC離開程式
  }
 }

 showmsg("Finish the replay process.\n");
 tool_FreeDEF(&def);                   //將新建的空間還回去

 close_rcservo();                   //關閉rcservo
}

/*================ function for play demo =====================*/
static void captureinitfrm(DEF_t *def, unsigned long *frm)          //讀取起始位置用
{
 rcservo_EnterCaptureMode();                 //到CaptureMode
 unsigned long usech = 0;
 int i;
 for(i = 0; i < 32; i++){
  frm[i] = 0;                    //預設frm為0
  if(def->channel[i].pin != -1)
   usech += (1 << def->channel[i].pin);            //有用到的channel加進去
 }
 rcservo_ReadPositions(usech,  RCSERVO_CMD_POWEROFF, frm);         //讀取馬達位置
}

static bool FindHomeFRM(DEF_t *def, ACT_NODE *act, unsigned long *frm)       //尋找HomeFrame用
{
 int i;
 if(def->initfrm != -1){                  //如果有起始位置
  for(i = 0; i < 32; i++)
   frm[i] = def->inithome[i];               //將PWM腳位設定為起始位置
 }
 else{                      //如果沒有起始位置
  while((act->type != -1) && (act->type != ACT_PLAYFRAMENO))        //判斷是否為可用的動作
   act += 1;                   //累加動作
  if(act->type == -1){                 //如果無可用之動作
   for(i = 0; i < def->actidx; i++){             //尋找所有可用之動作
    if(def->actpool[i].type == ACT_PLAYFRAMENO)          //如果有可以用的就跳出
     break;
   }
   if(i == def->actidx)                //如果沒有找到替代的起始動作就回傳false
    return false;
   act = &def->actpool[i];                //有找到就使用找到的
  }
  for(i = 0; i < 32; i++)                 //跑32根腳位
   frm[i] = act->frm[i];                //將起始位置存到frm內
 }
 return true;
}

static void init_play(DEF_t *def, ACT_NODE *act)
{
 bool feedback = true;                  //預設feedback為True
 unsigned long frm[32], read[32];
 int i;
 for(i = 0; i < 32; i++){                 //執行32次 即32個PWM
  if(def->channel[i].pin == -1)               //沒有用到的pin就不理他
   continue;
  if(Servo[def->channel[i].mode_idx].feedback != true){         //如果不是有feedback功能的
   feedback = false;                 //有一個不是就改為false
   break;                    //有一個不是就跳出
  }
 }
 if((feedback == true) && (def->initfrm == -1))            //若有feedback功能 且 沒有起始位置
  rcservo_EnterPlayMode();                //可以直接進入PlayMode
 else{
  if(FindHomeFRM(def, act, frm) != true){             //如果是沒有feedback功能的   那就必須要尋找起始位置
            showmsg("...WARNING: no frame in this demo!\n");
   return;
  }
  if(def->initfrm_mode == INIT_FRM_FALSE){            //若initfrm_mode為false
   captureinitfrm(def, read);               //捕捉目前所在位置
   for(i = 0; i < 32; i++){
    if(read[i] != 0)
     frm[i] = read[i];               //覆蓋過去
   }
  }
  rcservo_EnterPlayMode_HOME(frm);              //rcservo起始位置
 }
 return;
}

static void mode_playdemo(char* dirname, unsigned long idletime)
{
 #define NOPLAY  (0)
 #define PLAYNEXT (1)                  
 #define PLAYING  (2)                  
 int   i;
 unsigned c;
 DEF_t  def;
 CMD_NODE *cmd;
 ACT_NODE *act;
 unsigned long playframe[32];
 int  playing = NOPLAY;
 bool playpause;
 bool playmode = false;
 unsigned long idleTimeOut = timer_nowtime();

    showmsg("Loading %s folder\n", dirname);//show filaname
    if(tool_InitDemo(&def, dirname) != true)             //把值設定為預設值    在demo.cpp
        return;
 showmsg("-----------------------------------> completed\n");

 if (init_rcservo(def.channel, def.usedChannels, def.rbidx) == false) return;    //init rcservo

    #ifdef USE_SOUND
    showmsg("Initializing Audio...");
    ISoundEngine* audioEngine = createIrrKlangDevice(ESOD_AUTO_DETECT, ESEO_MULTI_THREADED | ESEO_LOAD_PLUGINS);
    if (!audioEngine)
     showmsg("...fail to initialize audio!\n");
    else
    {
     showmsg("...success\n\n");

     //allow the audioEngine to read audio files in a zipped file
     CMyFileFactory* demofileFactory = new CMyFileFactory();
     audioEngine->addFileFactory(demofileFactory);
     demofileFactory->drop();
    }
 #endif

 showmsg("\nCommand List\n=============\n");
 for(i = 0; i < def.cmdidx; i++)                //判斷有多少cmd指令就跑幾次迴圈
  showmsg("key: %5s  (%s)\n", cmdtostr(def.cmdpool[i].command), def.cmdpool[i].name);//show command

 showmsg("\nPress one command to play the action ([ESC] to quit)...\n");
 while ((c = keyboard_getkey()) != KBESC)             //執行到按鍵得到ESC為止
 {
  switch (playing)                  //用switch判斷目前所在的模式
  {
   case NOPLAY:
    if (timer_nowtime() >= idleTimeOut){           //當目前時間大於先前紀錄之時間時成立  會在按END鍵時成立
     rcservo_EnterCaptureMode();  //stop PWM output        //rcservo進入capture mode  可以停止PWM繼續輸出
     playmode = false;               //將playmode改為false   沒在做事
    }
    if((cmd = tool_FindCMDNode(def.cmdtable, c, def.cmdsize)) == NULL)    //把輸入的字拿去尋找相對應的節點 回傳回來    在demo.cpp
                    break;
                if(playmode == false){               //若是playmode為false   沒在做事
                    if(def.initact != -1){
                        showmsg("Playing init ACT: %d\n", def.initact);
                        act = def.initactnode;             //預設動作
                    }
                    else
                        act = cmd->act;               //執行下一個動作
     init_play(&def, act);              
     playmode = true;               //playmode為true    有執行動作了
     playing = PLAYNEXT;               //playing改為PLAYNEXT
     playpause = false;               //將playmode改為false
                }
                else{                   //若playmode不為false
                    act = cmd->act;                //執行下一個動作
                    playing   = PLAYNEXT;              //playing改為PLAYNEXT
                    playpause = false;               //將playpause改為false    改為沒在做事
                }
    break;
   case PLAYNEXT:
    if ((act == NULL) || (act->type == ACT_START)){         //如果動作為無   或動作動作型態在起點
     showmsg("Finish the action.\n");
     showmsg("\nPress a command to play an action ([ESC] to quit)...\n");
     playing = NOPLAY;               //將playing改為NOPLAY
     idleTimeOut = timer_nowtime() + idletime;         //將idleTimeOut改為目前時間加上delay的時間
                   #ifdef USE_SOUND
      if (audioEngine) audioEngine->removeAllSoundSources();
     #endif
     break;
    }

    switch (act->type)
    {
     case ACT_PLAYFRAMENO:              //如果ACTION為移動馬達           
      showmsg("  Move to [Frame %d]...", act->data.frmno);
      rcservo_SetAction(act->frm, act->time);         //rcservo設定動作為act的frame間格為act的time
      playing = PLAYING;              //playing改為PLAYING
      break;
     case ACT_PAUSE:                //如果ACTION為停止動作
      showmsg("  Pause %d ms...", act->time); 
                     for (i=0; i<32; i++) playframe[i] = 0L;         //將playframe改為0  基本上是會在原位置不動
      rcservo_SetAction(playframe, act->time);        //rcservo設定動作為act的frame間格為act的time
      playing = PLAYING;              //playing改為PLAYING
      break;
     case ACT_PLAYSND:
      showmsg("  Play Audio %s...", act->data.filename);
                        #ifdef USE_SOUND
        if (!audioEngine)
        showmsg("no audio device!");
        else
        audioEngine->play2D(curact->data.filename, false, false, true);
      #else
                             showmsg("no audio support!");
      #endif
      showmsg("\n");
      break;
    }//end switch (curact->type)
    act += 1;
    break;
   case PLAYING:
    if (c == KBEND)                 //如果按下END鍵
    {
     showmsg("Stop\n");
     rcservo_StopAction();              //rcservo停止動作
     act  = NULL;                //將目前動作改為NULL  即為直接變為無剩餘動作
     break;
    }
    else if (c == KBSPACE){               //如果按下空白鍵
     playpause = (playpause == false)? true : false;        //判斷應該要暫停   還是繼續
     if (playpause == false){             //如果應該要繼續
      showmsg("Play.");
      rcservo_ReleaseAction();            //rcservo繼續動作
     }
     else{                  //其他的  代表應該要暫停
      showmsg("Paused.");
      rcservo_PauseAction();             //rcservo暫時停止動作
     }
    }
    else if(c == KBPGUP){               //如果按下PAGE UP
     act -= 1;                 //把動作多扣一個    即為退到目前所在動作的前面
     if((act-1)->type != ACT_START)            //如果再減一個 (即為回到上一個動作) 不是起頭的話
      act -= 1;                //就回到上一個的前面   這樣就達到回到上個動作的效果
     rcservo_StopAction();              //rcservo停止動作  即目前動作結束
    }
    else if(c == KBPGDN){               //如果按下PAGE DONE
     showmsg("Skip.");
     rcservo_StopAction();              //rcservo停止動作  即目前動作結束
    }
                else if((cmd = tool_FindCMDNode(def.cmdtable, c, def.cmdsize)) != NULL){  //判斷其他輸入字元 若為有效指令則執行
                    showmsg("Playing command: %s\n", cmd->name);
                    act = cmd->act;                //將動作改至新指令的第一個個動作
                    rcservo_StopAction();              //rcservo停止動作  即目前動作結束
                }

    if (rcservo_PlayAction() == RCSERVO_PLAYEND){         //若動作已到結尾
     showmsg("\n");
     playing = PLAYNEXT;               //將playing改為PLAYNEXT
    }
    break;
  }//end switch (playing)
 }//end while ((c = keyboard_getkey()...
 if (playing == PLAYING) showmsg("\n");

 tool_FreeDEF(&def);
    #ifdef USE_SOUND
  if (audioEngine) audioEngine->drop();
 #endif
 close_rcservo();
}

#ifdef WINCE                    //winCE用
int _tmain(int argc, _TCHAR* _argv[])              //winCE的main function    _tmain
{
 char  argvbuf[50][100] = {'\0'};              
 char* argv[50];

 if (argc > 50) argc = 50; //lazy code:p
 for (int _argc=0; _argc<argc; _argc++)             //將winCE的argv轉換為1個byte    winCE的argv會是兩個byte
 {
  argv[_argc] = &(argvbuf[_argc][0]);
  wcstombs(argv[_argc], _argv[_argc], 100);
 }
#else                      //非winCE時用
int main(int argc, char* argv[])
{
#endif                      //讓兩個main function共用一個內容
 int mode;                    //宣告變數mode 用於儲存輸入的模式
 PLAY play = {                   //options用    用來存指令內容的
  2, // rbidx
  0, // mode_idx
  0, // frameno
  "000.frm", // filename
  0x000000ffL //usedchannel
 };
/*
 #if defined(RB_MSVC_WIN32) || defined(RB_MSVC_WINCE)
  DWORD Error; //, priClass;

  //priClass = GetPriorityClass(GetCurrentProcess());
  if (!SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS)) //REALTIME_PRIORITY_CLASS or HIGH_PRIORITY_CLASS
  {
   Error = GetLastError();
   errmsg("Error: failed to enter HIGH_PRIORITY mode (%d)!\n", Error);
   goto End;
  }
 #endif
*/

 //parse user's arguments
 int i, argc_all = argc;                  //將argc_all 直接放入argc的值   就可以得到輸入的個數
 for (i=1, argc=1; i<argc_all; i++){               //將argc從1開始  最大到argc_all
  if (argv[i][0] != '-')                 //若輸入的內容無包含'-'  即不是option
   argc++;                    //就讓argc累加1  這樣就可以知道option以外的東西共有幾個
  else
   break;                    //若進入option區域則直接跳出迴圈
 }
 if (argc <= 1){                    //如果最後得到的argc沒有超過1的話   代表輸入有誤
  showhelp();                    //呼叫showhelp()函式
  return 0;                    //結束程式
 }
 //for argv[1]
 if ((_stricmp(argv[1], "0") == 0) || (_stricmp(argv[1], "CAPTURE") == 0)){     //判斷當第一個輸入為"0"或"CAPTURE"
  mode = MODE_CAPTURES;                 //將mode改為MODE_CAPTURES 即為(1)
  if (argc > 2) play.filename = argv[2];             //將第二個輸入放入play結構的filename裡   (儲存檔案名稱)
  if (argc > 3) play.frameno = atoi(argv[3]);            //將第三個輸入放入play結構的frameno裡   (起始影格編號)
 }

 else if ((_stricmp(argv[1], "1") == 0) || (_stricmp(argv[1], "REPLAY") == 0)){    //判斷當第二個輸入為"1"或"REPLAY"
  mode = MODE_REPLAYS;                 //將mode改為MODE_REPLAYS 即為(2)
  if (argc > 2) play.filename = argv[2];             //將第二個輸入放入play結構的filename裡   (儲存檔案名稱)
 }

 else if ((_stricmp(argv[1], "2") == 0) || (_stricmp(argv[1], "DEMO") == 0)){    //判斷當第二個輸入為"2"或"DEMO"
  mode = MODE_DEMO;                  //將mode改為MODE_DEMO 即為(100)
  if (argc > 2)
   play.filename = argv[2];               //將第二個輸入放入play結構的filename裡   (儲存檔案名稱)
  else{                     //如果沒有兩個以上的輸入代表沒有給予主要的檔案
   showmsg("Error : Missing script directory !\n");
   return 0;                   //結束程式
  }   
  replaytime = 15000L;                 //DEMO MODE預設的replaytime
 } 
 else{
  showhelp();                     //如果有其他的  表示模式輸入錯誤    呼叫showhelp()
  return 0;                    //結束程式
 }

 for (i=argc; i<argc_all; i++){                //繼續將argc的值給i  做到argc_all為止   也就包括了所有的options
  if ((_stricmp(argv[i], "-OW") == 0) || (_stricmp(argv[i], "--overwrite") == 0)){  //判斷option    "-OW"  "--overwrite"
   overwrite = true;                 //成立就將靜態變數overwrite改為true   以便事後判斷
   continue;                   //繼續下一輪迴圈
  }
  else if ((_stricmp(argv[i], "-RT") == 0) || (_stricmp(argv[i], "--realtime") == 0)){ //判斷option    "-RT"  "--realtime"
   realtime = true;                 //成立就將靜態變數realtime改為true   以便事後判斷
   continue;                   //繼續下一輪迴圈
  }
  if ((_stricmp(argv[i], "-NM") == 0) || (_stricmp(argv[i], "--nomessage") == 0)){  //判斷option    "-NM"  "--nomessage"
   showmessage = false;                //成立就將靜態變數showmessage改為false   以便事後判斷
   continue;                   //繼續下一輪迴圈
  }

        if (i == argc_all-1) continue; // ignore last parameters        //忽略最後一個參數   抓到的可能為值  而非選項
  if ((argv[i][0] != '-') || (argv[i+1][0] == '-')) continue; // ignore -XX -YY   //當目前的數為值   而非選項時跳過

  if ((_stricmp(argv[i], "-CH") == 0) || (_stricmp(argv[i], "--channels") == 0)){   //判斷option    "-CH"  "--channels"
   play.usedchannels = htol(argv[i+1]);            //成立就將值 用htol改為long型態 存入play結構中的usedchannels
   }
  else if ((_stricmp(argv[i], "-t") == 0) || (_stricmp(argv[i], "--time") == 0)){   //判斷option    "-t"  "--time"
   replaytime = atol(argv[i+1]);              //成立就將值 用atol改為long型態 存入靜態變數replaytime
  }
  else if ((_stricmp(argv[i], "-SV") == 0) || (_stricmp(argv[i], "--servo") == 0)){  //判斷option    "-SV"  "--servo"
   if(tool_GetMode(argv[i+1], &play.mode_idx) != SUCCESS){        //判斷-SV的輸入是否符合規格
    showmsg("Error : unknown servo : %s\n", argv[i+1]);
    return 0;                  //結束程式
   }
  }
  else if ((_stricmp(argv[i], "-RB") == 0) || (_stricmp(argv[i], "--RoBoard") == 0)){  //判斷option    "-RB"  "--RoBoard"
   if(tool_GetItemRBVer(argv[i+1], &play.rbidx) != SUCCESS){       //判斷-RB的輸入是否符合規格
    showmsg("Error : unknown roboard : %s\n", argv[i+1]);
    return 0;                  //結束程式
   }
  }
 }//end for (i=argc...

 //setup log file
 if (err_SetLogFile("roboard.log") == false)
  showmsg("WARNING: can't open roboard.log for logging error messages!\n");

 keyboard_init(KEYBOARD_STD);                //呼叫函式keyboard_init()   在userio.cpp  引數KEYBOARD_STD為1 在userio.h
 switch (mode)                    //判斷模式
 {
  case MODE_CAPTURES:                  //模式為MODE_CAPTURES = 1
   mode_captureframes(&play);               //呼叫函式mode_captureframes
   break;
  case MODE_REPLAYS:                  //模式為MODE_REPLAYS = 2
   mode_replayframes(&play);               //呼叫函式mode_replayframes
   break;
  case MODE_DEMO:                   //模式為MODE_DEMO = 100
   mode_playdemo(play.filename, replaytime);           //呼叫函式mode_playdeme
   break;
 }

 err_CloseLogFile();
 keyboard_close();
 return 0;
}

/*================  general function =====================*/
static void showhelp(void)                  //此函式用於要讓使用者知道該如何正確輸入用
{
 keyboard_init(KEYBOARD_STD);
 showmsg("Parameters\n");
 showmsg("===========\n");
 showmsg("-CH xxxxxxxx or --channels xxxxxxxx:\n");
    showmsg("    Specify the PWM channels to use\n");
 showmsg("    \n");
 showmsg("-t xxxx or --time xxxx:\n");
    showmsg("    Specify the time for realtime frame capture,\n");
    showmsg("    frame replay, and demo play\n");
 showmsg("    \n");
 showmsg("-SV [DEFAULT, DEFAULT_NOFB, ...] or --servo [DEFAULT, DEFAULT_NOFB, ...]:\n");
    showmsg("    Specify servo motors\n");
 showmsg("    \n");

 showmsg("[press any key to continue...]\n"); keyboard_waitkey();
 showmsg("\n");
 showmsg("Parameters (cont.)\n");
 showmsg("===========\n");
 showmsg("-RB [RB_100, RB_110, ....] or --servo [RB_100, RB_110, ....]:\n");
    showmsg("    Specify servo motors\n");
 showmsg("    \n");
 showmsg("-OW or --overwrite:\n");
    showmsg("    Always overwrite the file of storing captured frames\n");
 showmsg("    \n");
 showmsg("-NM or --nomessage:\n");
    showmsg("    Not display messages when capturing or replaying frames,\n");
    showmsg("    being useful to speed up in Realtime modes\n");

 showmsg("\n");
 showmsg("[press any key to continue...]\n"); keyboard_waitkey();
 showmsg("\n");

 showmsg("Usage examples\n");
 showmsg("===============\n");
 showmsg("RoBoRC.exe CAPTURES(or 0) 0000.frm -CH 0000ffff:\n");
    showmsg("    Capture multiple frames of servo positions on channels 0~15.\n");
    showmsg("    The frames are saved to 0000.frm.\n");
 showmsg("    \n");
 showmsg("RoBoRC.exe REPLAYS(or 1) 0000.frm -CH 0000ffff -t 500:\n");
    showmsg("    Replay the multiple frames of servo positions in 0000.frm.\n");
    showmsg("    Each frame is replayed on channels 0~15 by 500ms.\n");
 showmsg("    \n");
 showmsg("RoBoRC.exe DEMO(or 2) demo_directory -t 10000:\n");
    showmsg("    Load the Demo in demo_directory and\n");
    showmsg("    set the action idle time to 10000ms (which means that\n");
    showmsg("    the servo power will be turned off if the robot\n");
    showmsg("    idles over 10s after finishing an action).\n");
 showmsg("\n");
 showmsg("[press any key to continue...]\n");keyboard_waitkey();
 showmsg("\n");
 keyboard_close();
}

static bool init_rcservo(CH_t *ch, unsigned long channels, int rbidx)       //此函式用於將rcservo init
{
 int i;
 showmsg("Set roboard version (for %s)...\n", RBVer[rbidx].name);
 roboio_SetRBVer(RBVer[rbidx].value);              //設定RB的版本
 for(i = 0; i < 32; i++){                 //用迴圈跑所有的pin腳  共32根
  if(ch[i].pin != -1){                 //判斷ch[i]結構不為-1的pin
   rcservo_SetServo(ch[i].pin, Servo[ch[i].mode_idx].value);       //SetServo
   showmsg("Initializing RCSERVO lib channel %d (PIN S%d) : %s)...\n", i, ch[i].pin + 1, Servo[ch[i].mode_idx].name);
  }
 }

 if (rcservo_Initialize(channels) == true)             //如果Init成功
 {
  showmsg("...success\n");
  rcservo_EnableMPOS();
  rcservo_SetFPS(100);
  return true;
 }

 showmsg("...fail!\n");                  //如果Init不成功
 errmsg("ERROR: RCSERVO lib fails to initialize (%s)!\n", roboio_GetErrMsg());
 return false;
}

static void close_rcservo(void)                 //rcservo_Close  多加兩行輸出
{
 showmsg("Closing RCSERVO lib...");
 rcservo_Close();
 showmsg("...done\n");
}

static const char* cmdtostr(unsigned cmd) {              //用於將輸入的指令轉為字串
 static char cmdstr[2] = {'\0', '\0'};
 int i;

 for (i=0; i<(int)NUM_SKEY; i++)                //指令共有NUM_SKEY = (17)個  在const.h
  if (cmd == SKey[i].value) return SKey[i].name;           //對照  是否有那個key  若有就回傳那個key的名稱

 cmdstr[0] = (char)cmd;                  //若無  則直接回傳那個key轉為char    通常為英數字
 return cmdstr;
}

static unsigned long htol(char* str)               //用於轉換16進位為long型態
{
    unsigned long value = 0L;                

 for (int i=0; i<(int)strlen(str); i++)              //迴圈  從0~字串長度 然後依序計算數值
  value = (str[i]>'9')? value*16+(unsigned long)(str[i]-'a'+10): value*16+(unsigned long)(str[i]-'0');

 return value;
}





雖然看程式碼很乏味
不過多看多理解
可以助於增加自己的程式功力喔!
Share:

2012/2/23

[RoBoRC第二回] Script Mode

 接下來要介紹RoBoRC的主軸

也就是Script Mode

這是一個你可以將編輯好的動作一一放置到喜喜歡的熱鍵上的模式




首先要先創建你需要的資料

一共有:


[def] file : 設定一些基本設定以及其他檔案的名稱
[act] file : 設定動作
[frm] file : 設定每個frame的馬達位置
[cmd] file : 設定按鍵相對應的動作
[offset] file : 偏移值


下面一一說明:

def file:

[Info]
demoName = (自定義的名稱)
date = (日期)
...........(自行設定   Info group只是顯示出來)
[Files]
offset = (檔案名稱).txt
frame = (檔案名稱).frm
act = (檔案名稱).act
cmd = (檔案名稱).cmd
[Settings]
RoBoard = (RB型號)
InitAction = (act編號)     ((預設的動作
HomeFrame = (frm編號) , (bool 是否預設homeframe 無feedback會直接設HomeFrame)

[channels]
channel (編號) = (PIN腳位) , (馬達型號)
..

frm file:
基本上跟Capture mode抓到的是一樣的

[Frame (編號)]
channel(編號) = (馬達位置)
..


act file:

[Action (編號)]
..


內容自訂   有兩種可用指令
MoveToFrame = (frm編號) , (花多長時間ms)
pause = (等待時間)



cmd file:


[Command]
name = (動作名稱)
command = (按鍵)

action = (動作編號)
...



offset file:

channel(編號) = (偏移值)
...






然後在cmd時要鍵入命令開啟RoBoRC

RoBoRC [mode] [directory] [option]

mode: 即為你的RoBoRC要為何種模式 , 在這設定 2 或 DEMO
directory: 要開啟的資料夾檔名
option1:   -t [TIME]        可以設定等待時間(可以讓馬達不會一直吃電)
Share:

2012/2/16

[RoBoRC第一回] Frame Capture/Replay Mode

最近本魔再研究這東西~~RoBoRC

本魔認為RoBoRC很好用!

使用它就可以很方便的操控自己專屬的機器人

接下來的連續幾周  

本魔會帶大家看RoBoRC的使用

也會說明RoBoRC內的程式碼

藉此讓大家對RoBoRC有更深入的研究

至於RoBoRC可以去RoBoard官網下載喔!








  • Frame Capture Mode(動作捕捉模式)

執行cmd:      
RoBoRC [mode] [filename] [number] [options …]

Frame Capture Mode用來將有FeedBack功能的馬達所在的值讀回
接下來我們來慢慢解釋各個參數的意義

mode: 即為你的RoBoRC要為何種模式 , 在這設定 0 或 CAPTURE
filename: 輸出檔案的名稱 , ex: out.frm
number: 捕捉後開始的第一個frame要在編號幾
option: 選項有不少個 , 下面細說
option1:    -rb [..]     設定RB型號
option2:    -sv [..]    設定馬達型號
option3:    -ch [..]    設定要使用的channel   以bit來看1為使用     ex: 0x000000ff  為使用前8個channel
option4:    -rt      設定是否要使用Realtime   預設time為1000ms
option5:    -t [..]      設定time的時間    單位為ms
option6:    -nm   設定執行時不顯示訊息
option7:    -ow   設定是否覆蓋原有檔案

Example:
RoBoRC CAPTURE out.frm 10 –rb RB_100 –sv KONDO_KRS78X –ch 00000007 -ow





  • Frame Replay Mode(動作重播模式)

執行cmd:      
RoBoRC [mode] [filename] [options …]

Frame Replay Mode用來將已有的frm檔  重播出來
接下來我們來慢慢解釋各個參數的意義

mode: 即為你的RoBoRC要為何種模式 , 在這設定 1 或 REPLAY
filename: 輸入檔案的名稱 , ex: in.frm
option: 選項有不少個 , 下面細說
option1:    -rb [..]     設定RB型號
option2:    -sv [..]    設定馬達型號     這邊沒有特別說要FeedBack的馬達
option3:    -ch [..]    設定要使用的channel   以bit來看1為使用     ex: 0x000000ff  為使用前8個channel
option4:    -rt      設定是否要使用Realtime   預設time為1000ms
option5:    -t [..]      設定time的時間    單位為ms
option6:    -nm   設定執行時不顯示訊息

Example:
RoBoRC REPLAY in.frm–rb RB_100 –sv KONDO_KRS78X –ch 00000007



其他更深入的可以自己研究看看RoBoRC User Manual




範例影片:
附註: Replay Mode我設定速度為Capture的1/2倍      (會晃似乎是接合處會不穩)
Share:

2012/2/7

[NXT系列第四彈] RoBoard控制Sound Sensor

NXT-Sound Sensor



終於到了最後一個Sensor

聲音感測器一樣是用AD來讀取回傳值的

連結方式如下

Sound Sensor要用到的線有6條

白,黑,紅,綠,黃,

白 ─┬─●──VCC(5V)
        └────AD
黑 ──────GND
紅 ──────AD-GND
綠 ──────VCC(5V)
黃 ──────GPIO
藍 ──────GPIO

附註: ●代表10歐姆電阻


相信大家會疑惑

這一次怎麼會有兩個GPIO呢??

Sound Sensor又沒有LED之類的東西是吧?



當然這兩個GPIO有特殊的用途

它們是用來設定測量單位的喔!

黃線是用來設定 dB / dBA
藍線則是設定模式的 0/1






範例影片:













範例code:
#include "stdio.h"
#include "conio.h"
#include "roboard.h"
int main(){
 roboio_SetRBVer(RB_110);
 int val = 0;
 spi_Init(SPICLK_21400KHZ);
 rcservo_Init(RCSERVO_USENOPIN);
 while(!kbhit()){
  rcservo_OutPin(RCSERVO_PINS1,0);
  rcservo_OutPin(RCSERVO_PINS2,1);
  val = adc_ReadCH(0);
  if(val == ADC_READFAIL){
   printf("error!");
   break;
  }
  printf("%4d\r",val);
 }
  
 rcservo_Close();
 spi_Close();
 return 0;
}
Share:

2012/2/2

[翻譯] MicroSD建議使用說明(繁中化)

如同大家知道的,RoBoard的儲存裝置是採用MicroSD卡。但是很多人忽視了MicroSD卡的速度對RoBoard性能劇烈的影響;因此,他們可能會因為使用緩慢的MicroSD卡,然而在安裝或操作WinXP/Linux時十分緩慢。


以下在RoBoard上對MicroSD卡的使用建議。


[一般]


1. 使用class 6或者class 10的MicroSD卡,而非class 2的MicroSD


2. 選擇值得信賴的MicroSD卡供應商;例如,我們已經測試過許多MicroSD卡的供應商,並發現SanDisk class 4的MicroSD卡 甚至比一些陌生供應商的class 10的MicroSD卡還要快。


3.使用磁碟基準測試軟體(在您的電腦或RoBoard上)檢查您MicroSD卡的真實速度;例如, FDBench可能是一個選擇http://www.hdbench.net/ja/fdbench/index.html 


4. 在RoBoard的BIOS "Advanced->IDE Configuration" 菜單,選擇 "OnBoard IDE Operate Mode -> Native Mode" 與 "ATA(PI) 80Pin Cable Detection -> Device"。(註:兩者都只是微調,可能沒有明顯的效果。) 


[WinXP]


1. 使用FAT32而非NTFS以減少磁碟寫入。


2. 設置一個固定大小的記憶體空間,(與/或)編輯表以禁用分頁:運行 regedit.exe -> 
HKEY_LOCAL_MACHINE -> SYSTEM -> CurrentControlSet -> Control -> Session Manager -> Memory Management -> DisablePagingExecutive,設置為1。




3.微調表。 運行regedit.exe ->
HKEY_LOCAL_MACHINE -> SYSTEM -> CurrentControlSet -> Control -> Session Manager -> Memory Management -> LargeSystemCache,設置為1。 

HKEY_LOCAL_MACHINE -> SYSTEM -> CurrentControlSet -> Control -> Session Manager -> Memory Management -> SecondLevelDataCache,設置為 256 (10進位). 


4. 如果您還是認為磁碟速度仍然不理想 (去 http://www.roboard.com/download_ml.htm 看應用說明)。 註: 在使用EWF之前,您應該禁用虛擬記憶體。 


[Linux]


1. 不要使用Ubuntu桌面,因為它消耗了大量的掛載記憶體(256MB);一台機器上只有256MB的記憶體,如RoBoard,它會頻繁的使用SWAP,使RoBoard速度變得十分緩慢。在一般情況下,您不應該用需要大於256MB記憶體的Linux於RoBoard上。


2. 在RoBoard使用記憶體較少的Linux版本;例如,Ubuntu或Lubuntu可能是好的選擇。


3. 選擇一個適合SSD的文件系統(例如,有些人說ext2可以盡量減少磁碟寫入至SD卡)。
如果您使用ext3或ext4,你可以掛載文件系統 "noatime" 和 "nodiratime" 選項以減少不必要的磁碟寫入。


4. 設定對I/O的合適調度;我們建議您設置"elevator=noop"。






















原文:


As everyone knows, RoBoard employs MicroSD as its storage. But many people overlook the dramatic influence of MicroSD's speed on RoBoard's performance; consequently, they may plug a slow MicroSD into RoBoard and then suffer from slow installation and operation of WinXP/Linux. 

The following are some suggestions for using MicroSD on RoBoard. 

[General] 

1. Use class 6 or 10 MicroSD cards rather than class 2 cards. 

2. Select MicroSD from trustworthy vendors; for example, we have tested many MicroSD from various vendors, and found that even class 4 MicroSD from SanDisk is faster than some class 10 MicroSD from unfamiliar vendors. 

3. Use a disk benchmark software (on your PC or on RoBoard) to check the real speed of your MicroSD; for example, FDBench may be a candidate: http://www.hdbench.net/ja/fdbench/index.html 

4. In RoBoard BIOS "Advanced->IDE Configuration" menu, select "OnBoard IDE Operate Mode -> Native Mode" and "ATA(PI) 80Pin Cable Detection -> Device". (Note: the two settings are just fine tuning and may has no obvious effect.) 

[WinXP] 

1. Use FAT32 rather than NTFS to reduce disk writes. 

2. Set the virtual memory to a fixed size. And/or edit the registry to disable paging: run regedit.exe -> 

HKEY_LOCAL_MACHINE -> SYSTEM -> CurrentControlSet -> Control -> Session Manager -> Memory Management -> DisablePagingExecutive, set as 1. 

3. Fine tune the registry: run regedit.exe -> 

HKEY_LOCAL_MACHINE -> SYSTEM -> CurrentControlSet -> Control -> Session Manager -> Memory Management -> LargeSystemCache, set as 1; 

HKEY_LOCAL_MACHINE -> SYSTEM -> CurrentControlSet -> Control -> Session Manager -> Memory Management -> SecondLevelDataCache, set as 256 (Decimal). 

4. If you feel that the disk speed is still unsatisfactory, you may try EWF (go to http://www.roboard.com/download_ml.htm to see the application note). Note: you should disable the virtual memory before enabling EWF. 

[Linux] 

1. Don't use Ubuntu Desktop because it consumes a large mount memory (> 256MB); on a machine with only 256MB RAM such as RoBoard, it will access SWAP frequently, which can slowdown the speed very much. In general, you shouldn't use on RoBoard any Linux distribution that requires > 256MB RAM. 

2. On RoBoard, use a Linux distribution that require less memory; for example, Ubuntu Server or Lubuntu may be your selection. 

3. Select a filesystem that is suitable for SSD (for example, some people say that ext2 can minimize disk writes to a SD card). 

If you use ext3 or ext4, you may mount the file systems with "noatime" & "nodiratime" options to reduce unnecessary disk writes. 

4. Set the I/O scheduler suitable for SSD; we suggest that you set "elevator=noop". 
Share:
技術提供:Blogger.

追蹤者