原创 基于Nios II和eCos的串口通信程序开发

2008-3-6 15:13 2671 7 7 分类: FPGA/CPLD

<?XML:NAMESPACE PREFIX = O />

 


嵌入式开发人员必须选择一款处理器,以此决定合适的系统性能。Nios II处理器使用指令和数据存储器分离的存储器结构,具有灵活的结构可修改性,支持自定制指令。Nios II处理器支持片上调试,通过JTAG调试通道,可以实现指令单步、断点、连续运行等调试功能。使用系统开发工具将处理器、外设、存储器和I/O接口集成在单片FPGA中,定制自己设计的系统,并且对各种外围设备的实现提供了强大的支持平台;SOPC Builder系统开发工具可以自动生成组件以及连接组件的总线,quartus ii 软件开发工具可以完成功能模块设计、综合布线和仿真,Nios II ide软件开发工具提供嵌入式应用软件的开发环境和调试环境。所有软件开发任务编辑、构建、程序调试都能够在(IDE)下完成,从而简化了开发过程,降低了系统成本、复杂性以及功耗,缩短了产品上市周期。


组件的可定制性是嵌入式开发的一个特点,SOPCSystem on Programmable Chip,片上可编程系统)是Altera公司提出的一种灵活、高效的SOC解决方案。它将处理器、存储器、I/O接口、DMA、定时器等系统设计需要的功能模块集成到一个PLD器件上,构建一个可编程的片上系统。


嵌入式应用软件都是运行在特定的硬件平台上的。我所使用的FPGAEP<?XML:NAMESPACE PREFIX = ST1 />1C6Q240C8,如果只是使用FPGA来实现一个串口通信,那么在Nios II IDE中使用Nios II device drivers来写一个串口通讯程序是容易实现的,但是我们的程序要保存一些数据到FLASH中,Nios II device drivers虽然提供了HALc库,但是没有提供可读写的文件系统,所以我们就选了提供文件系统支持的eCos嵌入式操作系统来实现所需要的功能。那么eCos怎么移植到nios II处理器上,又怎样在这样一个平台上开发串口通信程序呢!我把设计的过程分为三部分:硬件平台设计,eCos库的编译,串口通信程序开发和调试。


 


硬件平台设计


      根据fpga型号和flash类型先要制作目标板。Quartus ii有一个命令mk_target_board是用来制作目标板的,参看该命令的使用帮助请参考Quartus ii安装路径document目录下的flash-program-guide.pdf。生成目标板工程后,启动quartus ii 5.0sopc builder软件设计该目标板工程,sopc builder软件完成UARTFLASHRAM组件的添加和编译。quartus ii 5.0软件进行综合布线,编译生成flash program file(一个sof文件)  


然后使用quartus ii 5.0sopc Builder软件设计我们的应用工程,在sopc builder软件中选择我们自己设计的目标板。添加必需的组件和模块,串口通信需要的UART组件,保存数据的FLASH组件等。分配管脚、编译,生成FPGA硬件配置文件(也是sof文件)。Sopc Builder生成的ptf文件也是我们在后面配置ecos库的时候要用到的文件。


 


eCos库编译


eCos可以到Redhat的网站或者Nios Community论坛找到,Nios论坛的eCos是移植了的版本,而redhat网站上的还要自己移植到Nios II上。移植了的版本支持这些AlteraAvalon设备组件:TimerUARTJTAG UARTLan91C111Ethernet ChipLCD 16207 PanelCompact Flash等,后面提到的eCos都是指移植了的版本。安装移植了的版本eCos需要一个条件,就是quartus II软件要满足版本需求,现在Nios论坛上的eCos已经开发到5.1版了。


完成eCos的安装后,就可以配置eCos了,打开“开始—>程序—>altera—>niosii development kit—> niosii sdk shell”,在niosii sdk shell中启动配置工具nios2configtool,命令如下:


nios2configtool --ptf=/ecos-c/info_aquire/niosii_c.ptf  --cpu=cpu


参数niosii_c.ptf是我们在设计应用工程时生成的文件,cpu是使用Sopc Builder定制接口时添加Nios II处理器的名字。接着出现如下图形配置界面:


