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; // 卡工作异常
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/119959.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

FL Studio21.2最新订阅版本更新升级(详细功能介绍)

好消息&#xff01;FL Studio 21.2 在 10 月 26 日正式发布啦&#xff0c;它新增了 FL Cloud 在线采样库和 AI 音乐制作功能&#xff0c;还提供音乐分发到 Spotify、Apple Music 等主要音乐平台的服务。此外&#xff0c;还有新的音频分离功能、自定义波形颜色和新的合成器 Kepl…

Prompt设计与大语言模型微调

本文主要介绍了Prompt设计、大语言模型SFT和LLM在手机天猫AI导购助理项目应用。 ChatGPT基本原理 “会说话的AI”&#xff0c;“智能体” 简单概括成以下几个步骤&#xff1a; 预处理文本&#xff1a;ChatGPT的输入文本需要进行预处理。输入编码&#xff1a;ChatGPT将经过预处理…

Java实现SQL分页

在日常开发需要对数据进行分页&#xff0c;配置如下 <!-- baomidou --><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.4.0</version></dependency> 在控…

C++求欧拉角(eigen库中暴露的一些问题)

不同顺序欧拉角转旋转矩阵对照公式 eigen库求欧拉角公式 分别试验eigen库自带的matrix.eulerAngles()函数&#xff0c;与根据上述公式推导的两种方法求欧拉角 eigen库求得欧拉角的范围一定是 x − > r o l l x->roll x−>roll方向在 [ 0 , π ] [0,π] [0,π]之间&am…

【算法题】割后面积最大的蛋糕

题目&#xff1a; 矩形蛋糕的高度为 h 且宽度为 w&#xff0c;给你两个整数数组 horizontalCuts 和 verticalCuts&#xff0c;其中&#xff1a; horizontalCuts[i] 是从矩形蛋糕顶部到第 i 个水平切口的距离 verticalCuts[j] 是从矩形蛋糕的左侧到第 j 个竖直切口的距离 请你…

tinymce输入框怎么限制只输入空格或者回车时不能提交

项目场景&#xff1a; 项目相关背景&#xff1a; tinymce输入框只输入空格或者回车时提交的空数据毫无意义&#xff0c;所以需要限制一下 无意义的输入&#xff1a; 解决方案&#xff1a; 因为tinymce输入框传到后端的数据是代码形式&#xff0c;所以不能直接.trem&#…

测试用例的设计方法(全):等价类划分方法

一.方法简介 1.定义 是把所有可能的输入数据,即程序的输入域划分成若干部分&#xff08;子集&#xff09;,然后从每一个子集中选取少数具有代表性的数据作为测试用例。该方法是一种重要的,常用的黑盒测试用例设计方法。 2.划分等价类&#xff1a; 等价类是指某个输入域的…

当数据库遇上深度学习:AI DataLoader 助力因子管理模型训练全流程

深度学习模型有能力自动发现变量之间的关系&#xff0c;而这些关系通常是不可见的&#xff0c;这使得深度学习可以挖掘新的因子和规律&#xff0c;为量化投资策略提供更多可能性。在传统的量化策略开发流程中&#xff0c;通常会使用 Python 或第三方工具生成因子&#xff0c;并…

SpringBoot整合MyBatis-Plus详解(二)

文章目录 SpringBoot整合MyBatis-Plus详解&#xff08;二&#xff09;MyBatis-Plus简介条件构造器和常用接口⭐Wrapper介绍QueryWrapper&#xff08;Mapper接口提供的&#xff09;和QueryChainWrapper&#xff08;Service接口提供的&#xff09;案例1&#xff1a;组装查询条件案…

Unity报错:Microsoft Visual C# Compiler version

Unity报错:Microsoft Visual C# Compiler version 问题解决方案总结 问题 Microsoft Visual C# Compiler version 2.9.1.65535 (9d34608e) Copyright © Microsoft Corporation 切换版本或者使用老项目的时候可能会出现这个报错&#xff0c;这个报错就是项目设置的问题 …

【HarmonyOS】元服务卡片展示动态数据,并定点更新卡片数据

