STM32F407单片机通过FATFS文件系统写SD卡正确率只有98%,但是偶而出错,该如何彻底根治

ba_wang_mao 2021-04-08 09:09:32
我的单片机每隔10分钟自动重启(有重启电路,每次大概运行2分钟,运行完毕,自动关闭电源,下个整点到时,自动重启)。

每次运行时,先采集三路模拟量数据,然后将采集的模拟量数据写到SD卡,每个模拟量数据写一行,3个模拟量数据写三行。
经过长期运行观察,正确写SD卡的成功率达到98%,但是始终有2%的错误,发现偶而出现如下四种错误现象。

1、有时候SD卡的文件中会漏写三行。(注:共采集三路模拟量数据,每路模拟量数据写一行,3路模拟量数据写三行)
即:该次的数据没有被写到SD卡的文件中。
2、有时候SD卡的文件中会漏写二行。(注:共采集三路模拟量数据,每路模拟量数据写一行,3路模拟量数据写三行)
即:该次的数据只有一路模拟量被写到SD卡的文件中,另外二路没有被写到SD卡的文件中。
3、有时候SD卡的文件中会漏写一行。(注:共采集三路模拟量数据,每路模拟量数据写一行,3路模拟量数据写三行)
即:该次的数据只有二路模拟量被写到SD卡的文件中,另外一路没有被写到SD卡的文件中。
4、有时候创建的文件不能写入数据到文件中,该文件是个空文件,拨掉SD卡,插入电脑插口,只要拷贝该空文件到电脑上,就报错。



/*********************************************************************************************************
* 说 明 : 本例程移植FatFS文件系统(版本 R0.09b)
*********************************************************************************************************/
void PRINT_FileFatFS(void)
{
FRESULT result;
FATFS fs;
FIL file;
DIR DirInf;
uint32_t bw;
uint32_t fil_size;
char FileName[50];


if (!GPIO_SD_READY())
{
//printf("插槽中没有插入SD卡\r\n");
return ;
}

/* 挂载文件系统 */
result = f_mount(FS_SD, &fs); /* Mount a logical drive */
if (result != FR_OK)
{
//printf("挂载文件系统失败 (%d)\r\n", result);
return ;
}

/* 打开根文件夹 */
result = f_opendir(&DirInf, "/"); /* 如果不带参数,则从当前目录开始 */
if (result != FR_OK)
{
//printf("打开根目录失败 (%d)\r\n");
/* 卸载文件系统 */
f_mount(FS_SD, NULL);
return ;
}


/* 打开文件 */

strcpy(FileName ,"TEST.csv");
result = f_open(&file, FileName, FA_OPEN_ALWAYS | FA_WRITE);


if (result == FR_OK)
{
fil_size = f_size(&file);
result = f_lseek(&file,fil_size);
if (result == FR_OK)
{
Printf_FATFS_COM_Buffer(enum_COM1_CHANNEL_NO); //生成第一路模拟量数据
result = f_write(&file, FATFS_COM_Buffer , strlen(FATFS_COM_Buffer), &bw); //往SD卡写第一路模拟量数据

Printf_FATFS_COM_Buffer(enum_COM2_CHANNEL_NO); //生成第二路模拟量数据
result = f_write(&file, FATFS_COM_Buffer , strlen(FATFS_COM_Buffer), &bw); //往SD卡写第二路模拟量数据


Printf_FATFS_COM_Buffer(enum_COM3_CHANNEL_NO); //生成第三路模拟量数据
result = f_write(&file, FATFS_COM_Buffer , strlen(FATFS_COM_Buffer), &bw); //往SD卡写第三路模拟量数据
}//if (result == FR_OK)
}//if (result == FR_OK)

/* 关闭文件*/
f_close(&file);
/* 卸载文件系统 */
f_mount(FS_SD, NULL);
}
...全文
2643 21 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
21 条回复
切换为时间正序
请发表友善的回复…
发表回复
ba_wang_mao 2021-04-20
  • 打赏
  • 举报
回复
我做了如下改进: 1、写完第一行后,延时一段时间 WatchDog_Delay(50000); 2、写完第二行后,延时一段时间 WatchDog_Delay(50000); 3、写第三行 又连续运行3天,没有发现任何错误。 难道是写完第一行数据时,立即写第二行数据,文件系统反应不过来吗?
ba_wang_mao 2021-04-17
  • 打赏
  • 举报
