各位大神,STM32读sd卡里的文件无法删除图片能不...

液晶屏接口大多数情况下,学习STM32的人,都是会先学51的,学过51的话,就一定知道1602屏和12864屏了。如果你真的学会了使用51驱动的话,那你就应该知道屏任何单片机可以驱动,当然,STM32也可以驱动。不过,既然学51的时候就学会了,那么我们STM32开发板就不使用这两种屏了。彩色TFT液晶屏以前是FPGA的女朋友,由于科技的发展,现在单片机也可以驱动TFT了,不过,最多只能显示个图片,显示视频就不行了。单片机驱动TFT,还得益于ILI家族的控制器。例如,当下流行的ILI9341,它可以让单片机用16个IO口和若干控制引脚就可以驱动彩色TFT,这就使得任何单片机可以驱动彩色TFT,当然,51单片机也可以,只是速度不够快而已。就目前来说,彩色TFT也没有形成一个统一的标准,各家的TFT接口都不一样,为了方便,这里使用我曾经开发过的一款3.2寸的TFT屏:先来介(xuan)绍(yao)一下这款牛逼的屏吧。这款屏使用ILI9341驱动,带触摸屏,带背光打开和关闭控制引脚。采用FPC排线连接,如果屏不小心摔碎了,只要把连接器叩开即可更换,无需焊接。从上图也可以看出来,这款屏支持4种驱动方式。这4种驱动方式分别是:16位并口、8位并口、3线SPI和4线SPI串行接口。刷屏最快的就是16位并口,8位并口次之,SPI接口最慢。既然要使用这款屏,那我们的STM32底板也就需要设计相同接口定义的插座,使得屏插上去就可以用。我们使用最快的驱动方式:16位并口驱动。如果是用51单片机的话,可以使用2组IO口做并口(例如:P1和P2加起来正好16个引脚)。使用STM32的话,使用1组IO口就可以,因为STM32的每组IO口都是16个引脚(例如:PB0~PB15)。不过,我们这次要使用STM32的FSMC口。当人们听到一个从来没有听过的专业名词,就会产生恐惧感,不过,你有没有发现,你现在也掌握了很多的专业名词(不管是哪个行业的),你现在随便揪出一个来,是不是感觉弱爆了。所以,以后不要对任何专业名词产生恐惧了。这里我们先简单了解一下,FSMC口本来是STM32为了扩展内存增设的接口,我们知道,单片机读取内存,要求的速度是非常高的,所以我们就有了印象,FSMC口的速度一定很高,至少要比直接控制IO口要快很多。关于FSMC口的详细使用方法,当然是在后面做好开发板以后来讲的。从3脚到25脚,是TFT的控制引脚,26脚是背光亮灭控制引脚,从27到32脚是触摸屏控制引脚,使用SPI2口,关于这个,上一篇文章也提到过。(有些小伙伴会问,”为什么7脚和8脚没有用?“答:因为我们使用的是16位并口,这两个引脚是SPI控制引脚,所以就不用了。”那为什么不把这两个脚去掉,底下的引脚上移?“答:因为我们是依照屏的引脚来画的底板,把引脚上移后,屏插进来能用吗?)SD卡接口SD卡,有两种驱动方式,这个在SD卡协议里面有介绍。这两种驱动方式分别是:SDIO接口和SPI接口。一提到SPI接口,你就应该想到,任何单片机都可以驱动SD卡了,即使使用没有硬件SPI接口的51单片机,也可以用GPIO模拟SPI时序来驱动SD卡。但是,SPI接口没有SDIO的速度快,而且,STM32VET6自带SDIO接口,所以我们就使用STM32的SDIO接口来驱动SD卡吧:虽然我们使用了SDIO接口连接SD卡,不过,这样连接后,既可以使用SDIO接口驱动SD卡,又可以使用GPIO模拟SPI时序来驱动SD卡,那我们就可以学习SD卡的两种驱动方式了。到时候,我们就可以读出SD卡里面的文件夹,读出SD卡里面的图片,读出SD卡中的TXT文件等等。并且可以模仿电脑来显示SD卡的存储情况,如下图所示:(这是我曾经做过的一个程序,图中上半部分是我做的程序的效果,图中下半部分是SD卡在电脑上看到的效果。如果你对这个程序感兴趣的话,可以去我的网站(瑞生网)搜索”SD卡“就可以找到这篇文章,带源码。)今天就介绍到这里吧。如果你想看以前的文章或者要看后面的文章,请关注我的微信公众号:科技老顽童。科技老顽童(kjlwt8) 
 文章为作者独立观点,不代表大不六文章网立场
