平台: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)
|