6621D/F 串口Uart1 DMA接收

wen sir · 262次点击 · 8个月前

6621D/F DMA接收例程

< 注意:已知Keil5.33编译运行有Bug,请用5.36版本往上进行编译,即ArmClang 6.16+>

/**/
#include <stdint.h>
#include <string.h>
#include "peripheral.h"

/**
 *======== 使用说明  ===========
 0- 把此文件加到Keil工程中,参与编译
 1- 在打印或者接收前,调用初始化,不需要
 main()
 {
 ....
  pinmux_config(5, PINMUX_UART1_SDA_O_CFG);
  pinmux_config(6, PINMUX_UART1_SDA_I_CFG);
  uart1_dma_recv_open(115200);
 ....
 }
 数据处理在
 2- 替换Startup中的Uart1_中断向 UART1_IRQHandler
 3 - 打印正常打Log,data_process()中进行
 4-仅验证了115200工作正常。
 */

//类似Uart.c中的uart_env_t, 因为未在头文件中,只能重定义
typedef struct
{
    uart_rx_callback_t      rx_cb;
    uart_tx_cmp_callback_t  tx_cmp_cb;
} uart_callback_t;

typedef struct
{
    uart_callback_t uart[1];
} uart_env_t;

//DMA 中断的中断保存环境变量
static uart_env_t uart_dma_env;


//此数据结构定义,仅用于追踪测试,可以移除相关变量。
typedef struct
{
    uint32_t dma_isr_cnt;
    uint32_t uart_isr_cnt;
    uint32_t cti_cnt;
    uint32_t rdi_cnt;
    uint32_t rlsi_cnt;
    uint32_t recv_timeout_cnt;
} monitor_var_t;
monitor_var_t mon_var;

/**/
void dma_cb(dma_status_t status, uint32_t cur_src_addr, uint32_t cur_dst_addr, uint32_t xfer_size);//DMA回调
void uart_dma_rx_timeout_handler(uint8_t data);//DMA接收完毕,空闲超时
void uart1_dma_recv_open(uint32_t baud_rate);//初始化DMA接收
void data_process(uint8_t *pData, uint16_t len);//接收的数据处理
/**/
#define DMA_BUF_SIZE     (512)  //要大于连续发数据的最大值
__ALIGNED(4)  uint8_t Uar_recv_Dma_Buffer[DMA_BUF_SIZE]; //DMA接收缓冲,最好4字节对齐
HS_DMA_CH_Type *uartdma_ch; //dma分配的通道
dma_block_t block[1];
uint32_t uart_rx_index = 0; //计数
uint32_t uart_rx_len = 0; //计数

/*
DMA发生或者完成时的中断,根据配置定义
*/
void dma_cb(dma_status_t status, uint32_t cur_src_addr, uint32_t cur_dst_addr, uint32_t xfer_size)
{
    //仅调试用,可以不要
    mon_var.dma_isr_cnt++;
    if (status == DMA_STATUS_BLOCK_OK)
    {
        log_debug("DMA block transfer succeed, block info: ");
    }
    else if (status == DMA_STATUS_ABORT)
    {
        log_debug("DMA block transfer aborted, current info: ");
    }
    else
    {
        log_debug("DMA block transfer error, current info: ");
    }
    log_debug("status = 0x%08x, src_addr = 0x%08x, dst_addr = 0x%08x, xfer_size = %d\r\n",
              status, cur_src_addr, cur_dst_addr, xfer_size);
}

//内存配置
dma_dev_t dma_mem =
{
    .id         = MEM_DMA_ID,
    .addr       = Uar_recv_Dma_Buffer,//缓冲地址
    .addr_ctrl  = DMA_ADDR_CTRL_INC,
    .bus_width  = DMA_SLAVE_BUSWIDTH_8BITS,
    .burst_size = DMA_BURST_LEN_1UNITS,
};
//Uart1 DMA配置
dma_dev_t dma_uart =
{
    .id         = UART1_RX_DMA_ID,
    .addr       = (void *) &HS_UART1->RBR,
    .addr_ctrl  = DMA_ADDR_CTRL_FIX,
    .bus_width  = DMA_SLAVE_BUSWIDTH_8BITS,
    .burst_size = DMA_BURST_LEN_1UNITS,
};
//DMA 接收BLOCK配置
dma_block_config_t block_config =
{
    .src                 = &dma_uart,
    .dst                 = &dma_mem,
    .block_size_in_bytes = DMA_BUF_SIZE,//DMA接收长度,应用中要确保比最长的一帧数据要大
    .priority            = 0,
    .flow_controller     = DMA_FLOW_CONTROLLER_USE_NONE,
    .intr_en             = true,
};

