安德鲁

[笔记].菜农M0助学板之读SD卡块内容小练(库操作方式)

0
阅读(2295)

之前是移植到Nios II上,而且是用得IO模拟的,现在使用nuc1xx的spi核做的,不过还是参考了新唐的例程。关于sd的一些笔记,我贴在博客了。今天在此就不赘述了。

[笔记].菜农M0助学板之读SD卡块内容小练(库操作方式)

[原创].关于SD卡的隐藏分区的认识过程及结果

[原创][连载].基于SOPC的简易数码相框 -  Nios II SBTE部分(软件部分)-  从SD卡内读取图片文件,然后显示在TFT-LCD上

[原创][连载].基于SOPC的简易数码相框 -  Nios II SBTE部分(软件部分)-  SD卡(SPI模式)驱动

[转载].阿迪老师 - 《SD卡入门到精通》视频

[笔记].SD存储卡引脚定义.[SD Card]

 

上代码先。

main.h

#ifndef __MAIN_H__
	#define __MAIN_H__
	 
	#include 
	 
	/*********************************************************
	* 系统寄存器映射及库头文件
	*********************************************************/
	#include "NUC1xx.h" // 系统寄存器映射
	#include "DrvSYS.h"
	#include "DrvGPIO.h"
	#include "DrvUART.h"
	#include "DrvSPI.h"
	#include "sd_card.h"
	/**********************************************************
	* 自定义宏
	**********************************************************/
	#define APP_DEBUG
	 
	#ifdef APP_DEBUG
	    #define DEBUG printf
	#else
	    #define DEBUG(...)
	#endif
	 
	typedef enum{NO=0, YES=!NO}bool;
	 
	#endif /* __MAIN_H__ */

main.c

