Microchip® Advanced Software Framework

Quick Start Guide for Using DMA with SERCOM I2C Master

The supported board list:

  • SAMD21 Xplained Pro
  • SAMR21 Xplained Pro
  • SAML21 Xplained Pro
  • SAML22 Xplained Pro
  • SAMDA1 Xplained Pro
  • SAMC21 Xplained Pro
  • SAMHA1G16A Xplained Pro

In this use case, the I2C will used and set up as follows:

  • Master mode
  • 100KHz operation speed
  • Not operational in standby
  • 10000 packet timeout value
  • 65535 unknown bus state timeout value

Prerequisites

The device must be connected to an I2C slave.

Setup

Code

The following must be added to the user application:

  • A sample buffer to send, number of entries to send and address of slave:
    #define DATA_LENGTH 10
    static uint8_t buffer[DATA_LENGTH] = {
    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
    };
    #define SLAVE_ADDRESS 0x12
    Number of times to try to send packet if it fails:
    #define TIMEOUT 1000
  • Globally accessible module structure:
    struct i2c_master_module i2c_master_instance;
  • Function for setting up the module:
    static void configure_i2c_master(void)
    {
    /* Initialize config structure and software module. */
    struct i2c_master_config config_i2c_master;
    i2c_master_get_config_defaults(&config_i2c_master);
    /* Change buffer timeout to something longer. */
    config_i2c_master.buffer_timeout = 10000;
    #if SAMR30
    config_i2c_master.pinmux_pad0 = CONF_MASTER_SDA_PINMUX;
    config_i2c_master.pinmux_pad1 = CONF_MASTER_SCK_PINMUX;
    #endif
    /* Initialize and enable device with config. */
    i2c_master_init(&i2c_master_instance, CONF_I2C_MASTER_MODULE, &config_i2c_master);
    i2c_master_enable(&i2c_master_instance);
    }
  • Globally accessible DMA module structure:
    struct dma_resource example_resource;
  • Globally transfer done flag:
    static volatile bool transfer_is_done = false;
  • Globally accessible DMA transfer descriptor:
    DmacDescriptor example_descriptor SECTION_DMAC_DESCRIPTOR;
  • Function for transfer done callback:
    static void transfer_done(struct dma_resource* const resource )
    {
    UNUSED(resource);
    transfer_is_done = true;
    }
  • Function for setting up the DMA resource:
    static void configure_dma_resource(struct dma_resource *resource)
    {
    struct dma_resource_config config;
    config.peripheral_trigger = CONF_I2C_DMA_TRIGGER;
    config.trigger_action = DMA_TRIGGER_ACTION_BEAT;
    dma_allocate(resource, &config);
    }
  • Function for setting up the DMA transfer descriptor:
    static void setup_dma_descriptor(DmacDescriptor *descriptor)
    {
    struct dma_descriptor_config descriptor_config;
    descriptor_config.beat_size = DMA_BEAT_SIZE_BYTE;
    descriptor_config.dst_increment_enable = false;
    descriptor_config.block_transfer_count = DATA_LENGTH;
    descriptor_config.source_address = (uint32_t)buffer + DATA_LENGTH;
    descriptor_config.destination_address =
    (uint32_t)(&i2c_master_instance.hw->I2CM.DATA.reg);
    dma_descriptor_create(descriptor, &descriptor_config);
    }
  • Add to user application main():
    configure_i2c_master();
    configure_dma_resource(&example_resource);
    setup_dma_descriptor(&example_descriptor);
    dma_add_descriptor(&example_resource, &example_descriptor);
    dma_register_callback(&example_resource, transfer_done,

Workflow

  1. Configure and enable module:
    configure_i2c_master();
    1. Create and initialize configuration structure.
      struct i2c_master_config config_i2c_master;
      i2c_master_get_config_defaults(&config_i2c_master);
    2. Change settings in the configuration.
      config_i2c_master.buffer_timeout = 10000;
      #if SAMR30
      config_i2c_master.pinmux_pad0 = CONF_MASTER_SDA_PINMUX;
      config_i2c_master.pinmux_pad1 = CONF_MASTER_SCK_PINMUX;
      #endif
    3. Initialize the module with the set configurations.
      i2c_master_init(&i2c_master_instance, CONF_I2C_MASTER_MODULE, &config_i2c_master);
    4. Enable the module.
      i2c_master_enable(&i2c_master_instance);
  2. Configure DMA
    1. Create a DMA resource configuration structure, which can be filled out to adjust the configuration of a single DMA transfer.
      struct dma_resource_config config;
    2. Initialize the DMA resource 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.
    3. Set extra configurations for the DMA resource. It is using peripheral trigger. SERCOM TX trigger causes a transaction transfer in this example.
      config.peripheral_trigger = CONF_I2C_DMA_TRIGGER;
      config.trigger_action = DMA_TRIGGER_ACTION_BEAT;
    4. Allocate a DMA resource with the configurations.
      dma_allocate(resource, &config);
    5. 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 descriptor_config;
    6. Initialize the DMA transfer descriptor 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.
    7. Set the specific parameters for a DMA transfer with transfer size, source address, and destination address.
      descriptor_config.beat_size = DMA_BEAT_SIZE_BYTE;
      descriptor_config.dst_increment_enable = false;
      descriptor_config.block_transfer_count = DATA_LENGTH;
      descriptor_config.source_address = (uint32_t)buffer + DATA_LENGTH;
      descriptor_config.destination_address =
      (uint32_t)(&i2c_master_instance.hw->I2CM.DATA.reg);
    8. Create the DMA transfer descriptor.
      dma_descriptor_create(descriptor, &descriptor_config);

Implementation

Code

Add to user application main():

dma_start_transfer_job(&example_resource);
i2c_master_dma_set_transfer(&i2c_master_instance, SLAVE_ADDRESS,
DATA_LENGTH, I2C_TRANSFER_WRITE);
while (!transfer_is_done) {
/* Wait for transfer done */
}
while (true) {
}

Workflow

  1. Start the DMA transfer job.
    dma_start_transfer_job(&example_resource);
  2. Set the auto address length and enable flag.
    i2c_master_dma_set_transfer(&i2c_master_instance, SLAVE_ADDRESS,
    DATA_LENGTH, I2C_TRANSFER_WRITE);
  3. Waiting for transfer complete.
    while (!transfer_is_done) {
    /* Wait for transfer done */
    }
  4. Enter an infinite loop once transfer complete.
    while (true) {
    }