久久久久久久999_99精品久久精品一区二区爱城_成人欧美一区二区三区在线播放_国产精品日本一区二区不卡视频_国产午夜视频_欧美精品在线观看免费

專注電子技術學習與研究
當前位置:單片機教程網 >> Arduino >> 瀏覽文章

擴展NDS掌機連接Arduino (5)-NDS端BASIC語言解釋器的移植與擴展

作者:c_gao   來源:c_gao   點擊數:  更新時間:2014年07月27日   【字體:

由于焊接工具沒還沒到位,于是最近幾篇文章主要以方案細化與軟件開發為主。今天這篇文章主要做一件比較有意義的事情:NDS端BASIC語言解釋器的移植與擴展。文章附上了移植后軟件的運行截圖和實機運行效果,并在文章最后附上工程代碼,方便下載。順便打個廣告:有興趣的朋友可以加入QQ群:362445156 (Arduino極客群)。

 
一、為什么要移植和擴展BASIC語言解釋器?
 
到目前為止,所有的NDS硬件擴展,包括DS brut在內都只提供SDK軟件開發庫。這使得你要進行二次開發,必須花大量時間去看SDK的文檔甚至代碼,工作量開銷較大。特別是以下兩個問題:(a). 對于想進行快速測試一個外設的開發人員來說,閱讀文檔,源碼,以及在此基礎上進行自行開發程序調試程序的時間會比較多。(b). 如果需要控制另一個外設或調整軟件功能時,必須重新編譯程序,并將程序復制到NDS的燒錄卡內。因此,工序多了很多步驟。
 
BASIC語言在誕生之初就以簡單易用為哲學指導。我將秉持該指導思想來做為本方案的Demo技術演示。如果能將BASIC語言的解釋器移植到NDS上,將有如下優點:
(1)利用NDS的觸屏可以作為字符輸入設備,效率很高。
(2)用BASIC語言編寫程序,非常容易,幾乎不用學習,而且程序一般簡短易懂。
(3)通過BASIC語言方便快捷的寫程序,可以立桿見影,馬上看到執行效果,無需在SDK上進行再開發,明顯提高開發效率。這解決了上述的問題(a).
(4)通過擴展BASIC解釋器,為其加入對DLDI(在各NDS燒錄卡實現統一的文件讀寫功能)的支持,可以直接在NDS上將編好的程序寫入燒錄卡的SD卡的文件內,也可以直接從SD卡內將程序文件讀入內存。這解決了上述的問題(b).
 
當然沒有一個方案是十全十美的,相比直接在SDK上開發,利用BASIC解釋器的缺點是,程序運行速度沒有前者快。因此不適合作一些對外設SPI回傳數據響應速度要求很高的場合。比如想把NDS做成一個邏輯分析儀。
 
二、移植BASIC語言解釋器
 
要移植BASIC解釋器,那么就得選擇一個目標進行移植。早在2007年,就已經有一位網名叫zzo38computer的外國友人做了這個工作,項目名稱為DSBasic。他用來移植的BASIC解釋器源碼用C語言編寫,因此比較容易移植,只需要添加了NDS的軟鍵盤等功能。另外,網上流傳甚廣的開源BASIC解釋器源碼版本也比較多,比較有名的就是Tiny Basic。這個Tiny Basic說來話長,最早可以說到1975年。這里我們主要講一下我采用的代碼,來自TinyBASIC 2也采用的核心代碼BAS-INT.C這個文件。
 
經過查看源碼發現,原來DSBasic也是基于這個版本的代碼進行擴展的。而TinyBASIC 2的功能更加強大,還支持畫圖命令(需SDL庫支持,不過SDL庫開源且跨平臺)。額外一提:自己用C/C++寫個BASIC解釋器不難,網上也有不少文章介紹,請google之。
 
下載了TinyBASIC 2源碼后,查看BAS-INT.C文件,該文件采用了較早的C語言語法。于是首先修改語法,然后用gcc在我的Mac OS X下順利編譯通過,試著運行了幾個附帶的BASIC程序例子,一切順利。
 
接下來便是將代碼移植到NDS上。由于devkitPro并沒有提供太多的基于命令行的NDS開發示例程序。因此需要我加一些自己的代碼來實現簡單的光標、scanf功能等。
 
整個移植過程就不詳述了,具體可以下載后面提供的源碼。這里主要講一下,移植的幾個要點:
(1)添加光標。我簡單的用"_",即下劃線代替光標,該光標很簡單,不會閃爍,但基本達到使用的目標,除了一個小BUG:輸入文字到行末時,會自動跳到本行行首,而不是下一行。但該Bug不影響輸入的代碼。
(2)添加int get_input_number()函數實現INPUT命令的移植。因為我使用觸屏軟鍵盤后,NDS不支持scanf()函數從屏幕獲得輸入。
(3)添加"RUN"和"!"兩條命令來運行程序。由于BAS-INT.C運行程序是在命令行將需要執行的BASIC程序作為命令行參數進行調用執行的,因此不支持程序編輯功能。而在NDS上我添加了一個非常簡單的程序輸入功能(包括上面提到的光標)。
 
