S5PV210裸机(七):Nand和iNand

编程入门 行业动态 更新时间:2024-10-10 00:26:57

S5PV210<a href=https://www.elefans.com/category/jswz/34/1762357.html style=裸机(七):Nand和iNand"/>

S5PV210裸机(七):Nand和iNand

        本文主要探讨210Nand和iNand相关知识。

NandFlash
        型号与命
                
K9F2G08:K9F为发行商,2G为Nand大小是2Gbit(256MB),08为Nand是8位(8数据线即接口为8位:传输数据,地址,命令)

        功能
                
Nand是矩阵式存储,每块可存1bit位
                Nand单次访问最小单元为Page,Page大小是2KB+64B,每次读写n*2KB,random read模式可读1字节
                210中 1 block = 64 page,1 Device = 2028 block = 2048 × 64 × 2K = 256MB
                Page是读写最小单位,Block是擦除最小单位
                Nand芯片:Nand存储 + Nand接口
                Nand读写时地址通过IO线发送,地址有30位而IO只有8位,需要多个cycle才能完成
                SoC通过发送命令、地址、数据等信给Nand控制器来访问Nand(接口)

                 I/O8~15用在X16设备且仅用于输入命令和地址,用于数据输入和接口输出

        ECC(软件或硬件寄存器)
                2KB存储数据,64用于管理功能(存储ECC数据、存储坏块标志···),
                读取或写入前后会做校验,校验不通过则证明已损坏,会将坏块信息存储信息到Nand时会产生        

                ECC(校验信息)存储到64字节内,下次读写跳过坏块

        Nand操作流程
              
  坏块检查
                        nand使用前要擦除(块单位),擦除完后全是1,擦除后检测是否为0xff可知是否为坏块

                页写
                        写前需要擦除
                SoC写Flash通过命令线在IO线依次发送写入开始命令、要写入的页地址、要写入数据,写入结束命令
                SOC与Nand建立写入连接后,写入一页数据发给Nand接口,接口接收数据缓冲区,再写入Nand存储区
                nand接收和写入数据需要延时,通过状态寄存器判断是否写完,写完后再发送写入结束命令,再做ECC校验写入是否正常

        擦除
                擦除读写要发送对齐地址

        页读 

        210Nand相关寄存器

                gpio  

                nand configure 

 

 

demo1: 

                nand功能

nand.h

void nand_init(void);void nand_read_id(void);int nand_block_erase(unsigned long block_num);int copy_nand_to_sdram(unsigned char *sdram_addr, unsigned long nand_addr, unsigned long length);int copy_sdram_to_nand(unsigned char *sdram_addr, unsigned long nand_addr, unsigned long length);int nand_page_read(unsigned int pgaddr, unsigned char *buf, unsigned int length);int nand_page_write(unsigned int pgaddr, const unsigned char *buf, unsigned int length);int nand_random_read(unsigned long pgaddr,unsigned short offset, unsigned char *data);int nand_random_write(unsigned long pgaddr,unsigned short offset,unsigned char wrdata);

nand.c 

