STM32FATFS(未完待续)

注意,本博客适合像我一样的小白,会的不多,但是想快速做些东西,不适合会写驱动的大佬。另外,示例代码中的注释有误(从多个项目中移植过来的,未做更改),请不要被误导!!!

【免费】stm32f103c8t6SD卡驱动(Fatfs)资源-CSDN文库

一、copy源码,移植

我在CSDN上找到了一位大佬用HAL库和fatfs实现stm32f103c8t6对micro SD卡读写的驱动。于是借(抄)鉴(袭)之。

我决定使用SPI2,查阅引脚定义图可得

 * CS -》 PB12

 * SCK -》 PB13

 * MISO -》 PB14

 * MOSI -》 PB15

于是编写SPI初始化函数(CubeIDE编辑生成的初始化代码好像在main.c和xxx_msp.c,比较散乱,于是直接上手写)

/*开启时钟*/
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);	//开启GPIOA的时钟
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE);	//开启SPI1的时钟
	
	/*GPIO初始化*/
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);					//将PA4引脚初始化为推挽输出
	
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_15 | GPIO_Pin_14;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);					//将PA5和PA7引脚初始化为复用推挽输出
	
	
	/*SPI初始化*/
	SPI_InitTypeDef SPI_InitStructure;						//定义结构体变量
	SPI_InitStructure.SPI_Mode = SPI_Mode_Master;			//模式,选择为SPI主模式
	SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;	//方向,选择2线全双工
	SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;		//数据宽度,选择为8位
	SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;		//先行位,选择高位先行
	SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_128;	//波特率分频,选择128分频
	SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;				//SPI极性,选择低极性
	SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;			//SPI相位,选择第一个时钟边沿采样,极性和相位决定选择SPI模式0
	SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;				//NSS,选择由软件控制
	SPI_InitStructure.SPI_CRCPolynomial = 7;				//CRC多项式,暂时用不到,给默认值7
	SPI_Init(SPI2, &SPI_InitStructure);						//将结构体变量交给SPI_Init,配置SPI1
	
	SPI_Cmd(SPI2, ENABLE);									//使能SPI1,开始运行
	
	/*设置默认电平*/
	SD_CS(1);											//SS默认高电平

从原子哥的讲解来看,我们还需要更改SPI的速度,(也就是SPI_baudratePrescaler)可是 标准库当中并没有给出,我们只好操作寄存器,编写该函数(我比较菜,直接从原子哥的fatfs的SPI源码中copying过来)

 

函数如下 


void  SPI_setspeed(uint16_t SPI_BaudRatePrescaler)
{
	assert_param(IS_SPI_BAUDRATE_PRESCALER(SPI_BaudRatePrescaler));
	SPI2->CR1&=0XFFC7;
	SPI2->CR1|=SPI_BaudRatePrescaler;	//����SPI2�ٶ� 
	SPI_Cmd(SPI2,ENABLE); 

	/*设置默认电平*/
	SD_CS(1);											//SS默认高电平
}

之后出现了一些灵异事件! 

main.c中有一个读写文件的实例函数:

void WritetoSD(char SD_FileName[], uint8_t write_buff[],uint8_t bufSize)

// {

//  FATFS fs;

//  FIL file;

//  uint8_t res=0;

//  UINT Bw;    

   

//  res = SD_init();        //SD卡初始化



//  if(res == 1)

//  {

//      UsartPrintf(USART_DEBUG,"SD卡初始化失败! \r\n");  

//      OLED_ShowString(1,1,"failed!");

//  }

//  else

//  {

//      UsartPrintf(USART_DEBUG,"SD卡初始化成功! \r\n");      

//      OLED_ShowString(1,1,"init successed!");

//  }

   

//  res=f_mount(&fs,"0:",1);        //挂载

   

// //   if(test_sd == 0)        //用于测试格式化

//  if(res == FR_NO_FILESYSTEM)     //没有文件系统,格式化

//  {

// //       test_sd =1;             //用于测试格式化

//      UsartPrintf(USART_DEBUG,"没有文件系统! \r\n");        

//      OLED_ShowString(2,1,"no file system!");

//      res = f_mkfs("", 0, 0);     //格式化sd卡

//      if(res == FR_OK)

//      {

//          UsartPrintf(USART_DEBUG,"格式化成功! \r\n");

//          OLED_ShowString(1,1,"geshihua sed!");      

//          res = f_mount(NULL,"0:",1);         //格式化后先取消挂载

//          res = f_mount(&fs,"0:",1);          //重新挂载  

//          if(res == FR_OK)

//          {

//              UsartPrintf(USART_DEBUG,"SD卡已经成功挂载,可以进进行文件写入测试!\r\n");

