Shoteen 发表于 2024-11-25 14:40:23

关于UART RX DMA使用方法的问题

最近做项目需要用到串口通信,通信速率500Kbps,且不使用中断而使用主线程轮询检测接收数据,在500Kbps高速率不间断的向MCU发送数据时,主线程轮询的方法来查询RI标志位,在执行其他操作时,会导致数据丢失,因此改用UART RX DMA来进行数据缓存,但是实现过程中出现了莫名其妙的问题,请各位大佬帮忙看一看!感谢!
实现思路:
1、配置UART RX DMA指向1024字节的缓存数组,同时配置RX DMA的接收数据中断生成阈值为1024,同样在主循环中检测接收数据完成标志,标志完成后,重新开启RX DMA的接收工作,这样则可以不间断地接收RX的数据,数据从缓存数组0开始到1023,溢出后,继续从缓存数组0开始存放。(DMA在不断接收,UART_DMA_DONE寄存器在不断从0~1023计数)
2、主循环定时检查DMA已经传输的字节数,检测本次传输字节数与上次检查的传输字节数的差值,如果检测到传输成功字节数发生变化,则取本次与上次的差值,这个差值就是上次检测到本次检测时间内接受到的数据量,然后将这些数据取出放进串口状态机内。
串口及DMA初始化代码如下:
unsigned char xdata Uart_RxBuf;<span style="white-space:pre">                                                </span>/* 串口DMA接收缓存 */DMA中断处理代码如下(此函数放在主线程中不断轮询):
void DMARX_ISRHandler()
{
    if(DMA_UR1R_STA&0x01)
    {               
      LED = !LED;
      /* 清除缓存,并重新开始接收数据 */
      DMA_UR1R_STA &= ~0x01;
      DMA_UR1R_CR |= 0x01;
      DMA_UR1R_CR |= 0x20;
    }
}接收处理代码如下(同样放在主线程中不断轮询):
volatile unsigned longcounter = 0;
volatile unsigned longlastcounter = 0;
volatile unsigned long rec_count = 0;
volatile unsigned long process_length = 0;

void DMARx_Handler()主函数代码如下:
void main()
{
    HardwareInit();
    SoftwareInit();
rec_count 用于记录从上电到当前时间,串口接收到的数据总量,理论上这种思路只要保证每次轮询间隔时间内缓存足够大,MCU就能不丢失的处理高速率串口数据,但是我用STC32G12K128芯片测试的时候,rec_count总会莫名的多余接收的总字节数,如下图所示

发送78078个字节数据,但是内部记录到的实际接收字节数是00 01 a0 fe换算成十进制就是106750, 这种情况发生在缓存设置成大于256字节时,当缓存设置小于256字节,比如128字节时,记录的数据量就正确,这个代码逻辑放在stm32上测试过,在DMA加持的情况下,能保证发送数据量和接收数据量一致。

Shoteen 发表于 2024-11-25 14:41:54

补充一下初始化代码,不知道为什么自动删除了
void UartInit()
{
        /* 配置P3.0和P3.1为准双向口 */
        P3M0 &= ~0x03;
        P3M1 &= ~0x03;
       
        /* 配置USART1映射到P3.0和P3.1 */
        P_SW1 &= ~0xC0U;
       
        /* 配置USART1功能 */
        SM0 = 0;                                //数据位8位
        SM1 = 1;
        SMOD = 0;                                //波特率不加倍
        SMOD0 = 0;                                //禁用帧错误检测功能
        S1BRT = 1;                                //选择定时器2作为波特率发生器
        REN = 1;
        ES = 0;                                        //关闭中断
        EA = 0;                                        //全局中断关闭
       
        /* 波特率发生器配置 */
        T2R = 0;                                //TIM2停止计时
        ET2 = 0;                                //关闭TIM2中断
        TM2PS = 0;                                //TIM2 36M时钟
        T2x12 = 1;                                //TIM2工作1T模式
        T2_CT = 0;                                //TIM2用作定时器
        T2CLKO = 0;                                //关闭TIM2时钟输出
        T2L = 0xEE;                         //设置自动重载值
        T2H = 0xFF;
        T2R = 1;                                //TIM2开始计时
       
        /* 配置USART1 TX DMA */
        DMA_UR1T_CFG = 0x0A;       
        DMA_UR1T_CR |= 0x80;
        DMA_UR1T_STA = 0;
        DMA_UR1T_AMT = (23&0x00FF);
        DMA_UR1T_AMTH = ((23&0xFF00) >> 8);
        DMA_UR1T_TXAH = ((unsigned short)&Uart_TxPacket) >> 8;
        DMA_UR1T_TXAL = (((unsigned short)&Uart_TxPacket)&0xFF);

        /* 配置USART1 RX DMA */
        DMA_UR1R_CFG = 0x0F;
        DMA_UR1R_STA = 0;
    DMA_UR1R_AMT = (1023 & 0xFF);
        DMA_UR1R_AMTH = (1023>>8);
        // DMA_UR1R_AMT = (1023UL & 0xFF);
        // DMA_UR1R_AMTH = ((1023UL & 0xFF00) >> 8);
        DMA_UR1R_RXAH = (((unsigned short)&Uart_RxBuf) >> 8);
        DMA_UR1R_RXAL = (((unsigned short)&Uart_RxBuf)&0xFF);
        DMA_UR1R_CR |= 0xA1;
}

Shoteen 发表于 2024-11-25 14:43:23

补充一下处理代码,为啥会自动删除呀
void DMARx_Handler()
{
        unsigned short DONEH = DMA_UR1R_DONEH;
        unsigned short DONE = DMA_UR1R_DONE;
        const unsigned short defaultCounter = 1024;
       
        /* 处理DMA接收逻辑 */
        counter = ((DONEH << 8) + DONE);
        if(counter > lastcounter)
        {
                process_length = counter - lastcounter;
                rec_count += process_length;
        }
        else if(counter < lastcounter)
        {
                process_length = ((defaultCounter + counter) - lastcounter);
                rec_count += process_length;
        }
        else
        {
                process_length = 0;
        }
        lastcounter = counter;
}

Shoteen 发表于 2024-11-25 14:45:04

补充主函数代码
void main()
{
        HardwareInit();
        SoftwareInit();
      while(1)
        {
                DMARx_ISRHandler();
                DMARx_Handler();
        }
}

神农鼎 发表于 2024-11-25 16:51:54



【新提醒】4组串口UART使用DMA收发 @32G系列,易用,高效,稳定 !精品实战代码 - DMA支持: QSPI, SPI, I2S, I2C, 4组串口, i8080/MC6800-TFT 国芯技术交流网站 - AI32位8051交流社区 (stcaimcu.com)


用荣誉版主的完整程序 测试下
页: [1]
查看完整版本: 关于UART RX DMA使用方法的问题