标签:
CC2541不知道现在还有没有人用,当初算是BLE芯片里头资料比较丰富的一个,只是硬件资源太菜了,51内核真是捉急。去年因为某些原因,在上面实现了SD卡驱动,估计还没有人做过,现在把过程发出来,让大家瞧瞧。
CC2541本来是用于低功耗蓝牙通信的,一般也不会有人用来扩展SD卡。不过肯定还是存在一些特殊需求用户,对吧。高速的SD卡驱动一般都用SDIO总线,CC2541自然只能用SPI总线了,速度不会太快,SPI时钟只能到4M。而且受限于CC2541的BLE协议栈,不能存太多的东西,否则时间过长会影响其他的任务的。当然,如果只是单纯的存数据,不开BLE无线连接除外。
目前单片机端比较好用的SD卡驱动就是FATFS,http://www.elm-chan.org/fsw/ff/00index_e.html,这个驱动真是非常好用,只要你实现底层的SPI驱动函数就行,比如初始化、SPI读、SPI写等等。支持FAT32和FAT,足够了。
http://www.elm-chan.org/fsw/ff/ffsample.zip里头有各种单片机的实现,可以稍微借鉴一下,不过感觉对我的帮助不是太大。
主要的实现文件是这个,http://www.elm-chan.org/fsw/ff/ff11.zip。先说说里头的文件:
ff.c ff.h ffconf.h这三个是核心文件,一个ff.c就搞定了FAT32文件系统,真是NIU。ffconf.h是编译配置文件,可以通过更改配置裁剪文件大小,要不然CC2541的小flash根本装不下。我用的是CC2541F256版本,256K的flash,128K的绝对不行。
diskio.c diskio.h就是底层接口文件了,你可以在其他地方实现你的SPI读写函数,然后在这里头把函数写上就行。ff.c会去调用diskio.c,然后diskio.c会去调用你的SPI驱动。
integer.h是它的类型定义。syscall.c有一些关于操作同步的东西,可以忽略。不过如果使用长文件名的话,这个要包含,因为里头需要你实现你的内存分配函数来存储文件名。
unicode.c如果使用长文件名,需要自行实现unicode文件名映射,这个unicode.c链接了你的unicode字符映射。GBK的映射太恐怖了,708K,根本不敢用。只能使用小字符集的ccsbcs.c,这个才20K。如果不使用长文件名,syscall.c unicode.c ccsbcs.c这些都不需要。不过在条件允许的条件下还是建议使用长文件名,反正只需要英文,多个20K也不算多,文件名可以是你的采样时间等等,方便区分。
以上是需要的文件,下面先说说SPI驱动的事儿,再来讨论长文件名的事儿。一开始的话配置直接默认就行,先让SD卡能工作再说。
首先是SPI驱动。你需要实现以下这几个函数
DSTATUS disk_initialize(uint8 pdrv) //初始化的,pdrv代表设备号,一般默认就是0
DSTATUS disk_status( //获取设备状态
uint8 pdrv//Physical drive nmuber (0..)
)
DRESULT disk_read( //SPI读,需要注意,对于非SDHC的SD卡,这个sector参数是需要乘以512的
BYTE pdrv,//Physical drive nmuber (0..)
BYTE *buff,//Data buffer to store read data
DWORD sector,//Sector address (LBA)
UINT count//Number of sectors to read (1..128)
)
DWORD get_fattime() //获取时间,不管也行,但是你的文件的时间就全是初始时间了,好像是1978年
DRESULT disk_write ( // SPI写,跟SPI读一样
BYTE pdrv, /* Physical drive nmuber (0..) */
const BYTE *buff, /* Data to be written */
DWORD sector, /* Sector address (LBA) */
UINT count /* Number of sectors to write (1..128) */
)
DRESULT disk_ioctl ( //获取SD卡的寄存器的状态,这个没有特殊需求就不用
BYTE pdrv, /* Physical drive nmuber (0..) */
BYTE cmd, /* Control code */
void *buff /* Buffer to send/receive control data */
)
好了,首先来实现最棘手的初始化步骤。SD卡的初始化步骤非常令人恼火,网上一搜问题到处都是。
DSTATUS disk_initialize(uint8 pdrv)/* Physical drive nmuber (0..) */
{
DSTATUS stat = 0;
if (pdrv) return STA_NODISK; // only support 1 device
if (!hal_sd_card_init()) // init succeed
{
sd_status_L &= ~STA_NOINIT;//sd_status_L是全局变量,指示当前sd卡的状态,用在disk_status()函数里头
stat = 0;
}
else
stat = STA_NOINIT;
return stat;
}
这是初始化的函数,调用了hal_sd_card_init()函数,这个才是关键。
下面是sd卡的初始化函数,不要被吓到。其实关键的步骤就是这么几步:SPI初始化时钟不能太高,推荐是250K;然后在关闭CS的状态下连续发送10个SPI字节,也就是80个脉冲。网上一直在说需要至少74个脉冲,我找了一下SD卡的官方手册,https://www.sdcard.org/downloads/pls/part1_410.pdf,貌似没有提到这事儿。不过无所谓,照做就行。然后再打开CS,最关键的一步来了,让SD卡进入IDLE模式。如果SD卡能真正进入该模式,说明你的SD卡对你的命令有反映了,下面的工作基本就是顺理成章的事儿。否则得让你郁闷死。
插一句,SD卡发送命令有一个比较严格的流程,在SD卡手册和FATFS的说明都有提到。这里直接放代码
static uint8 sd_wait_ready(void) // wait for sd ready等待SD卡准备好
{
uint16 i;
for (i=1000;i>0;i--)//总共等个1s
{
yy=hal_sd_byte(0xff);//这里不要管yy...之前测试用的
if (yy == 0xff) break;// if card is ready, miso line will be set to high如果SD卡准备好了,它的MISO必须是高电平,否则是低电平
hal_delay_us(1000);//等个1ms
}
if (i) return (0); // succeed
return (1); // error
}
static uint8 hal_sd_cmd( uint8 cmd, uint32 arg ) //发送SD卡命令函数,SPI时钟不能太高,cmd是命令号,arg是命令的参数
{
uint8 i;
uint8 rsp;
halIntState_t intState;//这个是用来禁用CC2541的中断的,发送命令的时候如果中途有中断会导致SD卡通信失败。可有可无吧
//should be sure that cs is low before call this function//必须先保证SPI时钟为250K,官方是要求400K以下
if (sd_wait_ready())// wait for ready等待SD卡准备好
return 0xff; // error
HAL_ENTER_CRITICAL_SECTION(intState); // sending process shouldn‘t be interrupted//禁用中断
hal_sd_byte(0x40 | cmd); //cmd starts with 01 as 01xxxxxx, total 64 cmds//这个就是命令格式了,无需多解释
hal_sd_byte((arg >>24) &0x000000ff); // high order byte first!
hal_sd_byte((arg >>16) &0x000000ff);
hal_sd_byte((arg >>8) &0x000000ff);
hal_sd_byte((arg ) &0x000000ff);
switch (cmd)//send CRC//某些命令需要CRC校验
{
case CMD_GO_IDLE_STATE: // this cmd requires correct crc
hal_sd_byte(0x95);break;
case CMD_SEND_IF_COND: // this cmd requires correct crc
hal_sd_byte(0x87);break;
default: // don‘t need correct crc
hal_sd_byte(0xff);break;
}
HAL_EXIT_CRITICAL_SECTION(intState); // sending process shouldn‘t be interrupted
//send buffer clocks to insure card has finished all operations...
if (cmd == CMD_STOP_TRANSMISSION) hal_sd_byte(0xff);//skip one byte on CMD_STOP_TRANSMISSION//这个命令需要额外的一个字节时间
for( i=0; i<8; i++ ) // sd card should reply message in 8 bytes命令应该会在8个字节的时间以内返回
{
rsp = hal_sd_byte(0xff);
if(rsp != 0xff) return(rsp);//if it isn‘t 0xff, it is a response, so quit//如果不是0xff,说明有东西返回了,如果是0xff说明悲剧了
}
return (rsp); // if no correct rsp is received, return 0xff
}
uint8 hal_sd_card_init(void) // init sd card
{
uint16 i; // local counter
uint8 buf[4];
uint8 stat = 0;
sd_spi_250k(); //initial speed is slow //时钟不能太高!!!!!
sd_cs_disable(); // disable cs first!
//fill send data with all ones - 80 bits long to establish link with SD card
//this fulfills the 74 clock cycle requirement...
for(i = 0; i < 10 ; i++)
hal_sd_byte(0xff);
sd_cs_enable(); // the sd should have enter spi mode, so enable cs
if ( hal_sd_cmd(CMD_GO_IDLE_STATE, 0) == 0x01) // force to enter idle state发送IDLE指令,成功的话会返回0x01
{
if (hal_sd_cmd(CMD_SEND_IF_COND, 0x1aa) != 0x05)
// SDv1 test这个指令测试是不是SDv1卡,0x1aa只是一个测试代码,如果它真是SDv1卡,那么指令会返回0x05,
{ // not sd v1, so perhaps be sdv2 or sdhc
for(i=0;i<4;i++) // receive reply
{
buf[i] = hal_sd_byte(0xff);
}
if (buf[2] == 0x01 && buf[3] ==0xAA) // test the reply, if it‘s correct, it should contain the test bits we send as 0x1AA
{ // must be sdv2 or sdhc
for(i=1000;i>0;i--)//leave idle state, acmd41 with hcs (bit30) set
{
hal_sd_cmd(CMD_APP_CMD, 0); // send this cmd first before you send any ACMD
if (hal_sd_cmd(ACMD_SEND_OP_COND, 0x40000000) == 0)
break;
hal_delay_us(1000);
}
if (i) // not time out
{
if (hal_sd_cmd(CMD_READ_OCR, 0) == 0)
{
for(i=0;i<4;i++)
{
buf[i] = hal_sd_byte(0xff);
}
if (buf[0] & 0x40)
sd_type_L = SD_HC;
else
sd_type_L = SD_V2;
}
else // sending cmd error
stat = 1;
} // not time out
else // time out
stat = 1;
} // reply correct
else // result error
stat = 1;
} // not sdv1
else // perhaps SDv1
{
hal_sd_cmd(CMD_APP_CMD, 0);
if (hal_sd_cmd(ACMD_SEND_OP_COND, 0) <= 1) // sdv1!
{
sd_type_L = SD_V1;
for(i=1000;i>0;i--)//leave idle state
{
hal_sd_cmd(CMD_APP_CMD, 0);
if (hal_sd_cmd(ACMD_SEND_OP_COND, 0) == 0) break;
hal_delay_us(1000);
}
if (!i) // time out!
stat = 1;
}
else // mmcv3, not commonly seen
{
sd_type_L = MMC_V3;
for(i=1000;i>0;i--)//leave idle state
{
if (hal_sd_cmd(CMD_SEND_OP_COND, 0) == 0) break;
hal_delay_us(1000);
}
if (!i) // time out
stat = 1;
}
}
if (stat && (sd_type_L == SD_V1) || (sd_type_L == SD_V2)) // sdv1 and sdv2 card‘s block should be set to 512
{ // sdhc card‘s block is internally set to 512, so this cmd is ignored
if( hal_sd_cmd( CMD_SET_BLOCKLEN, 512 ) != 0 ) // sending cmd error
stat = 1;
}
} // leave idle state?
else // can‘t even enter idle state
{
stat = 1;
}
if (!stat)//everything is ok
{
sd_spi_4M(); // change spi speed to high
}
sd_cs_disable();
if (stat)
{
sd_type_L = INVALID_CARD;
}
return stat;
}
标签:
原文地址:http://www.cnblogs.com/Scienve/p/4446099.html