//              OLED_ShowString(3,1,"can test");    

//          }  

//      }

//      else

//      {

//          UsartPrintf(USART_DEBUG,"格式化失败! \r\n");    

//          OLED_ShowString(1,1,"geshihua fad!");      

//      }

//  }

//  else if(res == FR_OK)

//  {

//      UsartPrintf(USART_DEBUG,"挂载成功! \r\n");          

//      OLED_ShowString(1,1,"guazai sed!");

//  }

//  else

//  {

//      UsartPrintf(USART_DEBUG,"挂载失败! \r\n");      

//      OLED_ShowString(1,1,"guazai fad!");

//  }  

   

//  res = f_open(&file,SD_FileName,FA_OPEN_ALWAYS |FA_WRITE);

//  if((res & FR_DENIED) == FR_DENIED)

//  {

//      UsartPrintf(USART_DEBUG,"卡存储已满,写入失败!\r\n");        

//      OLED_ShowString(1,1,"write sed!");      

//  }

   

//  f_lseek(&file, f_size(&file));//确保写词写入不会覆盖之前的数据

//  if(res == FR_OK)

//  {

//      UsartPrintf(USART_DEBUG,"打开成功/创建文件成功! \r\n");      

//      OLED_ShowString(1,1,"create sed!");        

//      res = f_write(&file,write_buff,bufSize,&Bw);        //写数据到SD卡

//      if(res == FR_OK)

//      {

//          UsartPrintf(USART_DEBUG,"文件写入成功! \r\n");            

//          OLED_ShowString(1,1,"write sed!");          

//      }

//      else

//      {

//          UsartPrintf(USART_DEBUG,"文件写入失败! \r\n");        

//          OLED_ShowString(1,1,"write fad!");  

//      }      

//  }

//  else

//  {      

//      OLED_ShowString(1,1,"open fad!");  

//      UsartPrintf(USART_DEBUG,"打开文件失败!\r\n");

//  }  

   

//  f_close(&file);                     //关闭文件      

//  f_mount(NULL,"0:",1);        //取消挂载

   

// }

这个函数在运行时一直卡住,在函数第一行写了USARTprintf也没反应!检查接线也没问题!我删除以上代码,直接在main的开头写USART_Printf电脑却能收到!因此,我认为应该是乱码的问题(因为vscode中所有中文注释都不能正常识别,应该是编码错误),导致了一些编译上的错误 ,于是把所有的printf中的中文全部重写,然后再次编译,结果正常。

但是,我发现SPI一直卡在等待发送完成标志位上,检查连接没有问题。苦苦debug两天,最后,在一位大佬的博客中看到RCC_APB1PeriphClockCmd写错导致SPI没时序,我瞬间明白!!!

注意!!!SPI2是APB1上的外设,开启时钟要用RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE);不要写成RCC_APB2PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE);或者RCC_AHBPeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE);写代码或者移植代码时一定要仔细查验。

二、分析源码

课件图片+源代码(分析都写在注释里了)


/**
 * @brief 向SD卡发送指令,指令共48位
 * @param cmd 命令 8 bit
 * @param arg 命令参数 32 bit
 * @param crc crc校验值 8 bit
*/
int SD_sendcmd(uint8_t cmd,uint32_t arg,uint8_t crc){
	uint8_t r1;
	uint8_t retry;

	SD_CS(0);
		Delay_ms(20);
	SD_CS(1);
		//看看SD卡是否准备好
		//因为向SD卡发送数据时,MISO会拉低,直到完成写入才会为1
		do{
			retry=spi_readwrite(DFF);
		}while(retry!=0xFF);

	spi_readwrite(cmd | 0x40);
	//从高位开始发送
	spi_readwrite(arg >> 24);
	spi_readwrite(arg >> 16);
	spi_readwrite(arg >> 8);
	spi_readwrite(arg);
	spi_readwrite(crc);
	if(cmd==CMD12)spi_readwrite(DFF);//跳过无效字节
	do
		{
			r1=spi_readwrite(0xFF);
		}while(r1&0X80);
		
		return r1;
}

 



/**
 * @brief 读SD卡
 * @param buf 数据缓冲区
 * @param sector 扇区
 * @param CNT 扇区数
 * @retval
*/
uint8_t SD_ReadDisk(uint8_t*buf,uint32_t sector,uint8_t cnt)
{
	uint8_t r1;
	if(SD_TYPE!=V2HC)sector <<= 9;//转换为字节地址 <<9 就是*=512
	if(cnt==1)
	{
		r1=SD_sendcmd(CMD17,sector,0X01);//读命令
		if(r1==0)//指令发送成功
		{
			r1=SD_ReceiveData(buf,512);//接收512个字节   
		}
	}else
	{
		r1=SD_sendcmd(CMD18,sector,0X01);//连续读命令
		do
		{
			r1=SD_ReceiveData(buf,512);//接收512个字节   
			buf+=512;  
		}while(--cnt && r1==0); 	
		SD_sendcmd(CMD12,0,0X01);	//发送停止命令
	}   
	SD_CS(0);//取消片选
	return r1;
}



 

 


