有哪个网站做策划方案的营销策划与运营方案
- 作者: 五速梦信息网
- 时间: 2026年03月21日 06:53
当前位置: 首页 > news >正文
有哪个网站做策划方案的,营销策划与运营方案,wordpress 图片相册,定制级高端网站建设KEIL 5.38的ARM-CM3/4 ARM汇编设计学习笔记13 - STM32的SDIO学习5 - 卡的轮询读写擦 一、前情提要二、目标三、技术方案3.1 读写擦的操作3.1.1 读卡操作3.1.2 写卡操作3.1.3 擦除操作 3.2 一些技术点3.2.1 轮询标志位的选择不唯一3.2.2 写和擦的卡状态查询3.2.3 写的速度 四、代… KEIL 5.38的ARM-CM3/4 ARM汇编设计学习笔记13 - STM32的SDIO学习5 - 卡的轮询读写擦 一、前情提要二、目标三、技术方案3.1 读写擦的操作3.1.1 读卡操作3.1.2 写卡操作3.1.3 擦除操作 3.2 一些技术点3.2.1 轮询标志位的选择不唯一3.2.2 写和擦的卡状态查询3.2.3 写的速度 四、代码实现4.1 接口定义4.2 read_block接口的实现4.3 write_block接口的实现4.4 erase_block接口的实现4.5 send_cmd内部函数 五、测试与结论5.1 测试用例5.2 运行结果5.3 其他测试结果 六、总结 一、前情提要
在上一篇提到了要实现SDIO内存卡的读写擦但是由于程序在调试的时候出现了一些bug所以一直没有把这个坑填上。最近由于做了一些测试把读写擦实现了。所以特此来把这个坑填上。
二、目标
实现读写擦的函数。编写测试用例将一个块擦除写入内容再读出来。
三、技术方案
3.1 读写擦的操作
读写擦的具体的指令其实很多帖子都有介绍笔者就是参考的手册本身。但是具体的流程还是要说一下。
3.1.1 读卡操作
设置SDIO_DLEN和SDIO_DCTRL两个寄存器。在SDIO_DLEN设置块大小一般是512字节在SDIO_DCTRL里设置块大小是9代表512、数据传输方向是卡到MCU暂不启动。必要的话CMD16设置卡的块大小。CMD7RCA选中卡。置位SDIO_DCTRL的Dten位使能MCU的传输。CMD17块地址要求卡发送数据轮询SDIO_STA的SDIO_STA_RXFIFOHF位看看有没有数据到位轮询SDIO_STA的SDIO_STA_DATAEND位检查卡是否已经发送完成。轮询SDIO_STA的SDIO_STA_RXDAVL把管子里的数据都收拾出来。通过复位SDIO_DCTRL的Dten位以关闭DPSM。通过SDIO_ICR清了SDIO_STA。收工
3.1.2 写卡操作
设置SDIO_DLEN和SDIO_DCTRL两个寄存器。在SDIO_DLEN设置块大小一般是512字节在SDIO_DCTRL里设置块大小是9代表512、数据传输方向是MCU到卡暂不启动。必要的话CMD16设置卡的块大小。CMD7RCA选中卡。置位SDIO_DCTRL的Dten位使能MCU的传输。CMD24块地址通知卡MCU将要发送数据轮询SDIO_STA的SDIO_STA_TXFIFOHE位看看是否管子里有空间发数据轮询SDIO_STA的SDIO_STA_DATAEND位检查MCU是否已经发送完成。轮询SDIO_STA的SDIO_STA_DBCKEND直至发送结束通过复位SDIO_DCTRL的Dten位以关闭DPSM。CMD13轮询卡的状态。直至状态离开PROG状态。收工
3.1.3 擦除操作
CMD7RCA选中卡。CMD32设置起始块、CMD33设置终止块和CMD38执行擦除。CMD13轮询卡的状态。直至离开PROG状态。收工
3.2 一些技术点
3.2.1 轮询标志位的选择不唯一
由于没有采用DMA所以读写的时候都必须手动轮询SDIO_STA的某些标志位来保证读写操作的顺利完成。但是参考手册会发现对于读写操作并没有规定操作规范。有的时候对于同一个目的可以有多个标志位可以使用。这里确实是有多个标志位可以采用。但是要通过测试去验证。
3.2.2 写和擦的卡状态查询
卡在执行写和擦的操作时候会处于PROG状态。这个时候必须要轮询至状态转移才可以保证操作的成功。
3.2.3 写的速度
按照卡的SDIO_STATUS很多时候卡的速度都是24MBit/s或50MBit/s但是在实际操作的时候会发现速度似乎只能开到6.7MBit/s左右不论是单线模式还是4线模式都不行。看时钟信号发现是非常连续的所以排除了发送数据的时候有延迟的原因。这个问题目前没有解决如果未来找到了原因再记录。 从上面的这个图中可以看到时钟信号SDIO_CLK红色其实是非常连贯的。但是频率只能达到6.8也就是52分频。所以笔者认为其实换成DMA传输也不太可能改善这个。但是未来如果有机会试试再测试吧。
四、代码实现
4.1 接口定义
SDIO内存卡的接口定义如下所示
#ifndef _SDIO_Memory_CARDH
#define _SDIO_Memory_CARDH#include stdint.h
typedef enum {waitRsp_noRsp 0,waitRsp_shortRsp 1,waitRsp_longRsp 3,
}WaitRspKind;typedef struct {void (*init)(void);uint32_t (*card_identification)(void);uint32_t (*read_SD_status)(void);uint32_t (*erase_block)(uint32_t, uint32_t);uint32_t (read_block)(uint32_t, uint32_t);uint32_t (write_block)(uint32_t, uint32_t);
}SDIO_Memory_Card_Def;extern const SDIO_Memory_Card_Def SDIO_Memory_Card;
#endif
添加了3个函数接口分别是 uint32_t (*erase_block)(uint32_t, uint32_t);uint32_t (read_block)(uint32_t, uint32_t);uint32_t (write_block)(uint32_t, uint32_t);这三个接口的实现如下所示。但是由于前面说了逻辑具体的每行的解释这里就不说了。
4.2 read_block接口的实现
; uint32_t read_block(uint32_t, uint32_t*);
; r0 is the address of the block,
; r1 is the read buffer.align 4
read_block procpush {r4 - r11, lr}ldr rSDIO, SDIO_BaseAddrldr rCard_Info, card_info_datasub sp, #4 * 2ldrb r4, card_isSDSClsl r0, r4str r0, [sp, #0]str r1, [sp, #4]; Select the Card. mov r0, #7:or:SDIO_CMD_WAITRESP_Short:or:SDIO_CMD_CPSMENldr r1, card_rcabl send_cmd
; Set the block size in case of SDSC. mov r0, #16:or:SDIO_CMD_WAITRESP_Short:or:SDIO_CMD_CPSMENmov r1, #512 bl send_cmdldr r4, [sp, #0] ; The address of the block in the SD Memory Cardldr r5, [sp, #4] ; The address of the buffer
; Send CMD17 to inform the card to start a data sending.mov r0, #17:or:SDIO_CMD_WAITRESP_Short:or:SDIO_CMD_CPSMENmov r1, r4bl send_cmd mov r0, #0str r0, [rSDIO, #SDIO_DCTRL]mov r0, #512str r0, [rSDIO, #SDIO_DLEN]mov r0, #(9 :shl: 4):or:SDIO_DCTRL_DTDIR_Card2Controller :or: SDIO_DCTRL_Dtenstr r0, [rSDIO, #SDIO_DCTRL] reading_data_start ldr r0, [rSDIO, #SDIO_STA] tst r0, #SDIO_STA_RXACTbeq reading_data_start
reading_dataldr r0, [rSDIO, #SDIO_STA] tst r0, #SDIO_STA_DATAENDbne reading_clear_the_FIFOtst r0, #SDIO_STA_RXFIFOHFbeq reading_dataldr r1, [rSDIO, #SDIO_FIFO]str r1, [r5]add r5, #4b reading_data
reading_clear_the_FIFOldr r0, [rSDIO, #SDIO_STA]tst r0, #SDIO_STA_RXDAVLbeq reading_completedldr r1, [rSDIO, #SDIO_FIFO]str r1, [r5]add r5, #4b reading_clear_the_FIFO
reading_completed mov r0, #0str r0, [rSDIO, #SDIO_DCTRL]mov r0, #0x7ffstr r0, [rSDIO, #SDIO_ICR]mov r0, #7:or:SDIO_CMD_WAITRESP_Short:or:SDIO_CMD_CPSMENmov r1, #0bl send_cmdmov r0, #13:or:SDIO_CMD_WAITRESP_Short:or:SDIO_CMD_CPSMENldr r1, card_rcabl send_cmdldr r0, [rSDIO, #SDIO_RESP1]ubfx r0, r0, #9, #4add sp, #4 * 2pop {r4 - r11, lr}bx lrltorgendp4.3 write_block接口的实现
; uint32_t write_block(uint32_t, uint32_t*);align 4
write_block procpush {r4 - r11, lr} ldr rSDIO, SDIO_BaseAddrldr rCard_Info, card_info_dataldrb r4, card_isSDSClsl r0, r4sub sp, #4 * 2str r0, [sp, #0]str r1, [sp, #4]mov r0, #7:or:SDIO_CMD_WAITRESP_Short:or:SDIO_CMD_CPSMENldr r1, card_rcabl send_cmdmov r0, #16:or:SDIO_CMD_WAITRESP_Short:or:SDIO_CMD_CPSMENmov r1, #512 bl send_cmdldr r4, [sp, #0] ; The address of the block in the SD Memory Cardldr r5, [sp, #4] ; The address of the buffermov r6, #0;mov r0, #24:or:SDIO_CMD_WAITRESP_Short:or:SDIO_CMD_CPSMENmov r1, r4bl send_cmd mov r0, #0str r0, [rSDIO, #SDIO_DCTRL]mov r0, #512str r0, [rSDIO, #SDIO_DLEN]mov r0, #(9:shl:4):or:SDIO_DCTRL_Dtenstr r0, [rSDIO, #SDIO_DCTRL]
writing_data_startldr r0, [rSDIO, #SDIO_STA]tst r0, #SDIO_STA_TXACTbeq writing_data_startmov r1, #SDIO_STA_DBCKEND:or:SDIO_STA_DCRCFAIL
writing_dataldr r0, [rSDIO, #SDIO_STA]tst r0, r1bne writing_completedtst r0, #SDIO_STA_TXFIFOEbeq writing_dataldr r0, [r5]str r0, [rSDIO, #SDIO_FIFO]add r5, #4add r6, #1b writing_data
writing_completedmov r0, #0str r0, [rSDIO, #SDIO_DCTRL]
; mov r0, #12:or:SDIO_CMD_WAITRESP_Short:or:SDIO_CMD_CPSMEN
; ldr r1, card_rca
; bl send_cmd
write_block_the_card_is_programming mov r0, #13:or:SDIO_CMD_WAITRESP_Short:or:SDIO_CMD_CPSMENldr r1, card_rca bl send_cmdldr r0, [rSDIO, #SDIO_RESP1]ubfx r1, r0, #9, #4cmp r1, #7beq write_block_the_card_is_programmingmov r0, #0x7ffstr r0, [rSDIO, #SDIO_ICR]mov r0, #13:or:SDIO_CMD_WAITRESP_Short:or:SDIO_CMD_CPSMENldr r1, card_rca bl send_cmdldr r0, [rSDIO, #SDIO_RESP1]add sp, #4 * 2pop {r4 - r11, lr}bx lrendp4.4 erase_block接口的实现
; uint32_t erase_block(uint32_t, uint32_t);
; r0: Start block
; r1: End blockalign 4
erase_block procpush {r4 - r11, lr}ldr rSDIO, SDIO_BaseAddrldr rCard_Info, card_info_datasub sp, #4 * 2ldrb r4, card_isSDSClsl r0, r4lsl r1, r4str r0, [sp, #0]; [sp, #0] is the start block str r1, [sp, #4]; [sp, #4] is the end blockmov r0, #7:or:SDIO_CMD_WAITRESP_Short:or:SDIO_CMD_CPSMENldr r1, card_rcabl send_cmd mov r0, #13:or:SDIO_CMD_WAITRESP_Short:or:SDIO_CMD_CPSMENldr r1, card_rcabl send_cmdmov r0, #32:or:SDIO_CMD_WAITRESP_Short:or:SDIO_CMD_CPSMENldr r1, [sp, #0];bl send_cmdmov r0, #33:or:SDIO_CMD_WAITRESP_Short:or:SDIO_CMD_CPSMENldr r1, [sp, #4];bl send_cmdmov r0, #38:or:SDIO_CMD_WAITRESP_Short:or:SDIO_CMD_CPSMENmov r1, #0bl send_cmderase_block_the_card_is_programmingmov r0, #13:or:SDIO_CMD_WAITRESP_Short:or:SDIO_CMD_CPSMENldr r1, card_rca bl send_cmdldr r0, [rSDIO, #SDIO_RESP1]ubfx r1, r0, #9, #4cmp r1, #7beq erase_block_the_card_is_programmingmov r0, #7:or:SDIO_CMD_WAITRESP_Short:or:SDIO_CMD_CPSMENldr r1, card_rcabl send_cmd mov r0, #13:or:SDIO_CMD_WAITRESP_Short:or:SDIO_CMD_CPSMENldr r1, card_rcabl send_cmdldr r0, [rSDIO, #SDIO_RESP1]ubfx r0, r0, #9, #4add sp, #4 * 2pop {r4 - r11, lr}bx lrltorgendp4.5 send_cmd内部函数
虽然前面的几个帖子中也讲到了这个内部函数的实现。但是为了方便阅读这里还是把它粘过来了。
; Priviate Function Name: Send_cmd
; Args: r0 - cmd, r1 - arg
; 实现这个函数align 4
send_cmd procpush {r4 - r11, lr}ldr rSDIO, SDIO_BaseAddrldr rCard_Info, card_info_datamov r2, #0x7fstr r2, [rSDIO, #SDIO_ICR]str r1, [rSDIO, #SDIO_ARG]str r0, [rSDIO, #SDIO_CMD]
wait_for_responseldr r1, [rSDIO, #SDIO_STA]tst r1, #SDIO_STA_CMDREND:or:SDIO_STA_CCRCFAIL:or:SDIO_STA_CTIMEOUTbeq wait_for_responsebic r0, #SDIO_CMD_CPSMENstr r0, [rSDIO, #SDIO_CMD]pop {r4 - r11, lr} bx lrendp五、测试与结论
5.1 测试用例
#include cmsis_os2.h // CMSIS RTOS header file
#include SDIO_TestCase.h
#include SDIO_Memory_Card.h
#include stdio.h
/—————————————————————————- Thread 1 Thread_Name: Sample thread—————————————————————————/static osThreadId_t tid_SDIO_Testcase; // thread idvoid SDIO_Testcase (void *argument); // thread functionint Init_SDIO_Testcase (void) {tid_SDIO_Testcase osThreadNew(SDIO_Testcase, NULL, NULL);if (tid_SDIO_Testcase NULL) {return(-1);}return(0);
}__NO_RETURN void SDIO_Testcase (void *argument) {static uint32_t resp 0;static union{uint32_t buf32[512 / 4];char buf8[512];}rBuf, wBuf;(void)argument;sprintf(wBuf.buf8, I love Miao! I love you, Da Miao);SDIO_Memory_Card.card_identification();SDIO_Memory_Card.read_SD_status();// SDIO_Memory_Card.erase_block(0,0);SDIO_Memory_Card.read_block(0, rBuf.buf32);SDIO_Memory_Card.erase_block(10,15);SDIO_Memory_Card.read_block(10, rBuf.buf32);SDIO_Memory_Card.write_block(11, wBuf.buf32);SDIO_Memory_Card.read_block(11, rBuf.buf32);while (1) {resp (resp 1)%100;osDelay(101);}
}
5.2 运行结果 可以看到笔者成功地向第11块中写入了一行英文“I love you, Da Miao, Kitty and Andy!”。
5.3 其他测试结果
对于这种SDIO内存卡用单线还是四线数据总线模式其实都是成立的。只要MCU和卡的设置都协调好就可以了。
我把程序换到了另一个pin都引出来的板子上把波形打出来。 单线模式下的波形。红线是SDIO_CLK黄线是D0。也许有人会认为这不就是个高速的I2C么。其实不是的。会看到每一帧都没有ACK和NACK。所以不能替代I2C。 四线模式下的波形。红线是SDIO_CLK其他是D0、D1和D2。由于这个示波器是借的没有配数字探头所以看不全所有的信号。
但是从调试上可以看到都能实现操作。
这里注明一下因为HX32F4的开发板没有将SDIO有关的引脚引出所以无法测量。我用另一个板子做了测试但是用的IDE是Segger Embedded Studio做的也就是上面的这个界面。
六、总结
这样用轮询的方式实现读写擦SD卡的驱动就实现了。有若干的技术点
读的速率可以根据卡的额定速度来但是写入的速度不能高于6.8MBit/s。写入和擦除之后要轮询卡的状态确认卡已经从PROG状态中转出。读写擦的轮询的标志位其实可以有多个选择。但是要测试确认。管子的数据是32位的但是发送的是按照8位的。所以可以认为是一次发4个字节。读写缓冲区都必须是字对齐4字节对齐。要查询SDIO_STATUS根据版本号确认如何读写。这里还是点一下2GB以下的卡的寻址是以字节的而以上的都是以块寻址的。当然读写的时候都要以块为单位。比如1GB的卡以字节寻址到第10块但是还是要一次读1个整块。除非中途用CMD12打断。
这样就可以实现一套SDIO内存卡的驱动。
- 上一篇: 有哪个网站可以学做面条开面馆kuler网站
- 下一篇: 有哪里可以做兼职翻译的网站网站运营企业
相关文章
-
有哪个网站可以学做面条开面馆kuler网站
有哪个网站可以学做面条开面馆kuler网站
- 技术栈
- 2026年03月21日
-
有模板做ppt的网站有哪些老域名怎么做新网站
有模板做ppt的网站有哪些老域名怎么做新网站
- 技术栈
- 2026年03月21日
-
有名网站建设公司中山医疗网站建设
有名网站建设公司中山医疗网站建设
- 技术栈
- 2026年03月21日
-
有哪里可以做兼职翻译的网站网站运营企业
有哪里可以做兼职翻译的网站网站运营企业
- 技术栈
- 2026年03月21日
-
有哪些vue做的网站游戏落地页网站建设
有哪些vue做的网站游戏落地页网站建设
- 技术栈
- 2026年03月21日
-
有哪些好的网站建设公司国外网站网页设计
有哪些好的网站建设公司国外网站网页设计
- 技术栈
- 2026年03月21日
