韋東山嵌入式Linux學習----014 NOR Flash(3)

NOR Flash編程實現識別擦、寫、讀地址

/*
 *硬件平臺:韋東山嵌入式Linxu開發板(S3C2440.v3)
 *軟件平臺:運行於VMware Workstation 12 Player下UbuntuLTS16.04_x64 系統
 *參考資料:《嵌入式Linux應用開發完全手冊》韋東山,開發版原理圖,MX29LV160DBTI-70G(NOR FLASH) datasheet,MX29LV800BBTC datasheet
*/


一、基礎知識點

1、可視字符與不可視字符

可視字符 不可視字符
範圍 ASCII字符集中的95個可打印字符0x20-0x7E ASCII字符集中的33個控制字符0x00-0x1F,0x7F
用途 顯示在輸出設備上 向計算機發出一些特殊指令

具體介紹可點擊這裏查看。

二、實現功能

功能:讀取編寫某個地址信息擦除某個扇區

三、編程原理

繼續在上一篇博客的nor_flash.c文件上編寫

1、功能:讀某個指定地址

編程思路:通過串口中輸入操作地址打印地址信息時固定其長度位64字節,輸出格式爲:一行16個字節,共4行,每行先打印ASCII碼數值,最後打印ASCII碼字符。

1.1 獲取操作的地址

	unsigned int addr;
	volatile unsigned char *p; 
	
	/* 獲取地址 */
	printf("Enter the address to read: ");
	addr = get_uint();
	p = (volatile unsigned char *)addr;

1.2 打印地址信息

在打印字符時,需判斷字符是否可視,具體介紹可看本篇基礎知識點

	/* 各變量對應含義
	 * 用到的標誌位
	 * 存儲輸入的地址
	 * 存儲1字節地址的信息,存儲16字節的信息
	 * 存儲地址,以指針形式
	 */
	int i,j;
	unsigned int addr;
	unsigned char c,str[16];
	volatile unsigned char *p; 
	
	/* 打印地址信息
	 * 1、固定長度64字節
	 * 2、按照市面上的標準以16個字節爲一行,前爲數值,後爲字符
	 * 3、分辨字符是否可視
	 */
	 printf("Data: \n\r");
	 for(i=0;i<4;i++)
	 {

	 	for(j=0;j<16;j++)
	 	{
	 		c = *p++;
			str[j] = c;
			printf("%02x ",c);
		}
		
		for(j=0;j<16;j++)
	 	{
	 		if((str[j] < 0x20) || (str[j] > 0x7E))
				printf(".");
			else
				printf("%c",str[j]);
		}
		printf("\n\r");
	 }
}

2、功能:擦除nor flash某個指定扇區

編程思路:通過串口中輸入操作扇區地址,根據手冊上的指令執行擦除命令,最後通過查手冊可知:可通過判斷Q6位是否在變化,可知擦除是否完畢,不變則完,變則在擦除。

2.1 獲取操作的扇區地址

	unsigned int addr;
	volatile unsigned char *p; 
	
	/* 獲取地址 */
	printf("Enter the address of sector to erase: ");
	addr = get_uint();
	p = (volatile unsigned char *)addr;

2.2 按照手冊執行擦除命令

查MX29LV800BT/BB datasheet第10頁可知:

在這裏插入圖片描述

	nor_cmd(0x555,0xaa);     //解鎖
	nor_cmd(0x2aa,0x55);
	nor_cmd(0x555,0x80);
	printf("erasing ...\n\r");

	nor_cmd(0x555,0xaa);     //erase
	nor_cmd(0x2aa,0x55);
	nor_cmd(addr>>1,0x30);

補充說明:爲什麼這裏的addr>>1?
解釋:舉例說明,當我們輸入想操作的扇區地址addr爲0x100,由於在nor_cmd()函數中由於Nor Flash和S3c2440的數據線相差一位,函數編寫裏面已經addr<<1,實際到Nor Flash操作時地址變爲0x200,所以addr>>1。

2.3 判斷Q6位是否擦除完畢

查MX29LV800BT/BB datasheet第16頁可知:

