当用UART来做高速大数据量传输时,需要结合DMA来使用,以便减轻CPU的负担,避免影响其他程序功能。

(Silabs官方开发板上的Jlink CDC串口(VCOM)默认只能支持115200的波特率,若要用其他波特率,需要在Jlink的admin终端里去修改。)

因为DMA一次传输一块数据(如100字节),但UART传输数据没有固定长度的帧,所以需要加入一个超时机制,每隔一段时间把收到的零散数据取出处理。EFR32系列的USART模块带有一个硬件定时器,可以用它来产生接收超时中断。不多解释了,上代码:

文件uart_dma.h:

#ifndef UART_DMA_H_

#define UART_DMA_H_

#include "em_device.h"

#include "em_chip.h"

#include "em_emu.h"

#include "em_cmu.h"

#include "em_gpio.h"

#include "em_usart.h"

#include "em_ldma.h"

#include "em_core.h"

#include <string.h>

#include <stdlib.h>

#define UART_BUF_LEN        100

#define UART_BAUDRATE       115200

typedef struct{

uint32_t timeout;

}TypeSoftTimer;

typedef struct {

GPIO_Port_TypeDef rxport;

GPIO_Port_TypeDef txport;

GPIO_Port_TypeDef ctsport;

GPIO_Port_TypeDef rtsport;

uint8_t rxpin;

uint8_t txpin;

uint8_t ctspin;

uint8_t rtspin;

}TypeUartPins;

typedef struct {

void* next;

uint8_t len;

uint8_t buf[];

}TypeBufChainNode;

typedef struct {

TypeBufChainNode* head;

TypeBufChainNode* tail;

}TypeBufChain;

typedef struct {

USART_TypeDef* const regs;

TypeUartPins const pins;

uint8_t const rx_ldma_channel;

uint8_t const tx_ldma_channel;

uint8_t rxbuf[2][UART_BUF_LEN];

uint8_t pingpong;

uint8_t rx_timeout_buf[UART_BUF_LEN];

uint8_t rxlen;

TypeBufChain txbufchain;

LDMA_Descriptor_t ldmaTXDescriptor;

LDMA_TransferCfg_t ldmaTXConfig;

LDMA_Descriptor_t ldmaRXDescriptor[2];

LDMA_TransferCfg_t ldmaRXConfig;

volatile bool rxdone, txdone, rx_timeout;

}TypeUartDma;

typedef void (*TypeUartCallback)(uint8_t* rxbuf, int len);

void UartDmaInit(TypeUartDma* uart);

void LdmaISR(TypeUartDma* uart, uint32_t flags);

void UartRxISR(TypeUartDma* uart);

void UartDmaRecv(TypeUartDma* uart, TypeUartCallback callback);

void UartDmaSend(TypeUartDma* uart, uint8_t* buf, uint8_t len);

#endif /* UART_DMA_H_ */

文件uart_dma.c:

#include "uart_dma.h"

static inline bool IsChainEmpty(TypeBufChain* chain)

{

return (0 == chain->head);

}

void ChainAddNode(TypeBufChain* chain, TypeBufChainNode* node)

{

CORE_irqState_t irqState = CORE_EnterCritical();

if(IsChainEmpty(chain))

{

chain->head = node;

chain->tail = node;

}

else

{

chain->tail->next = node;

chain->tail = node;

}

CORE_ExitCritical(irqState);

}

void ChainRemoveNode(TypeBufChain* chain)

{

void* next;

CORE_irqState_t irqState = CORE_EnterCritical();

if(! IsChainEmpty(chain))

{

next = chain->head->next;

free(chain->head);

chain->head = next;

}

CORE_ExitCritical(irqState);

}

TypeBufChainNode* ChainNodeCreate(uint8_t* buf, uint8_t len)

{

TypeBufChainNode* node;

do{

node = malloc(len + sizeof(TypeBufChainNode));

}while(0 == node);

node->next = 0;

node->len = len;

memcpy(node->buf, buf, len);

return node;

}

void InitLdma(TypeUartDma* uart)

