OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 6698|回复: 1

S5pv210 NAND Flash学习笔记

[复制链接]

63

主题

305

帖子

1

精华

高级会员

Rank: 4

积分
853
金钱
853
注册时间
2012-8-3
在线时间
79 小时
发表于 2014-10-2 12:01:47 | 显示全部楼层 |阅读模式
平台:X210ii
Nand flash型号:K9F4G08U0D
nand结构由数据空间和附加字节组成,每页由2K存储空间和64个附加字节组成,附加字节不参与编址
总大小:(512M + 16M)*8bit = 4Gbit = (2K+64)B x 64Pages x 4,096 Blocks
Block总数 : 4096Blocks(1block = 64pages = (128K + 4k)byte)
1page = (2k + 64)bytes

代码借鉴了网友zjhsucceed_329 (CSDN)的裸机例程,在此表示感谢

本来为了编程方便,我定了一个结构体S5PV210_nand,用来指向nand寄存器,但是后来发现使用有些小问题,
(看来还是#define __REG (*(volatile unsigned int *) 0xXXXXXX 管用啊。)后来发现是忙检测延时问题,忙检测必须加延时,
但是,难道用结构体指针访问和宏定义指针访问还有效率的区别,宏定义指针访问稍微慢一些(客观上增加了延时)?

[mw_shl_code=c,true]#define S5PV210NAND_BASE 0xB0E00000[/mw_shl_code]


[mw_shl_code=c,true]typedef struct { S5PV210_REG32 NFCONF; S5PV210_REG32 NFCONT; S5PV210_REG32 NFCMMD; S5PV210_REG32 NFADDR; S5PV210_REG8 NFDATA; S5PV210_REG32 NFMECCD0; S5PV210_REG32 NFMECCD1; S5PV210_REG32 NFSECCD; S5PV210_REG32 NFSBLK; S5PV210_REG32 NFEBLK; S5PV210_REG32 NFSTAT; S5PV210_REG32 NFECCERR0; S5PV210_REG32 NFECCERR1; S5PV210_REG32 NFMECC0; S5PV210_REG32 NFMECC1; S5PV210_REG32 NFSECC; S5PV210_REG32 NFMLCBITPT; }S5PV210NAND_TypeDef; [/mw_shl_code]
在s5pv210-nand.文件里
[mw_shl_code=c,true]S5PV210NAND_TypeDef * S5PV210_nand = (S5PV210NAND_TypeDef *) S5PV210NAND_BASE;//定义nand寄存器地址 [/mw_shl_code]

注意:NFDATA必须定义为8bit的unsigned char指针类型,不能定义为32bit的int类型否则访问数据会不正常,韦东山老师的第一期视频也提到了。
比如可以这样定义:
#define S5PV210NAND_NFDATA  (*(volatile u8_t *)0xB0E00010)




一、NAND初始化:
初始化步骤:
1、通过NFCONF设定时序
2、通过NFCONT使能控制器,禁止片选
3、配置相关引脚为NAND flash工作模式(MP0_1,MP0_3,MP0_6)
4、发送一次复位命令


二、NAND读写
1、读操作
页内随机地址读一个字节:


1)先发送0x00命令;
2)再发地址(分5个字节发送)
3)发送0x30命令;
4)读NFDATA(8bit)
5)发送0x05命令,准备下一个列地址(页内偏移地址)
6)发送0xE0命令,读出数据;
7)重复第5~6步,读出页内其他地址数据;
页连续读操作(2KBYTE):
看手册吧
2、写操作
待续