#include "main.h"
	/**********************************************************
	* 变量申明
	**********************************************************/
	volatile bool g_tmr0_5ms = NO;
	/**********************************************************
	* 函数申明
	**********************************************************/
	extern char GetChar(void);
	extern void PFN_UART_CALLBACK(void);
	/**********************************************************
	* 系统上电初始化
	**********************************************************/
	void MAIN_INIT(void)
	{
	    UNLOCKREG();
	    {   /* 配置系统时钟 */
	        SYSCLK->PWRCON.XTL12M_EN = 1; //  设定12M外部晶振
	        DrvSYS_Delay(5000); // 等待时钟就绪
	        DrvSYS_SelectPLLSource(E_SYS_EXTERNAL_12M); // 选择12MHz为PLL输入
	        DrvSYS_Open(50000000); // 打开50MHz
	    }     
	    {   /* 配置串口 */
	        STR_UART_T param;
	         
	        DrvSYS_SelectIPClockSource(E_SYS_UART_CLKSRC, 0); //使能UART时钟
	        DrvGPIO_InitFunction(E_FUNC_UART0);    // 复用功能引脚设置
	                                                          
	        param.u32BaudRate        = 115200;    // 波特率
	        param.u8cDataBits        = DRVUART_DATABITS_8;  // 数据位
	        param.u8cStopBits        = DRVUART_STOPBITS_1;  // 停止位
	        param.u8cParity          = DRVUART_PARITY_NONE;    // 校验位
	        param.u8cRxTriggerLevel  = DRVUART_FIFO_1BYTES;    // FIFO存储深度1字节
	        param.u8TimeOut             = 0; // FIFO超时设定
	        DrvUART_Open(UART_PORT0, &param); // 串口开启、结构体整体赋值
	         
	        // 串口的中断类型比较丰富,此处仅打开接收中断
	        DrvUART_EnableInt(UART_PORT0, DRVUART_RDAINT, (PFN_DRVUART_CALLBACK*)PFN_UART_CALLBACK);
	        DrvUART_ClearIntFlag(UART_PORT0, DRVUART_RDAINT);
	    }
	    {   /* 配置GPIO */
	        NVIC_DisableIRQ(GPAB_IRQn);
	        NVIC_DisableIRQ(GPCDE_IRQn);
	        DrvGPIO_Open(E_GPB, 10, E_IO_OUTPUT); // 蜂鸣器
	        DrvGPIO_ClrBit(E_GPB, 10); // 关蜂鸣器
	    }
	    {   /* 配置TMR0 */
	        NVIC_DisableIRQ(TMR0_IRQn);
	        // 第一步 使能和选择定时器时钟源及使能定时器模块         
	        SYSCLK->CLKSEL1.TMR0_S = 0; // 选择12Mhz作为定时器时钟源
	        SYSCLK->APBCLK.TMR0_EN =1;  // 使能定时器0
	        TIMER0->TCSR.CEN = 1;       // 使能定时器模块
	        // 第二步 选择操作模式   
	        TIMER0->TCSR.MODE = 1; // 选择周期模式
	        TIMER0->TCSR.CRST = 1; // 清加1计数器   
	        // 第三步 输出时钟周期 = 定时器时钟源周期*(8位预分频因子 + 1) * (24位比较因子TCMP)
	        TIMER0->TCSR.PRESCALE = 11; // 12分频
	        TIMER0->TCMPR = 5000; // 12M/12/5000=200Hz, 5ms
	        // 第四步 使能中断
	        TIMER0->TISR.TIF = 1; // 清中断 
	        TIMER0->TCSR.IE = 1; // 使能中断
	        NVIC_EnableIRQ(TMR0_IRQn);  // 使能TMR0中断
	        // 第五步 使能定时器模块
	        TIMER0->TCSR.CRST = 1; // 复位向上计数器
	        TIMER0->TCSR.CEN = 1; // 使能TMR0
	        //TIMER0->TCSR.TDR_EN=1; // 无需读取加1计数器值
	    }
	//    {   /* 配置SPI0主机模式 */
	//        NVIC_DisableIRQ(SPI0_IRQn);
	//        // 第一步 使能SPI0核
	//        SYSCLK->APBCLK.SPI0_EN        =1;
	//        SYS->IPRSTC2.SPI0_RST         =1;
	//        SYS->IPRSTC2.SPI0_RST         =0;
	//        // 第二步 设置除GPIO外的第一功能
	//        SYS->GPCMFP.SPI0_SS0_I2SLRCLK =1;
	//        SYS->GPCMFP.SPI0_CLK_I2SBCLK  =1;       
	//        SYS->GPCMFP.SPI0_MISO0_I2SDI  =1;   
	//        SYS->GPCMFP.SPI0_MOSI0_I2SDO  =1;
	//        SYS->GPBMFP.TM2_SS01          =1;
	//        // 第三步 设置除GPIO外的第二功能
	//        SYS->ALTMFP.PC0_I2SLRCLK      =0;
	//        SYS->ALTMFP.PC1_I2SBCLK       =0;
	//        SYS->ALTMFP.PC2_I2SDI         =0;
	//        SYS->ALTMFP.PC3_I2SDO         =0;
	//        SYS->ALTMFP.PB10_S01          =1;
	//        // 第四步 配置SPI0模式
	//        SPI0->CNTRL.SLAVE = 0; // 0 主机模式;1 从机模式
	//        SPI0->CNTRL.CLKP = 0; // 0 SCLK低电平闲置;1 SCLK高电平闲置
	//        SPI0->CNTRL.TX_NEG = 0; // 0 上升沿传输;1 下降沿传输
	//        SPI0->CNTRL.TX_BIT_LEN = 8; // [0 31] 一次传输8位
	//        SPI0->CNTRL.TX_NUM = 0; // 0 每次传输完成一次收/发;1 每次传输完成两次收/发
	//        SPI0->CNTRL.LSB = 0; // 0 优先发送MSB;1 优先发送LSB
	//        // 第六步 配置从机选择
	//        SPI0->SSR.AUTOSS = 1; // 0 设置或清除SSR.SSR来决定是否产生从机选择信号
	//                              // 1 CNTRL.GO_BUSY设置后,SSR.SSR会由SPI核自动产生,只要一次收/发完成后结束
	//        SPI0->SSR.SS_LVL = 0; // 0 高电平/上升沿有效;1 低电平/下降沿有效
	//        // 第五步 设置时钟输出频率
	//        // 设置SPI0的时钟分频器
	//        SPI0->DIVIDER.DIVIDER = (uint16_t)((((DrvSYS_GetHCLKFreq()/2000000) + 1) >> 1) - 1); // 2MHz
	//        // 设置SPI0的第二个时钟分频器
	//        // SPI0->DIVIDER.DIVIDER2 = (uint16_t)((((DrvSYS_GetHCLKFreq()/10000) + 1) >> 1) - 1); // 10kHz
	//        SPI0->DIVIDER.DIVIDER2 = 0;
	//        // 第六步
	//        SPI0->TX[0] = 0; // 清TX[0]
	//        // SPI0->TX[1] = 0; // 清TX[1]
	//        SPI0->CNTRL.GO_BUSY = 1; // 触发一次SPI传输
	//        // 第七步 设置中断
	//        //SPI0->CNTRL.IE = 0; // 0 失能中断;1 使能中断
	//        //SPI0->CNTRL.IE = 1; // 写1清中断标志
	//        //NVIC_EnableIRQ(SPI0_IRQn);
	//       
	//    }
	    LOCKREG();
	}
	/**********************************************************
	* TMR0 ISR
	**********************************************************/
	void TMR0_IRQHandler(void) __irq
	{   // 注意:ISR内必须清中断
	    TIMER0->TISR.TIF = 1; // 清中断 
	 
	    g_tmr0_5ms = YES;  
	}
	/**********************************************************
	* UART0 回调函数
	**********************************************************/
	void PFN_UART_CALLBACK(void)
	{   // 注意:回调函数内无须清中断
	    switch(GetChar()) {       
	        default:
	            DEBUG("test");
	            break;       
	    }           
	}
	/**********************************************************
	* 主函数
	**********************************************************/
	int main(void)
	{
	    MAIN_INIT(); // 上电初始化系统
	    SD_CARD_Open();
	    SD_CARD_DEMO(); 
	     
	    while(1) {
	        if(g_tmr0_5ms != NO) {
	            g_tmr0_5ms = NO;
	        }
	 
	        if(0) break; // 跳出大循环
	    }
	     
	    SD_CARD_Close();
	    DrvUART_Close(UART_PORT0);
	    return 0;
	}