{

LDMA_PeripheralSignal_t tx_trigger, rx_trigger;

// Enable clock (not needed on xG21)

CMU_ClockEnable(cmuClock_LDMA, true);

// First, initialize the LDMA unit itself

LDMA_Init_t ldmaInit = LDMA_INIT_DEFAULT;

LDMA_Init(&ldmaInit);

switch((int)uart->regs)

{

case (int)USART0:

tx_trigger = ldmaPeripheralSignal_USART1_TXBL;

rx_trigger = ldmaPeripheralSignal_USART1_RXDATAV;

break;

case (int)USART1:

tx_trigger = ldmaPeripheralSignal_USART1_TXBL;

rx_trigger = ldmaPeripheralSignal_USART1_RXDATAV;

break;

#ifdef USART2

case (int)USART2:

tx_trigger = ldmaPeripheralSignal_USART2_TXBL;

rx_trigger = ldmaPeripheralSignal_USART2_RXDATAV;

break;

#endif

#ifdef USART3

case (int)USART3:

tx_trigger = ldmaPeripheralSignal_USART3_TXBL;

rx_trigger = ldmaPeripheralSignal_USART3_RXDATAV;

break;

#endif

default:

__BKPT(0);

}

// Source is bufRx, destination is USART0_TXDATA, and length is UART_BUF_LEN

uart->ldmaTXDescriptor = (LDMA_Descriptor_t)LDMA_DESCRIPTOR_SINGLE_M2P_BYTE(uart->txbufchain.head->buf, &(uart->regs->TXDATA), 0);

// Transfer a byte on free space in the USART buffer

uart->ldmaTXConfig = (LDMA_TransferCfg_t)LDMA_TRANSFER_CFG_PERIPHERAL(tx_trigger);

// Source is USART0_RXDATA, destination is buffer, and length is UART_BUF_LEN

uart->ldmaRXDescriptor[0] = (LDMA_Descriptor_t)LDMA_DESCRIPTOR_LINKREL_P2M_BYTE(&(uart->regs->RXDATA), uart->rxbuf[0], UART_BUF_LEN, 1);

uart->ldmaRXDescriptor[1] = (LDMA_Descriptor_t)LDMA_DESCRIPTOR_LINKREL_P2M_BYTE(&(uart->regs->RXDATA), uart->rxbuf[1], UART_BUF_LEN, -1);

// Transfer a byte on receive data valid

uart->ldmaRXConfig = (LDMA_TransferCfg_t)LDMA_TRANSFER_CFG_PERIPHERAL(rx_trigger);

}

void LdmaISR(TypeUartDma* uart, uint32_t flags)

{

if (flags & (1 << uart->tx_ldma_channel))

{

ChainRemoveNode(&uart->txbufchain);

if(! IsChainEmpty(&uart->txbufchain))

{

uart->ldmaTXDescriptor.xfer.srcAddr = uart->txbufchain.head->buf;

uart->ldmaTXDescriptor.xfer.xferCnt = uart->txbufchain.head->len - 1;

LDMA_StartTransfer(uart->tx_ldma_channel, &uart->ldmaTXConfig, &uart->ldmaTXDescriptor);

}

else

{

uart->txdone = true;

}

}

if (flags & (1 << uart->rx_ldma_channel))

{

uart->rxdone = true;

}

}

void InitUart(TypeUartDma* uart)

{

// Default asynchronous initializer (115.2 Kbps, 8N1, no flow control)

USART_InitAsync_TypeDef init = USART_INITASYNC_DEFAULT;

int i=0, rxirq=0;

GPIO_PinModeSet(uart->pins.txport, uart->pins.txpin, gpioModePushPull, 0);

GPIO_PinModeSet(uart->pins.rxport, uart->pins.rxpin, gpioModeInput, 0);

switch((int)uart->regs)

{

case (int)USART0:

i = 0;

rxirq = USART0_RX_IRQn;

CMU_ClockEnable(cmuClock_USART0, true);

break;

case (int)USART1:

i = 1;

rxirq = USART1_RX_IRQn;

CMU_ClockEnable(cmuClock_USART1, true);

break;

#ifdef USART2

case (int)USART2:

i = 2;

rxirq = USART2_RX_IRQn;

CMU_ClockEnable(cmuClock_USART2, true);

break;

#endif

#ifdef USART3

case (int)USART3:

i = 3;

rxirq = USART3_RX_IRQn;

CMU_ClockEnable(cmuClock_USART3, true);

break;

#endif

default:

__BKPT(0);

}

// Route USART pins, respectively

GPIO->USARTROUTE[i].TXROUTE = (uart->pins.txport << _GPIO_USART_TXROUTE_PORT_SHIFT)

| (uart->pins.txpin << _GPIO_USART_TXROUTE_PIN_SHIFT);

GPIO->USARTROUTE[i].RXROUTE = (uart->pins.rxport << _GPIO_USART_RXROUTE_PORT_SHIFT)

| (uart->pins.rxpin << _GPIO_USART_RXROUTE_PIN_SHIFT);

// Enable RX and TX signals now that they have been routed

GPIO->USARTROUTE[i].ROUTEEN = GPIO_USART_ROUTEEN_RXPEN | GPIO_USART_ROUTEEN_TXPEN;

// Configure and enable USART

init.baudrate = UART_BAUDRATE;

USART_InitAsync(uart->regs, &init);

//使能USART的硬件定时器,用它来产生接收超时中断

uart->regs->TIMECMP0 = USART_TIMECMP0_TSTART_RXEOF | USART_TIMECMP0_TSTOP_RXACT | 9;

NVIC_ClearPendingIRQ(rxirq);

NVIC_EnableIRQ(rxirq);

USART_IntEnable(uart->regs, USART_IF_TCMP0);

}

