MCU:STM32F429ZIT6 開發環境:STM32CubeMX+MDK5 外購了一個SPI介面的SD Card模塊,想要實現SD卡存儲數據的功能。 首先需要打開STM32CubeMX工具。輸入開發板MCU對應型號,找到開發板對應封裝的MCU型號,雙擊打開(圖中第三)。 此時,雙擊完後會關閉此界 ...
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