kjlwt8老顽童带你独家剖析当前最新科技,做科技潮人。热门文章最新文章kjlwt8老顽童带你独家剖析当前最新科技,做科技潮人。&&&&违法和不良信息举报***:183-
举报邮箱:
Copyright(C)2016 大不六文章网
京公网安备78查看: 731|回复: 2
stm32 SDIO读取SD卡扇区,使用HAL库
【猪圈丶嗨情歌的开发分享】
今天给大家带来的是我这几天学习的SDIO,用STM32系列芯片的SDIO读取SD卡的扇区。本人原创,这里转贴到51黑。
我使用的工具
开发平台:正点原子探索者STM32F407开发板
硬件:使用了NUCLEO-F446RE开发板的ST-Link作为调试器、SD卡、数据线、开发板的电源适配器、DELL一体机
软件:STM32CubeMX、Keil V5、串口助手
学习的知识点
1、使用STM32CubeMX配置SDIO
2、在Keil中初始化SDIO
3、读取SD卡状态、卡信息
4、sprintf函数的使用
共享的资源
完整的工程文件
(9.16 MB, 下载次数: 10)
18:10 上传
点击文件名下载附件
下载积分: 黑币 -5
写在前面的话
上次STM32F469I开发板申请的活动吃了不少苦头啊,手里没有什么精华帖,只能跑去申请小鲜肉分块的开发板了。结果这个活动异常火爆,最后1000+的人参与。这次决心做好开发分享系列的帖子,把自己平时学习STM32开发中的干货写成帖子在论坛里分享给大家。希望支持的坛友多多回复,给我顶一个人气。
本帖不解决硬件连接问题,如果硬件方面有问题最好自行搜索相关资料。
SD卡的通信方式有的是用SPI,我们这里是SDIO,速度更快更好用。如果你的SD卡模块有MOSI、MISO这些字样,那说明这些是SPI读写用的模块,不适合这篇帖子的分享内容。如果使用的是现成的开发板,请参考开发板的手册确定SD卡的通信方式。
如果你确定了SD卡的通信方式为SDIO无误并且正确连接了SD卡和芯片,然后记录下了SD卡的引脚和芯片的连接方式那么准备工作就基本完成了。
在STM32CubeMX中配置SDIO
SDIOCubeMX配置.png (97.6 KB, 下载次数: 8)
18:00 上传
我们来看看左边的红框,在外设里面找到SDIO、SYS、USART1(看自己的开发板来选择。还有我忘了打开这个分支,USART1我选的是第一个选项)。在下拉框里面选择和上图一样的选项,这样可以保证后面的项目一致。
我们在左边选择好了以后,可以看到右边的芯片引脚有一些是绿色的,就说明这些引脚有配置被激活了。
配置时钟.png (115.67 KB, 下载次数: 7)
18:00 上传
我们现在来玩一玩这个时钟的配置窗口,这个在Pinout标签页旁边大家自行点开。
那么时钟怎么配置呢?我一般是主频给我来最大,只要保证这里的输入框都是蓝色的就行了,如果是红色的就说明频率高了这个就要自己调整一下了。
剩下的内容就已经确定了,不需要我们来配置。如果你自己有什么别的想法的话,就自己动手来配置一下。
在Keil中初始化SDIO
我们现在要初始化SDIO来驱动我们的SD卡工作,现在贴出我在main函数中的初始化代码。下面的代码都是Cube自动生成的,这些代码就可以初始化我们的SDIO了。
& & /* Initialize all configured peripherals */
& && &MX_GPIO_Init();
& && &MX_SDIO_SD_Init();
& && &MX_USART1_UART_Init();复制代码
添加一些必要的初始化代码(12月14号追加)
为了保证SDIO正常工作,我们还要添加两个函数来初始化。这两个函数Cube没有事先给我们调用,我们要自己动一次手。在工程的文件树Application/User里面找到sdio.c文件,找到第一个void MX_SDIO_SD_Init(void)函数。我们把我们需要的初始化代码贴进去。
加入的这两行函数才是真正的初始化了SDIO,前面都是在配置一个结构体变量。最后的两行代码需要我们自己添加进来,这两个函数的定义大家可以自行查询。这两行代码没有写在用户代码保护模块里面,所以下一次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_S***E_DISABLE;
& && &hsd.Init.BusWide = SDIO_BUS_WIDE_1B;
& && &hsd.Init.HardwareFlowControl = SDIO_HARDWARE_FLOW_CO***OL_DISABLE;
& && &hsd.Init.ClockDiv = 0;
& && && && &HAL_SD_Init(&hsd, &SDCardInfo);
& && &HAL_SD_WideBusOperation_Config(&hsd, SDIO_BUS_WIDE_4B);
& & }复制代码
下面我贴出我在while循环里面的代码,分析一下这些代码。
首先我是用了HAL_SD_GetStatus函数来读取SD卡的状态,这些状态只有SD_TRANSFER_OK、SD_TRANSFER_BUSY、SD_TRANSFER_ERROR这三种。State = HAL_SD_GetStatus(&hsd);复制代码
接下来的条件语句里面,我用几个if来决定不同的状态应该做些什么。
1、现在SD是OK的状态,我们可以对SD卡进行读写操作
在读取扇区的数据之前,发送一个SD Card OK的消息。然后调用HAL_SD_ReadBlocks函数来读取扇区的内容。这个函数的第四个参数必须是512,因为目前还不支持其他的扇区大小。
该函数的返回值表明了读取操作的是否成功,如果为0表示成功,那么我们就发送一个Sector read is OK的消息。如果没有成功,则发送一个ERROR的消息同时用break来终止此次循环。
在接下来的for循环里面,我们把读取的32位扇区信息转化成8位的扇区信息并且使用sprintf函数格式化成字符串。
关于32位转化为8位这里不做过多的赘述,主要是C语言数据类型转换的时候丢失一部分信息和位运算的知识,大家自己看一下。
sprintf函数的使用
这里要特别提一下这个函数,这是C标准库中的一个函数,在我们的STM32平台上也有实现。这个函数可以把数据格式化为字符串,把数据作为字符的形式显示出来非常的实用。
想了解详细的函数使用方法,请自行百度一下。这里简单的介绍一下。
该函数的第一个参数是我们要转换的内容存储的指针,转换好的字符串存储在这个指针指向的空间里。第二个字符串就是格式化字符串,这个字符串里面要包含转换字符。我在代码里面使用的是%X这个转换字符,可以把数据用大写的16进制显示。后面的参数是和前面的转换字符一一对应的待转换变量,直接传递参数就可以了,不需要指针。
转换好了字符串之后,我就可以挨个发送这些字符。发送完之后就循环一直等待就行了,不用反复读取。
原型int sprintf( char *buffer, const char *format, [ argument] … );参数列表buffer:char型指针,指向将要写入的字符串的缓冲区。format:格式化字符串。[argument]...:可选参数,可以是任何类型的数据。返回值:字符串长度(strlen)
& & if( State == 0)
& && && && && && &&&{
& && && && && && && && && & HAL_UART_Transmit(&huart1, (uint8_t *)&SD Card OK\n&, 11, 500);& && && && && && && && && && && && && && && &
& && && && && && && && && & if(HAL_SD_ReadBlocks(&hsd, pReadBuffer, 0x2, 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);
& && && && && && && && && && && && &
& && && && && && && && && & }
& && && && && && &
& && && && && && && && && & 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);
& && && && && && &&&}复制代码 SD状态返回为错误,这基本上就是SD卡没有插入,要么就是卡坏掉了。这个时候如果我们不进行任何操作就进行下一次判断的话,就算是在插入SD卡不管怎么读取都是返回ERROR。所以我们在读取到这个状态的时候,调用一次HAL_SD_Init(&hsd, &SDCardInfo);这个函数来清除之前的状态(这个是我自己想的,不知道有没有卵用)。这样子操作的话,在下一次插入SD卡的时候就可以返回为OK的值并且可以读写。
最后用了一个延时函数来限制扫描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, 下载次数: 7)
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 CO***IBUTORS &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 CO***IBUTORS 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 CO***ACT, 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, 0x2, 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);
& && && && && && && && && && &&&
& && && && && && && && &}
& && && && && &
& && && && && && && && &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_OscInitS
&&RCC_ClkInitTypeDef RCC_ClkInitS
&&__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 */
/**
&&* @}
&&*/
/**
&&* @}
*/
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
复制代码
写在后面的话
注意!注意!注意!本人不是什么工程师,只不过是爱好嵌入式开发的学生一枚,如果你发现在这个帖子中的错误请及时提醒我。如果对本帖的内容有什么疑问请在下方留言,我会经常过来逛论坛的。
下一次给大家带来的可能就是FatFs了,这样我们就可以读写文件了。
 可以用么
 瞧瞧看,学习学习