sd_card.h

#ifndef SD_CARD_H_
	#define SD_CARD_H_
	 
	#include "main.h"
	 
	#define ENABLE_SD_CARD_DEBUG // 打开调试信息
	// debug switch
	#ifdef ENABLE_SD_CARD_DEBUG
	    #define SD_CARD_DEBUG    DEBUG
	#else
	    #define SD_CARD_DEBUG(...)
	#endif
	 
	 
	uint8_t SD_CARD_SPI_Transfer(uint8_t byte);
	uint8_t SD_CARD_Init(void);
	//
	void SD_CARD_Open(void);
	void SD_CARD_Close(void);
	uint8_t SD_CARD_Write_Sector(uint32_t addr,uint8_t *buf);
	uint8_t SD_CARD_Read_Sector(uint8_t *CMD,uint8_t *buf,uint16_t n_bytes);
	uint8_t SD_CARD_Read_Sector_Start(uint32_t sector);
	static void SD_CARD_Read_Data(uint16_t n_bytes,uint8_t *buf);
	void SD_CARD_Read_Data_LBA(uint32_t LBA,uint16_t n_bytes,uint8_t *buf);
	void SD_CARD_Read_Sector_End(void);
	static uint8_t SD_CARD_Read_CSD(uint8_t *buf);
	static uint8_t SD_CARD_Read_CID(uint8_t *buf);
	void SD_CARD_Get_Info(void);
	void SD_CARD_DEMO(void);
	 
	 
	#endif /* SD_CARD_H_ */

sd_card.c

