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发一帧。有些帧就粘在一起发送了。 如果把缓冲加大 #define DMA_BUF_SIZE (2048) 同样接收501的频繁发送,错帧比例会明显减少。如下所以,测试 连续间隔50ms,发送98万字节数据, 才出现26帧错误的. 需要应用层对数据有效性进行判断处理。 |