前一节,主要着重理论知识,讲述了eCos disk驱动的体系框架并大概分析了SPI接口SD/MMC卡驱动的组成,让大家对eCos的块设备(disk)系统有一个大概的了解,避免只见树木不见森林。
由于STM32 SD卡驱动代码编写工作量非常大,因此本节主要讲述如何编写STM32 SD卡驱动代码以及简单介绍下如何使用图形配置工具进行相关配置。
由前一节分析可知,eCos STM32 SD卡驱动划分在disk驱动体系中,因此,在devs/disk目录下新建目录路径:cortexm/stm32/mmc/current。然后在这个路径的current目录下分别新建cdl、include、src三个文件夹和ChangeLog文件,如右图所示。
cdl目录下建立mmc_stm32.cdl文件;
include目录下建立mmc_stm32.h文件;
src目录下建立mmc_stm32.c文件;
其中,cdl文件可以其它某个驱动的cdl文件作为模板,然后在此基础上进行修改,这样简单省事。cdl文件怎么编写和修改在这里就不详细叙述。最主要是确定驱动需要哪些配置项和所依赖的组件。
添加STM32 SD卡驱动组件
假设你完成了上述操作,并把cdl文件编写好了。这时就可以把STM32 SD卡驱动组件添加到ecos.db文件中,如下代码所示。
package CYGPKG_DEVS_DISK_CORTEXM_STM32_MMC { alias { "Disk driver for MMC cards for STM32" stm32_mmc_disk_driver } directory devs/disk/cortexm/stm32/mmc script mmc_stm32.cdl hardware description "MMC driver for STM32 family of CortexM controllers" }
然后把这个驱动组件添加你的目标板中(Add Target)。
配置STM32 SD卡驱动组件
如果要在eCos中正常操作SD卡的话,仅有STM32 SD卡驱动是不够的。一般SD卡都是格式化为FAT文件系统,所以我们还需要FAT System组件,而这个组件又需要其它2个组件的支持。它们的关系描述如下:
与应用相关的组件:
>>> CYGPKG_FS_FAT — FAT Filesystem组件包;
>>> CYGPKG_BLOCK_LIB — CYGPKG_FS_FAT 依赖的组件包;
>>> CYGPKG_LINUX_COMPAT — CYGPKG_BLOCK_LIB 依赖的组件包;
也许你会问,这些组件事先是怎么知道的呢?实际上,事先我也不知道,但我们至少知道需要FAT System组件,当你添加这个组件时,eCos图形配置工具会告诉你缺少依赖的组件,这时你只需要按照提示添加相应组件即可。
STM32 SD卡组件配置,如下图所示:
其它3个与应用相关的组件采用默认配置即可。
STM32 SD卡驱动代码编写
由前一节分析得知,SD卡驱动由3部分组成,分别是:设备入口定义、设备接口实现和SD/MMC卡底层操作。其中,前2个部分可以直接借鉴mmc_spi.c中的实现。可能最大不同是,init()函数添加了SD卡相关引脚的初始化和DMA stream(DMA流)的配置及其相关变量的初始化。具体代码就不贴出来了,可自行从http://52ecos.net/thread-600-1-1.html下载源码,注意mmc_stm32_disk_init()函数中的实现。
SD/MMC卡底层操作 代码编写
这部分代码则需要自己实现,也是工作量最大的一部分。实际上,这部分代码主要借鉴ST官方库中SD卡驱动的实现。从我提供的代码中可知,大部分函数实现、SD卡初始化流程、SD卡读写操作等都是参考ST官方库的。区别可能在于函数名、变量、数据类型定义等的不同。
综上所述,这部分代码主要是依据ST官方库中SD卡驱动进行重构的。所以如果你熟悉ST官方库的话,理解eCos STM32 SD卡驱动不是难事,同时,该驱动再次诠释了在eCos中如何借鉴其它现有代码(第三方代码)来实现自己的驱动。此外,编写eCos驱动时,不管你是新手还是老手,都不推荐从空白开始着手。
跟着SD卡操作流程简要分析下这部分代码的结构。
mmc_stm32_disk_init()函数
系统启动时,自动执行该函数,完成SD卡相关引脚和DMA stream等的初始化。
mmc_stm32_disk_lookup()函数
用户应用程序操作SD卡之前,必须先mount,才能进行数据读写等操作。该函数就是mount操作的具体实现。主要完成SD卡检测、卡识别及其初始化等。
stm32_mmc_disk_changed()函数
如果SD卡已经mount,当再次进行mount时,则会调用该函数,主要用于检测SD卡是否还在卡槽中或者是否同一张SD卡。
stm32_mmc_check_for_disk()函数
SD卡识别及其初始化的入口函数。它调用stm32_mmc_init()函数来实现卡识别及其初始化。
stm32_mmc_init()函数
这个函数对应ST官方库SD_Init()函数。也就是,该函数的实现、其调用函数顺序、执行流程完全遵照ST官方库的实现。所不同的无非就是函数名、变量名、局部实现细节优化和重构(ST官方库中的一些函数实现有些地方比较冗余,所以进行了重构和优化)等等。
STM32 DMA操作
SD卡STM32 DMA操作函数由stm32_dma_打头的一些函数组成。它们分别是:
- stm32_dma_channel_setup(), 用于设置SDIO DMA通道并启动DMA stream;
- stm32_dma_callback(), DMA stream传输完成时的回调函数;
- stm32_dma_transaction_begin(),DMA stream传输开始函数,主要是调用stm32_dma_channel_setup()函数;
- stm32_dma_transaction(), DMA stream传输并等待传输完成的函数;
- stm32_dma_transaction_end, DMA stream传输结束函数,主要完成数据拷贝函数;
刚开始编写代码的时候,DMA操作是参考spi_stm32.c中的DMA实现的。但调试的时候,DMA传输时老是出问题,所以最终实现与spi的DMA有所区别。
总体上来说,eCos的DMA还是很好操作的。总结如下:
- 定义DMA缓存区、DMA stream传输优先级等;
- 在驱动初始化函数中,初始化DMA相关变量,启动对应DMA stream通道;
- 定义DMA传输完成回调函数;
- 便能外设DMA功能;
- 设置DMA通道并启动传输;
- 等待DMA传输完成;
驱动测试程序
可以直接使用ecos/packages/fs/fat/current/tests下面的fatfs1.c测试程序,或者借鉴该测试程序自行编写。