基于Nios II和eCos的串口通信程序开发
嵌入式开发人员必须选择一款处理器,以此决定合适的系统性能。Nios II处理器使用指令和数据存储器分离的存储器结构,具有灵活的结构可修改性,支持自定制指令。Nios II处理器支持片上调试,通过JTAG调试通道,可以实现指令单步、断点、连续运行等调试功能。使用系统开发工具将处理器、外设、存储器和I/O接口集成在单片FPGA中,定制自己设计的系统,并且对各种外围设备的实现提供了强大的支持平台;SOPC Builder系统开发工具可以自动生成组件以及连接组件的总线,quartus ii 软件开发工具可以完成功能模块设计、综合布线和仿真,Nios II ide软件开发工具提供嵌入式应用软件的开发环境和调试环境。所有软件开发任务编辑、构建、程序调试都能够在(IDE)下完成,从而简化了开发过程,降低了系统成本、复杂性以及功耗,缩短了产品上市周期。
组件的可定制性是嵌入式开发的一个特点,SOPC(System on Programmable Chip,片上可编程系统)是Altera公司提出的一种灵活、高效的SOC解决方案。它将处理器、存储器、I/O接口、DMA、定时器等系统设计需要的功能模块集成到一个PLD器件上,构建一个可编程的片上系统。
嵌入式应用软件都是运行在特定的硬件平台上的。我所使用的FPGA为EP 1C 6Q 240C 8,如果只是使用FPGA来实现一个串口通信,那么在Nios II IDE中使用Nios II device drivers来写一个串口通讯程序是容易实现的,但是我们的程序要保存一些数据到FLASH中,Nios II device drivers虽然提供了HAL和c库,但是没有提供可读写的文件系统,所以我们就选了提供文件系统支持的eCos嵌入式操作系统来实现所需要的功能。那么eCos怎么移植到nios II处理器上,又怎样在这样一个平台上开发串口通信程序呢!我把设计的过程分为三部分:硬件平台设计,eCos库的编译,串口通信程序开发和调试。
硬件平台设计
根据fpga型号和flash类型先要制作目标板。Quartus ii有一个命令mk_target_board是用来制作目标板的,参看该命令的使用帮助请参考Quartus ii安装路径document目录下的flash-program-guide.pdf。生成目标板工程后,启动quartus ii 5.0和sopc builder软件设计该目标板工程,sopc builder软件完成UART、FLASH、RAM组件的添加和编译。quartus ii 5.0软件进行综合布线,编译生成flash program file(一个sof文件)。
然后使用quartus ii 5.0和sopc Builder软件设计我们的应用工程,在sopc builder软件中选择我们自己设计的目标板。添加必需的组件和模块,串口通信需要的UART组件,保存数据的FLASH组件等。分配管脚、编译,生成FPGA硬件配置文件(也是sof文件)。Sopc Builder生成的ptf文件也是我们在后面配置ecos库的时候要用到的文件。
eCos库编译
eCos可以到Redhat的网站或者Nios Community论坛找到,Nios论坛的eCos是移植了的版本,而redhat网站上的还要自己移植到Nios II上。移植了的版本支持这些Altera的Avalon设备组件:Timer、UART、JTAG UART、Lan 91C 111、Ethernet Chip、LCD 16207 Panel、Compact 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’;十六进制为0x0d,0x 0a |
3)程序主要代码分析
定义包的类型,PKG_DATE和PKG_STATUS是接收包类型,ANSWER_OK和ANSWER_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_type是ANSWER_OK时,使消息解析成功,且命令执行;当echo_type是ANSWER_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,0x 1c ,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==0x 0a )&&(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。