Powered by随笔 - 337&
文章 - 0&评论 - 14&trackbacks - 0
一、SD卡概述
  SD卡(安全数码卡),是一种基于半导体快闪记忆器的新一代记忆设备,它被广泛地于便携式装置上使用,例如数码相机、个人数码助理(外语缩写PDA)和多媒体播放器等。
2、容量等级
FAT 12, 16
2GB至 32GB
3、SD卡框图
&引脚说明:
4、SD卡与TF卡的区别
  TF卡又名micro SD卡,个头是比SD卡的1/4还小,可以通过&TF转SD卡套&转换成SD卡。
  详细区别参考:  
二、&SD卡内部结构
  (摘自SanDisk Secure Digital Card Product Manual Version 1.9)
1、 SD卡内部结构简图
   由SD卡控制器和存储阵列组成,SD卡与外界的通讯接口是SD Bus或者SPI Bus。
2、 存储阵列结构图
Block:     &
  读写时的单元(数据传输单元),它的单位是&字节&。
Sector:     
  如果CSD寄存器ERASE_BLK_EN = 0时,Sector是最小的擦除单元,它的单位是&块&。Sector的值等于CSD寄存器中的SECTOR_SIZE的值+1。
WP Group:   
  最小的写保护单元,它的单位是&扇区&。
  SD Card的Buffer最大容量定义在CSD寄存器的READ_BL_LEN和WRITE_BL_LEN。它们的值是一样的,而且有可能超过512字节,尽管这样Block还是要设置成512字节,因为512字节是数据边界(这句话不是太理解)。也就是SD卡上有数据传输缓冲器Buffer,不同的产品可能不一样,但是在使用时要将Buffer设置成512字节。
