当前位置: 首页 > news >正文

Uart 不定长收发的 DMA 方案

01 应用功能介绍

串口的通讯在不同应用上经常需要处理不同长度的收发报文需求,对于应用而言,由于发送和接收的长度未知,发送的不定长可通过配置 FIFO 空阈值和 DMA Burst 的方式实现,接收的不定长功能可通过配置 FIFO 空阈值、DMA Burst 和字符超时功能的方式实现。

综上所述,本文将分析实现不定长发送和不定长接收这两个功能。

02 发送不定长

发送不定长功能由三个对象主体构成,Uart TX 模块、DMA 触发器、用户 Memory 操作接口,结构如图 2.1 所示。

图片

图2.1 UART DMA 不定长发送功能框图

1串口配置

串口基本初始化如代码清单 2.1 所示,初始化内容包括 Uart 功能模式、奇偶校验功能、停止位配置、数据位配置、波特率配置、FIFO 阈值配置。其中注意发送 FIFO 空阈值的设置,与 DMA Burst 设置有密切关系,下文的 DMA 配置会讲。

代码清单 2.1 Uart 初始化

void User_UART1_Init(void)

{

LL_UART_MspInit();

UART1->CR0_b.UE = 0;

UART1->CR1 = 

                        (0x03 << UART1_CR1_LEN_Pos) |        //8bit 长度

                        (0x00 << UART1_CR1_STP_Pos) |        //1 个 stop 位

                        (0x00 << UART1_CR1_PEN_Pos);        //关闭奇偶校验

UART1->FIFOCTRL = (0xc << UART1_FIFOCTRL_TXFT_Pos) |        //发送 FIFO 空阈值

                                     (0x7 << UART1_FIFOCTRL_RXFT_Pos);        //接收 FIFO 满阈值

UART1->BAUD = SystemCoreClock/USER_UART_BAUDRATE;        //波特率

UART1->CR0 =

                        (1 << UART1_CR0_RFR_Pos) |              //RX FIFO 复位

                        (1 << UART1_CR0_TFR_Pos) |              //TX FIFO 复位

                        (0 << UART1_CR0_NFE_Pos) |             //使能 FIFO

                        (1 << UART1_CR0_DTE_Pos) |             //使能 TX DMA

                        (1 << UART1_CR0_UE_Pos) |               //使能 UART

                        (1 << UART1_CR0_RE_Pos) |               //使能 RX

                        (1 << UART1_CR0_TE_Pos) ;               //使能 TX

Uart_TxDMA_Config((uint32_t)&TXBuffer1, (uint32_t)&UART1->TDR, sizeof(TXBuffer1));   //配置RX DMA

}

Tx DMA 配置

DMA 配置如代码清单 2.2 所示,需要注意 DMA 传输模式和 DMA burst 的设置(标红部分),本案例传输模式为单次传输,使用连续传输会导致数据发送异常(因为是不定长发送,需每次数据填充完成后,将数据长度填入 DBL 寄存器)。

DMA Burst 配置:数据进入 UART Tx FIFO 后将由硬件自动搬出到 UART 的发送线上,所以一次填入 Tx FIFO 后,数据会不断减少,当剩余数据量到达设定的 UART TxFIFO 空阈值时,就可以产生一次 DMA 请求,此时将自动从内存里的 buf 搬运一次数据,即图 2.1中的用户层 buf(紫色)到 Tx Data 寄存器(蓝色)。

图片

图 2.2 UART TxFIFO 的 DMA 搬运结构

如图 2.2 所示,此时 UART Tx FIFO 内分为两部分,还未发送的 FIFO 出口段与空闲出的 FIFO 入口段。DMA 通道搬运到 FIFO 入口处的空闲段,所以每次 DMA 搬运的块大小要小于空闲段大小才能保证 Tx FIFO 不溢出。

例子:若 DMA burst 设置为每次搬运 4 个长度的数据,此时 UART FIFO 空阈值需设置为≤12,这样每次产生 DMA 请求,都能保证 FIFO 有足够的余量存 DMA 搬运过来的数据。

代码清单 2.2 DMA 配置

void Uart_TxDMA_Config(uint32_t src_addr, uint32_t dst_addr, uint16_t size)