#include "nand.h"
#include "stdio.h"#define rNFCONF 			( *((volatile unsigned long *)0xB0E00000) )
#define rNFCONT 			( *((volatile unsigned long *)0xB0E00004) )
#define rNFCMMD 			( *((volatile unsigned long *)0xB0E00008) )
#define rNFADDR 			( *((volatile unsigned long *)0xB0E0000C) )
#define rNFDATA 			( *((volatile unsigned long *)0xB0E00010) )
#define rNFDATA8 			( *((volatile unsigned char *)0xB0E00010) )
#define rNFSTAT 			( *((volatile unsigned long *)0xB0E00028) )#define rMP0_1CON 			( *((volatile unsigned long *)0xE02002E0) )
#define rMP0_2CON 			( *((volatile unsigned long *)0xE0200300) )
#define rMP0_3CON 			( *((volatile unsigned long *)0xE0200320) )#define MAX_NAND_BLOCK  			  8192 			
#define NAND_PAGE_SIZE  			  2048 			
#define NAND_BLOCK_SIZE 			  64  			//dealy time(12ns)
#define TACLS    					  1				
#define TWRPH0   					  4
#define TWRPH1   					  1//command
#define NAND_CMD_READ_1st             0x00			
#define NAND_CMD_READ_2st             0x30#define NAND_CMD_READ_CB_1st          0x00
#define NAND_CMD_READ_CB_2st          0x35#define NAND_CMD_RANDOM_WRITE         0x85
#define NAND_CMD_RANDOM_READ_1st      0x05
#define NAND_CMD_RANDOM_READ_2st      0xe0#define NAND_CMD_READ_ID              0x90
#define NAND_CMD_RESET                0xff
#define NAND_CMD_READ_STATUS          0x70#define NAND_CMD_WRITE_PAGE_1st       0x80
#define NAND_CMD_WRITE_PAGE_2st       0x10#define NAND_CMD_BLOCK_ERASE_1st      0x60
#define NAND_CMD_BLOCK_ERASE_2st      0xd0#define ECC_EN						  (1<<4)
#define CONTROL_EN					  (1<<0)static void nand_reset(void);
static void nand_wait_idle(void);
static void nand_select_chip(void);
static void nand_deselect_chip(void);
static void nand_send_cmd(unsigned long cmd);
static void nand_send_addr(unsigned long addr);
static unsigned char nand_read8(void);
static void nand_write8(unsigned char data);
static unsigned int nand_read32(void);
static void nand_write32(unsigned int data);typedef struct nand_id_info
{//marker codeunsigned char IDm; //device codeunsigned char IDd; unsigned char ID3rd;unsigned char ID4th;unsigned char ID5th;
}nand_id_info;//reset  
void nand_reset(void)
{nand_select_chip();nand_send_cmd(NAND_CMD_RESET);nand_wait_idle();nand_deselect_chip();
}//waite busy or read status  over
void nand_wait_idle(void)
{unsigned long i;while( !(rNFSTAT & (1<<4)) )for(i=0; i<10; i++);
}//connect nand  
void nand_select_chip(void)
{unsigned long i;rNFCONT &= ~(1<<1);for(i=0; i<10; i++);
}//disconnect nand  
void nand_deselect_chip(void)
{unsigned long i = 0;rNFCONT |= (1<<1);for(i=0; i<10; i++);
}//send command 
void nand_send_cmd(unsigned long cmd)
{unsigned long i = 0;rNFCMMD = cmd;for(i=0; i<10; i++);
}//send address  
void nand_send_addr(unsigned long addr)
{unsigned long i;unsigned long col, row;//address of interior page  col = addr % NAND_PAGE_SIZE;		//address of page  			row = addr / NAND_PAGE_SIZE;//write Column Address A0~A7  rNFADDR = col & 0xff;			for(i=0; i<10; i++);		//write  Column Address A8~A11  rNFADDR = (col >> 8) & 0x0f; 		for(i=0; i<10; i++);//write Row Address A12~A19	rNFADDR = row & 0xff;			for(i=0; i<10; i++);//write Row Address A20~A27	rNFADDR = (row >> 8) & 0xff;for(i=0; i<10; i++);//write Row Address A28~A30(A28,low level)	rNFADDR = (row >> 16) & 0xff;for(i=0; i<10; i++);
}//read nand(word)
unsigned int nand_read32(void)
{return rNFDATA;
}//write nand(word)
void nand_write32(unsigned int data)
{rNFDATA = data;
}//read nand(half word)  
unsigned char nand_read8(void)
{return rNFDATA8;
}//write nand(half word) 
void nand_write8(unsigned char data)
{rNFDATA8 = data;
}//get nand status
unsigned char nand_read_status(void)
{unsigned char ch;int i;//connect nand nand_select_chip();//get nand status  nand_send_cmd(NAND_CMD_READ_STATUS);for(i=0; i<10; i++);//read nandch = nand_read8();//disconnect nandnand_deselect_chip();return ch;
}// nand init
void nand_init(void)
{//set read nand signal dealy rNFCONF = (TACLS<<12)|(TWRPH0<<8)|(TWRPH1<<4)|(0<<3)|(0<<2)|(1<<1)|(0<<0);//set nand control,chip,set ECC,close read/busy status interrupt,close soft lockrNFCONT = (0<<18)|(0<<17)|(0<<16)|(0<<10)|(0<<9)|(0<<8)|(0<<7)|(0<<6)|(0x3<<1)|(1<<0);//set gpio nandflashrMP0_1CON = 0x22333322;rMP0_2CON = 0x00002222;rMP0_3CON = 0x22222222;//reset nand_reset();
}//get nand id 
void nand_read_id(void)
{nand_id_info nand_id;//connect nand nand_select_chip();//get nand id  nand_send_cmd(NAND_CMD_READ_ID);nand_send_addr(0x00);nand_wait_idle();nand_id.IDm = 	nand_read8();nand_id.IDd = 	nand_read8();nand_id.ID3rd = nand_read8();nand_id.ID4th = nand_read8();nand_id.ID5th = nand_read8();printf("nandflash: makercode = %x\r\n devicecode = %x\r\n ID3rd = %x\r\n ID4rd = %x\r\n ID5rd = %x\r\n", nand_id.IDm, nand_id.IDd, nand_id.ID3rd, nand_id.ID4th, nand_id.ID5th);//disconnect nandnand_deselect_chip();
}//earse the num of block(0 ~ MAX_NAND_BLOCK-1)  
int nand_block_erase(unsigned long block_num)
{unsigned long i = 0;//get the starting page address of block(block starting address)  unsigned long row = block_num * NAND_BLOCK_SIZE;//connect nand  nand_select_chip();//send earse command (0x60)  nand_send_cmd(NAND_CMD_BLOCK_ERASE_1st);//dealy timefor(i=0; i<10; i++);//set erase address//Row Address A12~A19	rNFADDR = row & 0xff;							for(i=0; i<10; i++);// Row Address A20~A27  rNFADDR = (row >> 8) & 0xff;for(i=0; i<10; i++);// Row Address A28~A30  rNFADDR = (row >> 16) & 0xff;	//clear RnB bit,aim to send second commandrNFSTAT |= (1<<4);			 //send second command(0xd0)nand_send_cmd(NAND_CMD_BLOCK_ERASE_2st);for(i=0; i<10; i++);//waite busy or read status  over nand_wait_idle();//judge read/busy status,0 earse success unsigned char status = nand_read_status();if (status & 1 ){//disconnect nandnand_deselect_chip();						printf("masking bad block %d\r\n", block_num);return -1;}else{nand_deselect_chip();return 0;}
}//erase the address of nand
int nand_erase(unsigned long block_addr)
{int i = 0;//judge write protect if((nand_read_status() & 0x80) == 0) {printf("Write protected.\n");return -1;}unsigned long row = block_addr >> 18;//connect nand  nand_select_chip();// send erase command(0x60)nand_send_cmd(NAND_CMD_BLOCK_ERASE_1st);//dealy timefor(i=0; i<10; i++);//set erase address// Row Address A12~A19	rNFADDR = row & 0xff;							for(i=0; i<10; i++);// Row Address A20~A27  rNFADDR = (row >> 8) & 0xff;for(i=0; i<10; i++);// Row Address A28~A30  rNFADDR = (row >> 16) & 0x01;	for(i=0; i<10; i++);//clear RnB bit,aim to send second commandrNFSTAT |= (1<<4);			//send second command(0xd0)nand_send_cmd(NAND_CMD_BLOCK_ERASE_2st);for(i=0; i<10; i++);//waite busy or read status  over nand_wait_idle();//judge erase ,0 sucess unsigned char status = nand_read_status();if (status & 1){nand_deselect_chip();						printf("masking bad block %d\r\n", block_addr);return -1;}else{nand_deselect_chip();return 0;}
}//copy nand to sdram 
int copy_nand_to_sdram(unsigned char *sdram_addr, unsigned long nand_addr, unsigned long length)
{unsigned long i = 0;//connect nandnand_select_chip();while(length){//send read command(0x00)nand_send_cmd(NAND_CMD_READ_1st);//send read starting addressnand_send_addr(nand_addr);//clear RnB bit,aim to send second commandrNFSTAT = (rNFSTAT)|(1<<4);//send second command (0x30)nand_send_cmd(NAND_CMD_READ_2st);//waite busy or read status  over nand_wait_idle();//get the address of start pageunsigned long col = nand_addr % NAND_PAGE_SIZE;i = col;//read one page,pre one time copy 1byte,need 2048 times,when length equal 0  over for(; i<NAND_PAGE_SIZE && length!=0; i++,length--){*sdram_addr = nand_read8();sdram_addr++;nand_addr++;}}//judge copy result  unsigned char status = nand_read_status();if (status & 1 ){nand_deselect_chip();printf("copy nand to sdram fail\r\n");return -1;}else{nand_deselect_chip();return 0;}
}//copy sdram to nand 
int copy_sdram_to_nand(unsigned char *sdram_addr, unsigned long nand_addr, unsigned long length)
{unsigned long i = 0;//connect nand nand_select_chip();while(length){//send write command(0x80) nand_send_cmd(NAND_CMD_WRITE_PAGE_1st);//send read starting addressnand_send_addr(nand_addr);//get the address of start page  unsigned long col = nand_addr % NAND_PAGE_SIZE;i = col;//write one page,pre one time write 1byte,need 2048 times,when length equal 0  overfor(; i<NAND_PAGE_SIZE && length!=0; i++,length--){nand_write8(*sdram_addr);sdram_addr++;nand_addr++;}//clear RnB bit,aim to send second commandrNFSTAT = (rNFSTAT)|(1<<4);//send second command (0x10)nand_send_cmd(NAND_CMD_WRITE_PAGE_2st);//waite busy or read status  overnand_wait_idle();}//judge copy result unsigned char status = nand_read_status();if (status & 1 ){nand_deselect_chip();printf("copy sdram to nand fail\r\n");return -1;}else{nand_deselect_chip();return 0;}
}//read bit on page
int nand_page_read(unsigned int pgaddr, unsigned char *buf, unsigned int length)
{int i = 0;//connect nand  nand_select_chip();//send read command(0x00) nand_send_cmd(NAND_CMD_READ_1st);//set read addressrNFADDR = 0;										rNFADDR = 0;rNFADDR = pgaddr&0xff;rNFADDR = (pgaddr>>8)&0xff;rNFADDR = (pgaddr>>16)&0xff;//clear RnB bit,aim to send second commandrNFSTAT |= (1<<4);//send second command(0x30) nand_send_cmd(NAND_CMD_READ_2st);//waite busy or read status  overnand_wait_idle();//read 2KB data area and 64  spare area(1byte)for (i=0; (i<NAND_PAGE_SIZE) && (length!=0); i++,length--)*buf++ = nand_read8();//judge read resultunsigned char status = nand_read_status();if (status & 1 ){  nand_deselect_chip();printf("nand random read fail\r\n");return -1;}else{nand_deselect_chip();return 0;}
}//read word on page
int nand_page_read32(unsigned int pgaddr, unsigned int *buf, unsigned int lengthB)
{int i = 0;//connect nandnand_select_chip();//send read command nand_send_cmd(NAND_CMD_READ_1st);//write read starting addressrNFADDR = 0;										rNFADDR = 0;rNFADDR = pgaddr&0xff;rNFADDR = (pgaddr>>8)&0xff;rNFADDR = (pgaddr>>16)&0xff;//clear RnB bit,aim to send second commandrNFSTAT |= (1<<4);//send second command(0x30) nand_send_cmd(NAND_CMD_READ_2st);//waite busy or read status  overnand_wait_idle();//read 2KB + 64B spare area(1 word)for (i=0; (i<NAND_PAGE_SIZE/4) && (lengthB!=0); i++,lengthB--)*buf++ = nand_read32();//judge read result unsigned char status = nand_read_status();if (status & 1 ){  nand_deselect_chip();printf("nand random read fail\r\n");return -1;}else{nand_deselect_chip();return 0;}
}//write page
int nand_page_write(unsigned int pgaddr, const unsigned char *buf, unsigned int length)
{int i = 0;//connect nandnand_select_chip();//send write command(0x80) nand_send_cmd(NAND_CMD_WRITE_PAGE_1st);//write start addressrNFADDR = 0;rNFADDR = 0;rNFADDR = pgaddr&0xff;rNFADDR = (pgaddr>>8)&0xff;rNFADDR = (pgaddr>>16)&0xff;//write page(1byte)for(; i<NAND_PAGE_SIZE && length!=0; i++,length--)nand_write8(*buf++);//clear RnB bit,aim to send second commandrNFSTAT = (rNFSTAT)|(1<<4);//sens second commandnand_send_cmd(NAND_CMD_WRITE_PAGE_2st);//wait busy or read status  overnand_wait_idle();//judge write result unsigned char status = nand_read_status();if (status & 1 ){nand_deselect_chip();printf("nand random write fail\r\n");return -1;}else{nand_deselect_chip();return 0;}}//read random 
int nand_random_read(unsigned long pgaddr,unsigned short offset, unsigned char *data)
{unsigned char readdata;//connect nand nand_select_chip();//send read command(0x00)nand_send_cmd(NAND_CMD_READ_1st);//write start addressrNFADDR = 0;										rNFADDR = 0;rNFADDR = pgaddr&0xff;rNFADDR = (pgaddr>>8)&0xff;rNFADDR = (pgaddr>>16)&0xff;//clear RnB bit,aim to send second commandrNFSTAT |= (1<<4);//second second commandnand_send_cmd(NAND_CMD_READ_2st);//wait busy or read status  overnand_wait_idle();//send read command(0x05)nand_send_cmd(NAND_CMD_RANDOM_READ_1st);//write the address of page that offset addressrNFADDR = offset&0xff; 							rNFADDR = (offset>>8)&0xff;//clear RnB bit,aim to send second command	rNFSTAT = (rNFSTAT)|(1<<4);//second second command(0xe0)nand_send_cmd(NAND_CMD_RANDOM_READ_2st);//read datareaddata = nand_read8();//judge read result  unsigned char status = nand_read_status();if (status & 1 ){nand_deselect_chip();printf("nand random read fail\r\n");return -1;}else{nand_deselect_chip();*data = readdata;return 0;}
}//random write
int nand_random_write(unsigned long pgaddr,unsigned short offset,unsigned char wrdata)
{//connect nandnand_select_chip();//send write command(0x80) nand_send_cmd(NAND_CMD_WRITE_PAGE_1st);//write start addressrNFADDR = 0;rNFADDR = 0;rNFADDR = pgaddr&0xff;rNFADDR = (pgaddr>>8)&0xff;rNFADDR = (pgaddr>>16)&0xff;//send write command (0x80)nand_send_cmd(NAND_CMD_RANDOM_WRITE);//write the address of page that offset addressrNFADDR = offset&0xff; 					rNFADDR = (offset>>8)&0xff;//write data(1byte)nand_write8(wrdata);//clear RnB bit,aim to send second commandrNFSTAT = (rNFSTAT)|(1<<4);//send second commandnand_send_cmd(NAND_CMD_WRITE_PAGE_2st);//wait busy or read status  overnand_wait_idle();//judge write result  unsigned char status = nand_read_status();if (status & 1 ){ nand_deselect_chip();printf("nand random write fail\r\n");return -1;}else{nand_deselect_chip();return 0;}
}

iNand
        iNand/eMMC/SDCard/MMCCard
                mmc对于nandflash的优势:卡片化便于拆装,接口协议Hmmc协议)统一便于封装
                SD卡(SD协议、SPI协议)兼容性好且有写保护、速率快、容量大

        iNand/eMMC与NandFlash
                iNand和nand内部由存储和接口组成
                iNand接口支持eMMC协议、ECC校验,使用ML,有cache 机制,速度快
                NandFlash:SLC 更稳定,容量小价格高;MLC易出错容量大价格低

        SD/iNand/eMMC
                210是4通道SD/MMC,SD/MMC0接iNand,SD/MMC2-3接SD卡
                SD卡是4IO线支持1、4、8线并行传输,iNand是8IO线支持1、4线并行传输
                硬件接口:DATA(数据)、CLK(时钟,25Mhz)、CMD(命令)
                SD协议是多标准命令,命令有使用条件和响应,少数命令没有响应
                SD卡命令是周期性组合(组合命令),在命令周期中,发送CMD到SD卡,SD卡执行命令并且返回响应,主机接收响应后再发下一条
                CMD和ACMD:SD协议命令:CMDx、ACMDx;CMD是单命令,ACMDy = CMDy + CMDz(y一般是55)
                SD卡处于某种状态(空闲状态、准备好状态、读写状态、出错状态····),接受某种执行命令(跳转命令)并返回响应,跳转到另一状态

 

 

 

       210SD/iNand寄存器
                RCA(相对地址寄存器)

 

                gpio

 

 

         

      

                设置时钟 

 

 

             状态寄存器(命令,写入,读取,中断...)   

 

                cmd

 

 

 

                命令参数

 

                错误状态 

 

 

                设置每次读取block大小 

 

                设置每次读取block数量 

 

                配置数据传输 

                命令控制寄存器 

                

                32位数据端口寄存器访问内部缓冲区

                sd响应

                设置中断状态

                sd命令参数 

                设置软件功能(是SD不是soc)

                设置超时

                设置错误中断状态