回复
我做了如下改进: 1、写完第一行后,延时一段时间 WatchDog_Delay(50000); 2、写完第二行后,延时一段时间 WatchDog_Delay(50000); 3、写第三行 连续运行2天,没有发现任何错误。 难道是写完第一行数据时,立即写第二行数据,文件系统反应不过来吗?
ba_wang_mao 2021-04-17
  • 打赏
  • 举报
回复
我检查了堆栈没有问题,我设置KEIL堆栈为800。 1、每个函数中的局部变量都不大 2、每个函数的入口参数占用的空间也不大 下面是我写的三行固定字符串,字符串长度=332
11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111
11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111
11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111
ba_wang_mao 2021-04-16
  • 打赏
  • 举报
回复
引用 15 楼 dceacho 的回复:
如果都是固定间隔错误,会不会是底层驱动有问题,比如有跨页
是随机错误。 一、以下四种情况,从来都不会发生错误
  1、 /* 挂载文件系统 */
    2、/* 打开根文件夹 */
    3、 /* 打开文件 */
    4、/*移动文件指针*/
   从来都不会发生错误
二、只有当打开文件后,往文件中写数据时才会出错,错误是随机发生的 我每次写三行数据。 有时候写第一行数据到文件时,result != FR_OK 有时候写第二行数据到文件时,result != FR_OK 有时候写第三行数据到文件时,result != FR_OK 三、我把三行数据改成固定字符串,长度=332(没有超过512),当打开文件后,往文件中写数据时才会出错,错误是随机发生的。 我每次写三行数据。 有时候写第一行数据到文件时,result != FR_OK 有时候写第二行数据到文件时,result != FR_OK 有时候写第三行数据到文件时,result != FR_OK
dceacho 2021-04-16
  • 打赏
  • 举报
回复
如果都是固定间隔错误,会不会是底层驱动有问题,比如有跨页
dceacho 2021-04-16
  • 打赏
  • 举报
回复
引用
我怀疑是由于单片机堆栈空间分配不足造成的
出错时记录下 FATFS fs; FIL file; 的值,如果哪里有溢出会不会导致FATFS系统崩溃,导致写入SD卡的是一个错误的格式的文件, 这样写入没问题,但文件已经崩了
worldy 2021-04-16
  • 打赏
  • 举报
回复
lz你是如何检查并发现了错误的?通过串口读回?
ba_wang_mao 2021-04-13
  • 打赏
  • 举报
回复
不会吧! 1、 /* 挂载文件系统 */ 2、/* 打开根文件夹 */ 3、 /* 打开文件 */ 4、/*移动文件指针*/ 从来都不会发生错误 错误只出现在 5、/*写数据到文件*/ result = f_write(&file, FATFS_COM_Buffer , strlen(FATFS_COM_Buffer), &bw); 结果连续运行3天(每小时写6次,1天写144次,3天写432次),结果发现如下规律,总共错误11次,错误都发生在FATFS文件系统【5./*写数据到文件(/】这一步其它4种情况从来都没有出过错。 以下是SD卡文件中缺失的数据,原因是执行 result = f_write(&file, FATFS_COM_Buffer , strlen(FATFS_COM_Buffer), &bw); 时返回result != FR_OK (注:单片机每次向SD卡文件中写三行数据) 2021.04.08 21:50 SD卡文件中缺失 第二行数据 SD卡文件中缺失 第三行数据 2021.04.09 00:20 SD卡文件中缺失 第三行数据 2021.04.09 03:00 SD卡文件中缺失 第三行数据 2021.04.09 06:40 SD卡文件中缺失 第三行数据 2021.04.09 08:00 SD卡文件中缺失 第一行数据 2021.04.09 08:00 SD卡文件中缺失 第二行数据 2021.04.09 08:00 SD卡文件中缺失 第三行数据 2021.04.09 10:30 SD卡文件中缺失 第三行数据 2021.04.12 10:30 SD卡文件中缺失 第二行数据 2021.04.12 10:30 SD卡文件中缺失 第三行数据
dceacho 2021-04-12
  • 打赏
  • 举报
回复
会不会是你的SD卡质量不行?
ba_wang_mao 2021-04-12
  • 打赏
  • 举报
回复
我对写SD卡的所有文件系统操作相关部分都添加追踪记录方面的代码,只有发现操作失败(result != FR_OK)一次就记录一次。 我对文件系统以下操作部分添加了追踪代码
   1、 	/* 挂载文件系统 */
	result = f_mount(FS_SD, &fs);		/* Mount a logical drive */

    2、	/* 打开根文件夹 */
	result = f_opendir(&DirInf, "/"); 	/* 如果不带参数,则从当前目录开始 */

    3、   /* 打开文件 */	
            result = f_open(&file, FileName, FA_OPEN_ALWAYS | FA_WRITE);

    4、/*移动文件指针*/
             	fil_size = f_size(&file);
	result = f_lseek(&file,fil_size);

    5、写数据到文件
         result = f_write(&file, FATFS_COM_Buffer , strlen(FATFS_COM_Buffer), &bw);
