DE2上移植uClinux系统

目的:由于项目需求,需要在DE2上移植uClinux系统,然后移植OpenCV,利用OpenCV中的函数处理视频图像

环境平台:Quartus 32-bit Version12.0 +Nios II Software Build Tools for Eclipse + VMware Workstation 9.0 + Ubuntu 11.0

目前只实现了uClinux的编译,其他部分随进展发布。


1 建立运行uClinux的最小Nios II 系统

最小系统至少包括Nios II Processor,SDRAM Controller,Flash Memory Interface(CPI),Interval Timer和用于通信的JTAG UART。注意要设置Nios II处理器的Reset Vector 和 Exception Vector。点击Generate,生成Nios II 系统,其中.ptf文件用于编译uClinux内核,.sopcinfo文件用于Nios II IDE创建工程时生成Nios II系统的硬件抽象。

系统生成后,可以用Nios II IDE编写程序测试一下系统是否可用,然后再进行下一步。

Nios II IDE新建项目时,会生成两个文件夹,含_bsp的文件夹是Nios II IDE根据SOPC Build定制的Nios II系统生成的。其中system.h包含了系统各组件信息,如名称,基地址,中断号等,且在工程编译连接成功后才能访问。Nios II 系统存储器与外设是统一的,而对于外设的访问都是通过访问外设寄存器达到目的的。

编译时需要设置Nios II 系统的属性,否者会因为内存不足而出错,因而需要优化内存大小。

右键项目 - Nios II - BSP Editer - Main - Settings:不勾选enable_plus_plus,勾选enable_small_c_library。enable_reduced_device_drivers 和 enable_clean_exit任选。Generate后重新编译。

编译后即可在线调试,我在调试时总是会遇到“Download ELF Process failed"的问题,网上一般说是相位延迟,管脚连线的原因,但我暂时还未解决奋斗,容后再说啊。

这部分主要参考真OO无双的博客如何自己用SOPC Builder建立在DE2上跑uC/OS-II的Nios II系统


需要准备的文件:Nios2gcc.tar.gz2(支持Nios II处理器的uClinux编译器);uClinux-dist-20070130.tar.gz,uCliux-dist-20070130-nios2-02.diff.gz(内核源码及其补丁文件);.ptf文件(SOPC生成的Nios II系统配置文件)。


2 在linux环境中配置交叉编译开发环境

解压Nios2gcc.tar.gz2:通过su命令获取root权限,然后tar -jxvf Nios2gcc.tar.gz2 -C / 。这样工具就安装到了/opt/nios2/bin中了。

添加环境变量以便在命令行中使用:PATH=$PATH:/opt/nios2/bin。如果在命令行中输入,那么这个设置在重启SHELL后会丢失。可以再./bash_profile或者./bashre(对于Ubuntu),该文件在用户名目录下,就是打开SHELL时的目录下,该文件是隐藏的,需要用ls -a才能看见。奇怪的是Ubuntu中俩个文件都有,我在./bash_profile设置时,用nios2-linux-gcc -v 命令检查是否安装时出错。所以直接在./bashrc的最后添加PATH=$PATH:/opt/nios2/bin,才成功。估计是我对linux的命令行了解太少了,实力不济啊。


3 编译uClinux(普通用户权限即可)

(1)解压uClinux-dist-20070130.tar.gz:tar -zxvf uClinux-dist-20070130.tar.gz;获得uClinux-dist文件

(2)打补丁:把补丁包拷贝到uClinux-dist文件下,gunzip -c uClinux-dist-20070130-nios2-02.diff.gz | patch -p0

(3)定制内核:输入内核配置命令 make menuconfig,根据实际选择。

(4)配置硬件:make vendor_hwselect SYSPTF=SOPC生成的.ptf文件,输入命令前要将.ptf文件拷贝到/uClinux-dist/linux-2.6.x文件夹(根据实际)。如不想拷贝,等号右边需要./ptf文件的完全路径。输入命令后会出现ptf文件描述的Nios II系统,包括CPU,存储器,选择对应的CPU及共内核运行的存储器,CPU只有一个,存储器没特殊情况都选SDRAM。

