The supported board list:
- SAM D21 Xplained Pro
- SAM R21 Xplained Pro
- SAM L21 Xplained Pro
- SAM L22 Xplained Pro
- SAM DA1 Xplained Pro
- SAM C21 Xplained Pro
- SAM R30 Xplained Pro
This quick start will transmit a buffer data from master to slave through DMA. In this use case the SPI master will be configured with the following settings on SAM Xplained Pro:
- Master Mode enabled
- MSB of the data is transmitted first
- Transfer mode 0
- SPI MUX Setting E
- 8-bit character size
- Not enabled in sleep mode
- Baudrate 100000
- GLCK generator 0
The SPI slave will be configured with the following settings:
- Slave mode enabled
- Preloading of shift register enabled
- MSB of the data is transmitted first
- Transfer mode 0
- SPI MUX Setting E
- 8-bit character size
- Not enabled in sleep mode
- GLCK generator 0
Note that the pinouts on other boards may different, see next sector for details.
Setup
Prerequisites
The following connections has to be made using wires:
- SAM D21/DA1 Xplained Pro.
- SS_0: EXT1 PIN15 (PA05) <–> EXT2 PIN15 (PA17)
- DO/DI: EXT1 PIN16 (PA06) <–> EXT2 PIN17 (PA16)
- DI/DO: EXT1 PIN17 (PA04) <–> EXT2 PIN16 (PA18)
- SCK: EXT1 PIN18 (PA07) <–> EXT2 PIN18 (PA19)
- SAM R21 Xplained Pro.
- SS_0: EXT1 PIN15 (PB03) <–> EXT1 PIN10 (PA23)
- DO/DI: EXT1 PIN16 (PB22) <–> EXT1 PIN9 (PA22)
- DI/DO: EXT1 PIN17 (PB02) <–> EXT1 PIN7 (PA18)
- SCK: EXT1 PIN18 (PB23) <–> EXT1 PIN8 (PA19)
- SAM L21 Xplained Pro.
- SS_0: EXT1 PIN15 (PA05) <–> EXT1 PIN12 (PA09)
- DO/DI: EXT1 PIN16 (PA06) <–> EXT1 PIN11 (PA08)
- DI/DO: EXT1 PIN17 (PA04) <–> EXT2 PIN03 (PA10)
- SCK: EXT1 PIN18 (PA07) <–> EXT2 PIN04 (PA11)
- SAM L22 Xplained Pro.
- SS_0: EXT1 PIN15 (PB21) <–> EXT2 PIN15 (PA17)
- DO/DI: EXT1 PIN16 (PB00) <–> EXT2 PIN17 (PA16)
- DI/DO: EXT1 PIN17 (PB02) <–> EXT2 PIN16 (PA18)
- SCK: EXT1 PIN18 (PB01) <–> EXT2 PIN18 (PA19)
- SAM C21 Xplained Pro.
- SS_0: EXT1 PIN15 (PA17) <–> EXT2 PIN15 (PB03)
- DO/DI: EXT1 PIN16 (PA18) <–> EXT2 PIN17 (PB02)
- DI/DO: EXT1 PIN17 (PA16) <–> EXT2 PIN16 (PB00)
- SCK: EXT1 PIN18 (PA19) <–> EXT2 PIN18 (PB01)
Code
Add to the main application source file, before user definitions and functions according to your board:
For SAM D21 Xplained Pro:
#define CONF_MASTER_SPI_MODULE EXT2_SPI_MODULE
#define CONF_MASTER_SS_PIN EXT2_PIN_SPI_SS_0
#define CONF_MASTER_MUX_SETTING EXT2_SPI_SERCOM_MUX_SETTING
#define CONF_MASTER_PINMUX_PAD0 EXT2_SPI_SERCOM_PINMUX_PAD0
#define CONF_MASTER_PINMUX_PAD1 PINMUX_UNUSED
#define CONF_MASTER_PINMUX_PAD2 EXT2_SPI_SERCOM_PINMUX_PAD2
#define CONF_MASTER_PINMUX_PAD3 EXT2_SPI_SERCOM_PINMUX_PAD3
#define CONF_SLAVE_SPI_MODULE EXT1_SPI_MODULE
#define CONF_SLAVE_MUX_SETTING EXT1_SPI_SERCOM_MUX_SETTING
#define CONF_SLAVE_PINMUX_PAD0 EXT1_SPI_SERCOM_PINMUX_PAD0
#define CONF_SLAVE_PINMUX_PAD1 EXT1_SPI_SERCOM_PINMUX_PAD1
#define CONF_SLAVE_PINMUX_PAD2 EXT1_SPI_SERCOM_PINMUX_PAD2
#define CONF_SLAVE_PINMUX_PAD3 EXT1_SPI_SERCOM_PINMUX_PAD3
#define CONF_PERIPHERAL_TRIGGER_TX SERCOM1_DMAC_ID_TX
#define CONF_PERIPHERAL_TRIGGER_RX SERCOM0_DMAC_ID_RX
For SAM R21 Xplained Pro:
#define CONF_MASTER_SPI_MODULE SERCOM3
#define CONF_MASTER_SS_PIN EXT1_PIN_10
#define CONF_MASTER_MUX_SETTING SPI_SIGNAL_MUX_SETTING_E
#define CONF_MASTER_PINMUX_PAD0 PINMUX_PA22C_SERCOM3_PAD0
#define CONF_MASTER_PINMUX_PAD1 PINMUX_UNUSED
#define CONF_MASTER_PINMUX_PAD2 PINMUX_PA18D_SERCOM3_PAD2
#define CONF_MASTER_PINMUX_PAD3 PINMUX_PA19D_SERCOM3_PAD3
#define CONF_SLAVE_SPI_MODULE EXT1_SPI_MODULE
#define CONF_SLAVE_MUX_SETTING EXT1_SPI_SERCOM_MUX_SETTING
#define CONF_SLAVE_PINMUX_PAD0 EXT1_SPI_SERCOM_PINMUX_PAD0
#define CONF_SLAVE_PINMUX_PAD1 EXT1_SPI_SERCOM_PINMUX_PAD1
#define CONF_SLAVE_PINMUX_PAD2 EXT1_SPI_SERCOM_PINMUX_PAD2
#define CONF_SLAVE_PINMUX_PAD3 EXT1_SPI_SERCOM_PINMUX_PAD3
#define CONF_PERIPHERAL_TRIGGER_TX SERCOM3_DMAC_ID_TX
#define CONF_PERIPHERAL_TRIGGER_RX SERCOM5_DMAC_ID_RX
For SAM L21 Xplained Pro:
#define CONF_MASTER_SPI_MODULE SERCOM2
#define CONF_MASTER_SS_PIN EXT1_PIN_12
#define CONF_MASTER_MUX_SETTING SPI_SIGNAL_MUX_SETTING_E
#define CONF_MASTER_PINMUX_PAD0 PINMUX_PA08D_SERCOM2_PAD0
#define CONF_MASTER_PINMUX_PAD1 PINMUX_UNUSED
#define CONF_MASTER_PINMUX_PAD2 PINMUX_PA10D_SERCOM2_PAD2
#define CONF_MASTER_PINMUX_PAD3 PINMUX_PA11D_SERCOM2_PAD3
#define CONF_SLAVE_SPI_MODULE EXT1_SPI_MODULE
#define CONF_SLAVE_MUX_SETTING EXT1_SPI_SERCOM_MUX_SETTING
#define CONF_SLAVE_PINMUX_PAD0 EXT1_SPI_SERCOM_PINMUX_PAD0
#define CONF_SLAVE_PINMUX_PAD1 EXT1_SPI_SERCOM_PINMUX_PAD1
#define CONF_SLAVE_PINMUX_PAD2 EXT1_SPI_SERCOM_PINMUX_PAD2
#define CONF_SLAVE_PINMUX_PAD3 EXT1_SPI_SERCOM_PINMUX_PAD3
#define CONF_PERIPHERAL_TRIGGER_TX SERCOM2_DMAC_ID_TX
#define CONF_PERIPHERAL_TRIGGER_RX SERCOM0_DMAC_ID_RX
For SAM L22 Xplained Pro:
#define CONF_MASTER_SPI_MODULE EXT2_SPI_MODULE
#define CONF_MASTER_SS_PIN EXT2_PIN_SPI_SS_0
#define CONF_MASTER_MUX_SETTING EXT2_SPI_SERCOM_MUX_SETTING
#define CONF_MASTER_PINMUX_PAD0 EXT2_SPI_SERCOM_PINMUX_PAD0
#define CONF_MASTER_PINMUX_PAD1 PINMUX_UNUSED
#define CONF_MASTER_PINMUX_PAD2 EXT2_SPI_SERCOM_PINMUX_PAD2
#define CONF_MASTER_PINMUX_PAD3 EXT2_SPI_SERCOM_PINMUX_PAD3
#define CONF_SLAVE_SPI_MODULE EXT1_SPI_MODULE
#define CONF_SLAVE_MUX_SETTING EXT1_SPI_SERCOM_MUX_SETTING
#define CONF_SLAVE_PINMUX_PAD0 EXT1_SPI_SERCOM_PINMUX_PAD0
#define CONF_SLAVE_PINMUX_PAD1 EXT1_SPI_SERCOM_PINMUX_PAD1
#define CONF_SLAVE_PINMUX_PAD2 EXT1_SPI_SERCOM_PINMUX_PAD2
#define CONF_SLAVE_PINMUX_PAD3 EXT1_SPI_SERCOM_PINMUX_PAD3
#define CONF_PERIPHERAL_TRIGGER_TX EXT2_SPI_SERCOM_DMAC_ID_TX
#define CONF_PERIPHERAL_TRIGGER_RX EXT1_SPI_SERCOM_DMAC_ID_RX
For SAM DA1 Xplained Pro:
#define CONF_MASTER_SPI_MODULE EXT2_SPI_MODULE
#define CONF_MASTER_SS_PIN EXT2_PIN_SPI_SS_0
#define CONF_MASTER_MUX_SETTING EXT2_SPI_SERCOM_MUX_SETTING
#define CONF_MASTER_PINMUX_PAD0 EXT2_SPI_SERCOM_PINMUX_PAD0
#define CONF_MASTER_PINMUX_PAD1 PINMUX_UNUSED
#define CONF_MASTER_PINMUX_PAD2 EXT2_SPI_SERCOM_PINMUX_PAD2
#define CONF_MASTER_PINMUX_PAD3 EXT2_SPI_SERCOM_PINMUX_PAD3
#define CONF_SLAVE_SPI_MODULE EXT1_SPI_MODULE
#define CONF_SLAVE_MUX_SETTING EXT1_SPI_SERCOM_MUX_SETTING
#define CONF_SLAVE_PINMUX_PAD0 EXT1_SPI_SERCOM_PINMUX_PAD0
#define CONF_SLAVE_PINMUX_PAD1 EXT1_SPI_SERCOM_PINMUX_PAD1
#define CONF_SLAVE_PINMUX_PAD2 EXT1_SPI_SERCOM_PINMUX_PAD2
#define CONF_SLAVE_PINMUX_PAD3 EXT1_SPI_SERCOM_PINMUX_PAD3
#define CONF_PERIPHERAL_TRIGGER_TX SERCOM1_DMAC_ID_TX
#define CONF_PERIPHERAL_TRIGGER_RX SERCOM0_DMAC_ID_RX
For SAM C21 Xplained Pro:
#define CONF_MASTER_SPI_MODULE EXT2_SPI_MODULE
#define CONF_MASTER_SS_PIN EXT2_PIN_SPI_SS_0
#define CONF_MASTER_MUX_SETTING EXT2_SPI_SERCOM_MUX_SETTING
#define CONF_MASTER_PINMUX_PAD0 EXT2_SPI_SERCOM_PINMUX_PAD0
#define CONF_MASTER_PINMUX_PAD1 PINMUX_UNUSED
#define CONF_MASTER_PINMUX_PAD2 EXT2_SPI_SERCOM_PINMUX_PAD2
#define CONF_MASTER_PINMUX_PAD3 EXT2_SPI_SERCOM_PINMUX_PAD3
#define CONF_SLAVE_SPI_MODULE EXT1_SPI_MODULE
#define CONF_SLAVE_MUX_SETTING EXT1_SPI_SERCOM_MUX_SETTING
#define CONF_SLAVE_PINMUX_PAD0 EXT1_SPI_SERCOM_PINMUX_PAD0
#define CONF_SLAVE_PINMUX_PAD1 EXT1_SPI_SERCOM_PINMUX_PAD1
#define CONF_SLAVE_PINMUX_PAD2 EXT1_SPI_SERCOM_PINMUX_PAD2
#define CONF_SLAVE_PINMUX_PAD3 EXT1_SPI_SERCOM_PINMUX_PAD3
#define CONF_PERIPHERAL_TRIGGER_TX SERCOM5_DMAC_ID_TX
#define CONF_PERIPHERAL_TRIGGER_RX SERCOM1_DMAC_ID_RX
Add to the main application source file, outside of any functions:
#define TEST_SPI_BAUDRATE 1000000UL
#define SLAVE_SELECT_PIN CONF_MASTER_SS_PIN
static const uint8_t buffer_tx[BUF_LENGTH] = {
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A,
0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14,
};
static uint8_t buffer_rx[BUF_LENGTH];
static volatile bool transfer_tx_is_done = false;
static volatile bool transfer_rx_is_done = false;
DmacDescriptor example_descriptor_tx SECTION_DMAC_DESCRIPTOR;
DmacDescriptor example_descriptor_rx SECTION_DMAC_DESCRIPTOR;
Copy-paste the following setup code to your user application:
static void transfer_tx_done(struct dma_resource* const resource )
{
transfer_tx_is_done = true;
}
static void transfer_rx_done(struct dma_resource* const resource )
{
transfer_rx_is_done = true;
}
static void configure_dma_resource_tx(struct dma_resource *tx_resource)
{
struct dma_resource_config tx_config;
dma_get_config_defaults(&tx_config);
tx_config.peripheral_trigger = CONF_PERIPHERAL_TRIGGER_TX;
tx_config.trigger_action = DMA_TRIGGER_ACTION_BEAT;
dma_allocate(tx_resource, &tx_config);
}
static void configure_dma_resource_rx(struct dma_resource *rx_resource)
{
struct dma_resource_config rx_config;
dma_get_config_defaults(&rx_config);
rx_config.peripheral_trigger = CONF_PERIPHERAL_TRIGGER_RX;
rx_config.trigger_action = DMA_TRIGGER_ACTION_BEAT;
dma_allocate(rx_resource, &rx_config);
}
static void setup_transfer_descriptor_tx(DmacDescriptor *tx_descriptor)
{
struct dma_descriptor_config tx_descriptor_config;
dma_descriptor_get_config_defaults(&tx_descriptor_config);
tx_descriptor_config.beat_size = DMA_BEAT_SIZE_BYTE;
tx_descriptor_config.dst_increment_enable = false;
tx_descriptor_config.block_transfer_count = sizeof(buffer_tx)/sizeof(uint8_t);
tx_descriptor_config.source_address = (uint32_t)buffer_tx + sizeof(buffer_tx);
tx_descriptor_config.destination_address =
(uint32_t)(&spi_master_instance.hw->SPI.DATA.reg);
dma_descriptor_create(tx_descriptor, &tx_descriptor_config);
}
static void setup_transfer_descriptor_rx(DmacDescriptor *rx_descriptor)
{
struct dma_descriptor_config rx_descriptor_config;
dma_descriptor_get_config_defaults(&rx_descriptor_config);
rx_descriptor_config.beat_size = DMA_BEAT_SIZE_BYTE;
rx_descriptor_config.src_increment_enable = false;
rx_descriptor_config.block_transfer_count = sizeof(buffer_rx)/sizeof(uint8_t);
rx_descriptor_config.source_address =
(uint32_t)(&spi_slave_instance.hw->SPI.DATA.reg);
rx_descriptor_config.destination_address =
(uint32_t)buffer_rx + sizeof(buffer_rx);
dma_descriptor_create(rx_descriptor, &rx_descriptor_config);
}
static void configure_spi_master(void)
{
slave_dev_config.ss_pin = SLAVE_SELECT_PIN;
config_spi_master.mode_specific.master.baudrate = TEST_SPI_BAUDRATE;
config_spi_master.mux_setting = CONF_MASTER_MUX_SETTING;
config_spi_master.pinmux_pad0 = CONF_MASTER_PINMUX_PAD0;
config_spi_master.pinmux_pad1 = CONF_MASTER_PINMUX_PAD1;
config_spi_master.pinmux_pad2 = CONF_MASTER_PINMUX_PAD2;
config_spi_master.pinmux_pad3 = CONF_MASTER_PINMUX_PAD3;
spi_init(&spi_master_instance, CONF_MASTER_SPI_MODULE, &config_spi_master);
}
static void configure_spi_slave(void)
{
config_spi_slave.mode_specific.slave.preload_enable = true;
config_spi_slave.mux_setting = CONF_SLAVE_MUX_SETTING;
config_spi_slave.pinmux_pad0 = CONF_SLAVE_PINMUX_PAD0;
config_spi_slave.pinmux_pad1 = CONF_SLAVE_PINMUX_PAD1;
config_spi_slave.pinmux_pad2 = CONF_SLAVE_PINMUX_PAD2;
config_spi_slave.pinmux_pad3 = CONF_SLAVE_PINMUX_PAD3;
spi_init(&spi_slave_instance, CONF_SLAVE_SPI_MODULE, &config_spi_slave);
}
Add to user application initialization (typically the start of main()
):
configure_spi_master();
configure_spi_slave();
configure_dma_resource_tx(&example_resource_tx);
configure_dma_resource_rx(&example_resource_rx);
setup_transfer_descriptor_tx(&example_descriptor_tx);
setup_transfer_descriptor_rx(&example_descriptor_rx);
dma_add_descriptor(&example_resource_tx, &example_descriptor_tx);
dma_add_descriptor(&example_resource_rx, &example_descriptor_rx);
dma_register_callback(&example_resource_tx, transfer_tx_done,
DMA_CALLBACK_TRANSFER_DONE);
dma_register_callback(&example_resource_rx, transfer_rx_done,
DMA_CALLBACK_TRANSFER_DONE);
dma_enable_callback(&example_resource_tx, DMA_CALLBACK_TRANSFER_DONE);
dma_enable_callback(&example_resource_rx, DMA_CALLBACK_TRANSFER_DONE);
Workflow
- Create a module software instance structure for the SPI module to store the SPI driver state while it is in use.
- Note
- This should never go out of scope as long as the module is in use. In most cases, this should be global.
- Create a module software instance structure for DMA resource to store the DMA resource state while it is in use.
struct dma_resource example_resource_tx;
struct dma_resource example_resource_rx;
- Note
- This should never go out of scope as long as the module is in use. In most cases, this should be global.
- Create transfer done flag to indication DMA transfer done.
static volatile bool transfer_tx_is_done = false;
static volatile bool transfer_rx_is_done = false;
- Define the buffer length for TX/RX.
- Create buffer to store the data to be transferred.
static const uint8_t buffer_tx[BUF_LENGTH] = {
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A,
0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14,
};
static uint8_t buffer_rx[BUF_LENGTH];
- Create the SPI module configuration struct, which can be filled out to adjust the configuration of a physical SPI peripheral.
- Initialize the SPI configuration struct with the module's default values.
- Note
- This should always be performed before using the configuration struct to ensure that all values are initialized to known default settings.
- Alter the SPI settings to configure the physical pinout, baudrate, and other relevant parameters.
config_spi_master.mux_setting = CONF_MASTER_MUX_SETTING;
config_spi_slave.mux_setting = CONF_SLAVE_MUX_SETTING;
- Configure the SPI module with the desired settings, retrying while the driver is busy until the configuration is stressfully set.
spi_init(&spi_master_instance, CONF_MASTER_SPI_MODULE, &config_spi_master);
spi_init(&spi_slave_instance, CONF_SLAVE_SPI_MODULE, &config_spi_slave);
- Enable the SPI module.
- Create the DMA resource configuration structure, which can be filled out to adjust the configuration of a single DMA transfer.
struct dma_resource_config tx_config;
struct dma_resource_config rx_config;
- Initialize the DMA resource configuration struct with the module's default values.
dma_get_config_defaults(&tx_config);
dma_get_config_defaults(&rx_config);
- Note
- This should always be performed before using the configuration struct to ensure that all values are initialized to known default settings.
- Set extra configurations for the DMA resource. It is using peripheral trigger. SERCOM TX empty and RX complete trigger causes a beat transfer in this example.
tx_config.peripheral_trigger = CONF_PERIPHERAL_TRIGGER_TX;
tx_config.trigger_action = DMA_TRIGGER_ACTION_BEAT;
rx_config.peripheral_trigger = CONF_PERIPHERAL_TRIGGER_RX;
rx_config.trigger_action = DMA_TRIGGER_ACTION_BEAT;
- Allocate a DMA resource with the configurations.
dma_allocate(tx_resource, &tx_config);
dma_allocate(rx_resource, &rx_config);
- Create a DMA transfer descriptor configuration structure, which can be filled out to adjust the configuration of a single DMA transfer.
struct dma_descriptor_config tx_descriptor_config;
struct dma_descriptor_config rx_descriptor_config;
- Initialize the DMA transfer descriptor configuration struct with the module's default values.
dma_descriptor_get_config_defaults(&tx_descriptor_config);
dma_descriptor_get_config_defaults(&rx_descriptor_config);
- Note
- This should always be performed before using the configuration struct to ensure that all values are initialized to known default settings.
- Set the specific parameters for a DMA transfer with transfer size, source address, and destination address.
tx_descriptor_config.beat_size = DMA_BEAT_SIZE_BYTE;
tx_descriptor_config.dst_increment_enable = false;
tx_descriptor_config.block_transfer_count = sizeof(buffer_tx)/sizeof(uint8_t);
tx_descriptor_config.source_address = (uint32_t)buffer_tx + sizeof(buffer_tx);
tx_descriptor_config.destination_address =
(uint32_t)(&spi_master_instance.hw->SPI.DATA.reg);
rx_descriptor_config.beat_size = DMA_BEAT_SIZE_BYTE;
rx_descriptor_config.src_increment_enable = false;
rx_descriptor_config.block_transfer_count = sizeof(buffer_rx)/sizeof(uint8_t);
rx_descriptor_config.source_address =
(uint32_t)(&spi_slave_instance.hw->SPI.DATA.reg);
rx_descriptor_config.destination_address =
(uint32_t)buffer_rx + sizeof(buffer_rx);
- Create the DMA transfer descriptor.
dma_descriptor_create(tx_descriptor, &tx_descriptor_config);
dma_descriptor_create(rx_descriptor, &rx_descriptor_config);
Use Case
Code
Copy-paste the following code to your user application:
dma_start_transfer_job(&example_resource_rx);
dma_start_transfer_job(&example_resource_tx);
while (!transfer_rx_is_done) {
}
while (true) {
}
Workflow
- Select the slave.
- Start the transfer job.
dma_start_transfer_job(&example_resource_rx);
dma_start_transfer_job(&example_resource_tx);
- Wait for transfer done.
while (!transfer_rx_is_done) {
}
- Deselect the slave.
- Enter endless loop.