结果连续运行3天(每小时写6次,1天写144次,3天写432次),结果发现如下规律,总共错误11次,错误都发生在FATFS文件系统【5.写数据到文件】这部分,其它4种情况从来都没有出过错。
ba_wang_mao 2021-04-09
  • 打赏
  • 举报
回复
我怀疑是由于单片机堆栈空间分配不足造成的,原因是FATFS文件系统太占用空间啦,MAX_SS分配了512字节,而且我的子程序内部又分配了100多个字节, 因此不应该将 FATFS fs; FIL file; 定义成局部变量,而应该定义成全局变量,这样就能减少堆栈的占用空间。 1、我的单片机工程项目堆栈分配了800个WORD,参见下图。 2、FATFS的FIL结构体太占用空间啦!我有2个写SD卡的函数,每个函数都在内部定义了局部变量 FIL file; 3、FATFS的FATFS结构体太占用空间啦!我有2个写SD卡的函数,每个函数都在内部定义了局部变量 FATFS fs; 4、函数1 5、函数2 6、MAX_SS分配了512字节 我准备将FATFS fs; FIL file; 定义成全局变量,然后每个环节如果出错就记录到我板子上的I2C芯片中,连续运行7天,如果没有错误,就说明是堆栈溢出的问题。
IamReason 2021-04-09
  • 打赏
  • 举报
回复
引用 9 楼 IamReason 的回复:
[quote=引用 8 楼 ba_wang_mao 的回复:][quote=引用 5 楼 IamReason 的回复:]以我的经验,和版本没关系
是不是你一次写入的数据太长了


我每一行都写332个字符。
单片机上电运行一次,共写3行,每一行都写332个字符。[/quote]

写的太长,第一写入128个应该就好了[/quote]

每次写入,判断一下是否成功,如果不成功重写一次
IamReason 2021-04-09
  • 打赏
  • 举报
回复
引用 8 楼 ba_wang_mao 的回复:
[quote=引用 5 楼 IamReason 的回复:]以我的经验,和版本没关系
是不是你一次写入的数据太长了


我每一行都写332个字符。
单片机上电运行一次,共写3行,每一行都写332个字符。[/quote]

写的太长,第一写入128个应该就好了
ba_wang_mao 2021-04-08
  • 打赏
  • 举报
回复
下面是SPI硬件中断配置

void SPI1_Configuration(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	SPI_InitTypeDef SPI_InitStructure;

	
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);

	GPIO_PinAFConfig(GPIOA, GPIO_PinSource5, GPIO_AF_SPI1);
	GPIO_PinAFConfig(GPIOA, GPIO_PinSource6, GPIO_AF_SPI1);
	GPIO_PinAFConfig(GPIOA, GPIO_PinSource7, GPIO_AF_SPI1);
	
	//Configure SPI1 Pins: SCK, MISO and MOSI
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
	GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;	
	GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_UP;
  	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	//Configure NSS Pin
	//GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
	GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;	
	GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_UP;
  	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;		//CS/NSS
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  	GPIO_Init(GPIOA, &GPIO_InitStructure);
	GPIO_SetBits(GPIOA, GPIO_Pin_4);//不选中(关闭片选)--->低电平选通SD卡

	SPI_I2S_DeInit(SPI1);
	RCC_APB2PeriphResetCmd(RCC_APB2Periph_SPI1,ENABLE);	//复位SPI1
	RCC_APB2PeriphResetCmd(RCC_APB2Periph_SPI1,DISABLE);//停止复位SPI1
	
	SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;	//双线双向全双工
	SPI_InitStructure.SPI_Mode = SPI_Mode_Master;		//主器件
	SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;	//8位数据长度
	SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;   		//这里要注意,一定要配置为上升沿数据有效,因为SD卡为上升沿数据有效
	SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;		//这里要注意,一定要配置为SPI_CPHA_2Edge(数据捕获于第2个时钟沿),参见SD卡协议要求
	SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;			//NSS信号由外部管脚管理
	SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256;//SPI速度为低速
	SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;	//数据传输的第一个字节为MSB
	SPI_InitStructure.SPI_CRCPolynomial = 7;			//CRC的多项式
	SPI_Init(SPI1,&SPI_InitStructure);
	SPI_Cmd(SPI1,DISABLE);
	SPI_Cmd(SPI1,ENABLE);
	
	
}



