2.5. DMA

2.5.1. Introduction

DMA is a memory access technology that can directly read and write system memory independently without processor intervention. Under the same degree of processor burden, DMA is a fast data transfer method. The DMA device in BL series MCU has the following characteristics:

  • 8 independent dedicated channels

  • Four transmission directions: memory to memory, memory to peripheral, peripheral to memory, peripheral to peripheral

  • LLI linked list

2.5.2. DMA Device Structure Definition

typedef struct dma_device
{
    struct device parent;
    uint8_t id;
    uint8_t ch;
    uint8_t direction;
    uint8_t transfer_mode;
    uint32_t src_req;
    uint32_t dst_req;
    uint8_t src_burst_size;
    uint8_t dst_burst_size;
    uint8_t src_width;
    uint8_t dst_width;
    dma_lli_ctrl_t *lli_cfg;
} dma_device_t;
  • parent inherits the properties of the parent class

  • id DMA id number, default 0, currently there is only one DMA

  • ch channel number

  • direction transmission direction

  • transfer_mode transfer mode

  • src_req source request

  • dst_req destination request

  • src_burst_size source burst bytes

  • dst_burst_size destination number of burst bytes

  • src_width source transmission bit width

  • dst_width destination transmission bit width

  • lli_cfg used to store some information of the dma channel, the user does not need to worry about it

direction provides the following types

typedef enum {
    DMA_MEMORY_TO_MEMORY = 0,                        /*!< DMA transfer tyep:memory to memory */
    DMA_MEMORY_TO_PERIPH,                            /*!< DMA transfer tyep:memory to peripheral */
    DMA_PERIPH_TO_MEMORY,                            /*!< DMA transfer tyep:peripheral to memory */
    DMA_PERIPH_TO_PERIPH,                            /*!< DMA transfer tyep:peripheral to peripheral */
}dma_transfer_dir_type;

transfer_mode provides the following types

#define DMA_LLI_ONCE_MODE     0
#define DMA_LLI_CYCLE_MODE    1

src_req provides the following types

#define DMA_REQUEST_NONE        0x00000000 /*!< DMA request peripheral:None */
#define DMA_REQUEST_UART0_RX    0x00000000 /*!< DMA request peripheral:UART0 RX */
#define DMA_REQUEST_UART0_TX    0x00000001 /*!< DMA request peripheral:UART0 TX */
#define DMA_REQUEST_UART1_RX    0x00000002 /*!< DMA request peripheral:UART1 RX */
#define DMA_REQUEST_UART1_TX    0x00000003 /*!< DMA request peripheral:UART1 TX */
#define DMA_REQUEST_I2C0_RX     0x00000006 /*!< DMA request peripheral:I2C RX */
#define DMA_REQUEST_I2C0_TX     0x00000007 /*!< DMA request peripheral:I2C TX */
#define DMA_REQUEST_SPI0_RX     0x0000000A /*!< DMA request peripheral:SPI RX */
#define DMA_REQUEST_SPI0_TX     0x0000000B /*!< DMA request peripheral:SPI TX */
#define DMA_REQUEST_I2S_RX      0x00000014 /*!< DMA request peripheral:I2S RX */
#define DMA_REQUEST_I2S_TX      0x00000015 /*!< DMA request peripheral:I2S TX */
#define DMA_REQUEST_ADC0        0x00000016 /*!< DMA request peripheral:ADC0 */
#define DMA_REQUEST_DAC0        0x00000017 /*!< DMA request peripheral:DAC0 */
#define DMA_REQUEST_USB_EP0     0x00000018 /*!< DMA request peripheral:USB EP0*/
#define DMA_REQUEST_USB_EP1     0x00000019 /*!< DMA request peripheral:USB EP1*/
#define DMA_REQUEST_USB_EP2     0x0000001A /*!< DMA request peripheral:USB EP2*/
#define DMA_REQUEST_USB_EP3     0x0000001B /*!< DMA request peripheral:USB EP3*/
#define DMA_REQUEST_USB_EP4     0x0000001C /*!< DMA request peripheral:USB EP4*/
#define DMA_REQUEST_USB_EP5     0x0000001D /*!< DMA request peripheral:USB EP5*/
#define DMA_REQUEST_USB_EP6     0x0000001E /*!< DMA request peripheral:USB EP6*/
#define DMA_REQUEST_USB_EP7     0x0000001F /*!< DMA request peripheral:USB EP7 */