{

DMA->CH[0].REG.CER = 0;                         //配置 DMA 先禁能对应通道

DMA->CH[0].REG.SAR = src_addr;             //配置 DMA 源地址

DMA->CH[0].REG.DAR = dst_addr;             //配置 DMA 目标地址

DMA->CH[0].REG.DBL = size;

DMA->CH[0].REG.CTR = (0x06 << DMA_CTR_PRI_Pos) |            //通道优先级为高优先级

                                         (0x00 << DMA_CTR_TM_Pos) |             //DMA 传输模式 单次传输

                                         (0x01 << DMA_CTR_TC_Pos) |              //DMA 传输类型 M2P

                                         (0x00 << DMA_CTR_SAI_Pos) |             //DMA 源地址递增

                                         (0x01 << DMA_CTR_DBL_Pos) |           //目标端 burst

                                         (0x01 << DMA_CTR_SBL_Pos) |           //源端 burst

                                         (0x01 << DMA_CTR_DAI_Pos) |            //DMA 目的地址不变

                                         (0x00 << DMA_CTR_SDS_Pos) |          //源端传输位宽 = byte

                                         (0x00 << DMA_CTR_DDS_Pos) |          //目的端传输位宽 = byte

                                         (0x06 << DMA_CTR_DHF_Pos) |          //目标端握手设置为 UART1_tX

                                         (0x00 << DMA_CTR_SMS_Pos) |

                                         (0x00 << DMA_CTR_DMS_Pos);

DMA->CH[0].REG.STR = 0x07;                    //清除标志位

}

代码清单 2.3 是 UART 中断相关的配置,本例程里不开启中断,后续根据应用开发可选择适当开启。

代码清单 2.3 中断配置

//UART ISR

UART0->IER.bit.TXDONEIE    = 0;        //UART TX Done Interrupt Enable

UART0->IER.bit.RXDATTOIE  = 0;        //UART RX Data Timeout Interrupt Enable

UART0->IER.bit.BREAKIE       = 0;        //UART Break Interrupt Enable

UART0->IER.bit.FEIE              = 0;        //UART Frame Error Interrupt Enable

UART0->IER.bit.PEIE              = 0;        //UART Parity Error Interrupt Enable

UART0->IER.bit.RXOVEIE      = 0;        //RXFIFO Overflow Interrupt Enable

UART0->IER.bit.TXEMPTYIE  = 0;        //TXFIFO Empty Interrupt Enable

UART0->IER.bit.RXFULLIE     = 0;        //RXFIFO Full Interrupt Enable

NVIC_EnableIRQ(UART0_IRQn);

NVIC_SetPriority(UART0_IRQn, 1);

3  不定长发送代码处理

不定长发送实现代码如代码清单 2.4 所示,应用流程图如图 2.2 所示。

图片

图 2.3 不定长发送应用框图

向 TXbuffer 填充数据,填充完成后,配置填充数据的长度,使能 DMA 传输(在 DMA配置内不要使能 DMA 传输),等待发送完成标志位置位,再进行下一次填入。通过此方案即可实现不定长发送功能。

代码清单 2.4 发送不定长函数

void test(void)

{

for(uint8_t i = 0;i<20;i++)                      //数据填充

{

        TXBuffer1[i] = i + 1;

}

DMA->CH[0].REG.DBL = 20;              //每次填充数据的长度

DMA->CH[0].REG.CER = 1;               //使能 DMA 传输

while(UART1->INT_b.TDIF == 0)        //等待发送完成标志

;

UART1->INT_b.TDIF = 1;

memset(TXBuffer1, 0, sizeof(TXBuffer1));

}

03 接收不定长

发送不定长功能由三个对象主体构成,Uart RX 模块、DMA 触发器、用户 Memory 操作接口,结构如图 3.1 所示。

图片

图 3.1 UART DMA 不定长接收功能框图

不定长接收实现如代码清单 3.3 所示,处理框图如图 3.2 所示。DMA 搬运完成后,若此时 FIFO 剩余数据不满足 DMA 搬运条件,则字符超时,通过 CPU 读取 FIFO 内剩余数据。

1串口配置

串口基本初始化如代码清单 3.1 所示,初始化内容包括 Uart 功能模式、奇偶校验功能、停止位配置、数据位配置、波特率配置、FIFO 阈值配置。其中注意发送 FIFO 空阈值的设置,与 DMA Burst 设置有密切关系,下文的 DMA 配置会讲。

代码清单 3.1 Uart 初始化

void User_UART1_Init(void)

