SD卡 【转载】STM32之SD卡

https://www.cnblogs.com/amanlikethis/p/3757876.html#lab11

目录

一、SD卡概述

  1、定义

  2、容量等级

  3、SD卡框图

  4、SD卡与TF卡的区别

二、 SD卡内部结构

  1、 SD卡内部结构简图

  2、 存储阵列结构图

  3、Buffer 

  4、“存储阵列Block”--最小的存储单元

  5、SD卡的特殊功能寄存器

三、SDIO接口 

四、SD卡协议的核心--数据读、写、擦除

  1、SD卡写数据块

  2、SD卡读数据块 

  3、擦除SD卡

五、SD卡物理层协议

  1、接口 

  2、命令格式

  3、响应格式

  4、SD卡的工作状态

  5、SD卡的两种状态信息

六、STM32与SD卡相配的外设--SDIO适配器

  1、SDIO adapter 结构图 

  2、命令状态机(CPSM)

  3、数据通道状态机(DPSM)

  4、SDIO的FIFO 

  5、SDIO的特殊功能寄存器

七、SD卡编程 

  1、SD卡编程的内容

  2、SD卡初始化 

  3、读SD卡的一个块

  4、写SD卡的一个块 

八、SD卡疑惑

  1、SD卡擦除后,其中的内容是0,还是1 ? 

  2、在SDIO_DCTRL中设置传输Block的要求

    3、STM32固件库“stm32_eval_sdio_sd.c version v4.5.0”偶遇BUG

    4、SD卡写Block是怎样进行的?

九、SD卡参数测试

一、SD卡概述

1、定义

  SD卡(安全数码卡),是一种基于半导体快闪记忆器的新一代记忆设备,它被广泛地于便携式装置上使用,例如数码相机、个人数码助理(外语缩写PDA)和多媒体播放器等。

2、容量等级

STM32启动过程--启动文件--分析

一、概述

1、说明

  每一款芯片的启动文件都值得去研究,因为它可是你的程序跑的最初一段路,不可以不知道。通过了解启动文件,我们可以体会到处理器的架构、指令集、中断向量安排等内容,是非常值得玩味的。

  STM32作为一款高端Cortex-M3系列单片机,有必要了解它的启动文件。打好基础,为以后优化程序,写出高质量的代码最准备。

  本文以一个实际测试代码--START_TEST为例进行阐述。

2、整体过程概括

  STM整个启动过程是指从上电开始,一直到运行到main函数之间的这段过程,步骤为(以使用微库为例):

①上电后硬件设置SP、PC

②设置系统时钟

③软件设置SP

④加载.data、.bss,并初始化栈区

⑤跳转到C文件的main函数

3、整个启动过程涉及的代码

  启动过程涉及的文件不仅包含startup_stm32f10x_hd.s,还涉及到了MDK自带的连接库文件entry.o、entry2.o、entry5.o、entry7.o等(从生成的map文件可以看出来)。

二、程序在Flash上的存储结构

  在真正讲解启动过程之前,先要讲解程序下载到Flash上的结构和程序运行时(执行到main函数)时的SRAM数据结构。程序在用户Flash上的结构如下图所示。下图是通过阅读hex文件和在MDK下调试综合提炼出来的。

MSP初始值        编译器生成,主堆栈的初始值

异常向量表        不多说

外部中断向量表      不多说

代码段          存放代码

初始化数据段       .data

未初始化数据段      .bss 

加载数据段和初始化栈的参数

  加载数据段和初始化栈的参数分别有4个,这里只讲解加载数据段的参数,至于初始化栈的参数类似。

0x0800 033c  Flash上的数据段(初始化数据段和未初始化数据段)起始地址

0x2000 0000  加载到SRAM上的目的地址

0x0000 000c  数据段的总大小

0x0800 02f4  调用函数_scatterload_copy

  需要说明的是初始化栈的函数--0x0800 0304与加载数据段的函数不一样,为_scatterload_zeroinit,它的目的就是将栈空间清零。

三、数据在SRAM上的结构

  程序运行时(执行到main函数)时的SRAM数据结构

四、详细过程分析

  有了以上的基础,现在详细分析启动过程。

1、上电后硬件设置SP、PC

  刚上电复位后,硬件会自动根据向量表偏移地址找到向量表,向量表偏移地址的定义如下:

  调试现象如下:

  看看我们的向量表内容(通过J-Flash打开hex文件)

  硬件这时自动从0x0800 0000位置处读取数据赋给栈指针SP,然后自动从0x0800 0004位置处读取数据赋给PC,完成复位,结果为:

SP = 0x0200 0810