在這裏插入圖片描述

功能說明:用戶可以通過查詢第I位切換指示是否有自動程序或擦除算法正在進行或完成
使用方法:執行擦除命令後,通過先後兩次讀取操作地址的第I位,判斷兩次的電平是否相同,若相同則命令執行完畢,否則命令未執行完畢。
編寫判斷函數:

void wait_readly(unsigned int addr)
{
	unsigned char val,flag;
	
	/* 先後兩次讀取操作地址*/
	val = nor_data(addr>>1);
	flag = nor_data(addr>>1);
	
	/*  提取第6位進行判斷 */
	while((val&(1<<6))!= (val&(1<<6)))
	{
		val =flag;
		flag = nor_data(addr>>1);;
	}
}
	wait_readly(addr);

3、功能:編寫某個指定地址

某個地址寫入值時的條件:
  先擦除地址信息,纔可寫入
  、此時地址信息爲0xff,可直接寫入。
編程思路:通過串口輸入操作的地址和信息,後根據手冊上的指令執行燒寫命令,最後通過查手冊可知:可通過判斷Q6位是否在變化,可知燒寫是否完畢,不變則完,變則在燒寫。

3.1 獲取地址

	unsigned int addr;

	/* 獲取地址 */
	printf("Enter the address of sector to write: ");
	addr = get_uint();

3.2 獲取寫入信息

	unsigned char str[100];
/* 獲取寫入信息 */
	printf("Enter the address to write: ");
	gets(str);

3.3 按照手冊執行寫命令

	int i = 0;
	/* 執行寫命令 
	 * 因爲芯片上爲16位數據線,所以傳輸數據時應該一次寫16位的數據,提高利用率
	 */	
	printf("writing ...\n\r");
	while( str[i] && str[i+1] )
	{
		val = str[i]+(str[i+1]<<8); 
		
		nor_cmd(0x555,0xaa);	 //解鎖
		nor_cmd(0x2aa,0x55);
		nor_cmd(0x555,0xa0);
		
		nor_cmd(addr>>1,val);        //proram
		
		i += 2;
		addr += 2;
	}
	val = str[i];	
	nor_cmd(0x555,0xaa);	 //解鎖
	nor_cmd(0x2aa,0x55);
	nor_cmd(0x555,0xa0);
	
	nor_cmd(addr>>1,val);		//proram
	

3.4 判斷Q6位是否寫完畢

	wait_readly(addr);

四、編程文件

1、完整nor flash.c文件

#include "my_printf.h"
#include "string_utils.h"


#define  NOR_FLASH_BASE 0  //nor--cs0,base addr :0



/* 構建一個函數,功能:往地址裏面寫東西 */
void nor_write_addr(unsigned int base,unsigned int offset,unsigned int val )
{
	volatile unsigned short *p = (volatile unsigned short *)(base + (offset<<1));
	*p = val;
}
/* 封裝nor_write_addr */
void nor_cmd(unsigned int offset,unsigned int cmd)
{
	nor_write_addr(NOR_FLASH_BASE,offset,cmd);
}

/* 構建一個函數,功能:讀取地址中的信息 */
unsigned int nor_read_addr(unsigned int base,unsigned int offset)
{
	volatile unsigned short *p = (volatile unsigned short *)(base + (offset<<1));
	return *p;
}

/* 封裝nor_read_addr */
unsigned int nor_data(unsigned int offset)
{
	return nor_read_addr(NOR_FLASH_BASE,offset);
}

void wait_readly(unsigned int addr)
{
	unsigned char val,flag;

	val = nor_data(addr>>1);
	flag = nor_data(addr>>1);
	
	while((val&(1<<6))!= (val&(1<<6)))
	{
		val =flag;
		flag = nor_data(addr>>1);;
	}
}