//SPI 速度设置函数
//SpeedSet:
//SPI_BaudRatePrescaler_2   2分频   (SPI 36M@sys 72M)
//SPI_BaudRatePrescaler_8   8分频   (SPI 9M@sys 72M)
//SPI_BaudRatePrescaler_16  16分频  (SPI 4.5M@sys 72M)
//SPI_BaudRatePrescaler_256 256分频 (SPI 281.25K@sys 72M)
void SPI1_SetSpeed(uint8_t SpeedSet)
{
	
    SPI_InitTypeDef SPI_InitStructure;
 
	
	SPI_I2S_DeInit(SPI1);
	RCC_APB2PeriphResetCmd(RCC_APB2Periph_SPI1,ENABLE);	//复位SPI1
	RCC_APB2PeriphResetCmd(RCC_APB2Periph_SPI1,DISABLE);//停止复位SPI1
	SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;	//双线双向全双工
	SPI_InitStructure.SPI_Mode = SPI_Mode_Master;		//主器件
	SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;	//8位数据长度
	SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;   		//这里要注意,一定要配置为上升沿数据有效,因为SD卡为上升沿数据有效
	SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;		//这里要注意,一定要配置为SPI_CPHA_2Edge(数据捕获于第2个时钟沿),参见SD卡协议要求
	SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;			//NSS信号由外部管脚管理

	switch (SpeedSet)
	{
		case enum_SPI_SPEED_LOW:			
			SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256;//设置到低速模式
			break;
		case enum_SPI_SPEED_HIGH:
			SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_16;	//设置到高速模式
			break;	
		default:
			SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256;//设置到低速模式
			break;			
	}

	SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;	//数据传输的第一个字节为MSB		
	SPI_InitStructure.SPI_CRCPolynomial = 7;			//CRC的多项式
	SPI_Init(SPI1,&SPI_InitStructure);
	SPI_Cmd(SPI1 , DISABLE);
	SPI_Cmd(SPI1 , ENABLE);
}



void SPI_WriteByte(uint8_t _ucByte)
{
	while(SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_TXE )==RESET);	//等待数据发送寄存器清空
	SPI_I2S_SendData(SPI1 , _ucByte);								//通过SPI发送出去一个字节数据
	while(SPI_I2S_GetFlagStatus(SPI1 , SPI_I2S_FLAG_RXNE )==RESET);	//等待接收到一个数据(接收到一个数据就相当于发送一个数据完毕)
	SPI_I2S_ReceiveData(SPI1);										//返回接收到的数据
}



uint8_t SPI_ReadByte(void)
{
	uint8_t ch;
	
	
	while(SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_TXE )==RESET);
	SPI_I2S_SendData(SPI1 , 0xFF);
	while(SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_RXNE )==RESET);
	ch = SPI_I2S_ReceiveData(SPI1);
	return (ch);
}



uint8_t SPI_ReadWriteByte(uint8_t _ucByte)
{  
	uint8_t ch;
	
	
	while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET);		// 等待发送缓冲区空
	SPI_I2S_SendData(SPI1, _ucByte);
	while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET);		// 等待数据接收完毕
	ch = SPI_I2S_ReceiveData(SPI1);
	return (ch);
}
ba_wang_mao 2021-04-08
  • 打赏
  • 举报
回复
下面是SD卡底层移植函数

/**************************************************************************************/
//SD卡SPI驱动程序
/**************************************************************************************/
#include "MMC_SD.h"
#include "BSP_SPI.h"



u8  SD_Type=0;//SD卡的类型 