(5)生成uClinux镜像:依次输入make romfs、make、make linux image,没有出错的哈,在../uClinux-dist/linux-2.6.x/arch/niosnommu/boot/下就会生成zImage镜像。

常见问题:

(1)make romfs时:error:nios2-elf-gcc command not found 和 cp:cannot stat 'boa' : No such file or directory。解决办法:make menuconfig时(None)libc Version。但我这样设置后,仍然有此这个问题。可以忽略,似乎不影响后续操作。

(2)make时出错,再make一次,就没有错误了(网上有部分人是这样的)。但我make多次后,仍有scripts/mod/sumversion.c : error:‘ PATH_MAX ‘ undeclared...。解决办法是:找到sumversion.c文件,在定义全局变量那部分代码添加一行:#define PATH_MAX 128(随意定)。然后我就成功生产了zImage镜像。


4 在NCS中验证uClinux是否在DE2上运行(Windows环境,需要连接DE2板)

(1)启动Nios II Command Shell,下载.sof文件:nios2-configure-sof Quartus生生的sof文件。NCS打开时进入的目录是Altera/12.0/nios2eds/,可以将sof文件和zImage镜像拷贝的此目录下。

(2)下载zImage到SDRAM中:nios2-download -g zImage。

(3)启动uClinux:nios2-terminal。


在线调试部分(第4步)还未成功,所以我还没有验证我的zImage是否能真正运行,只是看“Nios II系统上运行uClinuc“有关内容已经一星期了,先总结一下。有错的话,我再改正。还有是网上有关内容都是2010年左右的,大都用的是2007的uClinux,我想在运行成功后,编译最新的uClinux试试。晚上还看到有人移植OpenCV(只是opencv1.1,很古老的版本了)到Nios II上运行的uClinux,我也被老师要求将一些图像处理的内容在FPGA上实现,可能需要移植高版本的OpenCV,个人感觉成功的可能性不大,但还是全力以赴。也希望看到的人有有关方面研究的不吝赐教啊,希望我早日完成。EDA实验没好好学,真是悔不当初啊!




经过一周的折磨,我终于决定不自己用SOPC创建Nios II的最小系统了,而是用Demo里符合最小系统要求的DE2_NET程序,因为自己创建时会牵涉到引脚配置,连线等问题,功力不深厚是很难解决的。另外“Download ELF Process failed"确实是相位的问题,需要添加一个锁相环。


最后终于我也成功了啊啊哈哈哈哈哈哈哈哈哈哈哈哈:





但是这实际上只是在单片机(Nios II系统)上装了一个uClinux系统而已,还什么都不能做的,哇咔咔,现在的设想是在uClinux编写的程序同样编译到zImage镜像中,另外这个系统能不能正常调用DE2上的硬件还是个很大的问题哦,奋斗


经过一周的努力,终于可以再移植uClinux的Nios 2 系统上运行OpenCV程序了,啊啊啊啊哈。特别感谢Tyreal Han的工作,主要参考他的OpenCV在基于FPGA的嵌入式系统中的移植研究

编译OpenCV:

(1)      解压源文件:tar –zxvf opencv-1.1pre1.tar.gz

(2)      配置编译信息:./configure --host=nios2-linux --without-gtk --without-carbon--without-quicktime --without-1394libs --without-ffmpeg --without-python--without-swig –without-v4l --enable-static --disable-shared --disable-appsCXX=nios2-linux-g++ --prefix=/home/dh/sopc/opencv-1.1.0/uccv

-enable-static 和 –disable-shared:启用静态链接,禁用动态链接,即只生成静态链接库。这是考虑到嵌入式系统对内核和程序的要求设置的。如果不设置disable-shared会出现编译错误。配置后结果:

(3)      编译,输入:make。

