韦东山嵌入式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的扇区中。

发布了14 篇原创文章 · 获赞 1 · 访问量 1408

猜你喜欢

转载自blog.csdn.net/weixin_42813232/article/details/104675034