//SD卡初始化的时候,需要低速
void SD_SPI_SpeedLow(void)
{
 	//SPI2_SetSpeed(SPI_BaudRatePrescaler_256);//设置到低速模式
	SPI1_SetSpeed(enum_SPI_SPEED_LOW);
}



//SD卡正常工作的时候,可以配置为高速
void SD_SPI_SpeedHigh(void)
{
	//SPI1_SetSpeed(SPI_BaudRatePrescaler_8);//设置到高速模式
	SPI1_SetSpeed(enum_SPI_SPEED_HIGH);
}



//取消选择,释放SPI总线
void SD_DisSelect(void)
{
	GPIO_SetBits(GPIOA,GPIO_Pin_4);
	SPI_WriteByte(0xff);//提供额外的8个时钟
}



//选择sd卡,并且等待卡准备OK
//返回值:0,成功;1,失败;
u8 SD_Select(void)
{
	GPIO_ResetBits(GPIOA,GPIO_Pin_4);
	if(SD_WaitReady()==0)return 0;//等待成功
	SD_DisSelect();
	return 1;//等待失败
}



//等待卡准备好
//返回值:0,准备好了;其他,错误代码
u8 SD_WaitReady(void)
{
	u32 t=0;
	do
	{
		if (SPI_ReadByte()==0XFF)return 0;//OK
		t++;
	}while(t<0XFFF);//等待 
	return 1;
}



//等待SD卡回应
//Response:要得到的回应值
//返回值:0,成功得到了该回应值
//    其他,得到回应值失败
u8 SD_GetResponse(u8 Response)
{
	u16 Count=0xFFFF;//等待次数
	while ((SPI_ReadByte()!=Response) && Count) Count--;//等待得到准确的回应(等待得到读取数据令牌0xfe)
	if (Count==0)
		return MSD_RESPONSE_FAILURE;	//得到回应失败
	else 
		return MSD_RESPONSE_NO_ERROR;	//正确回应
}



//从sd卡读取一个数据包的内容
//buf:数据缓存区
//len:要读取的数据长度.
//返回值:0,成功;其他,失败;
u8 SD_RecvData(u8*buf,u16 len)
{
	if (SD_GetResponse(0xFE)) return 1;//等待SD卡发回数据起始令牌0xFE
	while(len--)//开始接收数据
	{
		*buf=SPI_ReadByte();
		buf++;
	}
	//下面是2个伪CRC(dummy CRC)
	SPI_WriteByte(0xFF);
	SPI_WriteByte(0xFF);
	return 0;//读取成功
}



//向sd卡写入一个数据包的内容 512字节
//buf:数据缓存区
//cmd:指令
//返回值:0,成功;其他,失败;
u8 SD_SendBlock(u8*buf,u8 cmd)
{
	u16 t;
	if(SD_WaitReady())return 1;//等待准备失效
	SPI_WriteByte(cmd);
	if(cmd!=0XFD)//不是结束指令
	{
		for(t=0; t<512; t++)SPI_WriteByte(buf[t]); //提高速度,减少函数传参时间
		SPI_WriteByte(0xFF);//忽略crc
		SPI_WriteByte(0xFF);
		t=SPI_ReadByte();//接收响应
		if((t&0x1F)!=0x05)return 2;//响应错误
	}
	return 0;//写入成功
}



//向SD卡发送一个命令
//输入: u8 cmd   命令
//      u32 arg  命令参数
//      u8 crc   crc校验值
//返回值:SD卡返回的响应
u8 SD_SendCmd(u8 cmd, u32 arg, u8 crc)
{
	u8 r1;
	u8 Retry=0;
	
	SD_DisSelect();//取消上次片选
	if(SD_Select())return 0XFF;//片选失效
	//发送
	SPI_WriteByte(cmd | 0x40);//分别写入命令
	SPI_WriteByte(arg >> 24);
	SPI_WriteByte(arg >> 16);
	SPI_WriteByte(arg >> 8);
	SPI_WriteByte(arg);
	SPI_WriteByte(crc);
	if(cmd==CMD12)SPI_WriteByte(0xff);//Skip a stuff byte when stop reading
	//等待响应,或超时退出

	Retry=0X1F;
	do
	{
		r1=SPI_ReadByte();
	}
	while((r1&0X80) && Retry--);
	//返回状态值
	return r1;
}