{

LL_UART_MspInit();

UART1->CR0_b.UE = 0;

UART1->CR1 =

                        (0x03 << UART1_CR1_LEN_Pos) |                            //8bit 长度

                        (0x00 << UART1_CR1_STP_Pos) |                            //1 个 stop 位

                        (0x00 << UART1_CR1_PEN_Pos);                            //关闭奇偶校验

UART1->FIFOCTRL = (0xc << UART1_FIFOCTRL_TXFT_Pos) |            //发送 FIFO 空阈值

                                    (0x7 << UART1_FIFOCTRL_RXFT_Pos);             //接收 FIFO 满阈值

UART1->BAUD = SystemCoreClock/USER_UART_BAUDRATE;            //波特率

UART1->CR0 =

                        (1 << UART1_CR0_RFR_Pos) |                                //RX FIFO 复位

                        (1 << UART1_CR0_TFR_Pos) |                                //TX FIFO 复位

                        (0 << UART1_CR0_NFE_Pos) |                               //使能 FIFO

                        (1 << UART1_CR0_DRE_Pos) |                               //使能 RX DMA

                        (1 << UART1_CR0_UE_Pos) |                                 //使能 UART

                        (1 << UART1_CR0_RE_Pos) |                                 //使能 RX

                        (1 << UART1_CR0_TE_Pos) ;                                 //使能 TX

Uart_RxDMA_Config((uint32_t)&UART1->RDR,(uint32_t)&RXBuffer1, sizeof(RXBuffer1));     //配置 RX DMA

}

2• Rx DMA 配置

DMA 配置如代码清单 3.2 所示。

DMA Burst 和 FIFO 满阈值配置:数据进入 UART Rx FIFO 后将由 DMA 搬进内存,所以一次填入 Rx FIFO 后,数据会不断增多,当剩余数据量到达设定的 UART RxFIFO 满阈值时,就可以产生一次 DMA 请求,此时将自动从 FIFO 向 buf 搬运一次数据,即图 3.1中的 Rx Data 寄存器(蓝色)到用户层 buf(紫色)。

UART FIFO 满阈值和 DMA burst 需要设置为合适的值(FIFO 满阈值>DMA Burst,保证每次接收数据够进入超时处理),避免数据接收异常(FIFO 满阈值=DMA Burst 时,无法触发接收超时,无法进入超时处理复位 DMA)。目前版本芯片,由于 DMA 启动传输后无法停止(无法修改 DMA 传输总长度),因此每次接收完一帧数据后,需重新复位 DMA。

例子:若 DMA burst 设置为每次搬运 4 个长度的数据,此时 UART FIFO 满阈值需设置为 8,这样每次有 8 个数据的时候,都能保证 DMA 能搬运数据,且保证每次都能置位字符超时标志位,实现每一帧数据接收完成后复位 DMA。

代码清单 3.2 DMA 配置

void Uart_TxDMA_Config(uint32_t src_addr, uint32_t dst_addr, uint16_t size)

{

DMA->CH[0].REG.CER = 0;                               //配置 DMA 先禁能对应通道

DMA->CH[0].REG.SAR = src_addr;                   //配置 DMA 源地址

DMA->CH[0].REG.DAR = dst_addr;                   //配置 DMA 目标地址

DMA->CH[0].REG.DBL = size;

DMA->CH[0].REG.CTR = (0x06 << DMA_CTR_PRI_Pos) |      //通道优先级为高优先级

                                        (0x01 << DMA_CTR_TM_Pos) |        //DMA 传输模式 连续传输

                                        (0x01 << DMA_CTR_TC_Pos) |        //DMA 传输类型 M2P

                                        (0x01 << DMA_CTR_SAI_Pos) |       //DMA 源地址不变

                                        (0x00 << DMA_CTR_DBL_Pos) |      //目标端 burst

                                        (0x00 << DMA_CTR_SBL_Pos) |      //源端 burst

                                        (0x01 << DMA_CTR_DAI_Pos) |       //DMA 目的地址递增

                                        (0x00 << DMA_CTR_SDS_Pos) |     //源端传输位宽 = byte

                                        (0x00 << DMA_CTR_DDS_Pos) |     //目的端传输位宽 = byte

                                        (0x07 << DMA_CTR_DHF_Pos) |     //目标端握手设置为 UART1_RX

                                        (0x00 << DMA_CTR_SMS_Pos) |

                                        (0x00 << DMA_CTR_DMS_Pos);

UART1->CR0 |= UART1_CR0_RTOE_Msk;                         //使能 RX 超时功能

UART1->RTO = 39;                                                               //配置 RX 超时时间

DMA->CH[0].REG.STR = 0x07;                        //清除标志位

DMA->CH[0].REG.CER = 1;                              //使能 DMA 传输

}

3接收不定长代码处理

不定长发送实现代码如代码清单 3.3 所示,应用流程图如图 3.2 所示。

图片

图 3.2 不定长接收框图

接收数据后,判断是否字符超时,超时则进入超时处理,清除超时标志位,获取当前DMA 传输长度,通过 CPU 读取 FIFO 内剩余数量,复位并重新配置 DMA。通过此方案即可实现不定长发送功能。

代码清单 3.3 接收不定长函数