圖1為移植成功后的運行效果。下文將該移植到NDS的BASIC解釋器項目簡稱:NDSBasic。


圖1. NDSBasic 運行效果。
 
圖1為最初植移的運行界面,下方為觸屏,提供軟鍵盤進行輸入。上屏為字符終端,和DOS,以及Terminal類似。
 
三、擴展BASIC語言解釋器
 
該BASIC解釋器 (BAS-INT.C),提供的命令非常有限,因此需要自己擴展添加新的BASIC語言命令。由于BAS-INT.C源碼本身編寫比較清晰,添加新命令過程非常簡單。只需以下幾步完成一個新命令添加:
(1)定義新命令宏,如 #define SEND 16
(2)在 struct commands 結構體中添加命令的字符串,以及對應的第(1)步中的宏,如"send", SEND,
(3)添加命令的執行函數聲明,以及函數代碼,如void exec_send();
(4)在主函數 (main)的switch命令中添加新命令的調用,如:  
case SEND:
exec_send();
  break;

如果該命令除了一般的邏輯處理外需要用到NDS硬件等功能,則可在第(3)步代碼中調用外部函數完成相應的硬件功能。這樣的設計代碼可移植性較好,邏輯功能代碼和硬件相關代碼分離。

我主要擴展添加了以下幾條命令:
(1)"RUN" 或 "!":如上文所述。
(2)"LIST":打印內存里的BASIC代碼到屏幕上。
(3)"NEW":清除內存里的BASIC代碼,開始編寫新的程序代碼。
(4)"?":和PRINT命令一樣,用一個簡短的符號,減少輸入時間。
(5)"SAVE filename":將當前編輯的內存里的代碼保存到filename文件中。
(6)"LOAD filename":將filename文件里的代碼讀入內存。可以直接輸入命令"RUN"或"!"運行。
(7)"PSET x,y,clr":畫像素。在 (x,y)處像素用clr號顏色點亮。
(8)"LINE x1,y1,x2,y2,clr [,B[F]]":從(x1,y1)到(x2,y2)用clr色號畫線、畫空心矩形、畫實心矩形命令。和QBASIC里的同名命令類似,區別是我為了方便命令的輸入,將QBASIC語法中的y1和x2之間的"-"改為了逗號","。另外,clr代表NDSBasic中的顏色號(預定義),"B"代表畫空心矩形,"BF"代表畫實心矩形。
(9)"CIRCLE x,y,r,clr":畫圓命令。x,y代表圓心,clr為顏色號。
(10)"DELAY ms":程序延遲ms毫秒。
(11)"SEND":發送數據到Arduino (Slot 1接口的SPI通道)上。我將該命令的語法設計成和PRINT一致,這樣使用起來比較靈活。
(12)"RECV n":從Arduino (Slot 1接口的SPI通道)上讀取n個字節的數據,并打印讀取到的數據。默認當讀取過程中遇到'\0'字符時也會停止讀取。
(13)"CLS":清屏命令。執行時將清除屏幕內所有的打印信息。
 
所有命令不分大小寫,解釋器能自動識別。實現過程中,
  • LINE命令用到了我的開源3D引擎Nomad3D中的畫線代碼,支持Cohen裁剪功能,且執行性能高效。
  • CIRCLE命令則用到了我的另一篇博文:基于NDS/GBA/ARM,從啟動到運行你自己的第一行C程序代碼(NDS篇)中的畫圓算法,效率也很高。
  • DELAY命令使用NDS的第0號硬件計時器 (Timer 0)實現,精度達到微秒級。而且每次用完就釋放計時器,不占用硬件資源。
  • SEND和RECV命令的實現用到了第三方庫:libspi-0.2 源碼,由于源碼對應的devkitPro版本太早,源碼中用的很多宏已經不存在或與當前版本(我用的是最新的版本:devkitARM r42,libnds-1.5.8)沖突。因此我重新修改了源碼并編譯成libspi.a庫文件方便以后使用。
其中SEND命令實現的源碼如下:
void exec_send()
{
//syntax: similar with PRINT
  int answer;
  int len=0, spaces;
  char last_delim;
char send_str[256];
char recv_buff[256];
char temp[50];
memset(send_str,0,256);
memset(recv_buff,0,256);
memset(temp,0,50);

  do {
    get_token();
    if(tok==EOL || tok==FINISHED) break;
    if(token_type==QUOTE) {
      sprintf(temp,token);
      strcat(send_str,temp);
      len += strlen(token);
      get_token();
    }
    else {
      putback();
      get_exp(&answer);
      get_token();
      len += sprintf(temp,"%d", answer);
      strcat(send_str,temp);
    }
    last_delim = *token; 

    if(*token==';') {
     
      spaces = 8 - (len % 8); 
      len += spaces;
      while(spaces) { 
sprintf(temp," ");
strcat(send_str,temp);
        spaces--;
      }
    }
    else if(*token==',') ;
    else if(tok!=EOL && tok!=FINISHED) serror(15); 
  } while (*token==';' || *token==',');

  if(tok==EOL || tok==FINISHED) 
  {
    if(last_delim != ';' && last_delim!=',') 
    {
    sprintf(temp,"\n");
    strcat(send_str,temp);
    }
    
     do_send(send_str,recv_buff,256);
     //printf(recv_buff);
  }
  else serror(15);
}