/**
 * @brief 写入数据
 * @param buf 缓冲区
 * @param sector 开始扇区
 * @param cnt 扇区数
 * @retval 0 成功; 其他 失败
 * 
*/
uint8_t SD_WriteDisk(uint8_t*buf,uint32_t sector,uint8_t cnt)
{
	uint8_t r1;
	if(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!=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);//发送连续写结束命令
		}
	}   
	SD_CS(0);//取消片选
	return r1;
}	

但是试验之后,一直卡在

    do

    {

        r1 = SD_sendcmd(CMD0 ,0, 0x95);

    }

    while(r1!=0x01);

 个人猜测是SD卡不支持SPI读取,可以换用SDIO读取,也可能只是片选和取消片选时没有等待8clk。爆肝一天,实在受不了,剩下的后续会完善。。。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/510031.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

ICLR 2024 | 鸡生蛋蛋生鸡?再论生成数据能否帮助模型训练

ChatGPT狂飙160天&#xff0c;世界已经不是之前的样子。 新建了人工智能中文站https://ai.weoknow.com 每天给大家更新可用的国内可用chatGPT资源 发布在https://it.weoknow.com 更多资源欢迎关注 随着生成模型&#xff08;如 ChatGPT、扩散模型&#xff09;飞速发展&#x…

【详细讲解语言模型的原理、实战与评估】

&#x1f308;个人主页:程序员不想敲代码啊&#x1f308; &#x1f3c6;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家&#x1f3c6; &#x1f44d;点赞⭐评论⭐收藏 &#x1f91d;希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提…

《2023网络安全行业薪资发展趋势报告》.pdf

《2023网络安全行业薪资发展趋势报告》.pdf 前段时间看到了一份网络安全的最新数据&#xff0c;反映了2023年截至目前的网络安全行业&#xff0c;以及网络安全人才的发展情况。 前段时间忙着赶项目一直没时间&#xff0c;今天终于有空和你唠嗑了。 很多网工小白对安全的向往…

基于DCT(离散余弦变换)的图像水印算法,Matlab实现

博主简介&#xff1a; 专注、专一于Matlab图像处理学习、交流&#xff0c;matlab图像代码代做/项目合作可以联系&#xff08;QQ:3249726188&#xff09; 个人主页&#xff1a;Matlab_ImagePro-CSDN博客 原则&#xff1a;代码均由本人编写完成&#xff0c;非中介&#xff0c;提供…

vue+elementUI搭建动态表头的表格

前提&#xff1a;以下代码是vue2项目结合elementUi完成的 数据结构 后端传来的数据是两个list&#xff0c;一个表头的list&#xff0c;一个表格内容的list // 表头 headTableAtts: [{ columnLabel: 姓名, columnName: name },{ columnLabel: 年龄, columnName: age },{ colu…

【Linux】防火墙iptables详解

目录 一、防护墙概述 二、防火墙 2.1名词 2.2使用规则 2.3表与链 2.3.1简介 2.3.2每个表说明 1&#xff09;filter表 2)nat表 2.4环境的配置 2.5iptables的命令参数 2.6 配置filter表规则 2.6.1备份与恢复 2.6.2案例1&#xff1a;禁止访问22端口 2.6.3案例2&…

GT收发器第六篇_GT channel内部时钟关系

文章目录 一、TX端时钟二、RX端时钟 一、TX端时钟 TX端可分为4个区域&#xff0c;分别为FPGA TX接口、PCS靠FPGA侧、PCS靠PMA侧、PMA&#xff0c;如下图。GTX/GTH发射器包括TXBUFFER和TX相位校准电路&#xff0c;以解决时钟域之间的相位差。TX相位校准电路用于TXBUFFER被旁路时…

element-ui badge 组件源码分享

今日简单分享 badge 组件的源码实现&#xff0c;主要从以下两个方面&#xff1a; 1、badge 组件页面结构 2、badge 组件属性 一、badge 组件页面结构 二、badge 组件属性 补充几个标签的用途&#xff1a; sub&#xff1a;下标、sup&#xff1a;上标、var 变量 代码如下&am…

AIGC之gradio系列学习教程(二)Components