#include "sd_card.h"
	 
	// 错误宏定义
	#define INIT_CMD0_ERROR     0x01
	#define INIT_CMD1_ERROR     0x02
	#define WRITE_BLOCK_ERROR   0x03
	#define READ_BLOCK_ERROR    0x04
	 
	#define Delay_Us DrvSYS_Delay // us级延时
	#define SD_CARD_Set_nCS DrvSPI_ClrSS(eDRVSPI_PORT1, eDRVSPI_SS0) // nCS = 1
	#define SD_CARD_Clr_nCS DrvSPI_SetSS(eDRVSPI_PORT1, eDRVSPI_SS0) // nCS = 0
	 
	typedef union{
	    uint8_t data[16];
	    struct{
	        uint8_t MID;    // Manufacture ID; Binary
	        uint8_t OLD[2]; // OEM/Application ID; ASCII
	        uint8_t PNM[5]; // Product Name; ASCII
	        uint8_t PRV;    // Product Revision; BCD
	        uint8_t PSN[4]; // Serial Number; Binary
	        uint8_t MDT[2]; // Manufacture Data Code; BCD; upper 4 bits of first byte are reserved
	        uint8_t CRC;    // CRC7_checksum; Binary; LSB are reserved
	    }CID;
	}CID_Info_STR;
	 
	typedef struct{
	    uint8_t data[16];
	    uint32_t capacity_MB;
	    uint8_t READ_BL_LEN;
	    uint16_t C_SIZE;
	    uint8_t C_SIZE_MULT;
	}CSD_Info_STR;
	 
	uint16_t gByteOffset=0; // 某字节在扇区内的偏移地址
	uint16_t gSectorOffset=0; // 扇区在SD卡内的偏移地址
	uint8_t gSectorOpened = 0; // 0 关闭扇区;1 打开扇区
 
	// 打开SD卡
	void SD_CARD_Open(void)
	{
	    DrvSYS_SetIPClock(E_SYS_SPI1_CLK,1);
	    DrvGPIO_InitFunction(E_FUNC_SPI1); // enable SPI funztion and pin
	     
	    DrvSPI_Open(eDRVSPI_PORT1,eDRVSPI_MASTER,eDRVSPI_TYPE1,8, FALSE);
	    DrvSPI_DisableAutoSS(eDRVSPI_PORT1);
	    DrvSPI_SetSlaveSelectActiveLevel(eDRVSPI_PORT1, eDRVSPI_ACTIVE_LOW_FALLING);
	    DrvSPI_SetEndian(eDRVSPI_PORT1, eDRVSPI_MSB_FIRST);
	    DrvSPI_SetClockFreq(eDRVSPI_PORT1,300000,0);
	     
	    while(SD_CARD_Init() != 0x55);
	    SD_CARD_DEBUG(("SD卡初始化完成\r"));
	    
	    DrvSPI_SetClockFreq(eDRVSPI_PORT1,10000000,0);
	}
	 
	 
	// 关闭SD卡
	void SD_CARD_Close(void)
	{
	    DrvSPI_Close(eDRVSPI_PORT1);
	}
	 
	 
	// 收发一个字节
	static uint8_t SD_CARD_SPI_Transfer(uint8_t byte)
	{
	    uint32_t SPIdata=(uint32_t)byte;
	    DrvSPI_BurstTransfer(eDRVSPI_PORT1,1,2);
	    DrvSPI_SingleWrite(eDRVSPI_PORT1,&SPIdata);
	    while (DrvSPI_IsBusy(eDRVSPI_PORT1));
	    DrvSPI_DumpRxRegister(eDRVSPI_PORT1,&SPIdata,1);
	    return (uint8_t)SPIdata;
	}
	 
	 
	// SD卡写命令
	static uint8_t SD_CARD_Write_CMD(uint8_t *CMD)
	{
	    uint8_t temp,retry;
	    uint8_t i;
	 
	    SD_CARD_Set_nCS; // 失能SD卡
	    SD_CARD_SPI_Transfer(0xFF); // 发8个脉冲
	    SD_CARD_Clr_nCS; // 使能SD卡
	 
	    // 写6个字节到SD卡
	    for(i=0;i<6;i++) SD_CARD_SPI_Transfer(*CMD++);
	 
	    // 读取16位响应
	    SD_CARD_SPI_Transfer(0xFF); // 第一个字节无效
	    retry=0;
	    do{ // 只取最后一个字节
	        temp=SD_CARD_SPI_Transfer(0xFF);
	        retry++;
	    }while((temp==0xFF) && (retry<100));
	     
	    return temp;
	}
	 
	 
	// 初始化SD卡;SPI模式
	static uint8_t SD_CARD_Init(void)
	{
	    uint8_t retry,temp;
	    uint8_t i;
	    uint8_t CMD[]={0x40,0x00,0x00,0x00,0x00,0x95};
	 
	    SD_CARD_Set_nCS;
	    Delay_Us(10*1000);   
	    for(i=0;i<10;i++) // 最少先发74个脉冲
	        SD_CARD_SPI_Transfer(0xFF);
	    SD_CARD_Clr_nCS;
	 
	    retry=0;
	    do{
	        temp=SD_CARD_Write_CMD(CMD);
	        retry++;
	        if(retry==200)
	            return INIT_CMD0_ERROR;
	    }while(temp!=1);
	 
	    CMD[0]=0x41;// CMD[1]
	    CMD[5]=0xFF;
	    retry=0;
	    do{
	        temp=SD_CARD_Write_CMD(CMD);
	        retry++;
	        if(retry==100)   
	            return INIT_CMD1_ERROR;
	    }while(temp!=0);
	 
	    SD_CARD_Set_nCS; // 失能SD卡
	     
	    return 0x55; // 初始化完成
         }
	 
	 
	// 写入一个块(扇区)
	uint8_t SD_CARD_Write_Sector(uint32_t addr,uint8_t *buf)
	{
	    uint8_t temp,retry;
	    uint16_t i;
	    uint8_t CMD[]={0x58,0x00,0x00,0x00,0x00,0xFF}; // CMD24
	 
	    // 地址转换:逻辑块(扇区)地址 --> 字节地址
	    addr=addr << 9;
	    CMD[1]=((addr & 0xFF000000) >>24 );
	    CMD[2]=((addr & 0x00FF0000) >>16 );
	    CMD[3]=((addr & 0x0000FF00) >>8 );
	 
	 
	    retry=0;
	    do{
	        temp=SD_CARD_Write_CMD(CMD);
	        retry++;
	        if(retry==100)
	            return temp;
	    } while(temp!=0);
	 
	    // 写数据前,先发100个脉冲;100/8=13
	    for(i=0;i<13;i++) SD_CARD_SPI_Transfer(0xFF);
	 
	    // 写入开始字节
	    SD_CARD_SPI_Transfer(0xFE);
	 
	    // 写入512个字节
	    for(i=0;i<512;i++) SD_CARD_SPI_Transfer(*buf++);
	 
	    SD_CARD_SPI_Transfer(0xFF); // 假读;读取CRC
	    SD_CARD_SPI_Transfer(0xFF); // 假读;读取CRC
	 
	    // 读取响应
	    temp=SD_CARD_SPI_Transfer(0xFF);
	    if( (temp & 0x1F)!=0x05 ) {// 数据被接收否?   
	        SD_CARD_Set_nCS; // 失能SD卡       
	        return WRITE_BLOCK_ERROR;
	    }
	 
	    // 等待,只到SD卡不忙
	    while(SD_CARD_SPI_Transfer(0xFF)!=0xFF);
	 
	    SD_CARD_Set_nCS; // 失能SD卡
	     
	    return 0;
	}
	 
	 
	// 读取块(扇区)内的字节(一般情况下,1块对应512字节)
	uint8_t SD_CARD_Read_Sector(uint8_t *CMD,uint8_t *buf,uint16_t n_bytes)
	{
	    uint16_t i;
	    uint8_t retry,temp;
	 
	    retry=0;
	    do{
	        temp=SD_CARD_Write_CMD(CMD);
	        retry++;
	        if(retry==100)
	            return READ_BLOCK_ERROR;
	    }while(temp!=0);
	 
	    // 读开始字节(0xFEh)
	    while(SD_CARD_SPI_Transfer(0xFF)!=0xFE);
	     
	    // 读取n个字节
	    for(i=0;i 字节地址
	    sector=sector << 9;
	    CMD[1]=((sector & 0xFF000000) >>24 );
	    CMD[2]=((sector & 0x00FF0000) >>16 );
	    CMD[3]=((sector & 0x0000FF00) >>8 );
	 
	    retry=0;
	    do{
	        temp=SD_CARD_Write_CMD(CMD);
	        retry++;
	        if(retry==100)
	            return READ_BLOCK_ERROR;
	    }while(temp!=0);
	 
	    // 读开始字节(0xFEh)
	    while(SD_CARD_SPI_Transfer(0xFF) != 0xFE);
	 
	    gSectorOpened = 1; // 将扇区打开标志置一
	     
	    return 0;
	}
	 
	 
	static void SD_CARD_Read_Data(uint16_t n_bytes, uint8_t *buf)
	{
	    uint16_t i;
	    for(i=0; ((i<512)); i++) {
	        *buf++ = SD_CARD_SPI_Transfer(0xFF);
	        gByteOffset++; // 读完一个字节;将扇区内的字节偏移地址加一
	    }
	    if(gByteOffset == 512) {
	        gByteOffset=0; // 将扇区内的字节偏移地址清零
	         
	        SD_CARD_SPI_Transfer(0xFF); // 假读;读取CRC
	        SD_CARD_SPI_Transfer(0xFF); // 假读;读取CRC
	         
	        gSectorOffset++; // 读完一个扇区;将SD卡内的扇区偏移地址加一
	         
	        gSectorOpened = 0; // 读完一个扇区后,扇区打开标志清零
	        SD_CARD_Set_nCS; // 失能SD卡
	    }
	}
	 
	 
	// 读取指定逻辑块地址(扇区偏移地址)内的数据;logic block address,LBA
	void SD_CARD_Read_Data_LBA(uint32_t LBA,uint16_t n_bytes,uint8_t *buf)
	{   // 如果某扇区被读完,则打开下一个扇区
	    if(gByteOffset == 0) while(SD_CARD_Read_Sector_Start(LBA));
	    SD_CARD_Read_Data(n_bytes,buf);
	}
	 
	 
	// 假读;读完某扇区内剩余的字节
	void SD_CARD_Read_Sector_End(void)
	{
	    uint8_t temp[1];
	    while((gByteOffset!=0x00) | (gSectorOpened==1))
	        SD_CARD_Read_Data(1,temp); // 假读
	}
	 
	 
	// 读SD卡的CSD寄存器
	static uint8_t SD_CARD_Read_CSD(uint8_t *buf)
	{
	    uint8_t CMD[]={0x49,0x00,0x00,0x00,0x00,0xFF};
	    return SD_CARD_Read_Sector(CMD,buf,16); // 读取16个字节
	}
	 
	 
	// 读SD卡的CID寄存器
	static uint8_t SD_CARD_Read_CID(uint8_t *buf)
	{
	    uint8_t CMD[]={0x4A,0x00,0x00,0x00,0x00,0xFF};
	    return SD_CARD_Read_Sector(CMD,buf,16); // 读取16个字节
	}
	 
	 
	void SD_CARD_Get_Info(void)
	{
	    CID_Info_STR CID;
	    CSD_Info_STR CSD;
	 
	    SD_CARD_Read_CID(CID.data);
	    SD_CARD_DEBUG("SD-CARD CID:\r");
	    SD_CARD_DEBUG("    Manufacturer ID(MID): 0x%.2X\r", CID.CID.MID);
	    SD_CARD_DEBUG("    OEM/Application ID(OLD): %c%c\r", CID.CID.OLD[0], CID.CID.OLD[1]);
	    SD_CARD_DEBUG("    Product Name(PNM): %c%c%c%c%c\r", CID.CID.PNM[0], CID.CID.PNM[1], CID.CID.PNM[2], CID.CID.PNM[3], CID.CID.PNM[4]);
	    SD_CARD_DEBUG("    Product Revision: 0x%.2X\r", CID.CID.PRV);
	    SD_CARD_DEBUG("    Serial Number(PSN): 0x%.2X%.2X%.2X%.2X\r", CID.CID.PSN[0], CID.CID.PSN[1], CID.CID.PSN[2], CID.CID.PSN[3]);
	    SD_CARD_DEBUG("    Manufacture Date Code(MDT): 0x%.1X%.2X\r", CID.CID.MDT[0] & 0x0F, CID.CID.MDT[1]);
	    SD_CARD_DEBUG("    CRC-7 Checksum(CRC7):0x%.2X\r", CID.CID.CRC >> 1);
	 
	    SD_CARD_Read_CSD(CSD.data);
	    CSD.C_SIZE = ((CSD.data[6]&0x03) << 10) | (CSD.data[7] << 2) | ((CSD.data[8]&0xC0) >>6);
	    CSD.C_SIZE_MULT = ((CSD.data[9]&0x03) << 1) | ((CSD.data[10]&0x80) >> 7);
	    CSD.READ_BL_LEN = (CSD.data[5]&0x0F);
	    CSD.capacity_MB = (((CSD.C_SIZE)+1) << (((CSD.C_SIZE_MULT) +2) + (CSD.READ_BL_LEN))) >> 20;
	    SD_CARD_DEBUG("SD-CARD CSD:\r");
	    SD_CARD_DEBUG("    max.read data block length: %d\r", 1<<512; i++) {
	        if(i%16 == 0) SD_CARD_DEBUG("0x%.3X ", i);
	        SD_CARD_DEBUG("%.2X ", buf[i]);
	        if((i+1) % 8 == 0) SD_CARD_DEBUG(" ");
	        if((i+1) % 16 == 0) SD_CARD_DEBUG("\r");
	    }
	    SD_CARD_DEBUG("读取SD卡的第一个块(扇区)完毕\r");
	}