Void Uart1_Read() {

uint16_t recvLen,temp,DBL_Len;

DBL_Len = sizeof(RXBuffer1);                              //DMA 搬运总长度

if(UART1->INT & UART1_INT_RTOI_Msk){         //利用字符超时标志接收不定长数据

        UART1->INT_b.RTOI = 1;                            //清除超时标志位

        recvLen =(DBL_Len - DMA->CH[0].REG.DBL);         //获取当前 DMA 搬运的数据长度

        temp = UART1->STATUS_b.RFL;                              //获取 DMA 搬运完 FIFO 剩余数据长度

        for(uint8_t i = 0; i < temp; i++)                                    //CPU 读取 FIFO 剩余数据到 buf

        {

                RXBuffer1[recvLen + i] = UART1->RDR;

        }

        RCU->KEYR = 0x3FAC87E4U;

        RCU->AHB0RSTR_b.DMARST = 0;                          //复位 DMA

        __NOP();__NOP();__NOP();__NOP();__NOP();

        RCU->AHB0RSTR_b.DMARST = 1;

        RCU->KEYR = 1;

        Uart_RxDMA_Config((uint32_t)&UART1->RDR,(uint32_t)&RXBuffer1, sizeof(RXBuffer1));     //重新配置 DMA

}

}

04FAQ

问 TX FIFO 的剩余数据小于 DMA 的一次搬运数据量的时候,是怎么处理?

答 该功能的 DMA 传输数据长度(DBL)与填充数据长度大小一致,DMA 每次搬运后,会递减相应的长度。当发送区数据小于 DMA 设置的 Burst,这时 DMA 传输数据长度(DBL)也小于 DMA Burst 长度,此时 DMA 内部会采用 Single 形式搬运,即一次搬一个数据。

问:DMA 发送请求如何生成?

答:当前 TX FIFO 数据小于等于设置的 TX FIFO 空阈值时,会生成 DMA 请求。

问:DMA 接收请求如何生成?

答:当前 RX FIFO 数据大于等于设置的 RX FIFO 满阈值时,会生成 DMA 请求。

问:超时检测在接收数据的意义?

答:由于不定长接收的数据长度未知,若此时 RX FIFO 内数据小于满阈值,未触发DMA 搬运,需要使用超时检测将剩余数据通过 CPU 读出。

问:为什么接收不会出现发送区的剩余数据小于 DMA 的一次搬运数据量,导致 DMA采用 Singal 的方式搬运?

答:发送可知长度,在填充数据后可直接将数据长度写入 DMA DBL,但接收无法知道数据长度,用户层一般把接收 DMA 的 DBL 设置为一个较大的值,且每次接收完成后会复位 DMA,不会出现 RX FIFO 的剩余数据小于 DMA 的一次搬运数据量。

http://www.dtcms.com/a/606187.html

相关文章:

  • 普宁市建设局网站金石文化规划 设计 开发风景区网站建设
  • 沈阳优化网站关键词wordpress 4.8正式版
  • 如何做切片网站广告设计公司vi
  • thinkPHP6.1使用PhpMqtt进行MQTT消息订阅和发布,并将订阅的消息入库保存,可控制超时退出订阅
  • 做网站作业什么主题商机网wordpress模板
  • Xcode的App Thinning Size Report分析包体积
  • 多机多卡训练指南
  • 深入浅出:进程和线程的区别与联系
  • 做一个静态网站需要多少钱关键词
  • 西安网站建设方案外包浏览器网页版打开网页
  • 【Redis】 SpringBoot集成Redis
  • 网易企业邮箱邮箱登录入口江西网站建设优化服务
  • 汕头吧 百度贴吧超级优化小说
  • 视觉学习篇——机器学习模型评价指标
  • Java Agent 和字节码注入技术原理和实现
  • Java后端常用技术选型 |(五)可视化工具篇
  • 【数据库】Apache IoTDB数据库在大数据场景下的时序数据模型与建模方案
  • 网站建设系统课程广东建设网 四川是什么网站
  • 不止于 API 调用:解锁 Java 工具类设计的三重境界 —— 可复用性、线程安全与性能优化
  • 数据结构与算法:树(Tree)精讲
  • AI入门系列之GraphRAG使用指南:从环境搭建到实战应用
  • 【SolidWorks】默认模板设置
  • 基于秩极小化的压缩感知图像重建的MATLAB实现
  • 无人机图传模块技术要点与难点
  • Spring Cloud Alibaba 2025.0.0 整合 ELK 实现日志
  • AI+虚拟仿真:开启无人机农林应用人才培养新路径
  • ELK 9.2.0 安装部署手册
  • 代码统计网站wordpress设置在新页面打开空白
  • 网站开发的流程 知乎设计培训网站建设
  • Qt 的字节序转换