/* 識別nor flash */
void do_scan_nor_flash(void)
{
	/* 各變量對應含義
	 * 用到的標誌位
	 * 設備容量大小
	 * 廠家ID,設備ID
	 * region的數量、第一個region的信息地址
	 * blocks的數量、block的地址、block的大小
	 */
	char str[4];  
	int i,j,flag;
	int device_size;
	int vendor,device;
	int regions,regions_info_base;
	int blocks,block_addr,block_size;

	/* 打印設備ID 
	 * 1、先解鎖
	 * 2、後根據信息輸入對應的nor_cmd指令
	 * 3、讀取數據後要reset
	 */
	nor_cmd(0x555,0xaa);
	nor_cmd(0x2aa,0x55);
	nor_cmd(0x555,0x90);
	
	vendor = nor_data(0);
	device = nor_data(1);

	nor_cmd(0,0xf0);
	
	/* 
	 * 1、首先進入cfi模式
	 * 2、對獲取信息的不同,執行nor_cmd進行寫操作
	 * 3、執行nor_data,讀出對應地址的信息
	 */
	nor_cmd(0x55,0x98);
	str[0] = nor_data(0x10);
	str[1] = nor_data(0x11);
	str[2] = nor_data(0x12);
	str[3] = '\0';
	printf("\n\r");
	printf("str = %s\n\r",str);
		
	/* 打印容量 */
	device_size = 1<<(nor_data(0x27));    //2的nor_data(0x27)次方
	printf("Vendor ID = 0x%x, Device ID = 0x%x, Nor Flash Size = 0x%x, %dM\n\r", vendor, device, device_size, (device_size/(1024*1024)));

	/* 打印各個扇區的起始地址 */
	/* 名詞解釋:
	 *    erase block region : 裏面含有1個或多個block, 它們的大小一樣
	 * 一個nor flash含有1個或多個region
	 * 一個region含有1個或多個block(扇區)

	 * Erase block region information:
	 *    前2字節+1    : 表示該region有多少個block 
	 *    後2字節*256  : 表示block的大小
	 */
	regions = nor_data(0x2C);
	regions_info_base = 0x2d;
	block_addr = 0;
	flag = 0;
	printf("\n\r");
	printf("Block/Sector Start Address:\n\r");

	for(i = 0;i<regions;i++)
	{		
		blocks = nor_data(regions_info_base)+(nor_data(regions_info_base+1)<<8)+1;     //計算該region中blocks的個數
		block_size = 256*(nor_data(regions_info_base+2)+(nor_data(regions_info_base+3)<<8));	  //計算每個block的大小
		regions_info_base +=4;   //使得進入下次循環時地址爲下一個region的信息地址
		
		for(j=0;j<blocks;j++)
		{
		
		   /* 打印每個block的起始地址 */
			printHex(block_addr);
			putchar(' ');
			flag++;
			block_addr +=block_size;
			if (flag % 5 == 0)
				printf("\n\r");		
		}
	}
	printf("\n\r");
	/* 退出CFI模式 */
	nor_cmd(0, 0xf0);
}

/* 擦除nor flash某個扇區 
 * 1、獲取扇區地址
 * 2、按照手冊執行擦除命令
 * 3、判斷Q6位是否擦除完畢
 */
void do_erase_nor_flash(void)
{
	/* 各變量對應含義
	 * 存儲輸入的地址
	 * 存儲地址,以指針形式
	 */
	unsigned int addr;
	volatile unsigned char *p; 
	
	/* 獲取地址 */
	printf("Enter the address of sector to erase: ");
	addr = get_uint();
	p = (volatile unsigned char *)addr;

	
	nor_cmd(0x555,0xaa);     //解鎖
	nor_cmd(0x2aa,0x55);
	nor_cmd(0x555,0x80);
	printf("erasing ...\n\r");

	nor_cmd(0x555,0xaa);     //erase
	nor_cmd(0x2aa,0x55);
	nor_cmd(addr>>1,0x30);
	
	wait_readly(addr);
	printf("\n\r");
}

/* 編寫某個地址 
 * 1、獲取地址
 * 2、獲取寫入信息
 * 3、按照手冊執行寫命令
 * 4、判斷Q6位是否寫完畢
 */
