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模式0SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;				//NSS,选择由软件控制SPI_InitStructure.SPI_CRCPolynomial = 7;				//CRC多项式,暂时用不到,给默认值7SPI_Init(SPI2, &SPI_InitStructure);						//将结构体变量交给SPI_Init,配置SPI1SPI_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会拉低,直到完成写入才会为1do{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 就是*=512if(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。爆肝一天,实在受不了,剩下的后续会完善。。。

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

基于51单片机的自动化窗户控制系统设计

**单片机设计介绍&#xff0c;基于51单片机的自动化窗户控制系统设计 文章目录 一 概要二、功能设计设计思路 三、 软件设计原理图 五、 程序六、 文章目录 一 概要 基于51单片机的自动化窗户控制系统设计是一个结合了硬件与软件技术的综合性项目。以下是对该设计的概要描述&a…

Github 2024-04-02Python开源项目日报 Top10

根据Github Trendings的统计,今日(2024-04-02统计)共有10个项目上榜。根据开发语言中项目的数量,汇总情况如下: 开发语言项目数量Python项目10系统设计指南 创建周期:2507 天开发语言:Python协议类型:OtherStar数量:241693 个Fork数量:42010 次关注人数:241693 人贡献…

Premiere Pro 2024:赋予创意翅膀,让你的视频飞翔 mac/win版

Premiere Pro 2024&#xff0c;作为Adobe旗下的旗舰视频编辑软件&#xff0c;自推出以来&#xff0c;一直在视频制作领域占据着重要的地位。随着技术的不断进步和创新&#xff0c;Premiere Pro 2024为用户带来了前所未有的编辑体验&#xff0c;重新定义了视频制作的标准。 Pre…

基于51单片机的简易计算器设计

1、任务 本课题模拟计算器设计硬件电路采用三部分电路模块构成&#xff0c; 第一部分是键盘模块电路&#xff0c;采用4*4矩阵式键盘作为输入电路&#xff1b; 第二部分是LCD1602液晶显示模块&#xff1b; 第三部分是以51单片机作为控制核心。 软件程序主要由三部分组成&am…

房间预定小程序怎么做_打造用户的专属空间预定小程序

在这个快节奏的时代&#xff0c;人们对于便捷、高效的生活方式有着越来越高的追求。无论是出差、旅行还是日常生活&#xff0c;一个好的住宿环境都是必不可少的。然而&#xff0c;传统的房间预定方式往往让人头疼不已&#xff0c;电话沟通、排队等待、繁琐的手续……这些问题不…

STM32 M3内核寄存器概念

内容主要来自<<M3内核权威指南>> 汇编程序中的最低有效位&#xff08;Least Significant Bit&#xff09;。LSB是二进制数中最右边的位&#xff0c;它代表了数值中的最小单位。在汇编程序中&#xff0c;LSB通常用于表示数据的最小精度或者作为标志位。 ---------…

电子积木方案开发商

东莞市酷得智能科技有限公司电子积木方案开发商 提供消费电子解决方案、提供IC技术支持&#xff0c;全国线上线下服务 积木小车底层驱动开发过程主要涉及到以下几个方面&#xff1a; 首先&#xff0c;需要对小车底盘结构、硬件、模块等有深入的了解。底盘承载着机器人定位、导…

vue + koa + Sequelize + 阿里云部署 + 宝塔:宝塔数据库连接

之前文章已经介绍了宝塔上传前后端代码并部署&#xff0c;不清楚的请看这篇文章&#xff1a; vue koa 阿里云部署 宝塔&#xff1a;宝塔前后端部署 下面是宝塔创建数据库&#xff1a; 我用的 koa Sequelize 连接的数据库&#xff0c;Sequelize 非常适合前端使用&#xf…

Three.js阴影贴图

生成阴影贴图的步骤如下&#xff1a; 从光位置视点&#xff08;阴影相机&#xff09;创建深度图。从相机的角度进行屏幕渲染在每个像素点&#xff0c;将阴影相机的MVP矩阵计算出的深度值与深度图值进行比较如果深度图值较低&#xff0c;则说明该像素点存在阴影 &#xff0c;因…

计算机网络-HTTP相关知识-HTTPS基础

HTTPS与HTTP的区别 在TCP和HTTP网络层之间加入了SSL/TLS安全协议。HTTPS在TCP三次握手之后&#xff0c;还需进行SSL/TLS的握手过程。HTTP默认端口号是80&#xff0c;HTTPS默认端口号是443。 HTTPS解决的风险 HTTPS主要解决了以下三种风险&#xff1a; 窃听风险&#xff1a;H…

极简7照训练法,奇趣相机引领儿童AI摄影潮流

近日&#xff0c;奇趣未来推出一款专注于儿童AI摄影市场的微信小程序——奇趣相机&#xff0c;搭载了专为中国儿童精心研发的AIGC大模型&#xff0c;精准捕捉并贴合亚洲儿童人脸特征&#xff0c;让每一个孩子的笑容都能被完美定格。它不仅涵盖了从3岁至12岁各个年龄段的儿童摄影…

Spring中BeanFactoryPostProcessor详解

目录 功能与作用 使用案例 spring提供的常见BeanFactoryPostProcessor 1.EventListenerMethodProcessor 2.BeanDefinitionRegistryPostProcessor 功能与作用 使用案例 spring提供的唯一BeanDefinitionRegistryPostProcessor 总结 功能与作用 参考BeanFactoryPostProce…