Reputation: 195
How do I write to an SD Card using SPI with DMA available for the PSoC 5LP (32-bit Cortex-M3) chip?
I currently have a DMA and SPI tx/rx pair working, but for a different purpose so if the actual transmission is not an issue, I just don't know how to interact with the SDcard.
The datasheet for the PSoC 5LP is here.
Basic Info:
I am using the DMA in simple mode and the DMA TD chain is setup for: 8 bit width, 4 Byte bursts auto complete the full TD (only needs initial HW request) Loop back to beginning of initial TD when done and wait for HW request
The SPI Master is initialized in a gui, I have it set using a 16Mhz clock, 8 bit tx/rx transfers with a 4 Byte tx/rx buffer. interrupts are set on rx FIFO full, connected to them is an rx DMA.
The pointers for the SDcard SPI rx/tx are SPIM_RX_PTR and SPIM_TX_PTR respectively. The DMA transfers to and from them. The Arrays that I am transferring from and to are SDcardout and SDcardin.
Upvotes: 0
Views: 3346
Reputation: 1
I have done work on exactly this problem.
I found that the existing SPI module provided with the PSoC 5 components library is not ideally suited to bulk transfers to / from an SD card. As far as I could tell, it was necessary to clear SPI module flags in software on each byte transfer, rendering DMA much less useful. I think one solution is to use two TDs (Transfer descriptors) - one to perform the data transfer and a second to clear the RX flag after the first TD has completed - anyway, that's off topic.
I also found that the emFile component supplied in the components library is limited in its capabilities. I couldn't see any way to attach DMA, and even if I could, its clock speed appeared to be very poor. On top of this, emFile requires compile-time selection of FAT16 or FAT32, limiting your design to one or another filesystem only.
As I didn't like the idea of a more complicated DMA setup, I decided to design my own SPI component hardware in the UDB editor. The project containing the component can be found at: https://github.com/PolyVinalDistillate/NSDSPI
This incorporates the excellent FatFS library mentioned above (thanks ChaN), which takes care of FAT12, FAT16 and FAT32 formatted cards. As stated, without the filesystem layer, you will only be accessing raw data blocks of 512 bytes each. With FatFS, you get analogues of fopen(), fclose(), etc.
If you look at my component in PSoC Creator, you'll see it's actually composed of 2 components: One is the specialised UDB component implementing the main SPI logic, the other is a schematic connecting my UDB component to DMA and some control logic. This second component also has the API files containing my hardware-specific code and is the component to drop into your TopDesign schematic.
FatFS is included as a precompiled library, and LowLevelFilesys.h in the API folder provides access to all the file functions.
This component was designed with bulk-reads in mind and the API does the following for read:
Writing the card is performed in a more typical fashion, with the DMA simply sending data to the SPI module after preparing the SD card for it.
If you run my project on your PSoC system, it will perform a read / write test on the SD card, depositing a file reporting the specs:
Testing Speed
Writing 16000 bytes to file, non-DMA
Took 94 ms
Rate 1361 kbps
Reading 16000 bytes to file, non-DMA
Took 50 ms
Verifying... All Good! :D
Rate 2560 kbps
Writing 16000 bytes to file, DMA
Took 17 ms
Rate 7529 kbps
Reading 16000 bytes to file, DMA
Took 12 ms
Verifying... All Good! :D
Rate 10666 kbps
Some SD cards give better results, some give worse. I believe this is down to the SD card itself (e.g. class, usage, age of tech, etc).
Upvotes: 0
Reputation: 93476
Having SPI communication will only get you the lowest command/block level access to the card; you will need a file system. SD cards come pre-formatted as FAT32, so a FAT file-system will provide the greatest comparability, is not the greatest reliability (corruption is likely if write is interrupted by power loss or reset for example). It also has the advantage of being relatively simple to implement and requires few resources.
There are several commercial and open-source FAT filesystems libraries available. I suggest that you look at ELM FatFs or ELM Petit FatFs both have permissive licences and are well documented. In each case you simply need to implement the disk I/O stubs to map them to your SPI driver. There are plenty of examples, documentation and application notes on the site to help you. You can start with an SPI SD implementation example for another target and adapt it to your driver (or adapt your driver perhaps). Other FAT filesystem libraries are broadly similar to this and require I/O layer implementation.
The diskio layer of ELM FatFs is not media specific, so you in fact need an additional MMC/SD layer between that and the SPI driver. It is unlikely that you will find an example for your specific target, but it is possible to work from examples for other targets since MMC/SD over SPI itself is not target specific, the hardware dependencies come only at the SPI level and the GPIO implementation for the card-detect and write-protect (optional) signals. There are several examples for various ARM targets here, a project for PSoC support here (apparently a work-in-progress at time of writing).
Upvotes: 4