stm32F4 串口DMA+环形缓冲区的实现

本文介绍了如何在STM32F4上实现串口DMA结合环形缓冲区的通信方式,以降低中断次数,提高高速波特率下的效率。程序在串口中断基础上设计,使用环形缓冲区作为一级缓存,DMA作为二级缓存。 DMA发送时,将环形缓存数据复制到固定大小的DMA缓冲区进行发送;接收端则涉及两个中断服务程序,处理可能不为16字节倍数的数据,并利用空闲中断确保数据完整提取。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

下面是串口DMA+环形缓冲区的实现,数据收发是异步的,不需要死等。
关于环形缓冲区参考:

http://blog.youkuaiyun.com/jieffantfyan/article/details/53572103

实现原理

程序是在串口中断收发方式的基础上设计的,应用层通过环形缓冲区进行串口数据读取,环形缓冲区作为一级缓存,增加DMA作为二级缓存。相对中断方式这种设计可以减少串口进入中断的次数,尤其是在高速波特率的情况下。由于使用DMA收发数据时,必须预设好发送/接收地址、长度等信息,软件内为DMA开辟了16个字节数组作为缓冲区。当发送环形缓存区内有数据需要发送时,程序将前16字节(如果不足则复制实际长度并重新设置DMA发送长度)复制到DMA发送缓冲区内并启动发送,数据发送完成之后将余下的数据按同样的方法复制到DMA发送缓冲区直至数据发送完成。而对于接收来说,处理比发送麻烦一些,需要两个中断服务程序配合,当DMA接收完成16字节之后,软件将这些数据写入到接收环形缓冲区内,以便应用程序读取。因为软件中将DMA接收缓存长度设置为16字节,即DMA必须连续接收到16字节才会进入中断服务程序,但多数情况下接收到的数据长度不可能全是16字节的倍数,比如MCU只收到10字节之后的一段时间内再也收不到数据了。对于这种情况可以使用定时器配合检测,一旦发现长时间收不到串口的数据则将DMA接收缓冲区的数据全部提取出来。由于STM32单片机提供了空闲中断,我们可以利用这个机制解决上述问题,当产生空闲中断时,意味着已经没有数据接收了,这时候则将DMA接收缓冲内的数据提取出来。

外部接口声明

下面将串口的初始化、读、写接口抽象出来。

/******************************************************************************
 * Copyright (C) 2016, roger
 * All rights reserved.
 *
 * 文件名称: tty.h
 * 摘    要:控制台驱动

 *             
 * 当前版本: 3.0
 * 作    者: roger
 * 完成日期: 2016-09-24
 *             
 * 取代版本: 2.0
 * 原作者  : roger
 * 完成日期: 2015-07-08
 ******************************************************************************/


#ifndef _TTY_H_
#define _TTY_H_

#define TTY_BAUDRATE          115200                    /*波特率 ------------*/
#define TTY_TXBUF_SIZE        256                       /*发送缓冲区长度 -----*/
#define TTY_RXBUF_SIZE        256                       /*接收缓冲区长度 -----*/
#define TTY_DMA_TX_LEN        10                        /*DMA 发送缓冲区 ----*/
#define TTY_DMA_RX_LEN        10                        /*DMA 接收缓冲区 ----*/

#define TTY_USE_DMA           1                         /*启用DMA -----------*/


/* Exported Structs ---------------------------------------------------------*/

typedef struct          
{
    void (*init)(void);                                     /*初始化 --------*/    
    unsigned int (*write)(void *buf, unsigned int len);     /*数据写 --------*/
    unsigned int (*read) (void *buf, unsigned int len);     /*读数据 --------*/
    void (*puts)(const char *str);                          /*输入一个字符串 */
    void (*clr)(void);                                      /*清除接收缓冲区 */
    unsigned int (*buflen)(void);                           /*接收缓冲区的长度*/
    void (*printf)(const char *format, ...);                /*格式化打印 ----*/
}tty_t;


/* Exported variables ------------------------------------------------------- */
extern const tty_t tty;


#endif  
接口实现 ##‘
/******************************************************************************
 * Copyright (C) 2016, roger
 * All rights reserved.
 *
 * 文件名称: tty.c
 * 摘    要:打印串口驱动

 *             
 * 当前版本: 3.0
 * 作    者: roger
 * 完成日期: 2016-09-24
 *             
 * 取代版本: 2.0
 * 原作者  : roger
 * 完成日期: 2015-07-08
 ******************************************************************************/

/* Includes ------------------------------------------------------------------*/
#include "tty.h"
#include "ringbuffer.h"
#include "stm32f4xx.h"
#include <stdarg.h>
#include <stdio.h>
#include <string.h>

static unsigned char rxbuf[TTY_TXBUF_SIZE];         /*接收缓冲区 ------------*/
static unsigned char txbuf[TTY_RXBUF_SIZE];         /*发送缓冲区 ------------*/
static ring_buf_t ringbuf_send, ringbuf_recv;       /*收发缓冲区管理 ---------*/

#if TTY_USE_DMA == 1
    static unsigned char dma_tx_buf[TTY_DMA_TX_LEN];/*DMA发送缓冲区 ---------*/
    static unsigned char dma_rx_buf[TTY_DMA_RX_LEN];/*DMA接收缓冲区 ---------*/      
#endif

/*******************************************************************************
 * 函数名称:port_conf
 * 功能描述:打印串口配置(PD8->USART3_TX, PD9->USART3_RX)
 * 输入参数:none
 * 返 回 值:none
 * 作    者:roger.luo
 *****
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值