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)








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