demo2:

                sd/emmc功能

Hsmmc.h

#ifndef __HSMMC_H__
#define __HSMMC_H__#ifdef __cplusplus
extern "C" {
#endif#include "stdint.h"// SD协议规定的命令码	
#define	CMD0	0
#define	CMD1	1
#define	CMD2	2	
#define	CMD3	3	
#define	CMD6	6
#define	CMD7	7
#define	CMD8	8
#define	CMD9	9
#define	CMD13	13
#define	CMD16	16
#define	CMD17	17
#define	CMD18	18
#define	CMD23	23	
#define	CMD24	24
#define	CMD25	25	
#define	CMD32	32	
#define	CMD33	33	
#define	CMD38	38	
#define	CMD41	41	
#define CMD51	51
#define	CMD55	55	// 卡类型
#define UNUSABLE		0
#define SD_V1			1	
#define	SD_V2			2	
#define	SD_HC			3
#define	MMC				4// 卡状态
#define CARD_IDLE		0			// 空闲态
#define CARD_READY		1			// 准备好
#define CARD_IDENT		2
#define CARD_STBY		3
#define CARD_TRAN		4
#define CARD_DATA		5
#define CARD_RCV		6
#define CARD_PRG		7			// 卡编程状态
#define CARD_DIS		8			// 断开连接// 卡回复类型	
#define CMD_RESP_NONE	0			// 无回复
#define CMD_RESP_R1		1
#define CMD_RESP_R2		2
#define CMD_RESP_R3		3
#define CMD_RESP_R4		4
#define CMD_RESP_R5		5
#define CMD_RESP_R6		6
#define CMD_RESP_R7		7
#define CMD_RESP_R1B	8typedef struct {uint32_t RESERVED1;uint32_t RESERVED2 : 16;	uint32_t SD_BUS_WIDTHS : 4;uint32_t SD_SECURITY : 3;	uint32_t DATA_STAT_AFTER_ERASE : 1;uint32_t SD_SPEC : 4;	uint32_t SCR_STRUCTURE : 4;
} SD_SCR;	int32_t Hsmmc_Init(void);
int32_t Hsmmc_GetCardState(void);
int32_t Hsmmc_GetSdState(uint8_t *pStatus);
int32_t Hsmmc_Get_SCR(SD_SCR *pSCR);
int32_t Hsmmc_Get_CSD(uint8_t *pCSD);
int32_t Hsmmc_EraseBlock(uint32_t StartBlock, uint32_t EndBlock);
int32_t Hsmmc_WriteBlock(uint8_t *pBuffer, uint32_t BlockAddr, uint32_t BlockNumber);
int32_t Hsmmc_ReadBlock(uint8_t *pBuffer, uint32_t BlockAddr, uint32_t BlockNumber);#ifdef __cplusplus
}
#endif#endif /*__HSMMC_H__*/