参考资料:  
  The card buffer size is described as maximum block length in the Card Specif ic Data (CSD) register for memory cards (for cards compliant with the Physical Layer Specification, READ_BL_LEN and WRITE_BL_LEN shall be the same) and in the Card Information Structure (CIS) for SDIO cards. Physical Layer Specification re-defines that maximum block length is only used to calculate capacity of memory card. Even though it indi cates larger than 512 bytes, block length shall be set to 512 byte for data transfer. This is because 512 bytes block l ength is required to keep compatibility with 512 bytes data boundary.
(摘自《Simplified_SD_Host_Controller_Spec.pdf》)
4、&存储阵列Block&--最小的存储单元
  资料上的Block通通指的是数据传输时的最小单元,定义这个数值是为了数据传输、CRC校验等。
  存储阵列通常采用NandFlash的结构,显然不能按字节存取,而这里讨论的&存储阵列Block&就是指这个概念。可惜的是目前,我还没有找到资料讨论这个问题,所以这一章节是笔者自己的推测。
  据我推测存储阵列Block应该是512Byes,因为众多的数据都围绕着512Bytes在转。比如说最小的擦除单元是512Byes,最小的读写单位应该被设置成512Bytes,那么有理由推测是这个数值。
5、SD卡特殊功能寄存器
CID:  宽度128位,卡标识号
RCA:  宽度16位,卡相对地址,在初始化的时候确定
CSD:  宽度128位,卡描述数据:卡操作条件的信息
SCR:  宽度64位,SD卡配置寄存器:SD卡特定信息数据
OCR:  宽度32位,操作条件寄存
三、SDIO接口
四、SD卡协议之数据读、写、擦除
1、SD卡写数据块 
  执行写数据块命令(CMD24-27) 时,主机把一个或多个数据块从主机传送到卡中,同时在每个数据块的末尾传送一个CRC码。主机传送数据,SD卡接收数据并将数据保存在Buffer中,累计接收数据达到Block长度的时候,SD卡把接下来的数据当做CRC校验码,并且开始数据校验。如果CRC校验错误,卡通过SDIO_D 线指示错误,传送的数据被丢弃而不被写入,所有后续(在多块写模式下)传送的数据块将被忽略。