【关键字】 元服务卡片、卡片展示动态数据、更新卡片数据 【写在前面】 本篇文章主要介绍开发元服务卡片时&#xff0c;如何实现卡片中动态显示数据功能&#xff0c;并实现定时数据刷新。本篇文章通过实现定时刷新卡片中日期数据为例&#xff0c;讲述展示动态数据与更新数据功…

C++前缀和算法的应用:从栈中取出 K 个硬币的最大面值和 原理源码测试用例

本文涉及的基础知识点 C算法&#xff1a;前缀和、前缀乘积、前缀异或的原理、源码及测试用例 包括课程视频 题目 一张桌子上总共有 n 个硬币 栈 。每个栈有 正整数 个带面值的硬币。 每一次操作中&#xff0c;你可以从任意一个栈的 顶部 取出 1 个硬币&#xff0c;从栈中移除…

深入探究ASEMI肖特基二极管MBR60100PT的材质

编辑-Z 在电子零件领域中&#xff0c;肖特基二极管MBR60100PT因其出色的性能和广泛的应用而显得尤为关键。理解其材质不仅有助于我们深入理解其运作原理&#xff0c;也有助于我们做出更合适的电子设计。那么&#xff0c;肖特基二极管MBR60100PT是什么材质呢? 首先&#xff0c…

电厂数据可视化三维大屏展示平台加强企业安全防范

园区可视化大屏是一种新型的信息化手段&#xff0c;能够将园区内各项数据信息以图像的形式直观呈现在大屏幕上&#xff0c;便于管理员和员工进行实时监控、分析和决策。本文将从以下几个方面介绍园区可视化大屏的作用和应用。 VR数字孪生园区系统是通过将实际园区的各种数据和信…

Vue 父子组件传参、插槽

setup 函数中有两个主要的参数&#xff1a;props、context 。 props 用于接收父组件传递过来的数据&#xff0c;父传子。 context 指的是 setup 的上下文&#xff0c;它有三个属性&#xff1a;attrs、slots、emit 。 attrs 用于&#xff1a;当父组件传递过来的数据&#xff…

网络协议--BOOTP:引导程序协议

16.1 引言 在第5章我们介绍了一个无盘系统&#xff0c;它在不知道自身IP地址的情况下&#xff0c;在进行系统引导时能够通过RARP来获取它的IP地址。然而使用RARP有两个问题&#xff1a;&#xff08;1&#xff09;IP地址是返回的唯一结果&#xff1b;&#xff08;2&#xff09;…

布隆过滤器(Bloom Filter)初学习

目录 1、布隆过滤器是什么 2、布隆过滤器的优缺点 3、使用场景 4、⭐基于Redis的布隆过滤器插件安装 4.1 下载布隆过滤器 4.2 创建文件夹并上传文件 4.3 安装gcc 4.4 解压RedisBloom压缩包 4.5 在解压好的文件夹下输入make 4.6 将编译的好的插件拷贝到docker redis容…

Windows下安装Anaconda、Pycharm以及iflycode插件图解

目录 一、下载Anaconda、Pycharm以及iflycode插件 二、创建相关文件夹 三、Pycharm社区版安装详细步骤 四、Anaconda安装详细步骤 五、配置Pycharm 六、安装iflycode插件 Anaconda是一款集成的Python环境&#xff0c;anaconda可以看做Python的一个集成安装&#xff0c;安…

Openssl数据安全传输平台011:base64的使用

文章目录 1 base641.1 概念1.2 应用场景 2 base64 算法 &#xff08;重要&#xff09;3 openssl 中base64的使用3.1 BIO 操作3.2 base64 编码 -> bio链的写操作3.3 base64 解码 -> bio链的读操作 1 base64 1.1 概念 Base64是一种基于64个可打印字符来表示二进制数据的表…

聊聊“JVM 调优JVM 性能优化”是怎么个事?

所谓“调优”就是一个诊断和处理手段&#xff0c;最终的目标是让系统的处理能力&#xff0c;也就是“性能”达到最优化。 计算机系统中&#xff0c;性能相关的资源主要分为这几类&#xff1a; CPU&#xff1a;CPU 是系统最关键的计算资源&#xff0c;在单位时间内有限&#xf…