[笔记].菜农M0助学板之读SD卡块内容小练(库操作方式)
已发贴至:http://bbs.21ic.com/icview-231086-1-1.html
之前是移植到Nios II上,而且是用得IO模拟的,现在使用nuc1xx的spi核做的,不过还是参考了新唐的例程。关于sd的一些笔记,我贴在博客了。今天在此就不赘述了。http://www.cnblogs.com/yuphone/category/272287.html
上代码先。
main.h
#ifndef __MAIN_H__ #define __MAIN_H__ #include <stdio.h> /********************************************************* * 系统寄存器映射及库头文件 *********************************************************/ #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, ¶m); // 串口开启、结构体整体赋值 // 串口的中断类型比较丰富,此处仅打开接收中断 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<n_bytes;i++) *buf++=SD_CARD_SPI_Transfer(0xFF); SD_CARD_SPI_Transfer(0xFF); // 假读;读取CRC SD_CARD_SPI_Transfer(0xFF); // 假读;读取CRC SD_CARD_Set_nCS; // 失能SD卡 return 0; } // 开始读取某个扇区 uint8_t SD_CARD_Read_Sector_Start(uint32_t sector) { uint8_t retry; uint8_t CMD[]={0x51,0x00,0x00,0x00,0x00,0xFF}; // CMD16 uint8_t temp; // 地址转换:逻辑块(扇区)地址 --> 字节地址 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<n_bytes) && (gByteOffset<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<<CSD.READ_BL_LEN); SD_CARD_DEBUG(" device size: %d\r", CSD.C_SIZE); SD_CARD_DEBUG(" device size multiplier: %d\r", CSD.C_SIZE_MULT); SD_CARD_DEBUG(" device capacity: %d MB\r", CSD.capacity_MB); } void SD_CARD_DEMO(void) { uint16_t i; uint8_t buf[512]; SD_CARD_DEBUG("\r"); SD_CARD_DEBUG("开始读取SD卡信息\r"); SD_CARD_Get_Info(); // 读取CID和CSD SD_CARD_DEBUG("SD卡信息读取完毕\r"); SD_CARD_DEBUG("\r"); SD_CARD_DEBUG("开始读取SD卡的第一个块(扇区)\r"); SD_CARD_Read_Data_LBA(0,512,buf); // 读取SD卡的第一个块(扇区) for(i=0; i<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"); }