如配置编译信息时,不添加-disable-shared参数会出现如下错误:

(4)      输入命令make install。生成的静态文件库在lib文件夹。


编写OpenCV程序:

(1)      写代码,不能调用highgui.h中的函数,保存为hello.cpp。代码另附

(2)      编译源文件:nios2-linux-g++ -c hello.c pphello.o –I/home/dh/sopc/opencv-1.1.0/uccv/include/opencv(头文件引用位置)

(3)      连接生成可执行程序:nios2-linux-g++ -o hello hello.o –L/home/dh/sopc/opencv-1.1.0/uccv/lib–lcv –lcvaux –cxcore –lml –lpthread –elf2flt

(4)      将生成的可执行文件复制到romfs,重新生成内核镜像。

(5)      下载镜像,执行程序。

分别以间接(函数调用)和直接方式访问CvMat和IplImage中的数据:

另附测试代码:

OpenCV程序hello.cpp

/*
 * DisplayImage.cpp
 *
 *  Created on: Dec 16, 2012
 *      Author: dh
 */

#include <cv.h>
//#include <highgui.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(int argc,char** argv)
{
	printf("Image Processing with OpenCV 1.1.0...\n");
	printf("CvMat Test :\n");
	double a[] = {1,2,3,4,5,6,7,8,9,10,11,12};
	CvMat m = cvMat(3,4,CV_64FC1,a);
	for(int i = 0;i < 3;i++)
	{
		for(int j = 0;j < 4;j++)
		{
			printf("m[i][j] = %f\t",cvmGet(&m,i,j));

		}
		printf("\n");
	}

	printf("Get CvMat in another way :\n");
	for(int i = 0;i < 3;i++)
	{
		for(int j = 0;j < 4;j++)
		{
			printf("m[i][j] = %f\t",m.data.db[i*m.cols+j]);

		}
		printf("\n");
	}

	printf("Gray Image Test :\n");
	IplImage *GrayImg = cvCreateImage(cvSize(10,10),IPL_DEPTH_8U,1);
	CvScalar s;
	for(int i = 0;i < GrayImg->height;i++)
		for(int j = 0;j< GrayImg->width;j++)
		{
			s = cvGet2D(GrayImg,i,j);
			s.val[0] = 0;
			cvSet2D(GrayImg,i,j,s);
		}
	for(int i = 0;i < GrayImg->height/2;i++)
		for(int j = 0;j< GrayImg->width;j++)
		{
			((uchar*)(GrayImg->imageData + i * GrayImg->widthStep))[j] = 255;
		}

	printf("Tht first line data of the Gray Image :\n");
	for(int i = 0;i < GrayImg->width;i++)
	{
		printf("m[0][i] = %f; \t %d\n",cvGet2D(GrayImg,0,i).val[0],
				((uchar*)(GrayImg->imageData))[i]);
	}

	//cvNamedWindow("GrayImage",1);
	//cvShowImage("GrayImage",GrayImg);
	//cvWaitKey(0);

	printf("RGB Image Test :\n");
	IplImage *RgbImg = cvCreateImage(cvSize(10,10),IPL_DEPTH_32F,3);
	for(int i = 0;i < RgbImg->height;i++)
		for(int j = 0;j< RgbImg->width;j++)
		{
			s = cvGet2D(RgbImg,i,j);
			s.val[0] = 111;
			s.val[1] = 0;
			s.val[2] = 0;
			cvSet2D(RgbImg,i,j,s);
		}
	for(int i = 0;i < RgbImg->height/2;i++)
		for(int j = 0;j< RgbImg->width;j++)
		{
			((float*)(RgbImg->imageData + i * RgbImg->widthStep))[j * RgbImg->nChannels + 0] = 0;
			((float*)(RgbImg->imageData + i * RgbImg->widthStep))[j * RgbImg->nChannels + 1] = 111;
			((float*)(RgbImg->imageData + i * RgbImg->widthStep))[j * RgbImg->nChannels + 2] = 0;
		}

	for(int i = 0;i <RgbImg->height;i++)
	{
		printf("RgbImg[%d][0] :\n",i);
		s = cvGet2D(RgbImg,i,0);
		printf("B = %f;\t G = %f;\t R = %f\n",s.val[0],s.val[1],s.val[2]);
		printf("B = %f;\t G = %f;\t R = %f\n",
((float*)(RgbImg->imageData + i * RgbImg->widthStep))[0],
					((float*)(RgbImg->imageData + i * RgbImg->widthStep))[1],
					((float*)(RgbImg->imageData + i * RgbImg->widthStep))[2]);
					
	}
	//cvNamedWindow("RgbImage",1);
	//cvShowImage("RgbImage",RgbImg);
	//cvWaitKey(0);

	//cvNamedWindow("BMP Image",1);
	//cvShowImage("BMP Image",m);
	//waitKey(0);
	//cvReleaseImage(&m);
	return 0;
}


