目录
DMA(Direct Memory Access)是一种计算机系统中的数据传输技术,它允许数据在不经过中央处理器(CPU)的直接控制下在内存和外设之间传输。UART(Universal Asynchronous Receiver/Transmitter)是一种串行通信协议,用于在设备之间传输数据。
在DMA接收和发送数据的情况下,DMA可以用于管理UART通信中的数据传输。具体来说:
DMA接收数据:
DMA发送数据:
总的来说,DMA在UART通信中的应用可以提高数据传输的效率,减少对CPU的依赖,使系统能够更有效地处理数据。
波特率115200? ? ? ? 1位停止位? ? ? ? 无奇偶校验
包含了一些头文件,包含了与底层硬件和外设驱动相关的定义和函数声明
义了一些与UART相关的宏,如UART的基地址、时钟名、DMA请求等
声明了两个全局变量 uart_tx_dma_done
和 uart_rx_dma_done
,用于表示UART的发送和接收DMA是否完成
#include "board.h"
#include "hpm_clock_drv.h"
#include "hpm_uart_drv.h"
#ifdef CONFIG_HAS_HPMSDK_DMAV2
#include "hpm_dmav2_drv.h"
#else
#include "hpm_dma_drv.h"
#endif
#include "hpm_dmamux_drv.h"
#include "hpm_l1c_drv.h"
#include "hpm_common.h"
#define TEST_UART BOARD_APP_UART_BASE
#define TEST_UART_CLK_NAME BOARD_APP_UART_CLK_NAME
#define TEST_UART_TX_DMA_REQ BOARD_APP_UART_TX_DMA_REQ
#define TEST_UART_RX_DMA_REQ BOARD_APP_UART_RX_DMA_REQ
#define TEST_UART_DMA_CONTROLLER BOARD_APP_HDMA
#define TEST_UART_DMAMUX_CONTROLLER BOARD_APP_DMAMUX
#define TEST_UART_TX_DMA_CHN (0U)
#define TEST_UART_RX_DMA_CHN (1U)
#define TEST_UART_TX_DMAMUX_CHN DMA_SOC_CHN_TO_DMAMUX_CHN(TEST_UART_DMA_CONTROLLER, TEST_UART_TX_DMA_CHN)
#define TEST_UART_RX_DMAMUX_CHN DMA_SOC_CHN_TO_DMAMUX_CHN(TEST_UART_DMA_CONTROLLER, TEST_UART_RX_DMA_CHN)
#define TEST_UART_DMA_IRQ BOARD_APP_HDMA_IRQ
#define TEST_BUFFER_SIZE (16U)
ATTR_PLACE_AT_NONCACHEABLE uint8_t uart_buff[TEST_BUFFER_SIZE];
volatile bool uart_tx_dma_done;
volatile bool uart_rx_dma_done;
hpm_stat_t uart_tx_trigger_dma(DMA_Type *dma_ptr,
uint8_t ch_num,
UART_Type *uart_ptr,
uint32_t src,
uint32_t size)
{
dma_handshake_config_t config;
dma_default_handshake_config(dma_ptr, &config);
config.ch_index = ch_num;
config.dst = (uint32_t)&uart_ptr->THR;
config.dst_fixed = true;
config.src = src;
config.src_fixed = false;
config.data_width = DMA_TRANSFER_WIDTH_BYTE;
config.size_in_byte = size;
return dma_setup_handshake(dma_ptr, &config, true);
}
参数说明:
dma_ptr
: DMA控制器的指针,指向用于配置和控制DMA的硬件寄存器。ch_num
: DMA通道号,表示要配置的DMA通道。uart_ptr
: UART控制器的指针,指向用于配置和控制UART的硬件寄存器。src
: 数据源的地址,这是UART发送数据的来源。size
: 要传输的数据大小,以字节为单位。函数逻辑:
dma_handshake_config_t
类型的结构体变量 config
,用于配置DMA的握手参数。dma_default_handshake_config
函数初始化 config
结构体,设置了一些默认的DMA握手参数。config
结构体的各个成员:
ch_index
: DMA通道号。dst
: 目的地地址,这里是UART的传输保持寄存器(THR - Transmitter Holding Register)的地址。dst_fixed
: 目的地地址是否固定,这里设置为 true
,表示目的地地址不变。src
: 数据源地址,即要发送的数据的地址。src_fixed
: 数据源地址是否固定,这里设置为 false
,表示数据源地址可能变化。data_width
: 数据传输宽度,这里设置为字节宽度。size_in_byte
: 要传输的数据大小。返回值:
dma_setup_handshake
函数,根据配置好的参数设置DMA握手,并返回相应的状态。注意事项:
这个函数的作用是通过DMA实现UART的发送数据,配置了DMA握手参数,确保数据正确地传输到UART传输保持寄存器中。
hpm_stat_t uart_rx_trigger_dma(DMA_Type *dma_ptr,
uint8_t ch_num,
UART_Type *uart_ptr,
uint32_t dst,
uint32_t size)
{
dma_handshake_config_t config;
dma_default_handshake_config(dma_ptr, &config);
config.ch_index = ch_num;
config.dst = dst;
config.dst_fixed = false;
config.src = (uint32_t)&uart_ptr->RBR;
config.src_fixed = true;
config.data_width = DMA_TRANSFER_WIDTH_BYTE;
config.size_in_byte = size;
return dma_setup_handshake(dma_ptr, &config, true);
}
参数说明:
dma_ptr
: DMA控制器的指针,指向用于配置和控制DMA的硬件寄存器。ch_num
: DMA通道号,表示要配置的DMA通道。uart_ptr
: UART控制器的指针,指向用于配置和控制UART的硬件寄存器。dst
: 数据目的地的地址,这是用于存储UART接收数据的缓冲区的地址。size
: 要传输的数据大小,以字节为单位。函数逻辑:
dma_handshake_config_t
类型的结构体变量 config
,用于配置DMA的握手参数。dma_default_handshake_config
函数初始化 config
结构体,设置了一些默认的DMA握手参数。config
结构体的各个成员:
ch_index
: DMA通道号。dst
: 目的地地址,即UART接收数据的缓冲区地址。dst_fixed
: 目的地地址是否固定,这里设置为 false
,表示目的地地址可能变化。src
: 数据源地址,这里是UART的接收保持寄存器(RBR - Receiver Buffer Register)的地址。src_fixed
: 数据源地址是否固定,这里设置为 true
,表示数据源地址不变。data_width
: 数据传输宽度,这里设置为字节宽度。size_in_byte
: 要传输的数据大小。返回值:
dma_setup_handshake
函数,根据配置好的参数设置DMA握手,并返回相应的状态。注意事项:
这个函数的作用是通过DMA实现UART的接收数据,配置了DMA握手参数,确保UART接收到的数据传输到指定的缓冲区中。
void dma_isr(void)
{
volatile hpm_stat_t stat_rx_chn, stat_tx_chn;
stat_rx_chn = dma_check_transfer_status(TEST_UART_DMA_CONTROLLER, TEST_UART_RX_DMA_CHN);
if (stat_rx_chn & DMA_CHANNEL_STATUS_TC) {
uart_rx_dma_done = true;
}
stat_tx_chn = dma_check_transfer_status(TEST_UART_DMA_CONTROLLER, TEST_UART_TX_DMA_CHN);
if (stat_tx_chn & DMA_CHANNEL_STATUS_TC) {
uart_tx_dma_done = true;
}
}
SDK_DECLARE_EXT_ISR_M(TEST_UART_DMA_IRQ, dma_isr)
函数逻辑:
stat_rx_chn
和 stat_tx_chn
,用于保存DMA通道的传输状态。dma_check_transfer_status
函数检查UART接收和发送的DMA通道的传输状态。DMA_CHANNEL_STATUS_TC
(传输完成),则将 uart_rx_dma_done
置为 true
,表示UART接收DMA完成。DMA_CHANNEL_STATUS_TC
,则将 uart_tx_dma_done
置为 true
,表示UART发送DMA完成。注意事项:
volatile
修饰 stat_rx_chn
和 stat_tx_chn
,表示这两个变量可能在中断服务例程之外被修改,确保编译器不会对它们进行优化。宏 SDK_DECLARE_EXT_ISR_M
:
dma_isr
并关联到 TEST_UART_DMA_IRQ
所指定的中断。这个中断服务例程的作用是在DMA传输完成时被调用,检查相应的DMA通道状态,并设置标志位以通知主程序相应的DMA传输已完成。
int main(void)
{
hpm_stat_t stat;
uart_config_t config = {0};
board_init();
printf("UART DMA \n");
printf("UART will send back received characters, echo every %d bytes\n", TEST_BUFFER_SIZE);
/* if TEST_UART is same as BOARD_CONSOLE_BASE, it has been initialized in board_init(); */
board_init_uart(TEST_UART);
uart_default_config(TEST_UART, &config);
config.fifo_enable = true;
config.dma_enable = true;
config.src_freq_in_hz = clock_get_frequency(TEST_UART_CLK_NAME);
config.tx_fifo_level = uart_tx_fifo_trg_not_full;
config.rx_fifo_level = uart_rx_fifo_trg_not_empty;
stat = uart_init(TEST_UART, &config);
if (stat != status_success) {
printf("failed to initialize uart\n");
while (1) {
}
}
intc_m_enable_irq_with_priority(TEST_UART_DMA_IRQ, 1);
dmamux_config(TEST_UART_DMAMUX_CONTROLLER, TEST_UART_RX_DMAMUX_CHN, TEST_UART_RX_DMA_REQ, true);
dmamux_config(TEST_UART_DMAMUX_CONTROLLER, TEST_UART_TX_DMAMUX_CHN, TEST_UART_TX_DMA_REQ, true);
while (1) {
/* config rx dma transfer */
stat = uart_rx_trigger_dma(TEST_UART_DMA_CONTROLLER,
TEST_UART_RX_DMA_CHN,
TEST_UART,
core_local_mem_to_sys_address(BOARD_RUNNING_CORE, (uint32_t)uart_buff),
TEST_BUFFER_SIZE);
if (stat != status_success) {
printf("uart rx trigger dma failed\n");
break;
}
while (!uart_rx_dma_done) {
__asm("nop");
}
uart_rx_dma_done = false;
/* config tx dma transfer */
stat = uart_tx_trigger_dma(TEST_UART_DMA_CONTROLLER,
TEST_UART_TX_DMA_CHN,
TEST_UART,
core_local_mem_to_sys_address(BOARD_RUNNING_CORE, (uint32_t)uart_buff),
TEST_BUFFER_SIZE);
if (stat != status_success) {
printf("uart tx trigger dma failed\n");
break;
}
while (!uart_tx_dma_done) {
__asm("nop");
}
uart_tx_dma_done = false;
}
while (1) {
__asm("nop");
}
return 0;
}
board_init
初始化板子。TEST_UART
与 BOARD_CONSOLE_BASE
相同,说明UART已在 board_init
中初始化。board_init_uart
初始化UART。uart_init
初始化UART,检查初始化是否成功。该主函数的主要任务是配置并执行UART的DMA传输,实现了UART接收到的数据的回显。
以下是上述代码的主要运行流程:
初始化:
设置中断和DMA多路复用:
主循环:
uart_buff
缓冲区中。uart_rx_dma_done
标志。uart_buff
的开头。uart_buff
缓冲区的数据。uart_tx_dma_done
标志。结束:
总体而言,该代码通过 DMA 实现了 UART 数据的异步传输。接收到的数据被存储在 uart_buff
缓冲区中,并在发送之前添加了 "send" 前缀。此过程一直在一个无限循环中进行,确保持续接收和发送数据。
当工程正确运行后,通过串口手动输入字符串,如 ‘1234567887654321’,则串口终端会收到如下信息:
UART DMA example UART will send back received characters, echo every 16 bytes 1234567887654321