Updated SPI SD DMA code and DMA Pattern Matching post

Updated SPI SD DMA code

The code is now more stable and cleaner, so I'm uploading it again here. Here are a list of changes:

  • Fixed bugs relating to SPI buffers overflowing, causing the program to stop working at different BRG settings.
  • No longer need to set SCK as an input.
  • Now works properly at multiple values of SPIBRG, so you can run at whatever SPI, CPU or System frequency you like.
  • Configuration settings have been moved to mmcpic32.h and diskio.h and diskio.c have been removed. You need to #include "mmcpic32.h" in your main program now.
  • Configuration made possible by changing a few lines of code in mmcpic32.h. Thanks again to Bryn Thomas and Ivo Colleoni for their help with this.
  • Added a callback function that will be called, if set, multiple times during an SPI DMA read.

New configuration settings

Upon opening mmcpic32.h, you will see this:

// ***************************************************
// ** CHANGE THE BELOW SETTINGS TO MATCH YOUR BOARD **
// ***************************************************
// SD card port and pin settings
#define CS_PORT H                   // Port on which CS is to be found, A - H
#define CS_PIN 12                   // Pin number of CS, 0 -15
#define SDO_PORT B                  // Port on which SDO/MOSI is to be found, A - H
#define SDO_PIN 5                   // Pin number of SDO/MOSI, 0 - 15

// SPI channel and DMA channel configuration
#define SPI_CHANNEL 2               // Channel number to use for SD card
#define DMA_RX_CHANNEL 0            // DMA channel number to use for Receiving data, 0 - 7
#define DMA_TX_CHANNEL 1            // DMA channel number to use for Transmitting data, 0 - 7
#define DMA_RX_CHANNEL_PRIORITY 3   // Priority of DMA receiving channel, 0 - 3
#define DMA_TX_CHANNEL_PRIORITY 2   // Priority of DMA transmitting channel, 0 - 3
#define DMA_RX_INT_PRIORITY 4       // Priority of DMA receive complete interrupt, 0 - 7
#define DMA_RX_INT_SUBPRIORITY 1    // Sub-priority of DMA receive complete interrupt, 0 - 3

I've tried to make it easy to see. The settings currently there are for my board, with the SD card's Chip Select on port H12 and the MOSI / SDO pin on RB5. Please change these to be correct for your board or nothing will work. Right under that is the only other setting you may have to change, the SPI channel number. Change this to whatever your SPI channel SD card is connected to. The rest of the settings can be left as is or changed as desired. The allowed ranges are shown in the comments for each line.

Callback function

The callback function will be called multiple times during a call to f_read(). It can be used for things like checking keys, starting other transfers, updating LCDs, whatever you want really. Do note that if you take too long in the callback function the DMA transfer's performance will either suffer or, in extreme cases, stop working (hasn't happened yet but who knows). So it is recommended you do something fairly short during the callback function.

The example callback function is also declared in mmcpic32.h, like this:

void (*DMA_CALLBACK)(int stage, int args);

stage refers to the stage of the DMA read it is in, which can be DMA_STAGE_WAIT_TOKEN (waiting for the 0xFE token) or DMA_STAGE_WAIT_READ (reading 512-byte sector).
args can be one of two things. In DMA_STAGE_WAIT_TOKEN it is how many bytes were read before 0xFE was found. In DMA_STAGE_WAIT_READ it is how many bytes were read (always 512 in this program).

You can change this callback function to whatever you like, I've just given an example of how it could be used.

To set your own callback function, create a function in main(), for example:

void my_callback(int stage, int args)
{
}

Then, call the set_callback() function like this:

set_callback(my_callback);

Done!

A word on DMA Pattern Matching mode in this program

As I've discussed previously, a multi-block read from an SD card looks like this:

  • Send the command for multiple block read (CMD18)
  • Send the starting sector number
  • Send 0xFF until the 0xFE token is returned
  • Send 0xFF and read the reply 512 times to read a sector
  • If you wish to read more sectors, go back to line 3 and repeat until done
  • Send the command to stop transmission (CMD12)

In this program, the DMA read starts on line 3, waiting for 0xFE. At this stage, we have been sending 0xFF until 0xFE was returned. The DMA Transfer channel is now aborted when a Pattern Match for 0xFE is found on channel 0, resulting in less data being left in the SPI buffer. I have added code to handle bytes left in the SPI buffer and I strongly suggest you do not remove this code, even if it seems that no bytes are remaining. At lower settings for SPIBRG there can sometimes be one or two bytes left over each time and that can quickly lead to an SPI buffer overflow if not handled correctly.

Please note: The standard for SPI mode on SD cards specifies up to 25MHz for transfers. I am using 50MHz and it works fine. However, if you want to use this code in something that requires reliability, please set your SPIBRG to 1 to halve the speed to 25MHz!

As always, here's the code. If there are any issues with it, please do let me know.

Categories: pic32

Tags: code, DMA, SD, SPI