void do_send(char* send_str, char* recv_buff, int max_len)
{
int i=0;
while(send_str[i] && i<= max_len)
{
recv_buff[i] = send_str[i];
i++;
}
recv_buff[i]='\0';
////////
int len=strlen(send_str);
char* p=send_str;
setupConsecutive_cardSPI(len);
while(*p)
writeBlocking_cardSPI(*(p++));
}
 
RECV命令實現源碼:
void exec_recv()
{
int num_recv_byte=0;
int num_byte=0;
get_exp(&num_byte);
memset(recv_buff,0,MAX_RECV_SIZE);
num_recv_byte = do_recv(recv_buff, num_byte, STOP_CHAR);
//the variable Z used to store the number of received byte.
//variables['Z'-'A'] = num_recv_byte;
printf("[received %d byte: %s]\n", num_recv_byte, recv_buff);
}

int do_recv(char* buff, int num_byte, char stop_byte)
{
u8 read_byte=0;
int i=0;
for(i=0; i
{
  writeBlocking_cardSPI(0x00);
  while(readBlocking_cardSPI(&read_byte) != CARD_SPI_STATUS_OK);
 
  if((char)read_byte == stop_byte)
  return i;
 
  buff[i] = (char)read_byte;
  }
}
 
最后,因為添加了畫圖功能,我將終端窗口從上屏移到下屏,和軟鍵盤放在一個屏內。終端窗口提供14行代碼顯示,上屏全部用來畫圖。最后運行效果如圖2, 圖3, 圖4所示。
 

圖2. LINE命令執行效果。
 

圖3. LINE、CIRCLE和DELAY命令在循環中執行的效果。


圖4. 實機運行效果。

基于Slot 1接口SPI通信的SEND, RECV命令沒能實測,但模擬器上執行來看應該是工作正確的。
 
代碼下載:NDSBasic + libspi
 
后續將先測試SEND和RECV命令,然后添加以下命令:
  • DWRITE pin,value:設置Arduino的第pin數字引腳為值value (1: HIGH高電平,0:LOW低電平)。
  • AWRITE pin,value:設置Arduino的第pin (PWM引腳)為值value (0~255之間的值,用于PWM信號)。
  • DREAD( pin ):讀取第pin數字引腳的電平狀態 (1:高電平,0:低電平)。
  • AREAD( analogPin ):讀取第analogPin模擬引腳的值 (0~1023之間)。
后記:
當時在考慮加畫圖命令時有兩種方案,除了當前使用的方案外,另一種可選方案是:
(1)仍然使用上屏作為主終端屏幕,當執行到畫圖命令時上屏自動切換到畫圖狀態(由于NDS硬件原因,同一屏幕畫圖狀態和顯終端狀態不可同時存在)。下屏軟鍵盤上方14行只用于顯示與Arduino的SPI通信的數據(發送和接收數據),不作他用。
 
本篇結束,后面將會涉及Arduino端具體的SPI通信代碼設計與編寫。
關閉窗口

相關文章

主站蜘蛛池模板: 亚洲精品4| 免费视频成人国产精品网站 | 国产欧美一级二级三级在线视频 | 91精品国产91久久综合桃花 | 一本久久a久久精品亚洲 | 午夜成人免费视频 | 美女131mm久久爽爽免费 | 日本a∨视频 | 青久草视频| 91精品久久 | av大片在线观看 | 91久色 | 日日夜夜精品免费视频 | 久久久久久久一区 | 国产欧美一区二区三区在线看 | 最新中文字幕在线 | 久久av一区| 亚洲免费在线 | 亚洲av毛片成人精品 | 在线视频一区二区三区 | 日本人爽p大片免费看 | 成人免费激情视频 | 精品不卡 | 中文字幕国产一区 | 亚洲婷婷六月天 | 国产乱码精品一区二区三区忘忧草 | 精品产国自在拍 | 91精品国产91久久久久久最新 | 欧美日韩国产在线 | 欧美日韩国产在线观看 | 四虎永久在线精品免费一区二 | 一区二区三区中文 | 久久99精品视频 | 国精产品一区二区三区 | 国产亚洲精品久久19p | 91av亚洲| 91国内视频在线 | 国产亚洲精品美女久久久久久久久久 | 日韩免费视频一区二区 | 日本三级做a全过程在线观看 | 自拍第一页 |