本文主要探讨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; // 卡工作异常
}