PC = 0x0800 0145

 2、设置系统时钟

  上一步中令PC=0x0800 0145的地址没有对齐,硬件自动对齐到0x0800 0144,执行SystemInit函数初始化系统时钟。

3、软件设置SP

  LDR   R0,=__main
  BX   R0

  执行上两条之类,跳转到__main程序段运行,注意不是main函数,___main的地址是0x0800 0130。

  可以看到指令LDR.W sp,[pc,#12],结果SP=0x2000 0810。

4、加载.data、.bss,并初始化栈区

 BL.W     __scatterload_rt2 

  进入 __scatterload_rt2代码段。

  

; Vector Table Mapped to Address 0 at Reset
                AREA    RESET, DATA, READONLY
                EXPORT  __Vectors
                EXPORT  __Vectors_End
                EXPORT  __Vectors_Size

__Vectors       DCD     __initial_sp               ; Top of Stack
                DCD     Reset_Handler              ; Reset Handler
                DCD     NMI_Handler                ; NMI Handler
                DCD     HardFault_Handler          ; Hard Fault Handler
                DCD     MemManage_Handler          ; MPU Fault Handler
                DCD     BusFault_Handler           ; Bus Fault Handler
                DCD     UsageFault_Handler         ; Usage Fault Handler
                DCD     0                          ; Reserved
                DCD     0                          ; Reserved
                DCD     0                          ; Reserved
                DCD     0                          ; Reserved
                DCD     SVC_Handler                ; SVCall Handler
                DCD     DebugMon_Handler           ; Debug Monitor Handler
                DCD     0                          ; Reserved
                DCD     PendSV_Handler             ; PendSV Handler
                DCD     SysTick_Handler            ; SysTick Handler

                ; External Interrupts
                DCD     WWDG_IRQHandler            ; Window Watchdog
                DCD     PVD_IRQHandler             ; PVD through EXTI Line detect
                DCD     TAMPER_IRQHandler          ; Tamper
                DCD     RTC_IRQHandler             ; RTC
                DCD     FLASH_IRQHandler           ; Flash
                DCD     RCC_IRQHandler             ; RCC
                DCD     EXTI0_IRQHandler           ; EXTI Line 0
                DCD     EXTI1_IRQHandler           ; EXTI Line 1
                DCD     EXTI2_IRQHandler           ; EXTI Line 2
                DCD     EXTI3_IRQHandler           ; EXTI Line 3
                DCD     EXTI4_IRQHandler           ; EXTI Line 4
                DCD     DMA1_Channel1_IRQHandler   ; DMA1 Channel 1
                DCD     DMA1_Channel2_IRQHandler   ; DMA1 Channel 2
                DCD     DMA1_Channel3_IRQHandler   ; DMA1 Channel 3
                DCD     DMA1_Channel4_IRQHandler   ; DMA1 Channel 4
                DCD     DMA1_Channel5_IRQHandler   ; DMA1 Channel 5
                DCD     DMA1_Channel6_IRQHandler   ; DMA1 Channel 6
                DCD     DMA1_Channel7_IRQHandler   ; DMA1 Channel 7
                DCD     ADC1_2_IRQHandler          ; ADC1 & ADC2
                DCD     USB_HP_CAN1_TX_IRQHandler  ; USB High Priority or CAN1 TX
                DCD     USB_LP_CAN1_RX0_IRQHandler ; USB Low  Priority or CAN1 RX0
                DCD     CAN1_RX1_IRQHandler        ; CAN1 RX1
                DCD     CAN1_SCE_IRQHandler        ; CAN1 SCE
                DCD     EXTI9_5_IRQHandler         ; EXTI Line 9..5
                DCD     TIM1_BRK_IRQHandler        ; TIM1 Break
                DCD     TIM1_UP_IRQHandler         ; TIM1 Update
                DCD     TIM1_TRG_COM_IRQHandler    ; TIM1 Trigger and Commutation
                DCD     TIM1_CC_IRQHandler         ; TIM1 Capture Compare
                DCD     TIM2_IRQHandler            ; TIM2
                DCD     TIM3_IRQHandler            ; TIM3
                DCD     TIM4_IRQHandler            ; TIM4
                DCD     I2C1_EV_IRQHandler         ; I2C1 Event
                DCD     I2C1_ER_IRQHandler         ; I2C1 Error
                DCD     I2C2_EV_IRQHandler         ; I2C2 Event
                DCD     I2C2_ER_IRQHandler         ; I2C2 Error
                DCD     SPI1_IRQHandler            ; SPI1
                DCD     SPI2_IRQHandler            ; SPI2
                DCD     USART1_IRQHandler          ; USART1
                DCD     USART2_IRQHandler          ; USART2
                DCD     USART3_IRQHandler          ; USART3
                DCD     EXTI15_10_IRQHandler       ; EXTI Line 15..10
                DCD     RTCAlarm_IRQHandler        ; RTC Alarm through EXTI Line
                DCD     USBWakeUp_IRQHandler       ; USB Wakeup from suspend
                DCD     TIM8_BRK_IRQHandler        ; TIM8 Break
                DCD     TIM8_UP_IRQHandler         ; TIM8 Update
                DCD     TIM8_TRG_COM_IRQHandler    ; TIM8 Trigger and Commutation
                DCD     TIM8_CC_IRQHandler         ; TIM8 Capture Compare
                DCD     ADC3_IRQHandler            ; ADC3
                DCD     FSMC_IRQHandler            ; FSMC
                DCD     SDIO_IRQHandler            ; SDIO
                DCD     TIM5_IRQHandler            ; TIM5
                DCD     SPI3_IRQHandler            ; SPI3
                DCD     UART4_IRQHandler           ; UART4
                DCD     UART5_IRQHandler           ; UART5
                DCD     TIM6_IRQHandler            ; TIM6
                DCD     TIM7_IRQHandler            ; TIM7
                DCD     DMA2_Channel1_IRQHandler   ; DMA2 Channel1
                DCD     DMA2_Channel2_IRQHandler   ; DMA2 Channel2
                DCD     DMA2_Channel3_IRQHandler   ; DMA2 Channel3
                DCD     DMA2_Channel4_5_IRQHandler ; DMA2 Channel4 & Channel5
__Vectors_End

View Code

   这段代码就是定义异常向量表,在之前有一个“J-Flash打开hex文件”的图片跟这个表格是一一对应的。编译器根据我们定义的函数 Reset_Handler、NMI_Handler等,在连接程序阶段将这个向量表填入这些函数的地址。

  使用微库就意味着我们不想使用MDK提供的库函数,而想用自己定义的库函数,比如说printf函数。那么这一点是怎样实现的呢?我们以printf函数为例进行说明。

1、不使用微库而使用系统库

  在连接程序时,肯定会把系统中包含printf函数的库拿来调用参与连接,即代码段有系统库的参与。

  在启动过程中,不使用微库而使用系统库在初始化栈的时候,还需要初始化堆(猜测系统库需要用到堆),而使用微库则是不需要的。

  

                  __main:
0x08000130 F000F802  BL.W     __scatterload_rt2_thumb_only (0x08000138)
0x08000134 F000F83C  BL.W     __rt_entry_sh (0x080001B0)
                 __scatterload_rt2_thumb_only:
0x08000138 A00A      ADR      r0,{pc}+4  ; @0x08000164
0x0800013A E8900C00  LDM      r0,{r10-r11}
0x0800013E 4482      ADD      r10,r10,r0
0x08000140 4483      ADD      r11,r11,r0
0x08000142 F1AA0701  SUB      r7,r10,#0x01
                 __scatterload_null:
0x08000146 45DA      CMP      r10,r11
0x08000148 D101      BNE      0x0800014E
0x0800014A F000F831  BL.W     __rt_entry_sh (0x080001B0)
0x0800014E F2AF0E09  ADR.W    lr,{pc}-0x07  ; @0x08000147
0x08000152 E8BA000F  LDM      r10!,{r0-r3}
0x08000156 F0130F01  TST      r3,#0x01
0x0800015A BF18      IT       NE
0x0800015C 1AFB      SUBNE    r3,r7,r3
0x0800015E F0430301  ORR      r3,r3,#0x01
0x08000162 4718      BX       r3
0x08000164 0298      LSLS     r0,r3,#10
0x08000166 0000      MOVS     r0,r0
0x08000168 02B8      LSLS     r0,r7,#10
0x0800016A 0000      MOVS     r0,r0
                 __scatterload_copy:
0x0800016C 3A10      SUBS     r2,r2,#0x10
0x0800016E BF24      ITT      CS
0x08000170 C878      LDMCS    r0!,{r3-r6}
0x08000172 C178      STMCS    r1!,{r3-r6}
0x08000174 D8FA      BHI      __scatterload_copy (0x0800016C)
0x08000176 0752      LSLS     r2,r2,#29
0x08000178 BF24      ITT      CS
0x0800017A C830      LDMCS    r0!,{r4-r5}
0x0800017C C130      STMCS    r1!,{r4-r5}
0x0800017E BF44      ITT      MI
0x08000180 6804      LDRMI    r4,[r0,#0x00]
0x08000182 600C      STRMI    r4,[r1,#0x00]
0x08000184 4770      BX       lr
0x08000186 0000      MOVS     r0,r0
                 __scatterload_zeroinit:
0x08000188 2300      MOVS     r3,#0x00
0x0800018A 2400      MOVS     r4,#0x00
0x0800018C 2500      MOVS     r5,#0x00
0x0800018E 2600      MOVS     r6,#0x00
0x08000190 3A10      SUBS     r2,r2,#0x10
0x08000192 BF28      IT       CS
0x08000194 C178      STMCS    r1!,{r3-r6}
0x08000196 D8FB      BHI      0x08000190
0x08000198 0752      LSLS     r2,r2,#29
0x0800019A BF28      IT       CS
0x0800019C C130      STMCS    r1!,{r4-r5}
0x0800019E BF48      IT       MI
0x080001A0 600B      STRMI    r3,[r1,#0x00]
0x080001A2 4770      BX       lr
                 __rt_lib_init:
0x080001A4 B51F      PUSH     {r0-r4,lr}
0x080001A6 F3AF8000  NOP.W    
                 __rt_lib_init_user_alloc_1:
0x080001AA BD1F      POP      {r0-r4,pc}
                 __rt_lib_shutdown:
0x080001AC B510      PUSH     {r4,lr}
                 __rt_lib_shutdown_user_alloc_1:
0x080001AE BD10      POP      {r4,pc}
                 __rt_entry_sh:
0x080001B0 F000F82F  BL.W     __user_setup_stackheap (0x08000212)
0x080001B4 4611      MOV      r1,r2
                 __rt_entry_postsh_1:
0x080001B6 F7FFFFF5  BL.W     __rt_lib_init (0x080001A4)
                 __rt_entry_postli_1:
0x080001BA F000F919  BL.W     main (0x080003F0)

View Code

2、使用微库而不使用系统库

  在程序连接时,不会把包含printf函数的库连接到终极目标文件中,而使用我们定义的库。

  启动时需要完成的工作就是之前论述的步骤1、2、3、4、5,相比使用系统库,启动过程步骤更少。

3、SD卡框图

 引脚说明:

4、SD卡与TF卡的区别

  TF卡又名micro SD卡,个头是比SD卡的1/4还小,可以通过“TF转SD卡套”转换成SD卡。

  详细区别参考:SD卡与TF卡的区别  

二、 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:   

  最小的写保护单元,它的单位是“扇区”。

3、Buffer

  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主机控制器,甚至是软件设计的流程,都进行了详细的规定。

1、接口

① 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、数据通道状态机

  当传输数据时,启动数据通道状态机。

4、FIFO

  有关FIFO的资料参考:异步FIFO的FPGA实现

  数据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初始化

  1. 端口配置、端口时钟、SDIO时钟、DMA2时钟使能
  2. SDIO寄存器复位
  3. 设置时钟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

  参考网页:http://www.cprogramdevelop.com/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卡的参数信息如下:

copycode.gif

uploading.gif

转存失败重新上传取消

复制代码

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 :1990197248

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 

copycode.gif

uploading.gif

转存失败重新上传取消

复制代码

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:  1990197248 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芯片手册》

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
基于STM32的门禁系统设计可以利用STM32单片机的高性能和丰富的外设资源来实现门禁系统的功能。首先,我们可以通过STM32的GPIO口连接门禁开关和电磁锁,实现对门的开关控制。同时,利用STM32的定时器和中断功能来设置定时关门和报警功能,保障门禁系统的安全性。 另外,我们可以通过STM32的USART或者SPI接口连接门禁读卡器,实现对刷卡的识别和验证功能。利用STM32的串口通信功能,可以与服务器或者管理端进行实时通讯,实现对进出人员的记录和统计。同时,结合STM32的内部存储器或外部FLASH芯片,可以存储门禁系统的配置参数和操作日志,以便后期管理和分析。 通过STM32的ADC接口,我们还可以连接红外传感器或者摄像头,实现对门禁区域的监控和报警功能。利用STM32的PWM功能,可以控制门禁系统的蜂鸣器和指示灯,实现对门禁状态的提示和警示。 在软件方面,我们可以利用STM32的开发工具进行程序开发,采用C语言或者汇编语言编写门禁系统的控制程序。结合RTOS实时操作系统,可以实现对门禁系统各个任务的分时调度和管理。同时,利用STM32的编程接口和调试工具,可以方便地对门禁系统进行程序调试和优化。 综上所述,基于STM32的门禁系统设计可以充分利用STM32单片机的强大功能和灵活性,实现对门禁系统各项功能的完善控制和管理。同时,结合丰富的外设资源和严谨的软件开发,可以确保门禁系统的稳定性和安全性。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值