点击看大图


 根据具体的UART芯片型号,从package目录选择相应芯片的驱动加入,如果需要的UART芯片驱动不存在,则可以参考已有的UART芯片驱动进行修改。


打开菜单“build->package”添加“serial device drivers”包和“FLASH device drivers”包,把我们需要的包添加完后,就可以编译eCos库了,打开菜单“build->library”编译生成eCos库,它包含include文件目录、lib文件目录和program_flash文件。然后可以使用Nios II IDE集成开发工具开发基于eCos嵌入式操作系统的串口通信程序。



 


串口通信程序开发和调试


  1)配置开发环境


打开Nios II IDE软件,新建一个“advanced c/c++ project”工程,选择我们串口通信程序所在的文件夹的目录路径。然后在“build command”中输入我们的定制编译命令:


make  INSTALL_DIR=/ecos-c/info_aquire/vehicle_install


这个目录/ecos-c/info_aquire/vehicle_install是我们的编译生成的eCos库所在的目录,不要和串口通信程序所在的目录混淆。


  2) 串口通信程序设计


程序工作原理:从PC机发送一条十六进制格式的数据包到串口通讯程序,串口通讯程序的读数据线程首先对接收到的数据进行判断,如果接收到包头标志,则重新开始填充缓冲区,如果接收到包尾标志,则将缓冲区数据传递给处理数据线程对数据包进行处理。处理数据线程按照包的类型标志进行处理,如果在处理的过程中出现错误,则把错误的信息返回给PC,成功执行了则返回成功的对应标志让PC确认操作执行成功。



  通讯包协议:



名称


类型


长度


描述


包头


Hex


1


固定为0xff


包类型


Hex


1


 


数据部长度


Hex


1


数据部的十六进制字节数


数据部


Hex


0<=n<=256


变长的数据


CRC校验


Hex


2


数据部的校验结果


包尾


Hex


2


固定为<CR><LF>(回车、换行),字符为‘\r’,‘\n’;十六进制为0x0d0x0a


 


3)程序主要代码分析


    定义包的类型,PKG_DATEPKG_STATUS是接收包类型,ANSWER_OKANSWER_ERR是返回标志。


声明线程函数和线程句柄,program_recv是数据接收线程函数,program_deal是处理数据线程函数。


  enum TPKG{PKG_DATE=0x50,PKG_STATUS=0x51 };


  enum TANSWER{ANSWER_OK=0,ANSWER_ERR=1};


  


cyg_thread thread_s[2];


char stack[2][4096];


cyg_handle_t   thread_recv,thread_deal;


cyg_thread_entry_t program_recv;


cyg_thread_entry_t program_deal;


下面函数是一个请求应答函数,当echo_typeANSWER_OK时,使消息解析成功,且命令执行;echo_typeANSWER_ERR时,表示接收消息错误或者命令没有成功执行。


void request_answer(int echo_type,int pkg_type,int err_type,int err_val)


{


     unsigned short ret;


     const unsigned char  *pb;


     short hi,lo;


     int nlen;


    if(echo_type==ANSWER_OK){//成功返回


      unsigned char chACK[]={
0xff,0x1c,0x01,pkg_type,0x00,0x00,'\r','\n'};


      ret=checkcrc(&chACK[3],1);


      hi=(ret>>8)&0xff;


      lo=ret&0xff;


      chACK[4]=lo;


      chACK[5]=hi;


      nlen=sizeof(chACK); 


      cyg_io_write( handle, chACK, &nlen );


     }  else if(echo_type==ANSWER_ERR) {  //失败返回     


       unsigned char chNAK[]={0xff,0x1a,0x03,pkg_type,err_type,err_val,0x00,0x00,'\r','\n'};


       ret=checkcrc(&chNAK[3],3);


       hi=(ret>>8)&0xff;


       lo=ret&0xff;


       chNAK[6]=lo;


       chNAK[7]=hi;


       nlen=sizeof(chNAK); 


       cyg_io_write(handle, chNAK, &nlen);


    }   


}