& & &如果主机传送部分数据而累计的数据长度未与数据块对齐,当不允许块错位( 未设置CSD的参数WRITE_BLK_MISALIGN),卡将在第一个错位的块之前检测到块错位错误( 设置状态寄存器中的ADDRESS_ERROR 错误位) 。当主机试图写一个写保护区域时,写操作也会被中止,此时卡会设置WP_VIOLATION位。 
  数据块Block的最大长度定义在CSD中的WRITE_BL_LEN,但是在数据传输时应该用CMD16指令将其设置为512Byets,不去在意WRITE_BL_LEN是1024或者2048Bytes。
  另外需要注意的是,Block的长度设置还要参考CSD寄存器的WRITE_BL_PARTIAL。当WRITE_BL_PARTIAL为0时,那么么办法Block只能设置为512Bytes;如果WRITE_BL_PARTIAL=1,那么允许将Block设置成更小的块,比如说一个字节。协议是这样规定的,但是据我分析如果这样的话SD卡的制作会非常复杂(写入的单位可以是字节),价格也会很高。笔者测试了自己的SD卡,WRITE_BL_PARTIAL 等于0,也就是不支持&块部分写&功能。&
2、SD卡读数据块
  在读数据块模式下,数据传输的基本单元是数据块Block。为保证数据传输的正确,传输一个数据块Blcok后都有一个CRC校验码。笔者认为主机在累计接收到Block长度数据后,软件可以把接下来的数据当做CRC校验码,并且进行校验。
  Block的最大值在CSD中(READ_BL_LEN) 给出了定义,但是在数据传输时应该用CMD16指令将其设置为512Byets,不去在意READ_BL_LEN是1024或者2048Bytes。
  如果CSD寄存器中的READ_BL_PARTIAL等于1,可以传送的较小数据块,较小数据块是指开始和结束地址完全包含在一个物理块中。事实上,协议规定READ_BL_PARTIAL永远等于1,也就说在任何SD卡上都允许&读部分块&,读的块的最小字节是1Bytes。使用这种功能,可以通过CMD16命令设置更小的Block(比如说等于128)。读取的这128字节必须在512Bytes边界内,不能跨越边界(其实因为存储阵列是以512Bytes为单位的,读取&部分块&只能在一个块内,不允许跨块读)。