dst_req provides the following types

#define DMA_REQUEST_NONE        0x00000000 /*!< DMA request peripheral:None */
#define DMA_REQUEST_UART0_RX    0x00000000 /*!< DMA request peripheral:UART0 RX */
#define DMA_REQUEST_UART0_TX    0x00000001 /*!< DMA request peripheral:UART0 TX */
#define DMA_REQUEST_UART1_RX    0x00000002 /*!< DMA request peripheral:UART1 RX */
#define DMA_REQUEST_UART1_TX    0x00000003 /*!< DMA request peripheral:UART1 TX */
#define DMA_REQUEST_I2C0_RX     0x00000006 /*!< DMA request peripheral:I2C RX */
#define DMA_REQUEST_I2C0_TX     0x00000007 /*!< DMA request peripheral:I2C TX */
#define DMA_REQUEST_SPI0_RX     0x0000000A /*!< DMA request peripheral:SPI RX */
#define DMA_REQUEST_SPI0_TX     0x0000000B /*!< DMA request peripheral:SPI TX */
#define DMA_REQUEST_I2S_RX      0x00000014 /*!< DMA request peripheral:I2S RX */
#define DMA_REQUEST_I2S_TX      0x00000015 /*!< DMA request peripheral:I2S TX */
#define DMA_REQUEST_ADC0        0x00000016 /*!< DMA request peripheral:ADC0 */
#define DMA_REQUEST_DAC0        0x00000017 /*!< DMA request peripheral:DAC0 */
#define DMA_REQUEST_USB_EP0     0x00000018 /*!< DMA request peripheral:USB EP0*/
#define DMA_REQUEST_USB_EP1     0x00000019 /*!< DMA request peripheral:USB EP1*/
#define DMA_REQUEST_USB_EP2     0x0000001A /*!< DMA request peripheral:USB EP2*/
#define DMA_REQUEST_USB_EP3     0x0000001B /*!< DMA request peripheral:USB EP3*/
#define DMA_REQUEST_USB_EP4     0x0000001C /*!< DMA request peripheral:USB EP4*/
#define DMA_REQUEST_USB_EP5     0x0000001D /*!< DMA request peripheral:USB EP5*/
#define DMA_REQUEST_USB_EP6     0x0000001E /*!< DMA request peripheral:USB EP6*/
#define DMA_REQUEST_USB_EP7     0x0000001F /*!< DMA request peripheral:USB EP7 */

src_burst_size provides the following types

#define DMA_BURST_INCR1     0
#define DMA_BURST_INCR4     1
#define DMA_BURST_INCR8     2
#define DMA_BURST_INCR16    3

dst_burst_size provides the following types

#define DMA_BURST_INCR1     0
#define DMA_BURST_INCR4     1
#define DMA_BURST_INCR8     2
#define DMA_BURST_INCR16    3

src_width provides the following types

#define DMA_TRANSFER_WIDTH_8BIT  0
#define DMA_TRANSFER_WIDTH_16BIT 1
#define DMA_TRANSFER_WIDTH_32BIT 2

dst_width: provide the following types

#define DMA_TRANSFER_WIDTH_8BIT  0
#define DMA_TRANSFER_WIDTH_16BIT 1
#define DMA_TRANSFER_WIDTH_32BIT 2

2.5.3. DMA Device Parameter Configuration Table

Each DMA device has a parameter configuration macro, the macro definition is located in the peripheral_config.h file under the bsp/board/xxx directory, and the variable definition is located in hal_dma.c, so there is no need for the user himself Define variables. When the user opens the macro of the corresponding device, the configuration of the device will take effect. For example, open the macro BSP_USING_DMA0_CH0, DMA0_CH0_CONFIG will take effect, and the DMA channel 0 device can be registered and used.

