【豬圈丶嗨情歌的開發(fā)分享】
今天給大家?guī)淼氖俏疫@幾天學習的SDIO,用STM32系列芯片的SDIO讀取SD卡的扇區(qū)。本人原創(chuàng),這里轉貼到51黑。
我使用的工具
開發(fā)平臺:正點原子探索者STM32F407開發(fā)板
硬件:使用了NUCLEO-F446RE開發(fā)板的ST-Link作為調(diào)試器、SD卡、數(shù)據(jù)線、開發(fā)板的電源適配器、DELL一體機
軟件:STM32CubeMX、Keil V5、串口助手
學習的知識點
1、使用STM32CubeMX配置SDIO
2、在Keil中初始化SDIO
3、讀取SD卡狀態(tài)、卡信息
4、sprintf函數(shù)的使用
共享的資源
完整的工程文件
SDIO.zip
(9.16 MB, 下載次數(shù): 160)
2016-6-17 18:10 上傳
點擊文件名下載附件
下載積分: 黑幣 -5
寫在前面的話
上次STM32F469I開發(fā)板申請的活動吃了不少苦頭啊,手里沒有什么精華帖,只能跑去申請小鮮肉分塊的開發(fā)板了。結果這個活動異;鸨,最后1000+的人參與。這次決心做好開發(fā)分享系列的帖子,把自己平時學習STM32開發(fā)中的干貨寫成帖子在論壇里分享給大家。希望支持的壇友多多回復,給我頂一個人氣。
準備工作
本帖不解決硬件連接問題,如果硬件方面有問題最好自行搜索相關資料。
SD卡的通信方式有的是用SPI,我們這里是SDIO,速度更快更好用。如果你的SD卡模塊有MOSI、MISO這些字樣,那說明這些是SPI讀寫用的模塊,不適合這篇帖子的分享內(nèi)容。如果使用的是現(xiàn)成的開發(fā)板,請參考開發(fā)板的手冊確定SD卡的通信方式。
如果你確定了SD卡的通信方式為SDIO無誤并且正確連接了SD卡和芯片,然后記錄下了SD卡的引腳和芯片的連接方式那么準備工作就基本完成了。
在STM32CubeMX中配置SDIO
SDIOCubeMX配置.png (97.6 KB, 下載次數(shù): 116)
下載附件
2016-6-17 18:00 上傳
我們來看看左邊的紅框,在外設里面找到SDIO、SYS、USART1(看自己的開發(fā)板來選擇。還有我忘了打開這個分支,USART1我選的是第一個選項)。在下拉框里面選擇和上圖一樣的選項,這樣可以保證后面的項目一致。
我們在左邊選擇好了以后,可以看到右邊的芯片引腳有一些是綠色的,就說明這些引腳有配置被激活了。
配置時鐘.png (115.67 KB, 下載次數(shù): 107)
下載附件
2016-6-17 18:00 上傳
我們現(xiàn)在來玩一玩這個時鐘的配置窗口,這個在Pinout標簽頁旁邊大家自行點開。
那么時鐘怎么配置呢?我一般是主頻給我來最大,只要保證這里的輸入框都是藍色的就行了,如果是紅色的就說明頻率高了這個就要自己調(diào)整一下了。
剩下的內(nèi)容就已經(jīng)確定了,不需要我們來配置。如果你自己有什么別的想法的話,就自己動手來配置一下。
在Keil中初始化SDIO
我們現(xiàn)在要初始化SDIO來驅動我們的SD卡工作,現(xiàn)在貼出我在main函數(shù)中的初始化代碼。下面的代碼都是Cube自動生成的,這些代碼就可以初始化我們的SDIO了。
- /* Initialize all configured peripherals */
- MX_GPIO_Init();
- MX_SDIO_SD_Init();
- MX_USART1_UART_Init();
復制代碼
添加一些必要的初始化代碼(12月14號追加)
為了保證SDIO正常工作,我們還要添加兩個函數(shù)來初始化。這兩個函數(shù)Cube沒有事先給我們調(diào)用,我們要自己動一次手。在工程的文件樹Application/User里面找到sdio.c文件,找到第一個void MX_SDIO_SD_Init(void)函數(shù)。我們把我們需要的初始化代碼貼進去。
加入的這兩行函數(shù)才是真正的初始化了SDIO,前面都是在配置一個結構體變量。最后的兩行代碼需要我們自己添加進來,這兩個函數(shù)的定義大家可以自行查詢。這兩行代碼沒有寫在用戶代碼保護模塊里面,所以下一次STM32CubeMX生成代碼的時候這里的代碼會被刪除,請悉知。- void MX_SDIO_SD_Init(void)
- {
- hsd.Instance = SDIO;
- hsd.Init.ClockEdge = SDIO_CLOCK_EDGE_RISING;
- hsd.Init.ClockBypass = SDIO_CLOCK_BYPASS_DISABLE;
- hsd.Init.ClockPowerSave = SDIO_CLOCK_POWER_SAVE_DISABLE;
- hsd.Init.BusWide = SDIO_BUS_WIDE_1B;
- hsd.Init.HardwareFlowControl = SDIO_HARDWARE_FLOW_CONTROL_DISABLE;
- hsd.Init.ClockDiv = 0;
- HAL_SD_Init(&hsd, &SDCardInfo);
- HAL_SD_WideBusOperation_Config(&hsd, SDIO_BUS_WIDE_4B);
- }
復制代碼
下面我貼出我在while循環(huán)里面的代碼,分析一下這些代碼。
首先我是用了HAL_SD_GetStatus函數(shù)來讀取SD卡的狀態(tài),這些狀態(tài)只有SD_TRANSFER_OK、SD_TRANSFER_BUSY、SD_TRANSFER_ERROR這三種。- State = HAL_SD_GetStatus(&hsd);
復制代碼
接下來的條件語句里面,我用幾個if來決定不同的狀態(tài)應該做些什么。
1、現(xiàn)在SD是OK的狀態(tài),我們可以對SD卡進行讀寫操作
在讀取扇區(qū)的數(shù)據(jù)之前,發(fā)送一個SD Card OK的消息。然后調(diào)用HAL_SD_ReadBlocks函數(shù)來讀取扇區(qū)的內(nèi)容。這個函數(shù)的第四個參數(shù)必須是512,因為目前還不支持其他的扇區(qū)大小。
該函數(shù)的返回值表明了讀取操作的是否成功,如果為0表示成功,那么我們就發(fā)送一個Sector read is OK的消息。如果沒有成功,則發(fā)送一個ERROR的消息同時用break來終止此次循環(huán)。
在接下來的for循環(huán)里面,我們把讀取的32位扇區(qū)信息轉化成8位的扇區(qū)信息并且使用sprintf函數(shù)格式化成字符串。
關于32位轉化為8位這里不做過多的贅述,主要是C語言數(shù)據(jù)類型轉換的時候丟失一部分信息和位運算的知識,大家自己看一下。
sprintf函數(shù)的使用
這里要特別提一下這個函數(shù),這是C標準庫中的一個函數(shù),在我們的STM32平臺上也有實現(xiàn)。這個函數(shù)可以把數(shù)據(jù)格式化為字符串,把數(shù)據(jù)作為字符的形式顯示出來非常的實用。
想了解詳細的函數(shù)使用方法,請自行百度一下。這里簡單的介紹一下。
該函數(shù)的第一個參數(shù)是我們要轉換的內(nèi)容存儲的指針,轉換好的字符串存儲在這個指針指向的空間里。第二個字符串就是格式化字符串,這個字符串里面要包含轉換字符。我在代碼里面使用的是%X這個轉換字符,可以把數(shù)據(jù)用大寫的16進制顯示。后面的參數(shù)是和前面的轉換字符一一對應的待轉換變量,直接傳遞參數(shù)就可以了,不需要指針。
轉換好了字符串之后,我就可以挨個發(fā)送這些字符。發(fā)送完之后就循環(huán)一直等待就行了,不用反復讀取。
- 原型
- int sprintf( char *buffer, const char *format, [ argument] … );
- 參數(shù)列表
- buffer:char型指針,指向將要寫入的字符串的緩沖區(qū)。
- format:格式化字符串。
- [argument]...:可選參數(shù),可以是任何類型的數(shù)據(jù)。
- 返回值:字符串長度(strlen)
- if( State == 0)
- {
- HAL_UART_Transmit(&huart1, (uint8_t *)"SD Card OK\n", 11, 500);
- if(HAL_SD_ReadBlocks(&hsd, pReadBuffer, 0x00000000, 512, 1) == 0)
- HAL_UART_Transmit(&huart1, (uint8_t *)"Sector read is OK\n", 18, 500);
- else
- {
- HAL_UART_Transmit(&huart1, (uint8_t *)"ERROR\n", 6, 500);
- break;
- }
-
- for(int i = 0;i < 128;i++)
- {
- for(int j = 0;j < 4;j++)
- {
- SendBuffer = pReadBuffer[i];
- pReadBuffer[i] >>= 8;
- sprintf((char*)&Char, "%X", SendBuffer);
- HAL_UART_Transmit(&huart1, &Char, 1, 500);
- }
-
- }
- while(1);
- }
復制代碼 2、SD卡處于繁忙的狀態(tài)
這個時候我們就發(fā)送一個Busy的消息就可以了,等待1S鐘進行下一次SD卡狀態(tài)的讀取。- else if(State == 1)
- {
- HAL_UART_Transmit(&huart1, (uint8_t *)"Busy\n", 5, 500);
- }
復制代碼 3、SD卡狀態(tài)讀取錯誤
SD狀態(tài)返回為錯誤,這基本上就是SD卡沒有插入,要么就是卡壞掉了。這個時候如果我們不進行任何操作就進行下一次判斷的話,就算是在插入SD卡不管怎么讀取都是返回ERROR。所以我們在讀取到這個狀態(tài)的時候,調(diào)用一次HAL_SD_Init(&hsd, &SDCardInfo);這個函數(shù)來清除之前的狀態(tài)(這個是我自己想的,不知道有沒有卵用)。這樣子操作的話,在下一次插入SD卡的時候就可以返回為OK的值并且可以讀寫。
最后用了一個延時函數(shù)來限制掃描SD卡的速度。- else {
- HAL_UART_Transmit(&huart1, (uint8_t *)"Error\n", 6, 500);
- HAL_SD_Init(&hsd, &SDCardInfo);
- }
-
- HAL_Delay(1000);
復制代碼
實際工作效果
0.png (33.63 KB, 下載次數(shù): 84)
下載附件
2016-6-17 17:57 上傳
我預先拔掉了SD卡,過了一會我在插上去,所以在串口上看到的就是這樣的效果。
- /**
- ******************************************************************************
- * File Name : main.c
- * Description : Main program body
- ******************************************************************************
- *
- * COPYRIGHT(c) 2015 STMicroelectronics
- *
- * Redistribution and use in source and binary forms, with or without modification,
- * are permitted provided that the following conditions are met:
- * 1. Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution.
- * 3. Neither the name of STMicroelectronics nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
- * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- ******************************************************************************
- */
- /* Includes ------------------------------------------------------------------*/
- #include "stm32f4xx_hal.h"
- #include "sdio.h"
- #include "usart.h"
- #include "gpio.h"
- /* USER CODE BEGIN Includes */
- /* USER CODE END Includes */
- /* Private variables ---------------------------------------------------------*/
- /* USER CODE BEGIN PV */
- /* Private variables ---------------------------------------------------------*/
- /* USER CODE END PV */
- /* Private function prototypes -----------------------------------------------*/
- void SystemClock_Config(void);
- /* USER CODE BEGIN PFP */
- /* Private function prototypes -----------------------------------------------*/
- /* USER CODE END PFP */
- /* USER CODE BEGIN 0 */
- /* USER CODE END 0 */
- int main(void)
- {
- /* USER CODE BEGIN 1 */
- uint8_t State = 0;
- uint32_t pReadBuffer[128] = { 0 };
- uint8_t SendBuffer = { 0 };
- uint8_t Char = 0;
- /* USER CODE END 1 */
- /* MCU Configuration----------------------------------------------------------*/
- /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
- HAL_Init();
- /* Configure the system clock */
- SystemClock_Config();
- /* Initialize all configured peripherals */
- MX_GPIO_Init();
- MX_SDIO_SD_Init();
- MX_USART1_UART_Init();
- /* USER CODE BEGIN 2 */
- HAL_UART_Transmit(&huart1, (uint8_t *)"Initializing is OK\n", 19, 500);
-
- /* USER CODE END 2 */
- /* Infinite loop */
- /* USER CODE BEGIN WHILE */
- while (1)
- {
- /* USER CODE END WHILE */
- /* USER CODE BEGIN 3 */
- State = HAL_SD_GetStatus(&hsd);
-
- if( State == 0)
- {
- HAL_UART_Transmit(&huart1, (uint8_t *)"SD Card OK\n", 11, 500); //在這里我們讀取SD卡的信息
- if(HAL_SD_ReadBlocks(&hsd, pReadBuffer, 0x00000000, 512, 1) == 0)
- HAL_UART_Transmit(&huart1, (uint8_t *)"Sector read is OK\n", 18, 500);
- else
- {
- HAL_UART_Transmit(&huart1, (uint8_t *)"ERROR\n", 6, 500);
- break;
- }
-
- for(int i = 0;i < 128;i++)
- {
- for(int j = 0;j < 4;j++)
- {
- SendBuffer = pReadBuffer[i];
- pReadBuffer[i] >>= 8;
- sprintf((char*)&Char, "%X", SendBuffer);
- HAL_UART_Transmit(&huart1, &Char, 1, 500);
- }
-
- }
- while(1);
- }
- else if(State == 1)
- {
- HAL_UART_Transmit(&huart1, (uint8_t *)"Busy\n", 5, 500);
- }
- else {
- HAL_UART_Transmit(&huart1, (uint8_t *)"Error\n", 6, 500);
- HAL_SD_Init(&hsd, &SDCardInfo);
- }
-
- HAL_Delay(1000);
- }
- /* USER CODE END 3 */
- }
- /** System Clock Configuration
- */
- void SystemClock_Config(void)
- {
- RCC_OscInitTypeDef RCC_OscInitStruct;
- RCC_ClkInitTypeDef RCC_ClkInitStruct;
- __PWR_CLK_ENABLE();
- __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
- RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
- RCC_OscInitStruct.HSIState = RCC_HSI_ON;
- RCC_OscInitStruct.HSICalibrationValue = 16;
- RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
- RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI;
- RCC_OscInitStruct.PLL.PLLM = 8;
- RCC_OscInitStruct.PLL.PLLN = 144;
- RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
- RCC_OscInitStruct.PLL.PLLQ = 6;
- HAL_RCC_OscConfig(&RCC_OscInitStruct);
- RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_SYSCLK|RCC_CLOCKTYPE_PCLK1
- |RCC_CLOCKTYPE_PCLK2;
- RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
- RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
- RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;
- RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;
- HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4);
- HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/1000);
- HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);
- /* SysTick_IRQn interrupt configuration */
- HAL_NVIC_SetPriority(SysTick_IRQn, 0, 0);
- }
- /* USER CODE BEGIN 4 */
- /* USER CODE END 4 */
- #ifdef USE_FULL_ASSERT
- /**
- * @brief Reports the name of the source file and the source line number
- * where the assert_param error has occurred.
- * @param file: pointer to the source file name
- * @param line: assert_param error line source number
- * @retval None
- */
- void assert_failed(uint8_t* file, uint32_t line)
- {
- /* USER CODE BEGIN 6 */
- /* User can add his own implementation to report the file name and line number,
- ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
- /* USER CODE END 6 */
- }
- #endif
- /**
- * @}
- */
- /**
- * @}
- */
- /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
復制代碼
寫在后面的話
注意!注意!注意!本人不是什么工程師,只不過是愛好嵌入式開發(fā)的學生一枚,如果你發(fā)現(xiàn)在這個帖子中的錯誤請及時提醒我。如果對本帖的內(nèi)容有什么疑問請在下方留言,我會經(jīng)常過來逛論壇的。
下一次給大家?guī)淼目赡芫褪荈atFs了,這樣我們就可以讀寫文件了。
|