3、擦除SD卡
  CSD寄存器ERASE_BLK_EN决定了SD卡的最小擦除单位。
  当ERASE_BLK_EN等于0的时候,主机擦除的最小单位是扇区。比如一个Sector包含32个Block,擦除时的起始地址是5,而结束地址是40,那么实际擦除的块是从0到63。
  当ERASE_BLK_EN等于1的时候,主机擦除的最小单位是512 Byetes。比如擦除时的起始地址是5,而结束地址是40,那么实际擦除的块就是从5到40。
五、SD卡物理层协议
  SD卡的协议相对于SPI、I2C等协议的存储器来说相对复杂,包含SD卡物理层(机械封装、管脚、芯片结构、命令集等)、SD卡接口(SDIO)、SD主机控制器,甚至是软件设计的流程,都进行了详细的规定。
① SDIO接口
  参考《Simplified_SDIO_Card_Spec.pdf》
&1& CLK 时钟同步线
&2& CMD 命令信号线,主机发出的命令以及从机对命令的响应都是通过这条线进行传输
&3& DAT[3:0] 表示4条数据线,主机和从机的数据都是从这四条数据线上传输
② SPI接口
2、命令格式
3、响应格式
  以R1为例
4、SD卡的工作状态
5、SD卡的两种状态信息
① Card Status
  执行命令过程中的状态信息,比如地址不对齐错误、块长度错误、卡锁、ECC校验错误等等
② SD Status
  SD卡的专有特征,编程中不经常涉及。这个状态值有512位,不是通过命令线传送给主机,而是通过数据线。
六、STM32与SD卡相配的外设--SDIO适配器
&1、SDIO adapter 结构图
2、命令状态机(CPSM)
  当发送命令和接收响应时,启动CPSM状态机。
3、数据通道状态机
  当传输数据时,启动数据通道状态机。
  有关FIFO的资料参考:
  数据FIFO(先进先出)子单元是一个具有发送和接收单元的数据缓冲区。
  FIFO包含一个每字32位宽、共32个字的数据缓冲区,和发送与接收电路。因为数据FIFO工作在AHB 时钟区域(HCLK/2),所有与SDIO时钟区域(SDIOCLK)连接的信号都进行了重新同步。依据TXACT和RXACT标志,可以关闭FIFO、使能发送或使能接收。TXACT和RXACT 由数据通道子单元设置而且是互斥的:
    ─ 当 TXACT 有效时,发送 FIFO 代表发送电路和数据缓冲区
    ─ 当 RXACT 有效时,接收 FIFO 代表接收电路和数据缓冲区
5、SDIO的特殊功能寄存器
SDIO电源控制寄存器(SDIO_POWER)
SDIO时钟控制寄存器(SDIO_CLKCR) : 时钟选择、分频
SDIO参数寄存器(SDIO_ARG)
SDIO命令寄存器(SDIO_CMD):控制发送命令
SDIO命令响应寄存器(SDIO_RESPCMD):包含响应命令中的命令索引
SDIO响应1..4寄存器(SDIO_RESPx):包含响应命令中的卡状态信息
SDIO数据定时器寄存器(SDIO_DTIMER)
SDIO数据长度寄存器(SDIO_DLEN):读或者写的长度,通常是是512的倍数
SDIO数据控制寄存器(SDIO_DCTRL):控制数据的读写方向、使能传输等信息
SDIO数据计数器寄存器(SDIO_DCOUNT):当DPSM状态机从Idle state切换到Wait_R或者Wait_S状态时,SDIO_LEN的数值加载到该寄存器中
SDIO状态寄存器(SDIO_STA)
SDIO清除中断寄存器(SDIO_ICR)
SDIO中断屏蔽寄存器(SDIO_MASK)
SDIO FIFO计数器寄存器(SDIO_FIFOCNT):当SDIO_DCTRL中的DTEN使能,并且DPSM处于Idle state时,SDIO_LEN/4的数值加载到该寄存器中
SDIO数据FIFO寄存器(SDIO_FIFO):读写数据缓冲FIFO
七、SD卡编程
  SD卡的编程在STM32官方固件库中就有例程,而且野火开发板对该例程进行了中文注释,不必再把源码贴入。这里着重讲一下SD卡编程流程,主要包含SD卡初始化、SD卡读、SD卡写、SD卡擦除。
