EFR32上用DMA实现UART高速数据传输
当用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高速数据传输相关推荐
- STM32单片机实现DMA+ADC+UART功能
突然想测试一下STM32单片机ADC采样速率问题,按照常规方法,可以通过ADC采样,然后将采样值打印出来.但是这种方法在处理和打印数据的时候会占用很多时间,导致处理数据的时间超过了ADC的采样时间.于 ...
- 网络协议之:基于 UDP 的高速数据传输协议 UDT
简介 简单就是美.在网络协议的世界中,TCP 和 UDP 是建立在 IP 协议基础上的两个非常通用的协议.我们现在经常使用的 HTTP 协议就是建立在 TCP 协议的基础上的.相当于 TCP 的稳定性 ...
- 网络协议之:基于UDP的高速数据传输协议UDT
文章目录 简介 UDT协议 UDT的缺点 总结 简介 简单就是美.在网络协议的世界中,TCP和UDP是建立在IP协议基础上的两个非常通用的协议.我们现在经常使用的HTTP协议就是建立在TCP协议的基础 ...
- linux两台服务器传输,Linux两台服务器之间高速数据传输命令:scp应用详解
Linux两台服务器之间高速数据传输命令:scp应用详解 Linux scp命令用于Linux之间复制文件和目录到另外一台,这个命令在多台服务器之间传输还是非常有用的,速度也是非常快的.比window ...
- 痞子衡嵌入式:实测i.MXRT1010上的普通GPIO与高速GPIO极限翻转频率
大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是i.MXRT1010上的普通GPIO与高速GPIO极限翻转频率. 上一篇文章 <聊聊i.MXRT1xxx上的普通GPIO与高速GP ...
- 痞子衡嵌入式:再测i.MXRT1060,1170上的普通GPIO与高速GPIO极限翻转频率
大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是i.MXRT1060/1170上的普通GPIO与高速GPIO极限翻转频率. 按照上一篇文章 <实测i.MXRT1010上的普通GP ...
- 雕刻机主轴电机上配带的刀具通过高速旋转
雕刻机主轴电机上配带的刀具通过高速旋转,对固定于工作台面上的加工材料进行切削,即可雕刻出在计算机中设计的各种平面或立体的浮雕图形及文字,完成雕刻加工作业.数控雕刻机又称之为数控雕刻机,广泛应用于多个行 ...
- 镭速介绍关于高速数据传输!
高效快速的沟通一直是我们的必需品.随着互联网的速度越来越快,网络和分组协议需要升级.通信的骨干一直是TCP和UDP协议.由于每个数据包确认的开销,TCP因可靠和慢速通信而闻名.UDP是防火和遗忘协议, ...
- 镭速关于高速数据传输的介绍!
高效快速的沟通一直是我们的必需品.随着互联网的速度越来越快,网络和分组协议需要升级.通信的骨干一直是TCP和UDP协议.由于每个数据包确认的开销,TCP因可靠和慢速通信而闻名.UDP是防火和遗忘协议, ...
最新文章
- 【M17】考虑使用缓式评估
- 一个类作为另一个类的数据成员
- Centos R安装
- sybase不支持的条件表达式_包教包会!7段代码带你玩转Python条件语句(附代码)...
- 使用Python将字符串转换为格式化的日期时间字符串
- 使用Jenkins打包和部署Maven工程步骤详解
- 加载JDBC驱动程序
- 数组作为方法的参数实例和细节(Java)
- 使用阿里云镜像加速器为docker pull提速
- 福利:appium+selenium+python 模拟手工点击趣头条(app赚钱软件)
- Freeradius安装和配置
- 《激荡三十年》七、国企难破局—“裁缝神话”步鑫生
- python入门学习随记(十二)
- 42.	注入篇——Havij、Pangolin使用
- C#开发的OpenRA游戏高性能内存访问的方法
- 5分钟学会Python爬虫神器autoscraper——自动化爬虫必备
- Mininet系列实验(三):Mininet命令延伸实验扩展
- 前端图片加载闪烁问题
- The Linux Kernel Module Programming Guide 2.4 中文版
- Blog技巧,让Google把你的blog翻译成英文
热门文章
- candence16.6出现license 类似retrieval of allegro_pcb_design_gxl的问题
- Jason数据的访问
- 无套路,鬼灭之刃同人游戏
- oRbIt 的专栏 用天文方法计算二十四节气(下)
- 光盘刻录系列之二刻录光盘的程序步骤
- 【STM8】PWM 捕获实战:占空比和频率(TIM1)
- STM32L031 ADC管脚电压采样
- 选来选去,最终决定把家安在这里。现在看来还没有什么问题。
- java双冒号_jdk8新特性之双冒号 :: 用法及详解
- 深信服 应用交付报表系统 download.php 任意文件读取漏洞