void do_write_nor_flash(void)
{
	/* 各變量對應含義
	 * 用到的標誌位
	 * 存儲輸入的地址,存儲構建的16位信息
	 * 存儲寫入的信息
	 */
	int i;
	unsigned int addr,val;
	unsigned char str[100];
	i = 0;

	/* 獲取地址 */
	printf("Enter the address of sector to write: ");
	addr = get_uint();

	/* 獲取寫入信息 */
	printf("Enter the address to write: ");
	gets(str);

	/* 執行寫命令 
	 * 因爲芯片上爲16位數據線,所以傳輸數據時應該一次寫16位的數據,提高利用率
	 */	
	printf("writing ...\n\r");
	while( str[i] && str[i+1] )
	{
		val = str[i]+(str[i+1]<<8); 
		
		nor_cmd(0x555,0xaa);	 //解鎖
		nor_cmd(0x2aa,0x55);
		nor_cmd(0x555,0xa0);
		
		nor_cmd(addr>>1,val);        //proram

		i += 2;
		addr += 2;
	}
	val = str[i];	
	nor_cmd(0x555,0xaa);	 //解鎖
	nor_cmd(0x2aa,0x55);
	nor_cmd(0x555,0xa0);
	
	nor_cmd(addr>>1,val);		//proram
	
	wait_readly(addr);
	printf("\n\r");
}

/* 讀某個地址 
 * 1、獲取地址
 * 2、打印地址信息
 */
void do_read_nor_flash(void)
{
	/* 各變量對應含義
	 * 用到的標誌位
	 * 存儲輸入的地址
	 * 存儲1字節地址的信息,存儲16字節的信息
	 * 存儲地址,以指針形式
	 */
	int i,j;
	unsigned int addr;
	unsigned char c,str[16];
	volatile unsigned char *p; 
	
	/* 獲取地址 */
	printf("Enter the address to read: ");
	addr = get_uint();
	p = (volatile unsigned char *)addr;

	
	/* 打印地址信息
	 * 1、固定長度64字節
	 * 2、按照市面上的標準以16個字節爲一行,前爲數值,後爲字符
	 * 3、分辨字符是否可視
	 */
	 printf("Data: \n\r");
	 for(i=0;i<4;i++)
	 {

	 	for(j=0;j<16;j++)
	 	{
	 		c = *p++;
			str[j] = c;
			printf("%02x ",c);
		}
		
		for(j=0;j<16;j++)
	 	{
	 		if((str[j] < 0x20) || (str[j] > 0x7E))
				printf(".");
			else
				printf("%c",str[j]);
		}
		printf("\n\r");
	 }
}

void nor_flash_test(void)
{
	char c;
	while(1)
	{
		/* 打印菜單,供選擇測試內容 */
		printf("[S] Scan nor flash\n\r");
		printf("[E] Erase nor flash\n\r");
		printf("[W] Write nor flash\n\r");
		printf("[R] Read nor flash\n\r");
		printf("[Q] Quit nor flash\n\r");
		printf("Enter Selection: ");

		c = getchar();
		printf("%c\n\r",c);
		
		/* 測試內容:
		 * 1、識別nor flash
		 * 2、擦除nor flash某個扇區
		 * 3、編寫某個地址
		 * 4、讀某個地址
		 */

		switch (c)
		{
			case 'Q':
			case 'q':
				return;
				break;
			case 'S':
			case 's':
				do_scan_nor_flash();
				break;
			case 'W':
			case 'w':
				do_write_nor_flash();
				break;
			case 'R':
			case 'r':
				do_read_nor_flash();
				break;
			case 'E':
			case 'e':
				do_erase_nor_flash();
				break;
			default:
				break;
		}
	}
}

五、運行結果截圖

1、讀取某個指定地址

在這裏插入圖片描述

  對比讀取0地址的運行截圖與反彙編代碼可知:功能實現成功。

2、擦除某個指定扇區

在這裏插入圖片描述

  可以看出,執行擦除命令之後,扇區起始地址爲0x100000的信息爲0xff,擦除成功。

3、編寫某個指定地址

在這裏插入圖片描述

  從圖中可以知道abcdef成功寫入到起始地址爲0x100000的扇區中。

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