/*Parameter configuration macro*/
#if defined(BSP_USING_DMA0_CH0)
#ifndef DMA0_CH0_CONFIG
#define DMA0_CH0_CONFIG \
{   \
 .id = 0, \
 .ch = 0,\
 .direction = DMA_MEMORY_TO_MEMORY,\
 .transfer_mode = DMA_LLI_ONCE_MODE, \
 .src_req = DMA_REQUEST_NONE, \
 .dst_req = DMA_REQUEST_NONE, \
 .src_width = DMA_TRANSFER_WIDTH_32BIT , \
 .dst_width = DMA_TRANSFER_WIDTH_32BIT , \
}
#endif
#endif


/*Variable definitions*/
static dma_device_t dmax_device[DMA_MAX_INDEX] =
{
#ifdef BSP_USING_DMA0_CH0
    DMA0_CH0_CONFIG,
#endif
#ifdef BSP_USING_DMA0_CH1
    DMA0_CH1_CONFIG,
#endif
#ifdef BSP_USING_DMA0_CH2
    DMA0_CH2_CONFIG,
#endif
#ifdef BSP_USING_DMA0_CH3
    DMA0_CH3_CONFIG,
#endif
#ifdef BSP_USING_DMA0_CH4
    DMA0_CH4_CONFIG,
#endif
#ifdef BSP_USING_DMA0_CH5
    DMA0_CH5_CONFIG,
#endif
#ifdef BSP_USING_DMA0_CH6
    DMA0_CH6_CONFIG,
#endif
#ifdef BSP_USING_DMA0_CH7
    DMA0_CH7_CONFIG,
#endif
};

备注

The above configuration can be modified through DMA_DEV(dev)->xxx and can only be used before calling device_open.

2.5.4. DMA Device Interface

The DMA device interface follows which provided by the standard device driver management layer. In order to facilitate the user to call, some standard interfaces are redefined using macros.

2.5.4.1. dma_register

dma_register is used to register a DMA device standard driver interface. Before registering, you need to open the channel macro definition of the corresponding DMA device. For example, after defining the macro BSP_USING_DMA_CH0, the 0 channel of the DMA device can be used. After the registration is completed, other interfaces can be used. If the macro is not defined, the 0 channel of the DMA device cannot be used.

int dma_register(enum dma_index_type index, const char *name);
  • index device index to be registered

  • name device name to be registered

index is used to select the configuration of a certain channel of DMA, an index corresponds to a channel configuration of a DMA, for example, DMA_CH0_INDEX corresponds to the configuration of DMA channel 0, and index has the following optional types

enum dma_index_type
{
#ifdef BSP_USING_DMA0_CH0
    DMA0_CH0_INDEX,
#endif
#ifdef BSP_USING_DMA0_CH1
    DMA0_CH1_INDEX,
#endif
#ifdef BSP_USING_DMA0_CH2
    DMA0_CH2_INDEX,
#endif
#ifdef BSP_USING_DMA0_CH3
    DMA0_CH3_INDEX,
#endif
#ifdef BSP_USING_DMA0_CH4
    DMA0_CH4_INDEX,
#endif
#ifdef BSP_USING_DMA0_CH5
    DMA0_CH5_INDEX,
#endif
#ifdef BSP_USING_DMA0_CH6
    DMA0_CH6_INDEX,
#endif
#ifdef BSP_USING_DMA0_CH7
    DMA0_CH7_INDEX,
#endif
    DMA_MAX_INDEX
};

2.5.4.2. device_open

device_open is used to open a channel of a dma device,this funtion calls dma_open actually.

int device_open(struct device *dev, uint16_t oflag);
  • dev device handle

  • oflag open mode

  • return Error code, 0 means opening is successful, other means error

oflag provides the following types