简述: 让我们继续了解一下 Gradio 的一些主要功能。本指南旨在对构建演示时应注意的各种事项进行高级概述。 Components Gradio 包含 30 多个预构建组件(以及许多用户构建的自定义组件),只需一行代码即可在演示中用作输入或输出。这些组件对应于机器学习和数据科学中的常…

C++初学者:如何优雅地写程序

我喜欢C语言的功能强大&#xff0c;简洁&#xff0c;我也喜欢C#的语法简单&#xff0c;清晰&#xff0c;写起来又方便好用。 一、为什么不用C语言写程序。 C语言用来做题目&#xff0c;考试研究是很方便的&#xff0c;但是用来写程序做软件&#xff0c;你就会发现&#xff0c…

解决Centos7无法连接网络和访问网页连接不上问题

一、网络无法连接问题 网络无法连接的问题我查到了一个很良心的操作&#xff0c;不用重装&#xff0c;因为可能是你虚拟机设置上的问题。我先写我的解决方案&#xff0c;再附上其他几种解决方案。 问题一&#xff1a; 虚拟机的问题****加粗样式 解决&#xff1a; &#xff08;…

landsat8数据产品说明

1、下载数据用户手册 手册下载网址&#xff0c;搜索landsat science关键词&#xff0c;并点击到官网下载。 2、用户手册目录 3、landsat8数据产品说明 具体说明在手册的第四章&#xff0c;4.1.4数据产品章节&#xff0c;具体描述如下&#xff1a; 英文意思&#xff1a; L8 的…

Plesk环境中签发免费SSL证书的操作与成效

在过去的一段时间内&#xff0c;我专注于在Plesk控制面板环境中为多个网站成功签发免费SSL证书&#xff0c;确保了这些站点的安全传输和用户数据保护。以下是对这一工作的全面总结&#xff0c;包括突出的工作亮点、具体实施过程、取得的成绩以及下一阶段的工作规划。 一、工作亮…

【C++】新的类功能和可变参数模板

目录 一、新的类功能1.1 默认成员函数1.1.1 移动构造函数1.1.2 移动赋值运算符重载 1.2 关键字default1.3 关键字delete 二、可变参数模板2.1 可变参数的函数模板2.2 递归方式展开函数2.3 empalce 一、新的类功能 1.1 默认成员函数 在之前的学习过程中&#xff0c;我们已经知…

走进车厂 | 移远通信以前沿车载技术,照亮智能网联汽车产业创新发展之路

无钥匙自动解锁方便快捷、实时路况导航精准高效、语音指令轻松控制车辆、车载娱乐系统丰富多样……随着智能化、数字化浪潮的不断推进&#xff0c;现如今的汽车出行焕然一新。 正如我们所见&#xff0c;汽车产业正在经历前所未有的变革。物联网、车联网等前沿技术的发展和应用&…

反弹shell的方法和场景

Netcat反弹Shell 1 NC正向反弹shell Netcat简称NC,是一个简单、可靠的网络工具,被誉为网络界的瑞士军刀。通NC可以进行端口扫描、 反弹Shell、端口监听和文件传输等操作,常用参数如下&#xff1a; -c指定连接后要执行的shell命令-e指定连接后要执行的文件名-k配置 Socket一…

C语言指针 深入浅出讲解

指针在我的理解就是一个指向值的地址&#xff0c;地址是连续的&#xff0c;比如这个&#xff1a; 我写了一个数组&#xff0c;分别赋值4&#xff0c;5&#xff0c;6...11&#xff1b; 它们的地址分别如下&#xff1a; 可以看到地址增长是4&#xff0c;这是因为&#xff0c;地…

C++其他语法..

1.运算符重载 之前有一个案例如下所示 其中我们可以通过add方法将两个点组成一个新的点 class Point {friend Point add(Point, Point);int m_x;int m_y; public:Point(int x, int y) : m_x(x), m_y(y) {}void display() {cout << "(" << m_x <<…

Codigger Desktop:开发者的利器,每个人的好帮手(一)

在当今这个信息化、数字化的时代&#xff0c;开发者们面临着前所未有的挑战和机遇。为了更好地助力开发者们应对这些挑战&#xff0c;抓住机遇&#xff0c;Codigger应运而生。其中Codigger Desktop 是一款基于 Codigger 系统的桌面应用&#xff0c;为用户提供直观易用的操作界面…

LEAP模型的能源环境发展、碳排放建模预测及不确定性分析教程

原文链接&#xff1a;LEAP模型的能源环境发展、碳排放建模预测及不确定性分析教程https://mp.weixin.qq.com/s?__bizMzUzNTczMDMxMg&mid2247599754&idx4&sn243c9f8bff355235a7056c2cbb1331fa&chksmfa82076dcdf58e7b871c3369c95ead9ff1d90baa0431318b26b6abd27…