Hsmmc.c

#include "ProjectConfig.h"
#include "Hsmmc.h"#define HSMMC_NUM		2#if (HSMMC_NUM == 0)
#define HSMMC_BASE	(0xEB000000)
#elif (HSMMC_NUM == 1)
#define HSMMC_BASE	(0xEB100000)
#elif (HSMMC_NUM == 2)
#define HSMMC_BASE	(0xEB200000)
#elif (HSMMC_NUM == 3)
#define HSMMC_BASE	(0xEB300000)
#else
#error "Configure HSMMC: HSMMC0 ~ HSMM3(0 ~ 3)"
#endif#define MAX_BLOCK  65535#define SWRST_OFFSET 0x2Fstatic uint8_t CardType; // 卡类型
static uint32_t RCA; // 卡相对地址static void Hsmmc_ClockOn(uint8_t On)
{uint32_t Timeout;if (On) {__REGw(HSMMC_BASE+CLKCON_OFFSET) |= (1<<2); // sd时钟使能Timeout = 1000; // Wait max 10 mswhile (!(__REGw(HSMMC_BASE+CLKCON_OFFSET) & (1<<3))) {// 等待SD输出时钟稳定if (Timeout == 0) {return;}Timeout--;Delay_us(10);}} else {__REGw(HSMMC_BASE+CLKCON_OFFSET) &= ~(1<<2); // sd时钟禁止}
}static void Hsmmc_SetClock(uint32_t Clock)
{uint32_t Temp;uint32_t Timeout;uint32_t i;Hsmmc_ClockOn(0); // 关闭时钟	Temp = __REG(HSMMC_BASE+CONTROL2_OFFSET);// Set SCLK_MMC(48M) from SYSCON as a clock source	Temp = (Temp & (~(3<<4))) | (2<<4);Temp |= (1u<<31) | (1u<<30) | (1<<8);if (Clock <= 500000) {Temp &= ~((1<<14) | (1<<15));__REG(HSMMC_BASE+CONTROL3_OFFSET) = 0;} else {Temp |= ((1<<14) | (1<<15));__REG(HSMMC_BASE+CONTROL3_OFFSET) = (1u<<31) | (1<<23);}__REG(HSMMC_BASE+CONTROL2_OFFSET) = Temp;for (i=0; i<=8; i++) {if (Clock >= (48000000/(1<<i))) {break;}}Temp = ((1<<i) / 2) << 8; // clock divTemp |= (1<<0); // Internal Clock Enable__REGw(HSMMC_BASE+CLKCON_OFFSET) = Temp;Timeout = 1000; // Wait max 10 mswhile (!(__REGw(HSMMC_BASE+CLKCON_OFFSET) & (1<<1))) {// 等待内部时钟振荡稳定if (Timeout == 0) {return;}Timeout--;Delay_us(10);}Hsmmc_ClockOn(1); // 使能时钟
}static int32_t Hsmmc_WaitForBufferReadReady(void)
{	int32_t ErrorState;while (1) {if (__REGw(HSMMC_BASE+NORINTSTS_OFFSET) & (1<<15)) { // 出现错误break;}if (__REGw(HSMMC_BASE+NORINTSTS_OFFSET) & (1<<5)) { // 读缓存准备好__REGw(HSMMC_BASE+NORINTSTS_OFFSET) |= (1<<5); // 清除准备好标志return 0;}				}ErrorState = __REGw(HSMMC_BASE+ERRINTSTS_OFFSET) & 0x1ff; // 可能通信错误,CRC检验错误,超时等__REGw(HSMMC_BASE+NORINTSTS_OFFSET) = __REGw(HSMMC_BASE+NORINTSTS_OFFSET); // 清除中断标志	__REGw(HSMMC_BASE+ERRINTSTS_OFFSET) = __REGw(HSMMC_BASE+ERRINTSTS_OFFSET); // 清除错误中断标志Debug("Read buffer error, NORINTSTS: %04x\r\n", ErrorState);return ErrorState;
}static int32_t Hsmmc_WaitForBufferWriteReady(void)
{int32_t ErrorState;while (1) {if (__REGw(HSMMC_BASE+NORINTSTS_OFFSET) & (1<<15)) { // 出现错误break;}if (__REGw(HSMMC_BASE+NORINTSTS_OFFSET) & (1<<4)) { // 写缓存准备好__REGw(HSMMC_BASE+NORINTSTS_OFFSET) |= (1<<4); // 清除准备好标志return 0;}				}ErrorState = __REGw(HSMMC_BASE+ERRINTSTS_OFFSET) & 0x1ff; // 可能通信错误,CRC检验错误,超时等__REGw(HSMMC_BASE+NORINTSTS_OFFSET) = __REGw(HSMMC_BASE+NORINTSTS_OFFSET); // 清除中断标志__REGw(HSMMC_BASE+ERRINTSTS_OFFSET) = __REGw(HSMMC_BASE+ERRINTSTS_OFFSET); // 清除错误中断标志Debug("Write buffer error, NORINTSTS: %04x\r\n", ErrorState);return ErrorState;
}static int32_t Hsmmc_WaitForCommandDone(void)
{uint32_t i;	int32_t ErrorState;// 等待命令发送完成for (i=0; i<0x20000000; i++) {if (__REGw(HSMMC_BASE+NORINTSTS_OFFSET) & (1<<15)) { // 出现错误break;}if (__REGw(HSMMC_BASE+NORINTSTS_OFFSET) & (1<<0)) {do {__REGw(HSMMC_BASE+NORINTSTS_OFFSET) = (1<<0); // 清除命令完成位} while (__REGw(HSMMC_BASE+NORINTSTS_OFFSET) & (1<<0));				return 0; // 命令发送成功}}ErrorState = __REGw(HSMMC_BASE+ERRINTSTS_OFFSET) & 0x1ff; // 可能通信错误,CRC检验错误,超时等__REGw(HSMMC_BASE+NORINTSTS_OFFSET) = __REGw(HSMMC_BASE+NORINTSTS_OFFSET); // 清除中断标志__REGw(HSMMC_BASE+ERRINTSTS_OFFSET) = __REGw(HSMMC_BASE+ERRINTSTS_OFFSET); // 清除错误中断标志	do {__REGw(HSMMC_BASE+NORINTSTS_OFFSET) = (1<<0); // 清除命令完成位} while (__REGw(HSMMC_BASE+NORINTSTS_OFFSET) & (1<<0));Debug("Command error, ERRINTSTS = 0x%x ", ErrorState);	return ErrorState; // 命令发送出错	
}static int32_t Hsmmc_WaitForTransferDone(void)
{int32_t ErrorState;uint32_t i;// 等待数据传输完成for (i=0; i<0x20000000; i++) {if (__REGw(HSMMC_BASE+NORINTSTS_OFFSET) & (1<<15)) { // 出现错误break;}											if (__REGw(HSMMC_BASE+NORINTSTS_OFFSET) & (1<<1)) { // 数据传输完								do {__REGw(HSMMC_BASE+NORINTSTS_OFFSET) |= (1<<1); // 清除传输完成位} while (__REGw(HSMMC_BASE+NORINTSTS_OFFSET) & (1<<1));									return 0;}}ErrorState = __REGw(HSMMC_BASE+ERRINTSTS_OFFSET) & 0x1ff; // 可能通信错误,CRC检验错误,超时等__REGw(HSMMC_BASE+NORINTSTS_OFFSET) = __REGw(HSMMC_BASE+NORINTSTS_OFFSET); // 清除中断标志__REGw(HSMMC_BASE+ERRINTSTS_OFFSET) = __REGw(HSMMC_BASE+ERRINTSTS_OFFSET); // 清除错误中断标志Debug("Transfer error, rHM1_ERRINTSTS = 0x%04x\r\n", ErrorState);	do {__REGw(HSMMC_BASE+NORINTSTS_OFFSET) = (1<<1); // 出错后清除数据完成位} while (__REGw(HSMMC_BASE+NORINTSTS_OFFSET) & (1<<1));return ErrorState; // 数据传输出错		
}static int32_t Hsmmc_IssueCommand(uint8_t Cmd, uint32_t Arg, uint8_t Data, uint8_t Response)
{uint32_t i;uint32_t Value;uint32_t ErrorState;// 检查CMD线是否准备好发送命令for (i=0; i<0x1000000; i++) {if (!(__REG(HSMMC_BASE+PRNSTS_OFFSET) & (1<<0))) {break;}}if (i == 0x1000000) {Debug("CMD line time out, PRNSTS: %04x\r\n", __REG(HSMMC_BASE+PRNSTS_OFFSET));return -1; // 命令超时}// 检查DAT线是否准备好if (Response == CMD_RESP_R1B) { // R1b回复通过DAT0反馈忙信号for (i=0; i<0x10000000; i++) {if (!(__REG(HSMMC_BASE+PRNSTS_OFFSET) & (1<<1))) {break;}		}if (i == 0x10000000) {Debug("Data line time out, PRNSTS: %04x\r\n", __REG(HSMMC_BASE+PRNSTS_OFFSET));			return -2;}		}__REG(HSMMC_BASE+ARGUMENT_OFFSET) = Arg; // 写入命令参数Value = (Cmd << 8); // command index// CMD12可终止传输if (Cmd == 0x12) {Value |= (0x3 << 6); // command type}if (Data) {Value |= (1 << 5); // 需使用DAT线作为传输等}	switch (Response) {case CMD_RESP_NONE:Value |= (0<<4) | (0<<3) | 0x0; // 没有回复,不检查命令及CRCbreak;case CMD_RESP_R1:case CMD_RESP_R5:case CMD_RESP_R6:case CMD_RESP_R7:		Value |= (1<<4) | (1<<3) | 0x2; // 检查回复中的命令,CRCbreak;case CMD_RESP_R2:Value |= (0<<4) | (1<<3) | 0x1; // 回复长度为136位,包含CRCbreak;case CMD_RESP_R3:case CMD_RESP_R4:Value |= (0<<4) | (0<<3) | 0x2; // 回复长度48位,不包含命令及CRCbreak;case CMD_RESP_R1B:Value |= (1<<4) | (1<<3) | 0x3; // 回复带忙信号,会占用Data[0]线break;default:break;	}__REGw(HSMMC_BASE+CMDREG_OFFSET) = Value;ErrorState = Hsmmc_WaitForCommandDone();if (ErrorState) {Debug("Command = %d\r\n", Cmd);}	return ErrorState; // 命令发送出错
}int32_t Hsmmc_Switch(uint8_t Mode, int32_t Group, int32_t Function, uint8_t *pStatus)
{int32_t ErrorState;int32_t Temp;uint32_t i;__REGw(HSMMC_BASE+BLKSIZE_OFFSET) = (7<<12) | (64<<0); // 最大DMA缓存大小,block为512位64字节			__REGw(HSMMC_BASE+BLKCNT_OFFSET) = 1; // 写入这次读1 block的sd状态数据Temp = (Mode << 31U) | 0xffffff;Temp &= ~(0xf<<(Group * 4));Temp |= Function << (Group * 4);__REG(HSMMC_BASE+ARGUMENT_OFFSET) = Temp; // 写入命令参数	// DMA禁能,读单块		__REGw(HSMMC_BASE+TRNMOD_OFFSET) = (0<<5) | (1<<4) | (0<<2) | (1<<1) | (0<<0);	// 设置命令寄存器,SWITCH_FUNC CMD6,R1回复__REGw(HSMMC_BASE+CMDREG_OFFSET) = (CMD6<<8)|(1<<5)|(1<<4)|(1<<3)|0x2;ErrorState = Hsmmc_WaitForCommandDone();if (ErrorState) {Debug("CMD6 error\r\n");return ErrorState;}ErrorState = Hsmmc_WaitForBufferReadReady();if (ErrorState) {return ErrorState;}pStatus += 64 - 1;for (i=0; i<64/4; i++) {Temp = __REG(HSMMC_BASE+BDATA_OFFSET);*pStatus-- = (uint8_t)Temp;*pStatus-- = (uint8_t)(Temp>>8);*pStatus-- = (uint8_t)(Temp>>16);*pStatus-- = (uint8_t)(Temp>>24);			}ErrorState = Hsmmc_WaitForTransferDone();if (ErrorState) {Debug("Get sd status error\r\n");return ErrorState;}	return 0;	
}// 512位的sd卡扩展状态位
int32_t Hsmmc_GetSdState(uint8_t *pStatus)
{int32_t ErrorState;uint32_t Temp;uint32_t i;if (CardType == SD_HC || CardType == SD_V2 || CardType == SD_V1) {if (Hsmmc_GetCardState() != CARD_TRAN) { // 必需在transfer statusreturn -1; // 卡状态错误}		Hsmmc_IssueCommand(CMD55, RCA<<16, 0, CMD_RESP_R1);__REGw(HSMMC_BASE+BLKSIZE_OFFSET) = (7<<12) | (64<<0); // 最大DMA缓存大小,block为512位64字节			__REGw(HSMMC_BASE+BLKCNT_OFFSET) = 1; // 写入这次读1 block的sd状态数据__REG(HSMMC_BASE+ARGUMENT_OFFSET) = 0; // 写入命令参数	// DMA禁能,读单块		__REGw(HSMMC_BASE+TRNMOD_OFFSET) = (0<<5) | (1<<4) | (0<<2) | (1<<1) | (0<<0);	// 设置命令寄存器,读状态命令CMD13,R1回复__REGw(HSMMC_BASE+CMDREG_OFFSET) = (CMD13<<8)|(1<<5)|(1<<4)|(1<<3)|0x2;ErrorState = Hsmmc_WaitForCommandDone();if (ErrorState) {Debug("CMD13 error\r\n");return ErrorState;}ErrorState = Hsmmc_WaitForBufferReadReady();if (ErrorState) {return ErrorState;}pStatus += 64 - 1;for (i=0; i<64/4; i++) {Temp = __REG(HSMMC_BASE+BDATA_OFFSET);*pStatus-- = (uint8_t)Temp;*pStatus-- = (uint8_t)(Temp>>8);*pStatus-- = (uint8_t)(Temp>>16);*pStatus-- = (uint8_t)(Temp>>24);			}ErrorState = Hsmmc_WaitForTransferDone();if (ErrorState) {Debug("Get sd status error\r\n");return ErrorState;}return 0;}return -1; // 非sd卡
}// Reads the SD Configuration Register (SCR).
int32_t Hsmmc_Get_SCR(SD_SCR *pSCR)
{uint8_t *pBuffer;int32_t ErrorState;uint32_t Temp;uint32_t i;Hsmmc_IssueCommand(CMD55, RCA<<16, 0, CMD_RESP_R1); __REGw(HSMMC_BASE+BLKSIZE_OFFSET) = (7<<12) | (8<<0); // 最大DMA缓存大小,block为64位8字节			__REGw(HSMMC_BASE+BLKCNT_OFFSET) = 1; // 写入这次读1 block的sd状态数据__REG(HSMMC_BASE+ARGUMENT_OFFSET) = 0; // 写入命令参数	// DMA禁能,读单块		__REGw(HSMMC_BASE+TRNMOD_OFFSET) = (0<<5) | (1<<4) | (0<<2) | (1<<1) | (0<<0);	// 设置命令寄存器,read SD Configuration CMD51,R1回复__REGw(HSMMC_BASE+CMDREG_OFFSET) = (CMD51<<8)|(1<<5)|(1<<4)|(1<<3)|0x2;ErrorState = Hsmmc_WaitForCommandDone();if (ErrorState) {Debug("CMD51 error\r\n");return ErrorState;}ErrorState = Hsmmc_WaitForBufferReadReady();if (ErrorState) {return ErrorState;}// Wide width data (SD Memory Register)pBuffer = (uint8_t *)pSCR + sizeof(SD_SCR) - 1;for (i=0; i<8/4; i++) {Temp = __REG(HSMMC_BASE+BDATA_OFFSET);*pBuffer-- = (uint8_t)Temp;*pBuffer-- = (uint8_t)(Temp>>8);*pBuffer-- = (uint8_t)(Temp>>16);*pBuffer-- = (uint8_t)(Temp>>24);		}ErrorState = Hsmmc_WaitForTransferDone();if (ErrorState) {Debug("Get SCR register error\r\n");return ErrorState;}return 0;
}// Asks the selected card to send its cardspecific data
int32_t Hsmmc_Get_CSD(uint8_t *pCSD)
{uint32_t i;uint32_t Response[4];int32_t State = -1;if (CardType != SD_HC && CardType != SD_V1 && CardType != SD_V2) {return State; // 未识别的卡}// 取消卡选择,任何卡均不回复,已选择的卡通过RCA=0取消选择,// 卡回到stand-by状态Hsmmc_IssueCommand(CMD7, 0, 0, CMD_RESP_NONE);for (i=0; i<1000; i++) {if (Hsmmc_GetCardState() == CARD_STBY) { // CMD9命令需在standy-by status				break; // 状态正确}Delay_us(100);}if (i == 1000) {return State; // 状态错误}	// 请求已标记卡发送卡特定数据(CSD),获得卡信息if (!Hsmmc_IssueCommand(CMD9, RCA<<16, 0, CMD_RESP_R2)) {pCSD++; // 跳过第一字节,CSD中[127:8]位对位寄存器中的[119:0]Response[0] = __REG(HSMMC_BASE+RSPREG0_OFFSET);Response[1] = __REG(HSMMC_BASE+RSPREG1_OFFSET);Response[2] = __REG(HSMMC_BASE+RSPREG2_OFFSET);Response[3] = __REG(HSMMC_BASE+RSPREG3_OFFSET);	for (i=0; i<15; i++) { // 拷贝回复寄存器中的[119:0]到pCSD中*pCSD++ = ((uint8_t *)Response)[i];}	State = 0; // CSD获取成功}Hsmmc_IssueCommand(CMD7, RCA<<16, 0, CMD_RESP_R1); // 选择卡,卡回到transfer状态return State;
}// R1回复中包含了32位的card state,卡识别后,可在任一状态通过CMD13获得卡状态
int32_t Hsmmc_GetCardState(void)
{if (Hsmmc_IssueCommand(CMD13, RCA<<16, 0, CMD_RESP_R1)) {return -1; // 卡出错} else {return ((__REG(HSMMC_BASE+RSPREG0_OFFSET)>>9) & 0xf); // 返回R1回复中的[12:9]卡状态}
}static int32_t Hsmmc_SetBusWidth(uint8_t Width)
{int32_t State;if ((Width != 1) || (Width != 4)) {return -1;}State = -1; // 设置初始为未成功__REGw(HSMMC_BASE+NORINTSTSEN_OFFSET) &= ~(1<<8); // 关闭卡中断Hsmmc_IssueCommand(CMD55, RCA<<16, 0, CMD_RESP_R1);if (Width == 1) {if (!Hsmmc_IssueCommand(CMD6, 0, 0, CMD_RESP_R1)) { // 1位宽__REGb(HSMMC_BASE+HOSTCTL_OFFSET) &= ~(1<<1);State = 0; // 命令成功}} else {if (!Hsmmc_IssueCommand(CMD6, 2, 0, CMD_RESP_R1)) { // 4位宽__REGb(HSMMC_BASE+HOSTCTL_OFFSET) |= (1<<1);State = 0; // 命令成功}}__REGw(HSMMC_BASE+NORINTSTSEN_OFFSET) |= (1<<8); // 打开卡中断	return State; // 返回0为成功
}int32_t Hsmmc_EraseBlock(uint32_t StartBlock, uint32_t EndBlock)
{uint32_t i;if (CardType == SD_V1 || CardType == SD_V2) {StartBlock <<= 9; // 标准卡为字节地址EndBlock <<= 9;} else if (CardType != SD_HC) {return -1; // 未识别的卡}	Hsmmc_IssueCommand(CMD32, StartBlock, 0, CMD_RESP_R1);Hsmmc_IssueCommand(CMD33, EndBlock, 0, CMD_RESP_R1);if (!Hsmmc_IssueCommand(CMD38, 0, 0, CMD_RESP_R1B)) {for (i=0; i<0x10000; i++) {if (Hsmmc_GetCardState() == CARD_TRAN) { // 擦除完成后返回到transfer状态Debug("Erasing complete!\r\n");return 0; // 擦除成功				}Delay_us(1000);			}		}		Debug("Erase block failed\r\n");return -1; // 擦除失败
}int32_t Hsmmc_ReadBlock(uint8_t *pBuffer, uint32_t BlockAddr, uint32_t BlockNumber)
{uint32_t Address = 0;uint32_t ReadBlock;uint32_t i;uint32_t j;int32_t ErrorState;uint32_t Temp;if (pBuffer == 0 || BlockNumber == 0) {return -1;}__REGw(HSMMC_BASE+NORINTSTS_OFFSET) = __REGw(HSMMC_BASE+NORINTSTS_OFFSET); // 清除中断标志__REGw(HSMMC_BASE+ERRINTSTS_OFFSET) = __REGw(HSMMC_BASE+ERRINTSTS_OFFSET); // 清除错误中断标志	while (BlockNumber > 0) {if (BlockNumber <= MAX_BLOCK) {ReadBlock = BlockNumber; // 读取的块数小于65536 BlockBlockNumber = 0; // 剩余读取块数为0} else {ReadBlock = MAX_BLOCK; // 读取的块数大于65536 Block,分多次读BlockNumber -= ReadBlock;}// 根据sd主机控制器标准,按顺序写入主机控制器相应的寄存器		__REGw(HSMMC_BASE+BLKSIZE_OFFSET) = (7<<12) | (512<<0); // 最大DMA缓存大小,block为512字节			__REGw(HSMMC_BASE+BLKCNT_OFFSET) = ReadBlock; // 写入这次读block数目if (CardType == SD_HC) {Address = BlockAddr; // SDHC卡写入地址为block地址} else if (CardType == SD_V1 || CardType == SD_V2) {Address = BlockAddr << 9; // 标准卡写入地址为字节地址}	BlockAddr += ReadBlock; // 下一次读块的地址__REG(HSMMC_BASE+ARGUMENT_OFFSET) = Address; // 写入命令参数		if (ReadBlock == 1) {// 设置传输模式,DMA禁能,读单块__REGw(HSMMC_BASE+TRNMOD_OFFSET) = (0<<5) | (1<<4) | (0<<2) | (1<<1) | (0<<0);// 设置命令寄存器,单块读CMD17,R1回复__REGw(HSMMC_BASE+CMDREG_OFFSET) = (CMD17<<8)|(1<<5)|(1<<4)|(1<<3)|0x2;} else {// 设置传输模式,DMA禁能,读多块			__REGw(HSMMC_BASE+TRNMOD_OFFSET) = (1<<5) | (1<<4) | (1<<2) | (1<<1) | (0<<0);// 设置命令寄存器,多块读CMD18,R1回复	__REGw(HSMMC_BASE+CMDREG_OFFSET) = (CMD18<<8)|(1<<5)|(1<<4)|(1<<3)|0x2;			}	ErrorState = Hsmmc_WaitForCommandDone();if (ErrorState) {Debug("Read Command error\r\n");return ErrorState;}	for (i=0; i<ReadBlock; i++) {ErrorState = Hsmmc_WaitForBufferReadReady();if (ErrorState) {return ErrorState;}if (((uint32_t)pBuffer & 0x3) == 0) {	for (j=0; j<512/4; j++) {*(uint32_t *)pBuffer = __REG(HSMMC_BASE+BDATA_OFFSET);pBuffer += 4;}} else {for (j=0; j<512/4; j++) {Temp = __REG(HSMMC_BASE+BDATA_OFFSET);*pBuffer++ = (uint8_t)Temp;*pBuffer++ = (uint8_t)(Temp>>8);*pBuffer++ = (uint8_t)(Temp>>16);*pBuffer++ = (uint8_t)(Temp>>24);}}						}ErrorState = Hsmmc_WaitForTransferDone();if (ErrorState) {Debug("Read block error\r\n");return ErrorState;}	}return 0; // 所有块读完
}int32_t Hsmmc_WriteBlock(uint8_t *pBuffer, uint32_t BlockAddr, uint32_t BlockNumber)
{uint32_t Address = 0;uint32_t WriteBlock;uint32_t i;uint32_t j;int32_t ErrorState;if (pBuffer == 0 || BlockNumber == 0) {return -1; // 参数错误}	__REGw(HSMMC_BASE+NORINTSTS_OFFSET) = __REGw(HSMMC_BASE+NORINTSTS_OFFSET); // 清除中断标志__REGw(HSMMC_BASE+ERRINTSTS_OFFSET) = __REGw(HSMMC_BASE+ERRINTSTS_OFFSET); // 清除错误中断标志while (BlockNumber > 0) {	if (BlockNumber <= MAX_BLOCK) {WriteBlock = BlockNumber;// 写入的块数小于65536 BlockBlockNumber = 0; // 剩余写入块数为0} else {WriteBlock = MAX_BLOCK; // 写入的块数大于65536 Block,分多次写BlockNumber -= WriteBlock;}if (WriteBlock > 1) { // 多块写,发送ACMD23先设置预擦除块数Hsmmc_IssueCommand(CMD55, RCA<<16, 0, CMD_RESP_R1);Hsmmc_IssueCommand(CMD23, WriteBlock, 0, CMD_RESP_R1);}// 根据sd主机控制器标准,按顺序写入主机控制器相应的寄存器			__REGw(HSMMC_BASE+BLKSIZE_OFFSET) = (7<<12) | (512<<0); // 最大DMA缓存大小,block为512字节		__REGw(HSMMC_BASE+BLKCNT_OFFSET) = WriteBlock; // 写入block数目	if (CardType == SD_HC) {Address = BlockAddr; // SDHC卡写入地址为block地址} else if (CardType == SD_V1 || CardType == SD_V2) {Address = BlockAddr << 9; // 标准卡写入地址为字节地址}BlockAddr += WriteBlock; // 下一次写地址__REG(HSMMC_BASE+ARGUMENT_OFFSET) = Address; // 写入命令参数			if (WriteBlock == 1) {// 设置传输模式,DMA禁能写单块__REGw(HSMMC_BASE+TRNMOD_OFFSET) = (0<<5) | (0<<4) | (0<<2) | (1<<1) | (0<<0);// 设置命令寄存器,单块写CMD24,R1回复__REGw(HSMMC_BASE+CMDREG_OFFSET) = (CMD24<<8)|(1<<5)|(1<<4)|(1<<3)|0x2;			} else {// 设置传输模式,DMA禁能写多块__REGw(HSMMC_BASE+TRNMOD_OFFSET) = (1<<5) | (0<<4) | (1<<2) | (1<<1) | (0<<0);// 设置命令寄存器,多块写CMD25,R1回复__REGw(HSMMC_BASE+CMDREG_OFFSET) = (CMD25<<8)|(1<<5)|(1<<4)|(1<<3)|0x2;					}ErrorState = Hsmmc_WaitForCommandDone();if (ErrorState) {Debug("Write Command error\r\n");return ErrorState;}	for (i=0; i<WriteBlock; i++) {ErrorState = Hsmmc_WaitForBufferWriteReady();if (ErrorState) {return ErrorState;}if (((uint32_t)pBuffer & 0x3) == 0) {for (j=0; j<512/4; j++) {__REG(HSMMC_BASE+BDATA_OFFSET) = *(uint32_t *)pBuffer;pBuffer += 4;}} else {for (j=0; j<512/4; j++) {__REG(HSMMC_BASE+BDATA_OFFSET) = pBuffer[0] + ((uint32_t)pBuffer[1]<<8) +((uint32_t)pBuffer[2]<<16) + ((uint32_t)pBuffer[3]<<24);pBuffer += 4;}}	}ErrorState = Hsmmc_WaitForTransferDone();if (ErrorState) {Debug("Write block error\r\n");return ErrorState;}for (i=0; i<0x10000000; i++) {if (Hsmmc_GetCardState() == CARD_TRAN) { // 需在transfer statusbreak; // 状态正确}}if (i == 0x10000000) {return -3; // 状态错误或Programming超时}		}return 0; // 写完所有数据
}int Hsmmc_Init(void)
{int32_t Timeout;uint32_t Capacity;uint32_t i;uint32_t OCR;uint32_t Temp;uint8_t SwitchStatus[64];SD_SCR SCR;uint8_t CSD[16];uint32_t c_size, c_size_multi, read_bl_len;	// 设置HSMMC的接口引脚配置
#if (HSMMC_NUM == 0)// channel 0,GPG0[0:6] = CLK, CMD, CDn, DAT[0:3]GPG0CON_REG = 0x2222222;// pull up enableGPG0PUD_REG = 0x2aaa;GPG0DRV_REG = 0x3fff;// channel 0 clock src = SCLKEPLL = 96MCLK_SRC4_REG = (CLK_SRC4_REG & (~(0xf<<0))) | (0x7<<0);// channel 0 clock = SCLKEPLL/2 = 48MCLK_DIV4_REG = (CLK_DIV4_REG & (~(0xf<<0))) | (0x1<<0);	#elif (HSMMC_NUM == 1)// channel 1,GPG1[0:6] = CLK, CMD, CDn, DAT[0:3]GPG1CON_REG = 0x2222222;// pull up enableGPG1PUD_REG = 0x2aaa;GPG1DRV_REG = 0x3fff;// channel 1 clock src = SCLKEPLL = 96MCLK_SRC4_REG = (CLK_SRC4_REG & (~(0xf<<4))) | (0x7<<4);// channel 1 clock = SCLKEPLL/2 = 48MCLK_DIV4_REG = (CLK_DIV4_REG & (~(0xf<<4))) | (0x1<<4);#elif (HSMMC_NUM == 2)// channel 2,GPG2[0:6] = CLK, CMD, CDn, DAT[0:3]GPG2CON_REG = 0x2222222;// pull up enableGPG2PUD_REG = 0x2aaa;GPG2DRV_REG = 0x3fff;// channel 2 clock src = SCLKEPLL = 96MCLK_SRC4_REG = (CLK_SRC4_REG & (~(0xf<<8))) | (0x7<<8);// channel 2 clock = SCLKEPLL/2 = 48MCLK_DIV4_REG = (CLK_DIV4_REG & (~(0xf<<8))) | (0x1<<8);	#elif (HSMMC_NUM == 3)// channel 3,GPG3[0:6] = CLK, CMD, CDn, DAT[0:3]GPG3CON_REG = 0x2222222;// pull up enableGPG3PUD_REG = 0x2aaa;GPG3DRV_REG = 0x3fff;// channel 3 clock src = SCLKEPLL = 96MCLK_SRC4_REG = (CLK_SRC4_REG & (~(0xf<<12))) | (0x7<<12);// channel 3 clock = SCLKEPLL/2 = 48MCLK_DIV4_REG = (CLK_DIV4_REG & (~(0xf<<12))) | (0x1<<12);	#endif// software reset for all 复位主机SoC控制器,而不是复位SD卡__REGb(HSMMC_BASE+SWRST_OFFSET) = 0x1;Timeout = 1000; // Wait max 10 mswhile (__REGb(HSMMC_BASE+SWRST_OFFSET) & (1<<0)) {if (Timeout == 0) {return -1; // reset timeout}Timeout--;Delay_us(10);}	// 上面设置的是SoC的SD控制器的时钟,现在设置的是SD卡的时钟Hsmmc_SetClock(400000); // 400k__REGb(HSMMC_BASE+TIMEOUTCON_OFFSET) = 0xe; // 最大超时时间__REGb(HSMMC_BASE+HOSTCTL_OFFSET) &= ~(1<<2); // 正常速度模式// 清除正常中断状态标志__REGw(HSMMC_BASE+NORINTSTS_OFFSET) = __REGw(HSMMC_BASE+NORINTSTS_OFFSET);// 清除错误中断状态标志__REGw(HSMMC_BASE+ERRINTSTS_OFFSET) = __REGw(HSMMC_BASE+ERRINTSTS_OFFSET);__REGw(HSMMC_BASE+NORINTSTSEN_OFFSET) = 0x7fff; // [14:0]中断状态使能__REGw(HSMMC_BASE+ERRINTSTSEN_OFFSET) = 0x3ff; // [9:0]错误中断状态使能__REGw(HSMMC_BASE+NORINTSIGEN_OFFSET) = 0x7fff; // [14:0]中断信号使能	__REGw(HSMMC_BASE+ERRINTSIGEN_OFFSET) = 0x3ff; // [9:0]错误中断信号使能// 从这里开始和SD卡通信,通信其实就是发命令然后收响应Hsmmc_IssueCommand(CMD0, 0, 0, CMD_RESP_NONE); // 复位所有卡到空闲状态	CardType = UNUSABLE; // 卡类型初始化不可用if (Hsmmc_IssueCommand(CMD8, 0x1aa, 0, CMD_RESP_R7)) { // 没有回复,MMC/sd v1.x/not cardfor (i=0; i<100; i++) {// CMD55 + CMD41 = ACMD41Hsmmc_IssueCommand(CMD55, 0, 0, CMD_RESP_R1);if (!Hsmmc_IssueCommand(CMD41, 0, 0, CMD_RESP_R3)) { // CMD41有回复说明为sd卡OCR = __REG(HSMMC_BASE+RSPREG0_OFFSET); // 获得回复的OCR(操作条件寄存器)值if (OCR & 0x80000000) { // 卡上电是否完成上电流程,是否busyCardType = SD_V1; // 正确识别出sd v1.x卡Debug("SD card version 1.x is detected\r\n");break;}} else {// MMC卡识别Debug("MMC card is not supported\r\n");return -1;}Delay_us(1000);				}} else { // sd v2.0Temp = __REG(HSMMC_BASE+RSPREG0_OFFSET);if (((Temp&0xff) == 0xaa) && (((Temp>>8)&0xf) == 0x1)) { // 判断卡是否支持2.7~3.3v电压OCR = 0;for (i=0; i<100; i++) {OCR |= (1<<30);Hsmmc_IssueCommand(CMD55, 0, 0, CMD_RESP_R1);Hsmmc_IssueCommand(CMD41, OCR, 0, CMD_RESP_R3); // reday态OCR = __REG(HSMMC_BASE+RSPREG0_OFFSET);if (OCR & 0x80000000) { // 卡上电是否完成上电流程,是否busyif (OCR & (1<<30)) { // 判断卡为标准卡还是高容量卡CardType = SD_HC; // 高容量卡Debug("SDHC card is detected\r\n");} else {CardType = SD_V2; // 标准卡Debug("SD version 2.0 standard card is detected\r\n");}break;}Delay_us(1000);}}}if (CardType == SD_HC || CardType == SD_V1 || CardType == SD_V2) {Hsmmc_IssueCommand(CMD2, 0, 0, CMD_RESP_R2); // 请求卡发送CID(卡ID寄存器)号,进入identHsmmc_IssueCommand(CMD3, 0, 0, CMD_RESP_R6); // 请求卡发布新的RCA(卡相对地址),Stand-by状态RCA = (__REG(HSMMC_BASE+RSPREG0_OFFSET) >> 16) & 0xffff; // 从卡回复中得到卡相对地址Hsmmc_IssueCommand(CMD7, RCA<<16, 0, CMD_RESP_R1); // 选择已标记的卡,transfer状态Hsmmc_Get_SCR(&SCR);if (SCR.SD_SPEC == 0) { // sd 1.0 - sd 1.01// Version 1.0 doesn't support switchingHsmmc_SetClock(24000000); // 设置SDCLK = 48M/2 = 24M			} else { // sd 1.10 / sd 2.0Temp = 0;for (i=0; i<4; i++) {if (Hsmmc_Switch(0, 0, 1, SwitchStatus) == 0) { // switch checkif (!(SwitchStatus[34] & (1<<1))) { // Group 1, function 1 high-speed bit 273					// The high-speed function is readyif (SwitchStatus[50] & (1<<1)) { // Group, function 1 high-speed support bit 401// high-speed is supportedif (Hsmmc_Switch(1, 0, 1, SwitchStatus) == 0) { // switchif ((SwitchStatus[47] & 0xf) == 1) { // function switch in group 1 is ok?// result of the switch high-speed in function group 1Debug("Switch to high speed mode: CLK @ 50M\r\n");Hsmmc_SetClock(48000000); // 设置SDCLK = 48M	Temp = 1;									}}}break;}} }if (Temp == 0) {Hsmmc_SetClock(24000000); // 设置SDCLK = 48M/2 = 24M}}if (!Hsmmc_SetBusWidth(4)) {Debug("Set bus width error\r\n");return -1; // 位宽设置出错}if (Hsmmc_GetCardState() == CARD_TRAN) { // 此时卡应在transfer态if (!Hsmmc_IssueCommand(CMD16, 512, 0, CMD_RESP_R1)) { // 设置块长度为512字节__REGw(HSMMC_BASE+NORINTSTS_OFFSET) = 0xffff; // 清除中断标志Hsmmc_Get_CSD(CSD);if ((CSD[15]>>6) == 0) { // CSD v1.0->sd V1.x, sd v2.00 standarread_bl_len = CSD[10] & 0xf; // [83:80]c_size_multi = ((CSD[6] & 0x3) << 1) + ((CSD[5] & 0x80) >> 7); // [49:47]c_size = ((int32_t)(CSD[9]&0x3) << 10) + ((int32_t)CSD[8]<<2) + (CSD[7]>>6); // [73:62]				Capacity = (c_size + 1) << ((c_size_multi + 2) + (read_bl_len-9)); // block(512 byte)} else {c_size = ((CSD[8]&0x3f) << 16) + (CSD[7]<<8) + CSD[6]; // [69:48]// 卡容量为字节(c_size+1)*512K byte,以1扇区512 byte字,卡的扇区数为		Capacity = (c_size+1) << 10;// block (512 byte)}Debug("Card Initialization succeed\r\n");				Debug("Capacity: %ldMB\r\n", Capacity / (1024*1024 / 512));return 0; // 初始化成功							}}}Debug("Card Initialization failed\r\n");return -1; // 卡工作异常
}

更多推荐

S5PV210裸机(七):Nand和iNand

本文发布于:2023-12-03 08:09:36,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1653046.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:裸机   iNand   Nand

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!