定义数据接收线程和数据处理线程,接收线程接收数据,接收到了一个整包,就把这个包拷贝给全局数据缓冲区,然后由数据处理线程进行处理。


代码:


 cyg_io_handle_t handle ;


 unsigned char g_package[256];//传递给处理线程的全局数据缓冲区


 int           g_npkg=0;         //缓冲区的字符数


static void program_recv( )


{ 


Cyg_ErrNo err;


//打开串口,返回串口的句柄给参数handle


    err = cyg_io_lookup("/dev/uart_usb", &handle); 


    unsigned char buff;


    unsigned char content[256];//临时缓冲区


    int count="0",len;


    while(1){


        len=1;


//读一个字节,放到buff


          err=cyg_io_read(handle,(void*)&buff, &len);


         if (ENOERR == err) {


             if(buff==0xff){


                 count=0;


                 content[count++]=buff;


          } else content[count++]=buff; 


     


         if((buff==0x0a)&&(content[count-2]==0x0d)){


            g_npkg=0; //拷贝数据到全局数据缓冲区  


            while((count--)>0){


            g_package[g_npkg]=content[g_npkg];


            g_npkg++;


           }         


         }


         if(count>=255) {   //防止意外发生,不能使数组越界


           count%=256;


         }


      } // end    if(ENOERR == err)         


    }//end while(1)


}


数据处理线程负责处理一个完整的数据包。


static void program_deal(cyg_addrword_t datat)


{


    int delay;          


    while(1){


     //全局数据缓冲区有数据,并且成功打开了串口则执行数据处理


       if(g_npkg&&handle){


         //首先通过checkcrc()进行crc的校验。


         unsigned short ret="checkcrc"(&g_package[3],g_package[2]);


         const unsigned char  *pb=&g_package[3]+g_package[2];


         short hi,lo;


         int nlen;


         hi=(ret>>8)&0xff;


         lo=ret&0xff;


         //比较校验后的结果,正确则处理包,错误则返回错误消息


         if((lo==*pb)&&(hi==*(pb+1))){


           //执行命令指定的动作………


         } else{


           request_answer(ANSWER_ERR,g_package[1],0,lo);


         }


         g_npkg=0;//不要忘了清空全局数据缓冲区


       }


}


}


4)编译、调试及下载


打开软件“Niosii,新建一个工程,就可以开始写代码了。代码写完后,通过菜单“project\build all”编译工程项目,编译成功后可以启动Nios ii的调试器进行在线调试,在需要调试的行设置断点,通过菜单”run\debug as\nios ii hardware”启动调试会话。调试完成则可以下载应用程序到FLASH中,需要打开Nios II SDK SHELL,进入到我们的应用程序目录,输入如下命令:


/my-ecos-lib-path/program-flash    application


这个my-ecos-lib-path就是我们编译的eCos库的路径,application是我们的Nios ii编译和调试好的应用程序。


 


 


本文是我在实际项目开发中的经验总结,希望对从事嵌入式开发的朋友有所帮助,特别是使用Nios II处理器开发的朋友。


 


相关资料:


NiosII处理器是Altera公司为其FPGA产品配套开发的软核CPU,它们是在FPGA上通过编程的方式实现的,作为一个组件可以和其它的部件组合在一起,在单片FPGA上就可以布置一个含有cpu、ram、flash、uart、以太网等接口的系统。NiosII是在FPGA上实现的,所以可以根据系统的需要裁剪和定制,更使其成为软硬件紧密结合的系统。据说有公司已经在研制使用这种定制的系统应用于大型服务器,可见其在非嵌入式领域也受到人们的关注。


eCos是嵌入式可配置操作系统,英文全称是Embedded Configurable Operating System。绝大多数代码使用C++写成。 eCos最大的特点是模块化、内核可配置。如果说嵌入式Linux太庞大了,那么eCos可能就能够满足要求。eCos有一套非常美妙的包管理机制,在组件可定制的硬件系统上更易于移植。eCos使用CDL语言进行配置管理,在编译时进行优化,生成的二进制代码大小只有几百KB。

文章评论0条评论)

登录后参与讨论
我要评论
0
7
关闭 站长推荐上一条 /2 下一条