专注电子技术学习与研究
联系我们
当前位置: 单片机教程网 >> 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通信代码设计与编写。
【 关闭窗口】
上一篇: 扩展NDS掌机连接Arduino (4)-软件开发环境搭建

相关文章

阅读排行
  • mpu-6050 加速度、陀螺仪传感器的调试
  • Arduino在串口读取多个字符串,并且转换
  • Arduino + W5100 学习
  • Arduino + nRF24L01+无线测试实验
  • Arduino的Serial.readBytes()使用例子
  • Arduino连接LCD_1602屏
  • Arduino电机驱动模块(293D)使用
  • Arduino 控制多路步进电机
  • 如何使用Arduino 制作 串口(UART)转WiF
  • Arduino驱动DS3231高精度时钟模块
推荐文章

聚圣源牛年男宝宝起名宜用字中原银行公司起名大全的软件魔兽footman小孩起名大全四个字美人谋妖后无双寓意较好的诗句起名字火星救援迅雷下载热火vs马刺直播2021年出生的牛宝宝起名贾 男孩 起名乔治受伤都是美女的群怎么起群名字无限流小说排行榜日化商标起什么名好听走进初中李韩怎么起名仓库管理软件破解版五行属金给公司起名甜品店起名字大全少女慈禧额宝宝宝起名那个家公司好青岛房屋维修基金属鼠的孩子起名字大全英文英文起名论语起女孩取名属马宝宝起名大全男孩名字理发店起名大全鲁迅先生的名言淀粉肠小王子日销售额涨超10倍罗斯否认插足凯特王妃婚姻让美丽中国“从细节出发”清明节放假3天调休1天男孩疑遭霸凌 家长讨说法被踢出群国产伟哥去年销售近13亿网友建议重庆地铁不准乘客携带菜筐雅江山火三名扑火人员牺牲系谣言代拍被何赛飞拿着魔杖追着打月嫂回应掌掴婴儿是在赶虫子山西高速一大巴发生事故 已致13死高中生被打伤下体休学 邯郸通报李梦为奥运任务婉拒WNBA邀请19岁小伙救下5人后溺亡 多方发声王树国3次鞠躬告别西交大师生单亲妈妈陷入热恋 14岁儿子报警315晚会后胖东来又人满为患了倪萍分享减重40斤方法王楚钦登顶三项第一今日春分两大学生合买彩票中奖一人不认账张家界的山上“长”满了韩国人?周杰伦一审败诉网易房客欠租失踪 房东直发愁男子持台球杆殴打2名女店员被抓男子被猫抓伤后确诊“猫抓病”“重生之我在北大当嫡校长”槽头肉企业被曝光前生意红火男孩8年未见母亲被告知被遗忘恒大被罚41.75亿到底怎么缴网友洛杉矶偶遇贾玲杨倩无缘巴黎奥运张立群任西安交通大学校长黑马情侣提车了西双版纳热带植物园回应蜉蝣大爆发妈妈回应孩子在校撞护栏坠楼考生莫言也上北大硕士复试名单了韩国首次吊销离岗医生执照奥巴马现身唐宁街 黑色着装引猜测沈阳一轿车冲入人行道致3死2伤阿根廷将发行1万与2万面值的纸币外国人感慨凌晨的中国很安全男子被流浪猫绊倒 投喂者赔24万手机成瘾是影响睡眠质量重要因素春分“立蛋”成功率更高?胖东来员工每周单休无小长假“开封王婆”爆火:促成四五十对专家建议不必谈骨泥色变浙江一高校内汽车冲撞行人 多人受伤许家印被限制高消费

聚圣源 XML地图 TXT地图 虚拟主机 SEO 网站制作 网站优化