//数据处理
void data_process(uint8_t *pData, uint16_t len)
{
    //用户可以在此处理数据
    //如 Memcpy(XXX,Uar_recv_Dma_Buffer,uart_rx_len)
    log_debug_array_ex("\nrecv", Uar_recv_Dma_Buffer, uart_rx_index);
}
//DMA 未收完指定长度的数据时,触发超时中断
void uart_dma_rx_timeout_handler(uint8_t data)
{
    static int recv_error_cnt = 0;
    (void) data;
    uint8_t Fifo_remain_cnt = HS_UART1->RFL;
    uint32_t dma_recv_cnt = 0;
    //先取DMA收到数据
    if (uartdma_ch)
    {
        dma_recv_cnt = DMA_BUF_SIZE - uartdma_ch->TranSize;//TranSize is Decreasing
    }
    //再取Uart FIFO中未取出的数据
    uart_rx_index += dma_recv_cnt;
    for (uint8_t i = 0; i < Fifo_remain_cnt; i++)
    {
        Uar_recv_Dma_Buffer[uart_rx_index++] = HS_UART1->RBR;
    }
    uart_rx_len = uart_rx_index; //此轮数据最终接收的内容
    //打印跟踪信息。应用中不要这里打印,先把数据Copy到别处再说
    log_debug("\n--------dma recv timeout- Recv=%d", uart_rx_index);
    log_debug("\n uartdma_ch->TranSize =%d", uartdma_ch->TranSize);
    log_debug("\n dma_recv_cnt =%d", dma_recv_cnt);
    log_debug("\n=mon_var.uart_isr_cnt=%d", mon_var.uart_isr_cnt);
    log_debug("\n=mon_var.cti_cnt=%d", mon_var.cti_cnt);
    log_debug("\n=mon_var.rdi_cnt=%d", mon_var.rdi_cnt);
    log_debug("\n=mon_var.rlsi_cnt=%d", mon_var.rlsi_cnt);
    log_debug("\n=mon_var.dma_isr_cnt=%d", mon_var.dma_isr_cnt);
    log_debug("\n=mon_var.recv_timeout_cnt=%d", mon_var.recv_timeout_cnt);
    data_process(Uar_recv_Dma_Buffer, uart_rx_index); //处理数据
    //如果dma_ch合法,重新复位 DMA并接收UART
    if (uartdma_ch)
    {
        //清空刚才接收的Buffer,准备新一帧数据接收
        memset(Uar_recv_Dma_Buffer, 0x00, sizeof(Uar_recv_Dma_Buffer));
        //清监测数据
        memset(&mon_var, 0x00, sizeof(monitor_var_t));
        //清Index和Len
        uart_rx_index = 0;
        uart_rx_len = 0;
        //停止dma 并重启,进行新一轮接收
        dma_stop(uartdma_ch);
        memset(Uar_recv_Dma_Buffer, 0x00, DMA_BUF_SIZE);
        dma_start_transfer(uartdma_ch, block, dma_cb);
    }
}

void uart1_dma_open(HS_UART_Type *uart, uint32_t baud_rate, uart_flow_ctrl_t flow_ctrl,
                    uart_rx_callback_t uart_rx_timeout_cb)
{
#define MODE_X_DIV          16
    uint16_t baud_divisor;
    // Reset and Bypass UART1
    register_set1(&HS_PSO->UART1_CFG, CPM_UART_SOFT_RESET_MASK);
    register_set0(&HS_PSO->UART1_CFG, CPM_UART_DIV_SEL_MASK | CPM_UART_GATE_EN_MASK);
    HS_PSO_UPD_RDY();
    uart_dma_env.uart[0].tx_cmp_cb = NULL;
    uart_dma_env.uart[0].rx_cb = NULL;
    /* Compute divisor value. Normally, we should simply return:
     *   NS16550_CLK / MODE_X_DIV / baudrate
     * but we need to round that value by adding 0.5.
     * Rounding is especially important at high baud rates.
     */
    if (cpm_get_clock(CPM_TOP_CLK) > 64000000 && baud_rate < 19200)
    {
        baud_divisor = 4;    // make sure 9600bps is supported
    }
    else
    {
        baud_divisor = 1;
    }
    cpm_set_clock(CPM_UART1_CLK, baud_divisor * baud_rate * MODE_X_DIV);
    // Disable LCR and irq
    uart->LCR = 0x00;
    uart->IER = 0;
    // Auto RTS – Becomes active when the following occurs:
    //   - Auto Flow Control is selected during configuration
    //   - FIFOs are implemented
    //   - RTS (MCR[1] bit and MCR[5]bit are both set)
    //   - FIFOs are enabled (FCR[0]) bit is set)
    //   - SIR mode is disabled (MCR[6] bit is not set)
    if (flow_ctrl == UART_FLOW_CTRL_ENABLED)
    {
        uart->MCR = UART_MCR_RTS | UART_MCR_AFCE;
    }
    else
    {
        uart->MCR = 0x00;
    }
    uart->FCR = UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT | UART_FCR_FIFO_EN | UART_FCR_TRIGGER_4;
    /* Baud rate setting.*/
    uart->LCR = UART_LCR_DLAB;
    uart->DLL = baud_divisor & 0xff;
    uart->DLH = (baud_divisor >> 8) & 0xff;
    /* 8 data, 1 stop, no parity */
    uart->LCR = UART_LCR_8N1;
    /* Set UARTs int mask */
    if (uart_rx_timeout_cb)
    {
        uart->IER = UART_IER_RLSI | UART_IER_RDI;
        NVIC_ClearPendingIRQ(UART1_IRQn);
        NVIC_SetPriority(UART1_IRQn, IRQ_PRIORITY_HIGH);
        NVIC_EnableIRQ(UART1_IRQn);
    }
    else
    {
        uart->IER = 0;
        NVIC_DisableIRQ(UART1_IRQn);
    }
    uart_dma_env.uart[0].tx_cmp_cb = NULL;
    uart_dma_env.uart[0].rx_cb = uart_rx_timeout_cb;
}


