• STM32F429 Discovery开发板应用:实现SPI-SD Card文件写入(搭载FatFS文件系统)


    MCU:STM32F429ZIT6

    开发环境:STM32CubeMX+MDK5

     

    外购了一个SPI接口的SD Card模块,想要实现SD卡存储数据的功能。

    首先需要打开STM32CubeMX工具。输入开发板MCU对应型号,找到开发板对应封装的MCU型号,双击打开(图中第三)。

     

    此时,双击完后会关闭此界面,然后打开一个新界面。

     

    然后,我们开始基本配置。

     

    现在我们选择一个LED作为系统LED,该步骤可以忽略,只是本人喜欢这样子。以硬件原理图的LD3为例子。

     

    基本配置除了时钟树外,基本上已经配置好了。

    现在配置时钟树

     

    基本配置已经配置完,现在开始配置实验使用的内容。

    配置USART1,重定向printf函数作为串口输出。

     

    然后配置SPI1,作为驱动SD Card读写的接口。

     

    然后配置文件系统,可以让文件的使用更方便。

     

    现在配置按键,触发中断处理一些事情。

     

    配置完成,完善工程,生成工程。

    到此,STM32CubeMX工具的使用结束!可以发现在桌面已经生成了SDCard_rw工程。

     

    使用MDK5打开SDCard_rw工程打开。点击魔法棒,勾选微库。选择对应的下载器,勾选下载完复位允许。USB线一端接开发板USB_Device,一端接PC。

     

    现在可以开始实验了

    在usart.c中重定向printf函数,并在usart.h中声明。

    1 //重定向c库函数printf到串口DEBUG_USART,重定向后可使用printf函数
    2 int fputc(int ch, FILE *f)
    3 {
    4     HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 1000);    
    5     return (ch);
    6 }

    在sdcard_write工程下创建UserCode文件夹,编写drive_spisd.c和drive_spisd.h。

    然后在MDK5这里的SDCard_rw工程添加一个新文件夹UserCode,装入drive_spisd.c。并在魔法棒这里加入头文件路径。

     

    drive_spisd.c如下

    复制代码
      1 /* Includes ------------------------------------------------------------------*/
      2 #include "drive_spisd.h"
      3 /* Private includes ----------------------------------------------------------*/
      4 #include "spi.h"
      5 #include "ff.h"
      6 #include "usart.h"
      7 /* Private typedef -----------------------------------------------------------*/
      8 
      9 /* Private define ------------------------------------------------------------*/
     10 
     11 /* Private macro -------------------------------------------------------------*/
     12 
     13 /* Private variables ---------------------------------------------------------*/
     14 uint8_t test;
     15 uint8_t SD_TYPE = 0x00;
     16 MSD_CARDINFO SD0_CardInfo;
     17 char SD_FileName[] = "hello.txt";
     18 /* Private function prototypes -----------------------------------------------*/
     19 static int         SD_SendCMD(uint8_t cmd, uint32_t arg, uint8_t crc);
     20 static uint8_t     SD_ReceiveData(uint8_t *data, uint16_t len);
     21 static uint8_t     SD_SendBlock(uint8_t*buf, uint8_t cmd);
     22 /* Private user code ---------------------------------------------------------*/
     23 
     24 /**
     25   * @brief  SPI_CS片选
     26   * @note   None
     27   * @retval None
     28   */
     29 void SPISD_CS(uint8_t p)
     30 {
     31     HAL_GPIO_WritePin(SPI1_CS_GPIO_Port, SPI1_CS_Pin, (p==0)?GPIO_PIN_SET:GPIO_PIN_RESET);
     32 }
     33 
     34 /**
     35   * @brief  发送命令(CMD0~CMD63),发完释放
     36   * @note   命令格式:0+传输标志(1-命令、0-响应)+CONTENT(6+32)+7CRC+1
     37   * @retval None
     38   */
     39 static int SD_SendCMD(uint8_t cmd, uint32_t arg, uint8_t crc)
     40 {
     41     uint8_t r1,retry;
     42 
     43     SPISD_CS(0);    //取消片选
     44     HAL_Delay(20);
     45     SPISD_CS(1);    //选通
     46     
     47     //SD卡的SPI通信协议规定,每个命令操作之前都需要发送至少8个时钟周期
     48     do
     49     {
     50         retry = SPI_ReadWrite(0xFF);
     51     }while(retry != 0xFF);
     52         
     53     SPI_ReadWrite(cmd | 0x40);
     54     SPI_ReadWrite(arg >> 24);
     55     SPI_ReadWrite(arg >> 16);
     56     SPI_ReadWrite(arg >> 8);
     57     SPI_ReadWrite(arg);
     58     SPI_ReadWrite(crc);
     59     
     60     if(cmd == CMD12)    
     61         SPI_ReadWrite(0xFF);
     62     do
     63     {
     64         r1 = SPI_ReadWrite(0xFF);
     65     }while(r1 & 0x80);
     66     
     67     return r1;
     68 }
     69 
     70 //SD卡初始化
     71 uint8_t SD_Init(void)
     72 {
     73     uint8_t     r1,i;    
     74     uint8_t     buff[6] = {0};
     75     uint16_t     retry; 
     76     
     77     SPI_SetSpeed(SPI_BAUDRATEPRESCALER_256);
     78     SPISD_CS(0);
     79     for(retry=0;retry<10;retry++)
     80         SPI_ReadWrite(0xFF);
     81     
     82     //SD卡进入IDLE状态
     83     do
     84     {
     85         r1 = SD_SendCMD(CMD0 ,0, 0x95);    
     86     }while(r1 != 0x01);
     87     
     88     //查看SD卡的类型
     89     SD_TYPE = 0;
     90     r1 = SD_SendCMD(CMD8, 0x1AA, 0x87);
     91     if(r1 == 0x01)
     92     {
     93         for(i=0;i<4;i++)
     94             buff[i] = SPI_ReadWrite(0xFF);                    //Get trailing return value of R7 resp
     95         if( buff[2]==0X01 && buff[3]==0XAA )                //卡是否支持2.7~3.6V
     96         {            
     97             retry = 0XFFFE;            
     98             do            
     99             {            
    100                 SD_SendCMD(CMD55, 0, 0X01);                    //发送CMD55
    101                 r1 = SD_SendCMD(CMD41, 0x40000000, 0X01);    //发送CMD41
    102             }while(r1&&retry--);
    103             
    104             if(retry && SD_SendCMD(CMD58, 0, 0X01) == 0)    //鉴别SD2.0卡版本开始
    105             {
    106                 for(i=0;i<4;i++)
    107                     buff[i] = SPI_ReadWrite(0XFF);            //得到OCR值
    108                 SD_TYPE = (buff[0]&0x40) ? V2HC:V2;    
    109             }
    110         }else
    111         {
    112             SD_SendCMD(CMD55, 0, 0X01);                        //发送CMD55
    113             r1 = SD_SendCMD(CMD41, 0, 0X01);                //发送CMD41
    114             if(r1<=1)
    115             {        
    116                 SD_TYPE = V1;
    117                 retry = 0XFFFE;
    118                 do                                             //等待退出IDLE模式
    119                 {
    120                     SD_SendCMD(CMD55, 0, 0X01);                //发送CMD55
    121                     r1 = SD_SendCMD(CMD41, 0, 0X01);        //发送CMD41
    122                 }while(r1&&retry--);
    123             }else                                            //MMC卡不支持CMD55+CMD41识别
    124             {
    125                 SD_TYPE = MMC;                                //MMC V3
    126                 retry = 0XFFFE;
    127                 do                                             //等待退出IDLE模式
    128                 {                                                
    129                     r1 = SD_SendCMD(CMD1, 0, 0X01);            //发送CMD1
    130                 }while(r1&&retry--);  
    131             }
    132             if( retry==0 || SD_SendCMD(CMD16, 512, 0X01)!=0 )
    133                 SD_TYPE = ERR;                                //错误的卡
    134         }
    135     }
    136     SPISD_CS(0);
    137     SPI_SetSpeed(SPI_BAUDRATEPRESCALER_4);
    138     
    139     return SD_TYPE?0:1;
    140 }
    141 
    142 void FileSystem_Init(void)
    143 {
    144     FATFS         *fs;
    145     DWORD         fre_clust, AvailableSize, UserSize;  
    146     uint8_t     res;
    147     uint8_t     *work;
    148     uint16_t     TotalSpace;
    149     
    150     res = SD_Init();        
    151     if(res == 1)
    152         printf("SD卡初始化失败! \r\n");        
    153     
    154     res = f_mount(&USERFatFS, USERPath, 1);                        //挂载
    155     if(res == FR_NO_FILESYSTEM)                                    //没有文件系统,格式化
    156     {
    157         printf("没有文件系统! \r\n");    
    158         
    159         work = malloc(_MIN_SS);
    160         res = f_mkfs(USERPath, FM_FAT, 0, work, _MIN_SS);        //格式化sd卡
    161         free(work);
    162         
    163         if(res == FR_OK)
    164         {        
    165             res = f_mount(NULL, USERPath, 1);                     //格式化后先取消挂载
    166             res = f_mount(&USERFatFS, USERPath, 1);                //重新挂载    
    167             if(res == FR_OK)
    168             {
    169                 printf("SD卡已经成功挂载,可以进进行文件写入测试! \r\n");
    170             }    
    171         }
    172         else
    173         {
    174             printf("格式化失败! \r\n");        
    175         }
    176     }else if(res == FR_OK)
    177     {
    178         printf("挂载成功! \r\n");        
    179     }else
    180     {
    181         printf("挂载失败! (%d)\r\n", res);
    182     }        
    183 
    184     res = f_getfree(USERPath, &fre_clust, &fs);  /* 根目录 */
    185     if ( res == FR_OK ) 
    186     {
    187         TotalSpace = (uint16_t)(((fs->n_fatent - 2) * fs->csize ) / 2 /1024);
    188         AvailableSize = (uint16_t)((fre_clust * fs->csize) / 2 /1024);
    189         UserSize = TotalSpace - AvailableSize;              
    190         /* Print free space in unit of MB (assuming 512 bytes/sector) */
    191         printf("\r\n%d MB total drive space.\r\n%ld MB available.\r\n%ld MB used.\r\n",TotalSpace, AvailableSize, UserSize);
    192     }
    193     else 
    194     {
    195         printf("Get SDCard Capacity Failed (%d)\r\n", res);
    196     }    
    197 //    f_mount(NULL, USERPath, 1);             //取消挂载
    198 } 
    199 
    200 /**
    201   * @brief  读取指定长度数据
    202   * @note   None
    203   * @retval None
    204   */
    205 static uint8_t SD_ReceiveData(uint8_t *data, uint16_t len)
    206 {
    207     uint8_t r1;
    208     
    209     SPISD_CS(1);                                       
    210     do
    211     { 
    212         r1 = SPI_ReadWrite(0xFF);    
    213         HAL_Delay(100);
    214     }while(r1 != 0xFE);    
    215     
    216     while(len--)
    217     {
    218         *data = SPI_ReadWrite(0xFF);
    219         data++;
    220     }
    221     SPI_ReadWrite(0xFF);
    222     SPI_ReadWrite(0xFF); 
    223     
    224     return 0;
    225 }
    226 
    227 /**
    228   * @brief  向SD卡写入一个数据包(512字节)的内容
    229   * @note   None
    230   * @retval None
    231   */
    232 static uint8_t SD_SendBlock(uint8_t*buf, uint8_t cmd)
    233 {    
    234     uint8_t     r1;    
    235     uint16_t     t;    
    236     
    237     do{
    238         r1 = SPI_ReadWrite(0xFF);
    239     }while(r1!=0xFF);
    240     
    241     SPI_ReadWrite(cmd);
    242     if(cmd != 0XFD)                    //不是结束指令
    243     {
    244         for(t=0; t<512; t++)
    245             SPI_ReadWrite(buf[t]);    //提高速度,减少函数传参时间
    246         SPI_ReadWrite(0xFF);        //忽略crc
    247         SPI_ReadWrite(0xFF);
    248         t = SPI_ReadWrite(0xFF);    //接收响应
    249         if( (t&0x1F) != 0x05 )
    250             return 2;                //响应错误                                                              
    251     }
    252     
    253     return 0;                        //写入成功
    254 }
    255 
    256 /**
    257   * @brief  CSD,卡的操作条件信息,128bit
    258   * @note   None
    259   * @retval None
    260   */
    261 uint8_t SD_GetCSD(uint8_t *csd_data)
    262 {
    263     uint8_t r1;    
    264     
    265     r1 = SD_SendCMD(CMD9, 0, 0x01);            //读取CSD寄存器
    266     if(r1 == 0x00)
    267         r1 = SD_ReceiveData(csd_data, 16);    //接收16个字节的数据 
    268     SPISD_CS(0);                            //取消片选
    269     
    270     return r1?1:0;
    271 }
    272 
    273 /**
    274   * @brief  CID,卡识别号,128bit
    275   * @note   None
    276   * @retval None
    277   */
    278 uint8_t SD_GetCID(uint8_t *cid_data)
    279 {
    280     uint8_t    r1;
    281     
    282     r1 = SD_SendCMD(CMD10, 0, 0x01);         //读取CID寄存器
    283     if(r1==0x00)
    284         r1 = SD_ReceiveData(cid_data, 16);    //接收16个字节的数据 
    285     SPISD_CS(0);                            //取消片选
    286     
    287     return r1?1:0;
    288 }
    289 
    290 //获取SD卡的总扇区数
    291 uint32_t SD_GetSectorCount(void)
    292 {
    293     uint8_t     n;
    294     uint8_t     csd[16];
    295     uint16_t     csize;
    296     uint32_t     Capacity;  
    297     
    298     if(SD_GetCSD(csd) != 0)         //取CSD信息,如果期间出错,返回0
    299         return 0;        
    300     
    301     if( (csd[0]&0xC0) == 0x40 )         //如果为SDHC卡,按照下面方式计算。V2.00的卡
    302     {    
    303         csize = csd[9] + ((uint16_t)csd[8] << 8) + 1;
    304         Capacity = (uint32_t)csize << 10;                //得到扇区数                
    305     }else                            //V1.xx的卡
    306     {    
    307         n = (csd[5] & 15) + ((csd[10] & 128) >> 7) + ((csd[9] & 3) << 1) + 2;
    308         csize = (csd[8] >> 6) + ((uint16_t)csd[7] << 2) + ((uint16_t)(csd[6] & 3) << 10) + 1;
    309         Capacity = (uint32_t)csize << (n - 9);            //得到扇区数   
    310     }
    311     
    312     return Capacity;
    313 }
    314 
    315 int MSD0_GetCardInfo(PMSD_CARDINFO SD0_CardInfo)
    316 {
    317     uint8_t r1;
    318     uint8_t CSD_Tab[16], CID_Tab[16];
    319     
    320     /* Send CMD9, Read CSD */
    321     r1 = SD_SendCMD(CMD9, 0, 0xFF);
    322     if(r1 != 0x00)
    323         return r1;
    324     if(SD_ReceiveData(CSD_Tab, 16))
    325         return 1;
    326     
    327     /* Send CMD10, Read CID */
    328     r1 = SD_SendCMD(CMD10, 0, 0xFF);
    329     if(r1 != 0x00)
    330         return r1;    
    331     if(SD_ReceiveData(CID_Tab, 16))
    332         return 2;
    333     
    334     /* Byte 0 */
    335     SD0_CardInfo->CSD.CSDStruct = (CSD_Tab[0] & 0xC0) >> 6;
    336     SD0_CardInfo->CSD.SysSpecVersion = (CSD_Tab[0] & 0x3C) >> 2;
    337     SD0_CardInfo->CSD.Reserved1 = CSD_Tab[0] & 0x03;
    338     /* Byte 1 */
    339     SD0_CardInfo->CSD.TAAC = CSD_Tab[1] ;
    340     /* Byte 2 */
    341     SD0_CardInfo->CSD.NSAC = CSD_Tab[2];
    342     /* Byte 3 */
    343     SD0_CardInfo->CSD.MaxBusClkFrec = CSD_Tab[3];
    344     /* Byte 4 */
    345     SD0_CardInfo->CSD.CardComdClasses = CSD_Tab[4] << 4;
    346     /* Byte 5 */
    347     SD0_CardInfo->CSD.CardComdClasses |= (CSD_Tab[5] & 0xF0) >> 4;
    348     SD0_CardInfo->CSD.RdBlockLen = CSD_Tab[5] & 0x0F;
    349     /* Byte 6 */
    350     SD0_CardInfo->CSD.PartBlockRead = (CSD_Tab[6] & 0x80) >> 7;
    351     SD0_CardInfo->CSD.WrBlockMisalign = (CSD_Tab[6] & 0x40) >> 6;
    352     SD0_CardInfo->CSD.RdBlockMisalign = (CSD_Tab[6] & 0x20) >> 5;
    353     SD0_CardInfo->CSD.DSRImpl = (CSD_Tab[6] & 0x10) >> 4;
    354     SD0_CardInfo->CSD.Reserved2 = 0; /* Reserved */
    355     SD0_CardInfo->CSD.DeviceSize = (CSD_Tab[6] & 0x03) << 10;
    356     /* Byte 7 */
    357     SD0_CardInfo->CSD.DeviceSize |= (CSD_Tab[7]) << 2;
    358     /* Byte 8 */
    359     SD0_CardInfo->CSD.DeviceSize |= (CSD_Tab[8] & 0xC0) >> 6;
    360     SD0_CardInfo->CSD.MaxRdCurrentVDDMin = (CSD_Tab[8] & 0x38) >> 3;
    361     SD0_CardInfo->CSD.MaxRdCurrentVDDMax = (CSD_Tab[8] & 0x07);
    362     /* Byte 9 */
    363     SD0_CardInfo->CSD.MaxWrCurrentVDDMin = (CSD_Tab[9] & 0xE0) >> 5;
    364     SD0_CardInfo->CSD.MaxWrCurrentVDDMax = (CSD_Tab[9] & 0x1C) >> 2;
    365     SD0_CardInfo->CSD.DeviceSizeMul = (CSD_Tab[9] & 0x03) << 1;
    366     /* Byte 10 */
    367     SD0_CardInfo->CSD.DeviceSizeMul |= (CSD_Tab[10] & 0x80) >> 7;
    368     SD0_CardInfo->CSD.EraseGrSize = (CSD_Tab[10] & 0x7C) >> 2;
    369     SD0_CardInfo->CSD.EraseGrMul = (CSD_Tab[10] & 0x03) << 3;
    370     /* Byte 11 */
    371     SD0_CardInfo->CSD.EraseGrMul |= (CSD_Tab[11] & 0xE0) >> 5;
    372     SD0_CardInfo->CSD.WrProtectGrSize = (CSD_Tab[11] & 0x1F);
    373     /* Byte 12 */
    374     SD0_CardInfo->CSD.WrProtectGrEnable = (CSD_Tab[12] & 0x80) >> 7;
    375     SD0_CardInfo->CSD.ManDeflECC = (CSD_Tab[12] & 0x60) >> 5;
    376     SD0_CardInfo->CSD.WrSpeedFact = (CSD_Tab[12] & 0x1C) >> 2;
    377     SD0_CardInfo->CSD.MaxWrBlockLen = (CSD_Tab[12] & 0x03) << 2;
    378     /* Byte 13 */
    379     SD0_CardInfo->CSD.MaxWrBlockLen |= (CSD_Tab[13] & 0xc0) >> 6;
    380     SD0_CardInfo->CSD.WriteBlockPaPartial = (CSD_Tab[13] & 0x20) >> 5;
    381     SD0_CardInfo->CSD.Reserved3 = 0;
    382     SD0_CardInfo->CSD.ContentProtectAppli = (CSD_Tab[13] & 0x01);
    383     /* Byte 14 */
    384     SD0_CardInfo->CSD.FileFormatGrouop = (CSD_Tab[14] & 0x80) >> 7;
    385     SD0_CardInfo->CSD.CopyFlag = (CSD_Tab[14] & 0x40) >> 6;
    386     SD0_CardInfo->CSD.PermWrProtect = (CSD_Tab[14] & 0x20) >> 5;
    387     SD0_CardInfo->CSD.TempWrProtect = (CSD_Tab[14] & 0x10) >> 4;
    388     SD0_CardInfo->CSD.FileFormat = (CSD_Tab[14] & 0x0C) >> 2;
    389     SD0_CardInfo->CSD.ECC = (CSD_Tab[14] & 0x03);
    390     /* Byte 15 */
    391     SD0_CardInfo->CSD.CSD_CRC = (CSD_Tab[15] & 0xFE) >> 1;
    392     SD0_CardInfo->CSD.Reserved4 = 1;
    393 
    394     if(SD0_CardInfo->CardType == V2HC)
    395     {
    396         /* Byte 7 */
    397         SD0_CardInfo->CSD.DeviceSize = (uint16_t)(CSD_Tab[8]) *256;
    398         /* Byte 8 */
    399         SD0_CardInfo->CSD.DeviceSize += CSD_Tab[9] ;
    400     }
    401     
    402     SD0_CardInfo->Capacity = SD0_CardInfo->CSD.DeviceSize * MSD_BLOCKSIZE * 1024;
    403     SD0_CardInfo->BlockSize = MSD_BLOCKSIZE;
    404     
    405     /* Byte 0 */
    406     SD0_CardInfo->CID.ManufacturerID = CID_Tab[0];
    407     /* Byte 1 */
    408     SD0_CardInfo->CID.OEM_AppliID = CID_Tab[1] << 8;
    409     /* Byte 2 */
    410     SD0_CardInfo->CID.OEM_AppliID |= CID_Tab[2];
    411     /* Byte 3 */
    412     SD0_CardInfo->CID.ProdName1 = CID_Tab[3] << 24;
    413     /* Byte 4 */
    414     SD0_CardInfo->CID.ProdName1 |= CID_Tab[4] << 16;
    415     /* Byte 5 */
    416     SD0_CardInfo->CID.ProdName1 |= CID_Tab[5] << 8;
    417     /* Byte 6 */
    418     SD0_CardInfo->CID.ProdName1 |= CID_Tab[6];
    419     /* Byte 7 */
    420     SD0_CardInfo->CID.ProdName2 = CID_Tab[7];
    421     /* Byte 8 */
    422     SD0_CardInfo->CID.ProdRev = CID_Tab[8];
    423     /* Byte 9 */
    424     SD0_CardInfo->CID.ProdSN = CID_Tab[9] << 24;
    425     /* Byte 10 */
    426     SD0_CardInfo->CID.ProdSN |= CID_Tab[10] << 16;
    427     /* Byte 11 */
    428     SD0_CardInfo->CID.ProdSN |= CID_Tab[11] << 8;
    429     /* Byte 12 */
    430     SD0_CardInfo->CID.ProdSN |= CID_Tab[12];
    431     /* Byte 13 */
    432     SD0_CardInfo->CID.Reserved1 |= (CID_Tab[13] & 0xF0) >> 4;
    433     /* Byte 14 */
    434     SD0_CardInfo->CID.ManufactDate = (CID_Tab[13] & 0x0F) << 8;
    435     /* Byte 15 */
    436     SD0_CardInfo->CID.ManufactDate |= CID_Tab[14];
    437     /* Byte 16 */
    438     SD0_CardInfo->CID.CID_CRC = (CID_Tab[15] & 0xFE) >> 1;
    439     SD0_CardInfo->CID.Reserved2 = 1;
    440     
    441     return 0;  
    442 }
    443 
    444 
    445 //写SD卡
    446 //buf:数据缓存区
    447 //sector:起始扇区
    448 //cnt:扇区数
    449 //返回值:0,ok;其他,失败.
    450 uint8_t SD_WriteDisk(uint8_t *buf, uint32_t sector, uint8_t cnt)
    451 {
    452     uint8_t r1;
    453     
    454     if(SD_TYPE != V2HC)
    455         sector *= 512;                            //转换为字节地址
    456     if(cnt == 1)
    457     {
    458         r1 = SD_SendCMD(CMD24, sector, 0x01);    //读命令
    459         if(r1 == 0)                                //指令发送成功
    460             r1=SD_SendBlock(buf, 0xFE);            //写512个字节       
    461     }else
    462     {
    463         if(SD_TYPE != MMC)
    464         {
    465             SD_SendCMD(CMD55, 0, 0x01);    
    466             SD_SendCMD(CMD23, cnt, 0x01);        //发送指令    
    467         }
    468          r1 = SD_SendCMD(CMD25, sector, 0x01);    //连续读命令
    469         if(r1 == 0)
    470         {
    471             do
    472             {
    473                 r1 = SD_SendBlock(buf,0xFC);    //接收512个字节     
    474                 buf += 512;  
    475             }while(--cnt && r1==0);
    476             r1 = SD_SendBlock(0,0xFD);            //接收512个字节 
    477         }
    478     }   
    479     SPISD_CS(0);                                //取消片选
    480     
    481     return r1;
    482 }    
    483 
    484 //读SD卡
    485 //buf:数据缓存区
    486 //sector:扇区
    487 //cnt:扇区数
    488 //返回值:0,ok;其他,失败.
    489 uint8_t SD_ReadDisk(uint8_t*buf,uint32_t sector,uint8_t cnt)
    490 {
    491     uint8_t r1;
    492     
    493     if(SD_TYPE != V2HC)
    494         sector <<= 9;                            //转换为字节地址
    495     if(cnt == 1)
    496     {
    497         r1 = SD_SendCMD(CMD17, sector, 0x01);    //读命令
    498         if(r1 == 0)        
    499             r1 = SD_ReceiveData(buf, 512);        //接收512个字节       
    500     }else
    501     {
    502         r1 = SD_SendCMD(CMD18, sector, 0x01);    //连续读命令
    503         do
    504         {
    505             r1 = SD_ReceiveData(buf, 512);        //接收512个字节     
    506             buf += 512;  
    507         }while(--cnt && r1==0);     
    508         SD_SendCMD(CMD12, 0, 0x01);                //发送停止命令
    509     }   
    510     SPISD_CS(0);                                //取消片选
    511     
    512     return r1;
    513 }
    514 
    515 uint8_t SPI_ReadWrite(uint8_t Txdata)
    516 {
    517     uint8_t Rxdata;    
    518     HAL_SPI_TransmitReceive(&hspi1, &Txdata, &Rxdata, 1, 100);
    519     return Rxdata;
    520 }
    521 
    522 //SPI1波特率设置
    523 void SPI_SetSpeed(uint8_t speed)
    524 {
    525     hspi1.Init.BaudRatePrescaler = speed;
    526     if (HAL_SPI_Init(&hspi1) != HAL_OK)
    527     {
    528         Error_Handler();
    529     }
    530 }
    531 ///////////////////////////END//////////////////////////////////////
    复制代码

     

    drive_spisd.h如下

    复制代码
      1 /* Define to prevent recursive inclusion -------------------------------------*/
      2 #ifndef __DRIVE_SPISD_H
      3 #define __DRIVE_SPISD_H
      4 
      5 #ifdef __cplusplus
      6 extern "C" {
      7 #endif
      8 
      9 /* Includes ------------------------------------------------------------------*/
     10 #include "main.h"
     11 /* Private includes ----------------------------------------------------------*/
     12 #include "fatfs.h"
     13 /* Exported types ------------------------------------------------------------*/
     14 enum _CD_HOLD
     15 {
     16     HOLD = 0,
     17     RELEASE = 1,
     18 };
     19 
     20 typedef struct                       /* Card Specific Data */
     21 {
     22     uint8_t  CSDStruct;                /* CSD structure */
     23     uint8_t  SysSpecVersion;           /* System specification version */
     24     uint8_t  Reserved1;                /* Reserved */
     25     uint8_t  TAAC;                     /* Data read access-time 1 */
     26     uint8_t  NSAC;                     /* Data read access-time 2 in CLK cycles */
     27     uint8_t  MaxBusClkFrec;            /* Max. bus clock frequency */
     28     uint16_t CardComdClasses;          /* Card command classes */
     29     uint8_t  RdBlockLen;               /* Max. read data block length */
     30     uint8_t  PartBlockRead;            /* Partial blocks for read allowed */
     31     uint8_t  WrBlockMisalign;          /* Write block misalignment */
     32     uint8_t  RdBlockMisalign;          /* Read block misalignment */
     33     uint8_t  DSRImpl;                  /* DSR implemented */
     34     uint8_t  Reserved2;                /* Reserved */
     35     uint32_t DeviceSize;               /* Device Size */
     36     uint8_t  MaxRdCurrentVDDMin;       /* Max. read current @ VDD min */
     37     uint8_t  MaxRdCurrentVDDMax;       /* Max. read current @ VDD max */
     38     uint8_t  MaxWrCurrentVDDMin;       /* Max. write current @ VDD min */
     39     uint8_t  MaxWrCurrentVDDMax;       /* Max. write current @ VDD max */
     40     uint8_t  DeviceSizeMul;            /* Device size multiplier */
     41     uint8_t  EraseGrSize;              /* Erase group size */
     42     uint8_t  EraseGrMul;               /* Erase group size multiplier */
     43     uint8_t  WrProtectGrSize;          /* Write protect group size */
     44     uint8_t  WrProtectGrEnable;        /* Write protect group enable */
     45     uint8_t  ManDeflECC;               /* Manufacturer default ECC */
     46     uint8_t  WrSpeedFact;              /* Write speed factor */
     47     uint8_t  MaxWrBlockLen;            /* Max. write data block length */
     48     uint8_t  WriteBlockPaPartial;      /* Partial blocks for write allowed */
     49     uint8_t  Reserved3;                /* Reserded */
     50     uint8_t  ContentProtectAppli;      /* Content protection application */
     51     uint8_t  FileFormatGrouop;         /* File format group */
     52     uint8_t  CopyFlag;                 /* Copy flag (OTP) */
     53     uint8_t  PermWrProtect;            /* Permanent write protection */
     54     uint8_t  TempWrProtect;            /* Temporary write protection */
     55     uint8_t  FileFormat;               /* File Format */
     56     uint8_t  ECC;                      /* ECC code */
     57     uint8_t  CSD_CRC;                  /* CSD CRC */
     58     uint8_t  Reserved4;                /* always 1*/
     59 }MSD_CSD;
     60 
     61 typedef struct                         /*Card Identification Data*/
     62 {
     63     uint8_t  ManufacturerID;           /* ManufacturerID */
     64     uint16_t OEM_AppliID;              /* OEM/Application ID */
     65     uint32_t ProdName1;                /* Product Name part1 */
     66     uint8_t  ProdName2;                /* Product Name part2*/
     67     uint8_t  ProdRev;                  /* Product Revision */
     68     uint32_t ProdSN;                   /* Product Serial Number */
     69     uint8_t  Reserved1;                /* Reserved1 */
     70     uint16_t ManufactDate;             /* Manufacturing Date */
     71     uint8_t  CID_CRC;                  /* CID CRC */
     72     uint8_t  Reserved2;                /* always 1 */
     73 }MSD_CID;
     74 
     75 typedef struct
     76 {
     77     MSD_CSD     CSD;
     78     MSD_CID     CID;
     79     uint32_t     Capacity;              /* Card Capacity */
     80     uint32_t     BlockSize;             /* Card Block Size */
     81     uint16_t     RCA;
     82     uint8_t     CardType;
     83     uint32_t     SpaceTotal;            /* Total space size in file system */
     84     uint32_t     SpaceFree;             /* Free space size in file system */
     85 }MSD_CARDINFO, *PMSD_CARDINFO;
     86 
     87 extern MSD_CARDINFO SD0_CardInfo;
     88 /* Exported constants --------------------------------------------------------*/
     89 
     90 /* Exported macro ------------------------------------------------------------*/
     91 //SD卡类型
     92 #define ERR             0x00
     93 #define MMC                0x01
     94 #define V1                0x02
     95 #define V2                0x04
     96 #define V2HC            0x06
     97 
     98 #define DUMMY_BYTE        0xFF 
     99 #define MSD_BLOCKSIZE    512
    100 
    101 /*
    102     CMD定义
    103     bc:发送到所有卡,不返回响应
    104     bcr:发送到所有卡,同时接收所有卡的响应
    105     ac:发送到选定卡,无数据传输
    106     adtc:发送到选定卡,有数据传输
    107 */
    108 #define CMD0    0       //bc,卡复位到IDLE状态
    109 #define CMD1    1       
    110 #define CMD8    8       //bcr,读SD卡接口条件,包含主机支持的电压信息,并询问卡是否支持
    111 #define CMD9    9       //ac,读CSD数据
    112 #define CMD10   10      //ac,读CID数据
    113 #define CMD12   12      //ac,停止数据传输
    114 #define CMD16   16      //ac,设置块长度(对于SDHC卡块命令长度固定为512字节) 应返回0x00
    115 #define CMD17   17      //adtc,读单个块
    116 #define CMD18   18      //adtc,读多个块。连续读块,直到被CMD12中断。
    117 #define CMD23   23      //设置多sector写入前预先擦除N个block
    118 #define CMD24   24      //adtc,写单个块
    119 #define CMD25   25      //adtc,写多个块。连续写块,直到被CMD12中断。
    120 #define CMD41   41      //应返回0x00
    121 #define CMD55   55      //ac,指定下个命令为特定应用命令(ACMD),不是标准命令,应返回0x01
    122 #define CMD58   58      //读OCR信息
    123 #define CMD59   59      //使能/禁止CRC,应返回0x00
    124 
    125 //数据写入回应字意义
    126 #define MSD_DATA_OK                0x05
    127 #define MSD_DATA_CRC_ERROR         0x0B
    128 #define MSD_DATA_WRITE_ERROR       0x0D
    129 #define MSD_DATA_OTHER_ERROR       0xFF
    130 
    131 //SD卡回应标记字
    132 #define MSD_RESPONSE_NO_ERROR      0x00
    133 #define MSD_IN_IDLE_STATE          0x01
    134 #define MSD_ERASE_RESET            0x02
    135 #define MSD_ILLEGAL_COMMAND        0x04
    136 #define MSD_COM_CRC_ERROR          0x08
    137 #define MSD_ERASE_SEQUENCE_ERROR   0x10
    138 #define MSD_ADDRESS_ERROR          0x20
    139 #define MSD_PARAMETER_ERROR        0x40
    140 #define MSD_RESPONSE_FAILURE       0xFF
    141 
    142 /* Exported functions prototypes ---------------------------------------------*/
    143 void        SPISD_CS(uint8_t p);
    144 uint8_t        SD_Init(void);
    145 void         WritetoSD(BYTE write_buff[],uint8_t bufSize);
    146 void         FileSystem_Init(void);
    147 uint32_t      SD_GetSectorCount(void);
    148 uint8_t     SD_GetCID(uint8_t *cid_data);
    149 uint8_t     SD_GetCSD(uint8_t *csd_data);
    150 int            MSD0_GetCardInfo(PMSD_CARDINFO SD0_CardInfo);
    151 uint8_t     SD_ReadDisk(uint8_t*buf, uint32_t sector, uint8_t cnt);
    152 uint8_t     SD_WriteDisk(uint8_t*buf, uint32_t sector, uint8_t cnt);
    153 void         SPI_SetSpeed(uint8_t speed);
    154 uint8_t     SPI_ReadWrite(uint8_t Txdata);
    155 
    156 /* Private defines -----------------------------------------------------------*/
    157 extern uint8_t SD_TYPE;
    158 #ifdef __cplusplus
    159 }
    160 #endif
    161 
    162 #endif /* __DRIVE_SPISD_H */
    163 
    164 /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
    复制代码

     

    然后在文件系统文件夹里修改内容。

    user_diskio.c如下

    复制代码
      1 /* USER CODE BEGIN Header */
      2 /**
      3  ******************************************************************************
      4   * @file    user_diskio.c
      5   * @brief   This file includes a diskio driver skeleton to be completed by the user.
      6   ******************************************************************************
      7   * @attention
      8   *
      9   * Copyright (c) 2023 STMicroelectronics.
     10   * All rights reserved.
     11   *
     12   * This software is licensed under terms that can be found in the LICENSE file
     13   * in the root directory of this software component.
     14   * If no LICENSE file comes with this software, it is provided AS-IS.
     15   *
     16   ******************************************************************************
     17   */
     18  /* USER CODE END Header */
     19 
     20 #ifdef USE_OBSOLETE_USER_CODE_SECTION_0
     21 /*
     22  * Warning: the user section 0 is no more in use (starting from CubeMx version 4.16.0)
     23  * To be suppressed in the future.
     24  * Kept to ensure backward compatibility with previous CubeMx versions when
     25  * migrating projects.
     26  * User code previously added there should be copied in the new user sections before
     27  * the section contents can be deleted.
     28  */
     29 /* USER CODE BEGIN 0 */
     30 /* USER CODE END 0 */
     31 #endif
     32 
     33 /* USER CODE BEGIN DECL */
     34 
     35 /* Includes ------------------------------------------------------------------*/
     36 #include <string.h>
     37 #include "ff_gen_drv.h"
     38 
     39 /* Private typedef -----------------------------------------------------------*/
     40 #include "drive_spisd.h"
     41 /* Private define ------------------------------------------------------------*/
     42 
     43 /* Private variables ---------------------------------------------------------*/
     44 /* Disk status */
     45 static volatile DSTATUS Stat = STA_NOINIT;
     46 
     47 /* USER CODE END DECL */
     48 
     49 /* Private function prototypes -----------------------------------------------*/
     50 DSTATUS USER_initialize (BYTE pdrv);
     51 DSTATUS USER_status (BYTE pdrv);
     52 DRESULT USER_read (BYTE pdrv, BYTE *buff, DWORD sector, UINT count);
     53 #if _USE_WRITE == 1
     54   DRESULT USER_write (BYTE pdrv, const BYTE *buff, DWORD sector, UINT count);
     55 #endif /* _USE_WRITE == 1 */
     56 #if _USE_IOCTL == 1
     57   DRESULT USER_ioctl (BYTE pdrv, BYTE cmd, void *buff);
     58 #endif /* _USE_IOCTL == 1 */
     59 
     60 Diskio_drvTypeDef  USER_Driver =
     61 {
     62   USER_initialize,
     63   USER_status,
     64   USER_read,
     65 #if  _USE_WRITE
     66   USER_write,
     67 #endif  /* _USE_WRITE == 1 */
     68 #if  _USE_IOCTL == 1
     69   USER_ioctl,
     70 #endif /* _USE_IOCTL == 1 */
     71 };
     72 
     73 /* Private functions ---------------------------------------------------------*/
     74 
     75 /**
     76   * @brief  Initializes a Drive
     77   * @param  pdrv: Physical drive number (0..)
     78   * @retval DSTATUS: Operation status
     79   */
     80 DSTATUS USER_initialize (
     81     BYTE pdrv           /* Physical drive nmuber to identify the drive */
     82 )
     83 {
     84   /* USER CODE BEGIN INIT */
     85     uint8_t res;
     86     res = SD_Init();
     87     if(res)    //STM32 SPI的bug,在sd卡操作失败的时候如果不执行下面的语句,可能导致SPI读写异常
     88     {
     89         SPI_SetSpeed(SPI_BAUDRATEPRESCALER_256);
     90         SPI_ReadWrite(0xff);    //提供额外的8个时钟
     91         SPI_SetSpeed(SPI_BAUDRATEPRESCALER_4);
     92     }
     93     if(res)    
     94         return STA_NOINIT;
     95     else 
     96         return RES_OK; 
     97   /* USER CODE END INIT */
     98 }
     99 
    100 /**
    101   * @brief  Gets Disk Status
    102   * @param  pdrv: Physical drive number (0..)
    103   * @retval DSTATUS: Operation status
    104   */
    105 DSTATUS USER_status (
    106     BYTE pdrv       /* Physical drive number to identify the drive */
    107 )
    108 {
    109   /* USER CODE BEGIN STATUS */
    110     switch (pdrv)
    111     {
    112         case 0 :
    113             return RES_OK;
    114         case 1 :
    115             return RES_OK;
    116         case 2 :
    117             return RES_OK;
    118         default:
    119             return STA_NOINIT;
    120     }
    121   /* USER CODE END STATUS */
    122 }
    123 
    124 /**
    125   * @brief  Reads Sector(s)
    126   * @param  pdrv: Physical drive number (0..)
    127   * @param  *buff: Data buffer to store read data
    128   * @param  sector: Sector address (LBA)
    129   * @param  count: Number of sectors to read (1..128)
    130   * @retval DRESULT: Operation result
    131   */
    132 DRESULT USER_read (
    133     BYTE pdrv,      /* Physical drive nmuber to identify the drive */
    134     BYTE *buff,     /* Data buffer to store read data */
    135     DWORD sector,   /* Sector address in LBA */
    136     UINT count      /* Number of sectors to read */
    137 )
    138 {
    139   /* USER CODE BEGIN READ */
    140     uint8_t res;
    141     if( !count )
    142     {    
    143         return RES_PARERR;  /* count不能等于0,否则返回参数错误 */
    144     }
    145     switch (pdrv)
    146     {
    147         case 0:
    148             res = SD_ReadDisk(buff,sector,count);     
    149             if(res == 0)
    150                 return RES_OK;
    151             else
    152                 return RES_ERROR;                                             
    153         default:
    154             return RES_ERROR;
    155     }
    156   /* USER CODE END READ */
    157 }
    158 
    159 /**
    160   * @brief  Writes Sector(s)
    161   * @param  pdrv: Physical drive number (0..)
    162   * @param  *buff: Data to be written
    163   * @param  sector: Sector address (LBA)
    164   * @param  count: Number of sectors to write (1..128)
    165   * @retval DRESULT: Operation result
    166   */
    167 #if _USE_WRITE == 1
    168 DRESULT USER_write (
    169     BYTE pdrv,          /* Physical drive nmuber to identify the drive */
    170     const BYTE *buff,   /* Data to be written */
    171     DWORD sector,       /* Sector address in LBA */
    172     UINT count          /* Number of sectors to write */
    173 )
    174 {
    175   /* USER CODE BEGIN WRITE */
    176   /* USER CODE HERE */
    177     uint8_t  res;
    178     if( !count )
    179         return RES_PARERR;  /* count不能等于0,否则返回参数错误 */
    180     switch (pdrv)
    181     {
    182         case 0:
    183             res=SD_WriteDisk((uint8_t *)buff,sector,count);
    184                 if(res == 0)
    185                     return RES_OK;
    186                 else
    187                     return RES_ERROR;                                              
    188         default:
    189             return RES_ERROR;
    190     }
    191   /* USER CODE END WRITE */
    192 }
    193 #endif /* _USE_WRITE == 1 */
    194 
    195 /**
    196   * @brief  I/O control operation
    197   * @param  pdrv: Physical drive number (0..)
    198   * @param  cmd: Control code
    199   * @param  *buff: Buffer to send/receive control data
    200   * @retval DRESULT: Operation result
    201   */
    202 #if _USE_IOCTL == 1
    203 DRESULT USER_ioctl (
    204     BYTE pdrv,      /* Physical drive nmuber (0..) */
    205     BYTE cmd,       /* Control code */
    206     void *buff      /* Buffer to send/receive control data */
    207 )
    208 {
    209   /* USER CODE BEGIN IOCTL */
    210     DRESULT res;
    211     switch(cmd)
    212     {
    213         case CTRL_SYNC:
    214             SPISD_CS(1);
    215             do{
    216                 HAL_Delay(20);
    217             }while(SPI_ReadWrite(0xFF)!=0xFF);
    218             res=RES_OK;
    219             SPISD_CS(0);
    220             break;     
    221         case GET_SECTOR_SIZE:
    222             *(WORD*)buff = 512;
    223             res = RES_OK;
    224             break;     
    225         case GET_BLOCK_SIZE:
    226             *(WORD*)buff = 8;
    227             res = RES_OK;
    228             break;     
    229         case GET_SECTOR_COUNT:
    230             *(DWORD*)buff = SD_GetSectorCount();
    231             res = RES_OK;
    232             break;
    233         default:
    234             res = RES_PARERR;
    235             break;
    236     }
    237     return res;
    238   /* USER CODE END IOCTL */
    239 }
    240 #endif /* _USE_IOCTL == 1 */
    复制代码

     

    main.c

    复制代码
      1 /* USER CODE BEGIN Header */
      2 /**
      3   ******************************************************************************
      4   * @file           : main.c
      5   * @brief          : Main program body
      6   ******************************************************************************
      7   * @attention
      8   *
      9   * Copyright (c) 2023 STMicroelectronics.
     10   * All rights reserved.
     11   *
     12   * This software is licensed under terms that can be found in the LICENSE file
     13   * in the root directory of this software component.
     14   * If no LICENSE file comes with this software, it is provided AS-IS.
     15   *
     16   ******************************************************************************
     17   */
     18 /* USER CODE END Header */
     19 /* Includes ------------------------------------------------------------------*/
     20 #include "main.h"
     21 #include "fatfs.h"
     22 #include "spi.h"
     23 #include "usart.h"
     24 #include "gpio.h"
     25 
     26 /* Private includes ----------------------------------------------------------*/
     27 /* USER CODE BEGIN Includes */
     28 #include "drive_spisd.h"
     29 /* USER CODE END Includes */
     30 
     31 /* Private typedef -----------------------------------------------------------*/
     32 /* USER CODE BEGIN PTD */
     33 
     34 /* USER CODE END PTD */
     35 
     36 /* Private define ------------------------------------------------------------*/
     37 /* USER CODE BEGIN PD */
     38 /* USER CODE END PD */
     39 
     40 /* Private macro -------------------------------------------------------------*/
     41 /* USER CODE BEGIN PM */
     42 
     43 /* USER CODE END PM */
     44 
     45 /* Private variables ---------------------------------------------------------*/
     46 
     47 /* USER CODE BEGIN PV */
     48 uint8_t     status = 0;
     49 uint8_t        writeBuf[] = "demo program forever no bug!!!\r\n"; 
     50 /* USER CODE END PV */
     51 
     52 /* Private function prototypes -----------------------------------------------*/
     53 void SystemClock_Config(void);
     54 /* USER CODE BEGIN PFP */
     55 
     56 /* USER CODE END PFP */
     57 
     58 /* Private user code ---------------------------------------------------------*/
     59 /* USER CODE BEGIN 0 */
     60 
     61 /* USER CODE END 0 */
     62 
     63 /**
     64   * @brief  The application entry point.
     65   * @retval int
     66   */
     67 int main(void)
     68 {
     69   /* USER CODE BEGIN 1 */
     70     UINT     Bw;
     71     FIL     file;
     72     uint16_t cb_task = 0;
     73     uint8_t res1 = 0, res2 = 0;
     74   /* USER CODE END 1 */
     75 
     76   /* MCU Configuration--------------------------------------------------------*/
     77 
     78   /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
     79   HAL_Init();
     80 
     81   /* USER CODE BEGIN Init */
     82 
     83   /* USER CODE END Init */
     84 
     85   /* Configure the system clock */
     86   SystemClock_Config();
     87 
     88   /* USER CODE BEGIN SysInit */
     89 
     90   /* USER CODE END SysInit */
     91 
     92   /* Initialize all configured peripherals */
     93   MX_GPIO_Init();
     94   MX_SPI1_Init();
     95   MX_USART1_UART_Init();
     96   MX_FATFS_Init();
     97   /* USER CODE BEGIN 2 */
     98     FileSystem_Init();    //初始化文件系统
     99     
    100     status = 1;
    101     res1 = f_open(&file, "sdRW1.txt", FA_OPEN_ALWAYS | FA_WRITE);
    102     if((res1 & FR_DENIED) == FR_DENIED)
    103         printf("卡存储已满,写入失败! \r\n");
    104   /* USER CODE END 2 */
    105 
    106   /* Infinite loop */
    107   /* USER CODE BEGIN WHILE */
    108   while (1)
    109   {
    110     /* USER CODE END WHILE */
    111 
    112     /* USER CODE BEGIN 3 */
    113     if(status == 1)
    114     {
    115         if(res1 == FR_OK)
    116         {
    117             f_lseek(&file, f_size(&file));                                //确保写入不会覆盖之前的数据
    118             res2 = f_write(&file, writeBuf, sizeof(writeBuf), &Bw);        //写数据到SD卡            
    119             if(res2 != FR_OK)
    120             {
    121                 printf("文件写入失败! \r\n");
    122                 HAL_GPIO_WritePin(User_led_GPIO_Port, User_led_Pin, GPIO_PIN_RESET);
    123             }else
    124             {
    125                 HAL_GPIO_WritePin(User_led_GPIO_Port, User_led_Pin, GPIO_PIN_SET);
    126             }                
    127         }
    128         else
    129         {
    130             printf("打开文件失败! %d\r\n",res1);
    131         }    
    132         if(++cb_task%4096==0)
    133             f_sync(&file);            
    134     }else if(status == 2)
    135     {
    136         f_close(&file);
    137         f_mount(NULL, USERPath, 1);             //取消挂载
    138         HAL_GPIO_WritePin(User_led_GPIO_Port, User_led_Pin, GPIO_PIN_RESET);
    139         status = 0;
    140     }
    141     HAL_Delay(10);
    142   }
    143   /* USER CODE END 3 */
    144 }
    145 
    146 /**
    147   * @brief System Clock Configuration
    148   * @retval None
    149   */
    150 void SystemClock_Config(void)
    151 {
    152   RCC_OscInitTypeDef RCC_OscInitStruct = {0};
    153   RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
    154 
    155   /** Configure the main internal regulator output voltage
    156   */
    157   __HAL_RCC_PWR_CLK_ENABLE();
    158   __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
    159   /** Initializes the RCC Oscillators according to the specified parameters
    160   * in the RCC_OscInitTypeDef structure.
    161   */
    162   RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
    163   RCC_OscInitStruct.HSEState = RCC_HSE_ON;
    164   RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
    165   RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
    166   RCC_OscInitStruct.PLL.PLLM = 4;
    167   RCC_OscInitStruct.PLL.PLLN = 168;
    168   RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
    169   RCC_OscInitStruct.PLL.PLLQ = 4;
    170   if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
    171   {
    172     Error_Handler();
    173   }
    174   /** Initializes the CPU, AHB and APB buses clocks
    175   */
    176   RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
    177                               |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
    178   RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
    179   RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
    180   RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;
    181   RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;
    182 
    183   if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5) != HAL_OK)
    184   {
    185     Error_Handler();
    186   }
    187 }
    188 
    189 /* USER CODE BEGIN 4 */
    190 void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
    191 {
    192     if(GPIO_Pin == User_Key_Pin)
    193     {
    194         status = 2;
    195     }
    196 }
    197 /* USER CODE END 4 */
    198 
    199 /**
    200   * @brief  This function is executed in case of error occurrence.
    201   * @retval None
    202   */
    203 void Error_Handler(void)
    204 {
    205   /* USER CODE BEGIN Error_Handler_Debug */
    206   /* User can add his own implementation to report the HAL error return state */
    207   __disable_irq();
    208   while (1)
    209   {
    210   }
    211   /* USER CODE END Error_Handler_Debug */
    212 }
    213 
    214 #ifdef  USE_FULL_ASSERT
    215 /**
    216   * @brief  Reports the name of the source file and the source line number
    217   *         where the assert_param error has occurred.
    218   * @param  file: pointer to the source file name
    219   * @param  line: assert_param error line source number
    220   * @retval None
    221   */
    222 void assert_failed(uint8_t *file, uint32_t line)
    223 {
    224   /* USER CODE BEGIN 6 */
    225   /* User can add his own implementation to report the file name and line number,
    226      ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
    227   /* USER CODE END 6 */
    228 }
    229 #endif /* USE_FULL_ASSERT */
    复制代码

     

    实测是可以的,但是写速度并没有网上说的1M/s那么快,也有可能是我引线的问题。

     

    时代越来越好,开发效率越来越高,希望能帮助到你!!!

    还有就是,开源万岁。

  • 相关阅读:
    Windows10安装配置allure
    飞天诚信:CRM与战略同行,管理精度提升30%
    [数据可视化] 霍乱时期的可视化医师
    第 12 章 栈与队列
    关于ubuntu开发环境配置
    【仿牛客网笔记】项目进阶,构建安全高效的企业服务——置顶、加精、删除
    基于java的乡村图书馆管理系统的设计与实现毕业设计-附源码191505
    python 裁剪图片并保存
    [自研开源] MyData 数据集成之数据过滤 v0.7.2
    Allegro如何导出带有钻孔数据的dxf文件?
  • 原文地址:https://www.cnblogs.com/couvrir/p/17462810.html