1、SD卡编程的内容
  SD卡主要就是用来存储数据的,所以核心就是读写。为了实现这个目标,必须实现响应的驱动。
  配置过程中,不仅要设置好SD控制器,还需要将SD卡设置到合适的状态。在读取状态的时候,不仅涉及到SD控制器的状态,还涉及到SD卡的状态。
2、SD卡初始化
① STM32外设SDIO初始化
端口配置、端口时钟、SDIO时钟、DMA2时钟使能
SDIO寄存器复位
设置时钟SDIO_CK为400KHz以下,设置数据线宽度,开启时钟、开启SDIO电源
② SD卡上电初始化
  上电初始化流程如上图所示,笔者认为官方库提供的例程没有完全按照这个流程图的指示去做。事实上,官方库的程序只做了如上图红色方框内的流程,之外的没涉及。
  CMD0命令复位所有的卡。
  SD协议规定:在初始化的时候,使用ACMD41之前,必须先使用CMD8命令。而且ACMD41命令属于应用命令,在使用之前需要先发送命令CMD55。
  CMD8命令是为了核查电源是否匹配。ACMD41命令不断询问SD卡是否支持主机提供的电压,并且询问SD卡是否上电完成进入准备状态。ACMD41命令还能询问SD卡的类型(SDSC、SDHC)。
③卡进一步核查、获取卡信息
  发送命令CMD2,以获取CID信息。
  发送命令CMD3,以获取RCA相对地址,可以通过多次发送CMD3获取不同的RCA值,但是只有最后一次的才是有效的RCA地址。
  发送命令CMD9,以获取CSD寄存器。
④ 设置SDIO工作在数据传输模式
  设置SDIO的时钟为24MHz、数据线宽度为4位。
  通过SD_GetCardInfo函数将之前得到CID、CSD处理成卡的信息。
  通过CMD7命令选择匹配地址的卡,而取消选择其他的卡。
  至此,初始化完成。
3、读SD卡的一个块
 数据控制寄存器(SDIO_DCTRL)清零
 发送命令CMD16,设置SD卡的Block大小
 调用函数SDIO_DataConfig设置SDIO数据传输方式
 发送命令CMD17,读单个块
 SDIO数据传输结束中断使能
 SDIO的DMA传输功能使能
 DMA设置,并使能
4、写SD卡的一个块&
 数据控制寄存器(SDIO_DCTRL)清零
 发送命令CMD16,设置SD卡的Block大小
 发送命令CMD24,写单个块
 调用函数SDIO_DataConfig设置SDIO数据传输方式
 SDIO数据传输结束中断使能
 DMA设置,并使能
 使能SDIO的DMA传输功能&
八、SD卡疑惑
1、SD卡擦除后,其中的内容是0,还是1 ?&
  The data at the card after an erase operation is either '0' or '1', depends on the card vendor.The SCR register bit DATA_STAT_AFTER_ERASE (bit 55) defines whether it is '0' or '1'.(摘自《SD Specifications_Part_1_Physical_Layer_Specification_Ver3.00_Final_090416.pdf》)
  也就是说这是芯片厂商生产工艺决定的,可以通过SCR寄存器的&DATA_STAT_AFTER_ERASE位得知。