移植uClinux,编译OpenCV完成后,需要操作DE2板上的硬件,主要是摄像头,LCD等外设,然后基于OpenCV 进行图像处理。

预计要进行内核裁剪,编写底层硬件的驱动了。不知道有没有简单的方法。


还有想在Nios II Software Build Tools for Eclipse中调用编译好的OpenCV函数,总是出现错误,感觉这样会简单一些。

有没有做过的高人呢,跪求求求求求求求求求求。。。。。。。。。。。。。。。。。。


2012-12-23:

在搭建好基于Nios II uClinux系统的OpenCV平台后,我开始测试OpenCV 1.1.0中常用的数据结构函数,为接下来要进行的图像拼接等其他处理做好准备。

However, However 突然有一天在make uclinux源码时出现错误,大概和boa有关,错误形式"undefined reference to "bzero" "index“......”——百思不得其解啊 无奈我重装了系统,按照上面的步骤重新做一遍,成功生成了镜像。然后我在引用OpenCV 中的函数,然后打算用nios2-linux-g++命令编译源文件,突然发现我安装的交叉编译环境中没有这个命令(我用的nios2gcc.tar.bz2),然后我直接重新安装了nios2gcc-20080203.tar.bz2,果然里面有nios2-linux-g++ 的命令,并顺利编译,在板子上成功运行。


此时 我猜想是检查编译环境与uClinux-dist不匹配,果然,我删除原来的uClinux-dist文件夹,重新解压一个,按上面步骤重新制作镜像(注:此时的交叉编译环境是nio2gcc20080203的),果然有出现了上面的问题。由此我确定确实是交叉编译环境与uClinux(20070130)不匹配的原因。


好了,原因发现后,问题就好解决了。只要保证二者匹配就好了。有由于我现在只在uClinux-dist-20070130的源码包基础上make成功过,只能用nios2gcc.tar.bz2交叉编译环境。SO 我只需要用nios2-linux-gcc编译我的源程序就行了,BUT 这样做竟然出错了,而且是一大堆错误,惨不忍睹。无奈我安装nios2gcc-20080203的交叉编译环境编译成功了。


难道真是鱼和熊掌不可兼得——我既要要make成功生成镜像,又要nios2-linux-g++编译调用OpenCV函数的程序 ——但能make成功的交叉编译环境不含nios2-linux-g++命令。


观察发现两个交叉编译环境文件结构基本相同——都是bin,include,lib那几个文件,我只需要让他们合并就好了 。

所以我在Windows环境下,解压二者,然后合并,注意是将nios2gcc.tar.bz2覆盖nios2gcc-20080203.tar.bz2中的文件,然后复制到linux环境的opt文件夹下——make nios2-linux-g++这样都能成功了。


我都有点佩服自己了啊哈哈哈哈哈哈哈哈哈哈哈哈哈浩