//获取SD卡的CID信息,包括制造商信息
//输入: u8 *cid_data(存放CID的内存,至少16Byte)
//返回值:0:NO_ERR
//		 1:错误
u8 SD_GetCID(u8 *cid_data)
{
	u8 r1;
	//发CMD10命令,读CID
	r1=SD_SendCmd(CMD10,0,0x01);
	if(r1==0x00)
	{
		r1=SD_RecvData(cid_data,16);//接收16个字节的数据
	}
	SD_DisSelect();//取消片选
	if(r1)return 1;
	else return 0;
}



//获取SD卡的CSD信息,包括容量和速度信息
//输入:u8 *cid_data(存放CID的内存,至少16Byte)
//返回值:0:NO_ERR
//		 1:错误
u8 SD_GetCSD(u8 *csd_data)
{
	u8 r1;
	r1=SD_SendCmd(CMD9,0,0x01);//发CMD9命令,读CSD
	if(r1==0)
	{
		r1=SD_RecvData(csd_data, 16);//接收16个字节的数据
	}
	SD_DisSelect();//取消片选
	if(r1)return 1;
	else return 0;
}



//获取SD卡的总扇区数(扇区数)
//返回值:0: 取容量出错
//       其他:SD卡的容量(扇区数/512字节)
//每扇区的字节数必为512,因为如果不是512,则初始化不能通过.
u32 SD_GetSectorCount(void)
{
	u8 csd[16];
	u32 Capacity;
	u8 n;
	u16 csize;
	//取CSD信息,如果期间出错,返回0
	if(SD_GetCSD(csd)!=0) return 0;
	//如果为SDHC卡,按照下面方式计算
	if((csd[0]&0xC0)==0x40)	 //V2.00的卡
	{
		csize = csd[9] + ((u16)csd[8] << 8) + 1;
		Capacity = (u32)csize << 10;//得到扇区数
	}
	else //V1.XX的卡
	{
		n = (csd[5] & 15) + ((csd[10] & 128) >> 7) + ((csd[9] & 3) << 1) + 2;
		csize = (csd[8] >> 6) + ((u16)csd[7] << 2) + ((u16)(csd[6] & 3) << 10) + 1;
		Capacity= (u32)csize << (n - 9);//得到扇区数
	}
	return Capacity;
}



//初始化SD卡
u8 SD_Initialize(void)
{
	u8 r1;      // 存放SD卡的返回值
	u16 retry;  // 用来进行超时计数
	u8 buf[4];
	u16 i;
	

	SPI1_Configuration();
	SD_SPI_SpeedLow();	//设置到低速模式		

	for(i=0; i<15; i++)SPI_WriteByte(0XFF); //发送最少74个脉冲
	retry=2000;
	do
	{
		r1=SD_SendCmd(CMD0,0,0x95);//进入IDLE状态
	}
	while((r1!=0X01) && retry--);
	SD_Type=0;//默认无卡
	if(r1==0X01)
	{
		if(SD_SendCmd(CMD8,0x1AA,0x87)==1)//SD V2.0
		{
			for(i=0; i<4; i++)buf[i] = SPI_ReadWriteByte(0XFF);	//Get trailing return value of R7 resp
			if(buf[2]==0X01&&buf[3]==0XAA)//卡是否支持2.7~3.6V
			{
				retry=0XFFFE;
				do
				{
					SD_SendCmd(CMD55,0,0X01);	//发送CMD55
					r1=SD_SendCmd(CMD41,0x40000000,0X01);//发送CMD41
				}
				while(r1&&retry--);
				if(retry&&SD_SendCmd(CMD58,0,0X01)==0)//鉴别SD2.0卡版本开始
				{
					for(i=0; i<4; i++)buf[i] = SPI_ReadWriteByte(0XFF); //得到OCR值
					if(buf[0]&0x40)SD_Type=SD_TYPE_V2HC;    //检查CCS
					else SD_Type=SD_TYPE_V2;
				}
			}
		}
		else //SD V1.x/ MMC	V3
		{
			SD_SendCmd(CMD55,0,0X01);		//发送CMD55
			r1=SD_SendCmd(CMD41,0,0X01);	//发送CMD41
			if(r1<=1)
			{
				SD_Type=SD_TYPE_V1;
				retry=0XFFFE;
				do //等待退出IDLE模式
				{
					SD_SendCmd(CMD55,0,0X01);	//发送CMD55
					r1=SD_SendCmd(CMD41,0,0X01);//发送CMD41
				}
				while(r1&&retry--);
			}
			else
			{
				SD_Type=SD_TYPE_MMC;//MMC V3
				retry=0XFFFE;
				do //等待退出IDLE模式
				{
					r1=SD_SendCmd(CMD1,0,0X01);//发送CMD1
				}
				while(r1&&retry--);
			}
			if(retry==0||SD_SendCmd(CMD16,512,0X01)!=0)SD_Type=SD_TYPE_ERR;//错误的卡
		}
	}
	SD_DisSelect();//取消片选
	SD_SPI_SpeedHigh();//SPI配置为高速模式
	
	if (SD_Type)
		return 0;
	else if(r1)
		return r1;
	return (0xaa);//其他错误
}