void UartRxISR(TypeUartDma* uart)

{

int len;

LDMA_StopTransfer(uart->rx_ldma_channel);

len = UART_BUF_LEN - LDMA_TransferRemainingCount(uart->rx_ldma_channel);

memcpy(uart->rx_timeout_buf, uart->rxbuf[uart->pingpong], len);

LDMA_StartTransfer(uart->rx_ldma_channel, &uart->ldmaRXConfig, uart->ldmaRXDescriptor);

uart->pingpong = 0;

uart->rxlen = len;

uart->rx_timeout = 1;

// Clear the requesting interrupt before exiting the handler

USART_IntClear(uart->regs, USART_IF_TCMP0);

}

void UartDmaInit(TypeUartDma* uart)

{

InitUart(uart);

InitLdma(uart);

uart->pingpong = 0;

uart->rxdone = false;

uart->rx_timeout = false;

uart->txdone = true;

// Clear any garbage in the receive FIFO

uart->regs->CMD = USART_CMD_CLEARRX;

// Start RX channel

LDMA_StartTransfer(uart->rx_ldma_channel, &uart->ldmaRXConfig, uart->ldmaRXDescriptor);

}

void UartDmaRecv(TypeUartDma* uart, TypeUartCallback callback)

{

if(uart->rxdone == true)

{

// Set the receive state to not done

uart->rxdone = false;

callback(uart->rxbuf[uart->pingpong], UART_BUF_LEN);

uart->pingpong ^= 1;

}

if(uart->rx_timeout == true)

{

uart->rx_timeout = false;

callback(uart->rx_timeout_buf, uart->rxlen);

}

}

void UartDmaSend(TypeUartDma* uart, uint8_t* buf, uint8_t len)

{

TypeBufChainNode* node;

node = ChainNodeCreate(buf, len);

ChainAddNode(&uart->txbufchain, node);

if(uart->txdone)

{

uart->txdone = false;

uart->ldmaTXDescriptor.xfer.srcAddr = node->buf;

uart->ldmaTXDescriptor.xfer.xferCnt = len - 1;

LDMA_StartTransfer(uart->tx_ldma_channel, &uart->ldmaTXConfig, &uart->ldmaTXDescriptor);

}

}

文件main.c:

TypeUartDma uart1 = {

.regs = USART1,

.pins.rxport = gpioPortA,

.pins.rxpin = 6,

.pins.txport = gpioPortA,

.pins.txpin = 5,

.rx_ldma_channel = 0,

.tx_ldma_channel = 1,

};

void InitGpio(void)

{

CMU_ClockEnable(cmuClock_GPIO, true);

// Configure PB4 as output and set high (VCOM_ENABLE)

// comment out next line to disable VCOM, to use 921600 baud rate, IO via expansion header

//  GPIO_PinModeSet(gpioPortB, 4, gpioModePushPull, 1);

}

void Loopback(uint8_t* rxbuf, int len)

{

UartDmaSend(&uart1, rxbuf, len);

}

void LDMA_IRQHandler(void)

{

uint32_t flags = LDMA_IntGet();

LdmaISR(&uart1, flags);

LDMA_IntClear(flags);

}

void USART1_RX_IRQHandler(void)

{

UartRxISR(&uart1);

}

int main(void)

{

// Chip errata

CHIP_Init();

InitGpio();

UartDmaInit(&uart1);

while (1)

{

UartDmaRecv(&uart1, Loopback);

}

}