&2、在SDIO_DCTRL中设置传输Block的要求
  Block大小决定了主机在发送数据时,发送到什么程度时开始发送CRC校验码;而在接收数据时,在接收到什么程度时开始把SD卡的数据作为CRC校验码并进行校验。Block还可能影响着其他的时序。在STM32的SDIO寄存器组中,SDIO_DCTRL中的位段DBLOCKSIZE决定主机Block大小。
  在摘自《Simplified_SD_Host_Controller_Spec.pdf》的引文中,提到这样的配置要求:主机的Block一定要与SD卡设置的Block一样大小,这显然是必要的。我们经常设置SD卡的Block大小是512Bytes,所以设置DBLOCKSIZE为9(2^9 = 512)。
&3、STM32固件库&stm32_eval_sdio_sd.c version v4.5.0&偶遇BUG
  参考网页:/3742318/
&4、SD卡写Block是怎样进行的?
  写SD卡的单位是Block(512Bytes),再写之前要先整块擦除,然后才能写。
  在多块写操作中,可以在发送多块写命令CMD25之前,有选择性的先发送命令ACMD23设置预擦除。怎样理解呢?
  既然是有选择性的,也可以不发送ACMD23命令。在多块写的过程中,由于SD卡事先不知道你要写入几个块(CMD25命令只告诉SD卡要写入的起始地址),所以写入的过程是:根据需要判断要写一个块时,先擦除然后再写,再判断是否要写入下一个块,如果是就再擦除再写。
  倘若发送ACMD23命令就不一样了,ADM23命令会在写命令CMD25生效之前,告诉SD卡准备写入的块数N。这样当CMD25命令生效的时候,SD卡会一次性先将这N个块都擦除,然后再一个块一个块写。由于擦除操作比较集中,所以整个多块写操作更节省时间。
九、SD卡参数测试
  使用野火开发板配套例程做测试,笔者测试用的SD卡是金士顿的2G内存块,打印SD卡的参数信息如下:
Card Type is :1
ManufacturerID is :2
Card device size is :3795
Card Block Size is :1024
Card device size multiplier is :7
Card Capacity is :
the maximum read date block length is :1024
partial blocks for write allowed is :0
the maximum write date block length is :1024
erase single block enable is :1
erase sector size is :127
write protect group size is :0
RCA is :4660&
Card Type:1 & & & & SDSC卡版本2.0
Card device size: &C_SIZE(CSD),为3795
Card Block Size:  max read data block length(READ_BL_LEN(CSD)) ,为1024 Bytes
Card device size multiplier is: &C_SIZE_MULT(CSD),为7
Card Capacity:   Bytes
计算方法(摘自《Simplified_Physical_Layer_Spec.pdf》):
memory capacity = BLOCKNR * BLOCK_LEN &
  BLOCKNR = (C_SIZE+1) * MULT &(C_SIZE &= 4096)   MULT = 2^(C_SIZE_MULT+2) & & & &(C_SIZE_MULT & 8)
  BLOCK_LEN = 2^READ_BL_LEN, & &(READ_BL_LEN & 12)
注意:SDSC最大为2GB。
the maximum read date block length:READ_BL_LEN(CSD) ,为1024 Bytes
partial blocks for write allowed:WRITE_BL_PARTIAL(CSD),为不支持
the maximum write date block length:WRITE_BL_LEN(CSD) ,为1024 Bytes
erase single block enable:ERASE_BLK_EN(CSD),为1,支持单块擦除
erase sector size:SECTOR_SIZE(CSD),实际扇区擦除的block数为(SECTOR_SIZE+1),为128 Blocks
write protect group size:WP_GRP_SIZE(CSD),实际保护的扇区数为(WP_GRP_SIZE+1),为1 Sector
RCA: SD卡相对地址为4660
参考资料:《Simplified_SDIO_Card_Spec.pdf》
     《Simplified_Physical_Layer_Spec.pdf》
     &&《Simplified_SD_Host_Controller_Spec.pdf》
     《STM32芯片手册》
阅读(...) 评论()

参考资料

 

随机推荐