//读SD卡
//buf:数据缓存区
//sector:扇区
//cnt:扇区数
//返回值:0,ok;其他,失败.
u8 SD_ReadDisk(u8*buf,u32 sector,u8 cnt)
{
	u8 r1;
	if(SD_Type!=SD_TYPE_V2HC)sector <<= 9;//转换为字节地址
	if(cnt==1)
	{
		r1=SD_SendCmd(CMD17,sector,0X01);//读命令
		if(r1==0)//指令发送成功
		{
			r1=SD_RecvData(buf,512);//接收512个字节
		}
	}
	else
	{
		r1=SD_SendCmd(CMD18,sector,0X01);//连续读命令
		do
		{
			r1=SD_RecvData(buf,512);//接收512个字节
			buf+=512;
		}
		while(--cnt && r1==0);
		SD_SendCmd(CMD12,0,0X01);	//发送停止命令
	}
	SD_DisSelect();//取消片选
	return r1;//
}



//写SD卡
//buf:数据缓存区
//sector:起始扇区
//cnt:扇区数
//返回值:0,ok;其他,失败.
u8 SD_WriteDisk(u8*buf,u32 sector,u8 cnt)
{
	u8 r1;
	if(SD_Type!=SD_TYPE_V2HC)sector *= 512;//转换为字节地址
	if(cnt==1)
	{
		r1=SD_SendCmd(CMD24,sector,0X01);//读命令
		if(r1==0)//指令发送成功
		{
			r1=SD_SendBlock(buf,0xFE);//写512个字节
		}
	}
	else
	{
		if(SD_Type!=SD_TYPE_MMC)
		{
			SD_SendCmd(CMD55,0,0X01);
			SD_SendCmd(CMD23,cnt,0X01);//发送指令
		}
		r1=SD_SendCmd(CMD25,sector,0X01);//连续读命令
		if(r1==0)
		{
			do
			{
				r1=SD_SendBlock(buf,0xFC);//接收512个字节
				buf+=512;
			}
			while(--cnt && r1==0);
			r1=SD_SendBlock(0,0xFD);//接收512个字节
		}
	}
	SD_DisSelect();//取消片选
	
	return r1;//
}
forget345 2021-04-08
  • 打赏
  • 举报
回复
不好查,R0.09b太旧了,换新版的文件系统试试
seedundersnow 2021-04-08
  • 打赏
  • 举报
回复
猜测是FATFS移植不成功。关闭文件操作没有确保数据写入媒体就返回,导致后来掉电时数据丢失。
ba_wang_mao 2021-04-08
  • 打赏
  • 举报
回复
引用 5 楼 IamReason 的回复:
以我的经验,和版本没关系 是不是你一次写入的数据太长了
我每一行都写332个字符。 单片机上电运行一次,共写3行,每一行都写332个字符。
ba_wang_mao 2021-04-08
  • 打赏
  • 举报
回复
我一行写332个字符
ba_wang_mao 2021-04-08
  • 打赏
  • 举报
回复
我一行写这么多数据
9C-A5-25-A9-3E-F4 COM1,2021-03-11,17:00:00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,18.37,17.81,0.00,18.31,18.28,18.34,18.12,18.28,18.53,18.31,18.68,18.56,18.06,18.18,18.46,18.03,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.096183524,12.95,12.87
加载更多回复(1)

27,511

社区成员

发帖
与我相关
我的任务
社区描述
硬件/嵌入开发 单片机/工控
社区管理员
  • 单片机/工控社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