我要做的是图像拼接等处理,需要读取图片,就直接用了Tyreal Han共享的bmpDecoder.cpp和bmpDecoder.h程序,但这个程序有错误,编译的时候重视提示cvLoadImage()函数找不到。原来是在bmpDecoder.h文件中没有对这个函数声明,只要在.h文件中添加生命就行了,然后在CPP文件中去掉默认参数(只需要在生命时,制定默认参数就行了)。


另外 nios2-linux编译环境不支持#ragma pack(push)等命令,就直接注释掉了,后面的程序只用到这个结构体的大小,程序运行表明这个结构大小总是14,就直接用宏定义代替了,当然直接用 14做参数也可。

#pragma pack(push) 						// 保存对齐状态
#pragma pack(2)							// 设定为2字节对齐
typedef struct tagBITMAPFILEHEADER
{
	WORD bfType; 						// 位图文件类型,必须为BM(0-1字节)
	DWORD bfSize; 						// 位图文件大小,以字节为单位(2-5字节)
	WORD bfReserved1; 					// 位图文件保留字,必须为0(6-7字节)
	WORD bfReserved2; 					// 位图文件保留字,必须为0(8-9字节)
	DWORD bfOffBits; 					// 位图数据的起始位置,以相对于位图(10-13字节),文件头的偏移量,以字节为单位
} BITMAPFILEHEADER;
#pragma pack(pop)


我还发现了一个更简单的编译链接程序的方法,不必先生成目标文件,直接生成可执行程序

nios2-linux-g++ -Wall -o bmpShow bmpShow.cpp -I/home/dh/Sopc/uccv/include/opencv -L/home/dh/Sopc/uccv/lib -lcv -lcvaux -lcxcore -lml -lpthread -elf2flt

读图程序成功实现,添加到镜像中,运行结果如图:


读图代码:

#include <cv.h>
//#include <highgui.h>
#include <stdio.h>
#include <stdlib.h>

#define BMPFILEHEADERSIZE 14

typedef unsigned long DWORD;
typedef unsigned short int WORD;
typedef long LONG;
typedef unsigned char BYTE;

typedef struct tagBITMAPINFOHEADER
{
	DWORD biSize; 						// 本结构所占用的字节数(14-17字节)
	LONG biWidth; 						// 位图的宽度,以像素为单位(18-21字节)
	LONG biHeight; 						// 位图的高度,以像素为单位(22-25字节)
	WORD biPlanes; 						// 目标设备的级别,必须为1(26-27字节)
	WORD biBitCount;					// 每个像素所需的位数,必须是1(双色),4(16色),8(256色)或24(真彩色)之一(26-29字节)
	DWORD biCompression; 					// 位图压缩类型,必须是0(不压缩),1(BI_RLE8)或(BI_RLE4)(30-33字节)
	DWORD biSizeImage; 					// 位图的大小,以字节为单位(34-37字节)
	LONG biXPelsPerMeter; 					// 位图水平分辨率,每米像素数(38-41字节)
	LONG biYPelsPerMeter; 					// 位图垂直分辨率,每米像素数(42-45字节)
	DWORD biClrUsed;					// 位图实际使用大颜色表中的颜色数(46-49字节)
	DWORD biClrImportant;					// 位图显示过程中重要的颜色数(50-53字节)
} BITMAPINFOHEADER;

typedef struct tagRGBQUAD
{
	BYTE rgbBlue;						// 蓝色的亮度(0-255)
	BYTE rgbGreen; 						// 绿色
	BYTE rgbRed; 						// 红色
	BYTE rgbReserved;					// 保留,必须为0
} RGBQUAD;	



int main(int argc,char **argv)
{
	
	//cvShowImage("Bitmap",img);
	IplImage* cvLoadBmpImage(char *sFileName);
	IplImage *img = cvLoadBmpImage(argv[1]);
	printf("The Basic Informations of the Bitmap image:\n");
	printf("width = %d\t,height = %d\t,widthStep = %d\n",img->width,img->height,img->widthStep);
	printf("total size = %d\t,depth = %d\t,nChannels = %d\n",img->nSize,img->depth,img->nChannels);
	switch(img->origin)
	{
	case 0:		printf("The Bmp is Top-Left origin !\n");break;
	case 1: 	printf("The Bmp is Bottom-Left origin,that is Windows bitmaps style.\n");break;
	default:	printf("Can't identify its origin style");
	}
	return 0;
}