#define DEVICE_OFLAG_STREAM_TX  0x001 /* The device is turned on in polling sending mode */
#define DEVICE_OFLAG_STREAM_RX  0x002 /* The device is turned on in polling receiving mode */
#define DEVICE_OFLAG_INT_TX     0x004 /* The device is turned on in interrupt sending mode */
#define DEVICE_OFLAG_INT_RX     0x008 /* The device is turned on in interrupt receiving mode */
#define DEVICE_OFLAG_DMA_TX     0x010 /* The device is turned on in DMA transmission mode */
#define DEVICE_OFLAG_DMA_RX     0x020 /* The device is turned on in DMA receiving mode */

2.5.4.3. device_close

device_close is used to close a channel of a dma device,this funtion calls dma_close actually.

int device_close(struct device *dev);
  • dev device handle

  • return error code, 0 means closing is successful, others mean error

2.5.4.4. device_control

device_control is used to control and modify the parameters of the dma device according to commands.This funtion calls dma_control actually.

int device_control(struct device *dev, int cmd, void *args);
  • dev device handle

  • cmd device control command

  • args control parameters

  • return different control commands return different meanings

In addition to standard control commands, DMA devices also have their own special control commands.

#define DMA_CHANNEL_GET_STATUS  0x10
#define DMA_CHANNEL_START       0x11
#define DMA_CHANNEL_STOP        0x12
#define DMA_CHANNEL_UPDATE      0x13

args input is different depending on cmd, the list is as follows:

table1

cmd

args

description

DEVICE_CTRL_SET_INT

NULL

Enable DMA transfer completion interrupt

DEVICE_CTRL_CLR_INT

NULL

Disable DMA transfer completion interrupt

DMA_CHANNEL_GET_STATUS

NULL

Get DMA channel completion status

DMA_CHANNEL_START

NULL

Open dma channel

DMA_CHANNEL_STOP

NULL

Close dma channel

DMA_CHANNEL_UPDATE

NULL

Update dma transmission configuration

2.5.4.5. device_set_callback

device_set_callback is used to register a DMA channel interrupt callback function.

int device_set_callback(struct device *dev, void (*callback)(struct device *dev, void *args, uint32_t size, uint32_t event));
  • dev Device handle

  • callback The interrupt callback function to be registered

    • dev device handle

    • args unused

    • size unused

    • event interrupt event type

event type definition is as follows:

enum dma_event_type
{
    DMA_EVENT_COMPLETE,
};

2.5.4.6. dma_channel_start

dma_channel_start is used to open the DMA channel. It actually calls device_control, where cmd is DMA_CHANNEL_START.

dma_channel_start(dev)
  • dev dma channel handle that needs to be opened

2.5.4.7. dma_channel_stop

dma_channel_stop is used to close the DMA channel. It actually calls device_control, where cmd is DMA_CHANNEL_STOP.

dma_channel_stop(dev)
  • dev dma channel handle that needs to be closed

2.5.4.8. dma_channel_update

dma_channel_update is used to update the DMA configuration. The actual call is device_control, where cmd is DMA_CHANNEL_UPDATE.

dma_channel_update(dev,list)
  • dev dma channel handle that needs to be updated

  • list dma_lli_ctrl_t handle

2.5.4.9. dma_channel_check_busy

dma_channel_check_busy is used to query whether the currently used DMA channel has completed the transfer. It actually calls device_control, where cmd is DMA_CHANNEL_GET_STATUS.

dma_channel_check_busy(dev)
  • dev DMA channel handle to be queried

  • return return the current DMA status, 0 means the transfer is complete, 1 means the transfer is not complete

2.5.4.10. dma_reload

dma_reload is used to update the configuration of a certain channel of DMA. Compared with dma_channel_update, this function does not require the user to pass many parameters, but only needs to fill in the source address, destination address, and length. After this function is called, the DMA channel is not turned on. You need to manually call the dma_channel_start function.

int dma_reload(struct device *dev, uint32_t src_addr, uint32_t dst_addr, uint32_t transfer_size);
  • dev DMA channel handle to be queried

  • src_addr transmission source address

  • dst_addr transmission destination address

  • transfer_size the total length of transferred bytes. If the number of bits transferred is 16 bits or 32 bits, it needs to be converted into byte length here.