EFR32上用DMA实现UART高速数据传输相关推荐

  1. STM32单片机实现DMA+ADC+UART功能

    突然想测试一下STM32单片机ADC采样速率问题,按照常规方法,可以通过ADC采样,然后将采样值打印出来.但是这种方法在处理和打印数据的时候会占用很多时间,导致处理数据的时间超过了ADC的采样时间.于 ...

  2. 网络协议之:基于 UDP 的高速数据传输协议 UDT

    简介 简单就是美.在网络协议的世界中,TCP 和 UDP 是建立在 IP 协议基础上的两个非常通用的协议.我们现在经常使用的 HTTP 协议就是建立在 TCP 协议的基础上的.相当于 TCP 的稳定性 ...

  3. 网络协议之:基于UDP的高速数据传输协议UDT

    文章目录 简介 UDT协议 UDT的缺点 总结 简介 简单就是美.在网络协议的世界中,TCP和UDP是建立在IP协议基础上的两个非常通用的协议.我们现在经常使用的HTTP协议就是建立在TCP协议的基础 ...

  4. linux两台服务器传输,Linux两台服务器之间高速数据传输命令:scp应用详解

    Linux两台服务器之间高速数据传输命令:scp应用详解 Linux scp命令用于Linux之间复制文件和目录到另外一台,这个命令在多台服务器之间传输还是非常有用的,速度也是非常快的.比window ...

  5. 痞子衡嵌入式:实测i.MXRT1010上的普通GPIO与高速GPIO极限翻转频率

    大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是i.MXRT1010上的普通GPIO与高速GPIO极限翻转频率. 上一篇文章 <聊聊i.MXRT1xxx上的普通GPIO与高速GP ...

  6. 痞子衡嵌入式:再测i.MXRT1060,1170上的普通GPIO与高速GPIO极限翻转频率

    大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是i.MXRT1060/1170上的普通GPIO与高速GPIO极限翻转频率. 按照上一篇文章 <实测i.MXRT1010上的普通GP ...

  7. 雕刻机主轴电机上配带的刀具通过高速旋转

    雕刻机主轴电机上配带的刀具通过高速旋转,对固定于工作台面上的加工材料进行切削,即可雕刻出在计算机中设计的各种平面或立体的浮雕图形及文字,完成雕刻加工作业.数控雕刻机又称之为数控雕刻机,广泛应用于多个行 ...

  8. 镭速介绍关于高速数据传输!

    高效快速的沟通一直是我们的必需品.随着互联网的速度越来越快,网络和分组协议需要升级.通信的骨干一直是TCP和UDP协议.由于每个数据包确认的开销,TCP因可靠和慢速通信而闻名.UDP是防火和遗忘协议, ...

  9. 镭速关于高速数据传输的介绍!

    高效快速的沟通一直是我们的必需品.随着互联网的速度越来越快,网络和分组协议需要升级.通信的骨干一直是TCP和UDP协议.由于每个数据包确认的开销,TCP因可靠和慢速通信而闻名.UDP是防火和遗忘协议, ...

最新文章

  1. 【M17】考虑使用缓式评估
  2. 一个类作为另一个类的数据成员
  3. Centos R安装
  4. sybase不支持的条件表达式_包教包会!7段代码带你玩转Python条件语句(附代码)...
  5. 使用Python将字符串转换为格式化的日期时间字符串
  6. 使用Jenkins打包和部署Maven工程步骤详解
  7. 加载JDBC驱动程序
  8. 数组作为方法的参数实例和细节(Java)
  9. 使用阿里云镜像加速器为docker pull提速
  10. 福利:appium+selenium+python 模拟手工点击趣头条(app赚钱软件)
  11. Freeradius安装和配置
  12. 《激荡三十年》七、国企难破局—“裁缝神话”步鑫生
  13. python入门学习随记(十二)
  14. 42. 注入篇——Havij、Pangolin使用
  15. C#开发的OpenRA游戏高性能内存访问的方法
  16. 5分钟学会Python爬虫神器autoscraper——自动化爬虫必备
  17. Mininet系列实验(三):Mininet命令延伸实验扩展
  18. 前端图片加载闪烁问题
  19. The Linux Kernel Module Programming Guide 2.4 中文版
  20. Blog技巧,让Google把你的blog翻译成英文

热门文章

  1. candence16.6出现license 类似retrieval of allegro_pcb_design_gxl的问题
  2. Jason数据的访问
  3. 无套路,鬼灭之刃同人游戏
  4. oRbIt 的专栏 用天文方法计算二十四节气(下)
  5. 光盘刻录系列之二刻录光盘的程序步骤
  6. 【STM8】PWM 捕获实战:占空比和频率(TIM1)
  7. STM32L031 ADC管脚电压采样
  8. 选来选去,最终决定把家安在这里。现在看来还没有什么问题。
  9. java双冒号_jdk8新特性之双冒号 :: 用法及详解
  10. 深信服 应用交付报表系统 download.php 任意文件读取漏洞