IplImage* cvLoadBmpImage(char *sFileName)
{
	FILE *fp = fopen(sFileName,"rb");
	if(fp == NULL)
	{
		printf("Open BMP File failed !\n");
		return NULL;
	}
	else
	{
		printf("Open BMP File Success !\n");
	}
	
	// 跳过位图头文件
	fseek(fp,  BMPFILEHEADERSIZE,0);
	BITMAPINFOHEADER head;
	int width,height;
	int biBitCount,lineByte;
	
	// 定义位图信息头结构变量,读取位图信息头进内存,存放在head中
	fread(&head, sizeof(BITMAPINFOHEADER), 1,fp);
	width = head.biWidth;
	height = head.biHeight;
	printf("Width = %d\t,Height = %d\n",width,height);

	// 计算图像每行所占的 字节数(必须是4大倍数)
	biBitCount = head.biBitCount;
	lineByte = (width * biBitCount/8)/4*4;
	printf("biBitCount = %d\t,lineByte = %d\n",biBitCount,lineByte);

	RGBQUAD *pColorTable = NULL;
	unsigned char* blocks = NULL;
	printf("Size of the Structure RGBQUAD:\t%d\n",sizeof(RGBQUAD));
	if(biBitCount == 8)
	{
		// 申请颜色表所需要的空间,读颜色表进内存
		pColorTable = (RGBQUAD *)malloc(256*sizeof(RGBQUAD));
		fread(pColorTable,sizeof(RGBQUAD),256,fp);
		
		
	}
	else
	{
		printf("biBitCount != 8, Can't Create Color Table Now !\n");
	}
	
	blocks = (unsigned char *)malloc(lineByte*height*sizeof(char));
	if(blocks == NULL)
	{
		printf("Malloc Blocks Failed !\n");
		return NULL;
	}
	else
	{	printf("Malloc Blocks Success !\n");	}
	
	fread(blocks,1,lineByte*height,fp);
	printf("Fread Blocks Success !\n");

	
	free(pColorTable);
	free(blocks);
	printf("Free pColorTable and Blocks Success !\n");

	IplImage *img = NULL;
	img = cvCreateImageHeader(cvSize(width,height),8,3);
	printf("Create Image Header Success !\n");
	cvSetData(img,blocks,lineByte*3);
	printf("Set Image Data Success !\n");
	img->origin = IPL_ORIGIN_BL;
	
	return img;
}


Altera官网上的uClinux-dist已经到2010版本了,我尝试过还没有成功,但这才是大道,正途。

我这里走的都是歪门邪道,一切只为实现,哎呀,显得自己好邪恶啊。

等我吧官网上的2010的编译成功后在来和大家分享吧。


OpenCV 1.1.0 貌似还不能满足我的需要啊,主要还是不想自己编写底层代码。

所以我会尝试着配置下OpenCV 2.0,然后要试着编写摄像头,VGA/LCD的底层驱动,主要是为了获取图像和显示图像的


末日已过,诸神保佑我吧








其他有用的资料链接:

真OO无双——(原创)如何在DE2上安装uClinux作业系统(台湾的同胞)

TYTRAL HAN——UCLINUX在NIOS II平台上的移植

百度空间中D-Zone关于uClinux的部分,提供了make是错误的解决办法

duckfly的博客——duckfly:在niosii上跑uClinux

MyFPGA论坛:liyongjie的帖子:DE2_114 uClinux移植

小鲁的博客:(原创)uClinux在Nios II平台上的移植(基于NIOS II的SOPC软硬件系统)(DE2)








發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章