//应用调用此接口进行初始化
void uart1_dma_recv_open(uint32_t baud_rate)
{
    uart1_dma_open(HS_UART1, baud_rate, UART_FLOW_CTRL_DISABLED, uart_dma_rx_timeout_handler);
    //init dma uart
    dma_init();
    //构建Block
    dma_build_block(&block[0], &block_config);
    //使能
    HS_UART1->DMASA = 1;
    //开始DMA接收,接收数据过程中有必要阻止睡眠
    uartdma_ch = dma_start_transfer(NULL, block, dma_cb);
    if (NULL == uartdma_ch)
    {
        log_debug("uart rx dma allocated error\n");
        return;
    }
}

/**
 * @brief 重写UART1的中断,
 *!!!记得要把替换启动文件<startup_hs6621d_flash.s> 中的【UART1_IRQHandler】 都替换为 中的【UART1_DMA_IRQHandler】
 * 使用本中断处理超时接收
 */
void UART1_DMA_IRQHandler(void)
{
    uint8_t status;
    volatile uint8_t ch;
    mon_var.uart_isr_cnt++;
    while (1)
    {
        status = HS_UART1->IIR & UART_IIR_ID;
        switch (status)
        {
            case UART_IIR_RDI:
                mon_var.rdi_cnt++;
                while (HS_UART1->LSR & UART_LSR_DR)
                {
                    ch = HS_UART1->RBR;
                    if (uart_dma_env.uart[0].rx_cb)
                    {
                        uart_dma_env.uart[0].rx_cb(ch);
                    }
                }
                break;
            case UART_IIR_THRI:
                HS_UART1->IER &= ~UART_IER_THRI;
                break;
            case UART_IIR_NO_INT:
                return;
            case UART_IIR_BDI:
                status = HS_UART1->USR;
                break;
            case UART_IIR_RLSI:
                mon_var.rlsi_cnt++;
                ch = HS_UART1->LSR;
                break;
            case UART_IIR_CTI://UART Rx空闲了超时接收
                ch = HS_UART1->LSR;
                mon_var.cti_cnt++;
                if (uart_dma_env.uart[0].rx_cb)
                {
                    uart_dma_env.uart[0].rx_cb(ch);
                }
                break;
            default:
                break;
        }
    }
}


/* 运行LOG,连续发送
running 0

--------dma recv timeout- Recv=279
 uartdma_ch->TranSize =236
 dma_recv_cnt =276
=mon_var.uart_isr_cnt=277
=mon_var.cti_cnt=1
=mon_var.rdi_cnt=0
=mon_var.rlsi_cnt=0
=mon_var.dma_isr_cnt=0
=mon_var.recv_timeout_cnt=0
recv: 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF
 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF
 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF
 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF
 11 22 33 DD EE FF 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF
 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF
 11 22 33 [279bytes]

--------dma recv timeout- Recv=279
 uartdma_ch->TranSize =236
 dma_recv_cnt =276
=mon_var.uart_isr_cnt=277
=mon_var.cti_cnt=1
=mon_var.rdi_cnt=0
=mon_var.rlsi_cnt=0
=mon_var.dma_isr_cnt=0
=mon_var.recv_timeout_cnt=0
recv: 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF
 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF
 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF
 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF
 11 22 33 DD EE FF 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF
 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF
 11 22 33 [279bytes]

--------dma recv timeout- Recv=279
 uartdma_ch->TranSize =236
 dma_recv_cnt =276
=mon_var.uart_isr_cnt=277
=mon_var.cti_cnt=1
=mon_var.rdi_cnt=0
=mon_var.rlsi_cnt=0
=mon_var.dma_isr_cnt=0
=mon_var.recv_timeout_cnt=0
recv: 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF
 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF
 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF
 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF
 11 22 33 DD EE FF 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF
 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF
 11 22 33 [279bytes]

*/

补充:

(1)当PC发送端定时50ms连续发送大量超过Buffer定义的数据时,会出现接收错误。原因是电脑有时发送会堵塞,做不到严格的隔50ms发一帧。有些帧就粘在一起发送了。

image.png

如果把缓冲加大

#define DMA_BUF_SIZE     (2048) 

同样接收501的频繁发送,错帧比例会明显减少。如下所以,测试 连续间隔50ms,发送98万字节数据, 才出现26帧错误的. 需要应用层对数据有效性进行判断处理。

image.png




om6621Fx_uart1_dma-202403093122.c
被收藏 0  ∙  0 赞  
加入收藏
点赞
0 回复  
善言善语 (您需要 登录 后才能回复 没有账号 ?)

请先登录网站