三、其他操作
1、忙检测(非常重要)
读取NFSTAT寄存器,最低位 Flasn_RnB 为 1 则NAND FLASH准备就绪。
要注意NFSTAT寄存器不能放在连续读,两次忙检测之间应该加入适当的延时,我就卡在这很久。。。
错误:
[mw_shl_code=c,true]static void S5PV210_wait_idle(void) { while((S5PV210_nand->NFSTAT & 0x00000001) == 0x00); }[/mw_shl_code]
正确代码:
[mw_shl_code=c,true]static void S5PV210_wait_idle(void) { while((S5PV210_nand->NFSTAT & 0x00000001) == 0x00){ udelay(1);//延时1微秒[/mw_shl_code] [mw_shl_code=c,true]        } }[/mw_shl_code]

完整代码:
s5pv210-nand.h头文件
[mw_shl_code=c,true]#ifndef __S5PV210_NAND_H__ #define __S5PV210_NAND_H__ #ifdef __cplusplus extern "C" { #endif #include <types.h> #include <s5pv210/reg-nand.h> typedef u32_t S5PV210_REG32; typedef u8_t S5PV210_REG8; typedef struct { S5PV210_REG32 NFCONF; S5PV210_REG32 NFCONT; S5PV210_REG32 NFCMMD; S5PV210_REG32 NFADDR; S5PV210_REG8 NFDATA; S5PV210_REG32 NFMECCD0; S5PV210_REG32 NFMECCD1; S5PV210_REG32 NFSECCD; S5PV210_REG32 NFSBLK; S5PV210_REG32 NFEBLK; S5PV210_REG32 NFSTAT; S5PV210_REG32 NFECCERR0; S5PV210_REG32 NFECCERR1; S5PV210_REG32 NFMECC0; S5PV210_REG32 NFMECC1; S5PV210_REG32 NFSECC; S5PV210_REG32 NFMLCBITPT; }S5PV210NAND_TypeDef; #define S5PV210NAND_BASE 0xB0E00000 //#define SetBit(reg,bit) (reg |= 1<<bit) //#define ClrBit(reg,bit) (reg &= ~(1<<bit)) #define PAGE_SIZE 2048 #define BLOCK_SIZE (PAGE_SIZE * 64) //时钟基准为PSYS-PCLK,133Mhz,周期约为7.5ns #define TACLS 2 //(15ns = 7.5ns*2 ),芯片手册最小值为0 TCLS -TWP = 12ns - 12ns #define TWRPH0 2 //nWE脚的脉宽,7.5ns*(2+1) = 22ns,手册值为12ns,加上PCB延迟10ns #define TWRPH1 0 //nWE恢复高电平后,CLE的保持时间,7.5ns * (0+1),手册值为5ns #define AddrCycle 1 //K9F4G的地址周期为5个,寄存器设置AddrCycle为1 #define S5PV210NAND_NFCONF (*(volatile u32_t *)0xB0E00000) #define S5PV210NAND_NFCONT (*(volatile u32_t *)0xB0E00004) #define S5PV210NAND_NFCMMD (*(volatile u32_t *)0xB0E00008) #define S5PV210NAND_NFADDR (*(volatile u32_t *)0xB0E0000C) #define S5PV210NAND_NFDATA (*(volatile u8_t *)0xB0E00010) #define S5PV210NAND_NFSTAT (*(volatile u32_t *)0xB0E00028) #define S5PV210NAND_MP0_1CON (*(volatile u32_t *)0xE02002E0) #define S5PV210NAND_MP0_3CON (*(volatile u32_t *)0xE0200320) #define S5PV210NAND_MP0_6CON (*(volatile u32_t *)0xE0200380) void S5PV210_read_id(u8_t id[]); void nand_init(void); void nand_erase(u32_t addr); void nand_read_page(u8_t *buf, u32_t addr); void nand_read_random(u8_t *buf, u32_t addr, u32_t size); void nand_write_page(u8_t *buf, u32_t addr); #ifdef __cplusplus } #endif #endif /* __S5PV210_NAND_H__ */ [/mw_shl_code]

s5pv210-nand.c文件
[mw_shl_code=c,true]#include <types.h> #include <stddef.h> #include <io.h> #include <string.h> #include <sizes.h> #include <s5pv210/reg-clk.h> #include <s5pv210-clk.h> #include <s5pv210-nand.h> #include <s5pv210/reg-gpio.h> #include <s5pv210-serial-stdio.h> #include <s5pv210-tick-delay.h> S5PV210NAND_TypeDef * S5PV210_nand = (S5PV210NAND_TypeDef *) S5PV210NAND_BASE;//定义nand寄存器地址 static void S5PV210_nand_reset(void); static void S5PV210_wait_idle(void); static void S5PV210_nand_select_chip(void); static void S5PV210_nand_deselect_chip(void); static void S5PV210_write_cmd(u32_t cmd); static void S5PV210_write_addr(u32_t addr); static u8_t S5PV210_read_data(void); static void nand_read_buf(u8_t *buf, s32_t size); static void nand_write_buf(u8_t *buf, s32_t size); /* S5PV210的NAND Flash操作函数 */ /* 复位 */ static void S5PV210_nand_reset(void) { S5PV210_nand_select_chip(); udelay(10); S5PV210_write_cmd(0xFF); // 复位命令 udelay(100); S5PV210_wait_idle(); S5PV210_nand_deselect_chip(); udelay(100); } /* 等待NAND Flash就绪 */ static void S5PV210_wait_idle(void) { while((S5PV210_nand->NFSTAT & 0x00000001) == 0x00){ udelay(1); //延时1us } /* * 这里如果换成 while((S5PV210NAND_NFSTAT & 0x00000001) == 0x00);就不需要加延时 * 百思不得骑姐 */ } /* 发出片选信号 */ static void S5PV210_nand_select_chip(void) { S5PV210NAND_NFCONT &= ~(1 << 1); } /* 取消片选信号 */ static void S5PV210_nand_deselect_chip(void) { S5PV210NAND_NFCONT |= (1 << 1); } /* 发出命令 */ static void S5PV210_write_cmd(u32_t cmd) { S5PV210NAND_NFCMMD = cmd; } /* 发出地址 */ static void S5PV210_write_addr(u32_t addr) { u32_t col = addr % PAGE_SIZE; /* 页内偏移 *addr - addr >>12*/ u32_t row = addr / PAGE_SIZE; /* 页地址 */ S5PV210NAND_NFADDR = col & 0xFF; S5PV210NAND_NFADDR = (col >> 8) & 0xF; S5PV210NAND_NFADDR = row & 0xFF; S5PV210NAND_NFADDR = (row >> 8) & 0xFF; S5PV210NAND_NFADDR = (row >> 16) & 0x07; } /* 读取1字节数据 */ static u8_t S5PV210_read_data(void) { return S5PV210NAND_NFDATA; } /* 写1byte数据 */ static void inline S5PV210_nand_write(u8_t data) { S5PV210NAND_NFDATA = data; } /* 读取数据 */ void S5PV210_read_id(u8_t id[]) { int i; S5PV210_nand_select_chip(); S5PV210_write_cmd(0x90); S5PV210NAND_NFADDR = 0x00; for (i = 0; i < 5; i++) { id = S5PV210_read_data(); } S5PV210_nand_deselect_chip(); } /* 初始化NAND Flash */ void nand_init(void) { /* 设置时序 */ S5PV210NAND_NFCONF = (0x01 << 23) | //禁用ECC (TACLS<<12) | (TWRPH0<<8) | (TWRPH1<<4) | (0x00<<3) | //0:SLC NAND (0x01<<2) | //1:2kB/page (AddrCycle<<1); //1:addrcycle为5 /* 使能NAND Flash控制器, 初始化ECC, 禁止片选 */ S5PV210NAND_NFCONT = (1<<1) | (1<<0); //引脚初始化 S5PV210NAND_MP0_1CON &= ~(0xFFFF << 8); S5PV210NAND_MP0_1CON |= (0x3333 << 8); S5PV210NAND_MP0_3CON = 0x22222222; S5PV210NAND_MP0_6CON = 0x22222222; /* 复位NAND Flash */ S5PV210_nand_reset();; } /* 读size byte数据 */ static void nand_read_buf(u8_t *buf, s32_t size) { int i; for (i = 0;i < size; i++) buf = S5PV210NAND_NFDATA; } /* 写size byte数据 */ static void nand_write_buf(u8_t *buf, s32_t size) { int i; for (i = 0; i < size; i++) S5PV210NAND_NFDATA = buf; } /* 擦除一个块 */ void nand_erase(u32_t addr) { if (addr & (BLOCK_SIZE - 1)) { serial_printf(0,"not block align\n"); return; } u32_t row = addr / PAGE_SIZE; S5PV210_nand_select_chip(); S5PV210_write_cmd(0x60); S5PV210NAND_NFADDR = row & 0xff; S5PV210NAND_NFADDR = (row >> 8) & 0xff; S5PV210NAND_NFADDR = (row >> 16) & 0x07; S5PV210_write_cmd(0xD0); S5PV210_wait_idle(); S5PV210_nand_deselect_chip(); } /* 读一页数据 */ void nand_read_page(u8_t *buf, u32_t addr) { if (addr & (PAGE_SIZE - 1)) { serial_printf(0,"not page align\n"); return; } S5PV210_nand_select_chip(); S5PV210_write_cmd(0); S5PV210_write_addr(addr); S5PV210_write_cmd(0x30); S5PV210_wait_idle(); nand_read_buf(buf, PAGE_SIZE); S5PV210_nand_deselect_chip(); } /* 随机读:从任意地址读任意字节的数据 */ void nand_read_random(u8_t *buf, u32_t addr, u32_t size) { u32_t i; S5PV210_nand_select_chip(); S5PV210_write_cmd(0); S5PV210_write_addr(addr); S5PV210_write_cmd(0x30); S5PV210_wait_idle(); u32_t col = addr % PAGE_SIZE; /* 页内偏移 */ for(i = col; i < size + col; i++) { S5PV210_write_cmd(0x05); S5PV210NAND_NFADDR = i & 0xFF; S5PV210NAND_NFADDR = (i >> 8) & 0xF; S5PV210_write_cmd(0xE0); *buf++ = S5PV210_read_data(); } S5PV210_nand_deselect_chip(); } /* 写一页数据 */ void nand_write_page(u8_t *buf, u32_t addr) { if (addr & (PAGE_SIZE - 1)) { serial_printf(0,"not page align\n"); return; } S5PV210_nand_select_chip(); S5PV210_write_cmd(0x80); S5PV210_write_addr(addr); S5PV210_wait_idle(); nand_write_buf(buf, PAGE_SIZE); S5PV210_write_cmd(0x10); S5PV210_wait_idle(); S5PV210_nand_deselect_chip(); } [/mw_shl_code]

main.c文件
[mw_shl_code=c,true]#include <main.h> extern int tester_serial_stdio(int argc, char * argv[]); static void do_system_initial(void) { malloc_init(); s5pv210_clk_initial(); s5pv210_irq_initial(); s5pv210_tick_initial(); s5pv210_tick_delay_initial(); s5pv210_serial_initial(); //s5pv210_fb_initial(); led_initial(); //beep_initial(); //key_initial(); } void bzero(u8_t *s, s32_t size) { int i = 0; for (; i < size; i++) s = 0; } int main(int argc, char * argv[]) { u8_t buf[2048]; u32_t i; bzero(buf, 2048); do_system_initial(); nand_init(); S5PV210_read_id(buf); serial_printf(0,"\nID:"); for (i = 0; i < 5; i++) { serial_printf(0,"i%d= %X ", i,buf); } serial_printf(0,"\n"); serial_printf(0,"start erase\n"); nand_erase(0x4000000); /* 擦除以0x4000000地址开始的一个块 */ for (i = 0; i < 2048; i++) buf = i & 0xFF; //初始化buf serial_printf(0,"start write\n"); nand_write_page(buf, 0x4000000); serial_printf(0,"write sucessful!\n"); bzero(buf, 2048); //buf清零 serial_printf(0,"start read\n");//读出 nand_read_page(buf, 0x4000000); //读出nandflash数据到buff //nand_read_random(buf, 0x0000000, 2048); //打印读出的buf数据 while(1){ for (i = 0; i < 2048; i++) { if (i % 16 == 0) { serial_printf(0,"\n i = %d\n",i); led_set_status(LED_NAME_LED1,LED_STATUS_ON); mdelay(500); led_set_status(LED_NAME_LED1,LED_STATUS_OFF); mdelay(500); } serial_printf(0,"%X ", buf); } } return 0; } [/mw_shl_code]
实验截图:


 



2014.10.5 更新:
修正S5PV210_nand 的定义方法,修正忙检测函数。
实际上S5PV210_nand 不应该定义成一个变量,而应使用宏定义的方式,如:

#define S5PV210NAND ((S5PV210NAND_TypeDef *) S5PV210NAND_BASE)

我是参考STM32的库源码才发现的,忙检测之前的错误实际上不是因为延时的问题,而是我指针用错了(其实是仿照韦东山的代码。。。),具体代码不去深究了。因此忙检测函数修改为
[mw_shl_code=c,true]/* 等待NAND Flash就绪 */ static void inline S5PV210_wait_idle(void) { while( ((S5PV210NAND->NFSTAT) & 0x01) == 0x00); }[/mw_shl_code]
在C文件中删去结构体变量S5PV210_nand的定义(寄存器地址定义成变量,多浪费啊。。。)
头文件加入S5PV210NAND的定义

#define S5PV210NAND_BASE ((u32_t) 0xB0E00000)
#define S5PV210NAND ((S5PV210NAND_TypeDef *) S5PV210NAND_BASE) 

 
正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165540
金钱
165540
注册时间
2010-12-1
在线时间
2117 小时
发表于 2014-10-2 22:17:46 | 显示全部楼层
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2025-6-30 18:00

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表