目的:由于项目需求,需要在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)