打印
[STM32F4]

原创 STM32F4 DMA串口收发驱动

[复制链接]
5161|22
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
gowow|  楼主 | 2017-3-20 10:18 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
本帖最后由 gowow 于 2017-3-20 17:16 编辑

谢绝amobbs转载!

收发全用DMA实现
发送先丢进FIFO,然后触发DMA发送到FIFO尾,再从FIFO头发送
接收直接使用循环DMA,用DMA指针去确认是否有数据到达

已测试过DMA1的UART2/3/4/5,但是UART1有问题,应该是和DMA2有关的UART未调好,哪位有闲就改进一下吧

串口定义样例 注意FIFO大小必须为2的幂,如256 512 1024
//PC10  TX4                                DMA1.4.4
//PC11  RX4                                DMA1.2.4
DMA_TX_IRQH_INIT_FUNC(UART4, 1,4,4);
DMA_RX_INIT_FUNC(UART4, 1,2,4);
UART_INIT(uart_xxx, UART4, GPIOC, 11, 1024, GPIOC, 10, 1024, 1);
调用样例

uart_xxx->init(57600);
uart_xxx->read(............


头文件
#ifndef _UART_DMA_H_
#define _UART_DMA_H_

typedef const struct
{
  u32 (*write_space)();
  u32 (*read_valid_len)();
  bool (*read)(u8 *ptr);
  u32 (*reads)(u8 *ptr, u32 len);
  bool (*write)(u8 data);
  u32 (*writes)(const u8 *ptr, u32 len);
  void (*init)(u32 bps);
}UART_OPS;

extern const UART_OPS *uart_xxx;
extern const UART_OPS *uart_yyy;
extern const UART_OPS *uart_zzz;
#endif


C文件
typedef struct _fifo
{
  u32 size;    /* the size of the allocated buffer */
  u32 in;    /* data is added at offset (in % size) */
  u32 out;    /* data is extracted from off. (out % size) */
  u8 *buffer;    /* the buffer holding the data */
}FIFO;

uint32_t is2n(uint32_t un)
{
  return un&(un-1);
}

uint32_t max2n(uint32_t un)
{
  //下面的函数返回不大于un的2的最大幂
  uint32_t mi = is2n(un);
  return mi?max2n(mi):un;
}

static void fifo_init(FIFO *fifo, u8 *buff, u32 size)
{
  if (!is2n(size))
  {
    size = max2n(size);
  }

  fifo->size = size;
  fifo->in = fifo->out = 0;
  fifo->buffer = buff;
}

static u32 kfifo_put(FIFO *fifo, const u8 *buffer, u32 len)
{
  u32 l;

  len = min(len, fifo->size - fifo->in + fifo->out);                //剩余空间
  l = min(len, fifo->size - (fifo->in & (fifo->size - 1)));         //从in到fifo尾剩余空间

  memcpy(fifo->buffer + (fifo->in & (fifo->size - 1)), buffer, l);
  memcpy(fifo->buffer, buffer + l, len - l);

  fifo->in += len;

  return len;
}

static u32 kfifo_get(FIFO *fifo, u8 *buffer, u32 len)
{
  u32 l;

  len = min(len, fifo->in - fifo->out);                           //数据长度
  l = min(len, fifo->size - (fifo->out & (fifo->size - 1)));      //out到fifo尾数据长度

  memcpy(buffer, fifo->buffer + (fifo->out & (fifo->size - 1)), l);   //先拷队尾
  memcpy(buffer + l, fifo->buffer, len - l);                            //再拷前面

  fifo->out += len;

  return len;
}

//tx fifo
static u32 txfifo_put(FIFO *fifo, const u8 *buffer, u32 len)
{
  u32 l;

  len = min(len, fifo->size - fifo->in + fifo->out);
  l = min(len, fifo->size - (fifo->in & (fifo->size - 1)));

  memcpy(fifo->buffer + (fifo->in & (fifo->size - 1)), buffer, l);
  memcpy(fifo->buffer, buffer + l, len - l);

  fifo->in += len;

  return len;
}

static u32 txfifo_dma_get_valid(FIFO *fifo, u8 **pptr, u32 *plen)
{

  u32 len;
  len = min((fifo->in - fifo->out),
            fifo->size - (fifo->out & (fifo->size - 1)));

  *pptr = fifo->buffer + (fifo->out & (fifo->size - 1));
  *plen = len;

  fifo->out += len;

  return len;
}

static u32 txfifo_write_space(FIFO *fifo)
{
  s32 valid_len;
  valid_len = (fifo->in - fifo->out);
  if (valid_len >= 0)
    return fifo->size - valid_len;
  else
    return fifo->size;
}

//rx fifo
static u32 rxfifo_get_valid_len(FIFO *fifo, u32 dma_ndtr)
{
  s32 valid_len;
  valid_len = (fifo->size - dma_ndtr) - (fifo->out & (fifo->size - 1));
  if (valid_len < 0)
    valid_len += fifo->size;
  return valid_len;
}

static u32 rxfifo_get(FIFO *fifo, u32 dma_ndtr, u8 *buffer, u32 len)
{
  u32 l;

  len = min(len, rxfifo_get_valid_len(fifo, dma_ndtr));
  l = min(len, fifo->size - (fifo->out & (fifo->size - 1)));

  memcpy(buffer, fifo->buffer + (fifo->out & (fifo->size - 1)), l);
  memcpy(buffer + l, fifo->buffer, len - l);
  fifo->out += len;

  return len;
}

typedef  struct _UART_CONTEXT{
  USART_TypeDef *uart_type;
  FIFO *fifo_rx;
  FIFO *fifo_tx;
  DMA_Stream_TypeDef *dma_tx;
        DMA_Stream_TypeDef *dma_rx;
}UART_CONTEXT;

static u32 uart_write_space(UART_CONTEXT *context)
{
  return txfifo_write_space(context->fifo_tx);
}

static u32 uart_read_valid_len(UART_CONTEXT *context)
{
  return rxfifo_get_valid_len(context->fifo_rx, context->dma_rx->NDTR);
}

static bool uart_read(UART_CONTEXT *context, u8 *ptr)
{
  return (1 == rxfifo_get(context->fifo_rx, context->dma_rx->NDTR, ptr, 1));
}

static u32 uart_reads(UART_CONTEXT *context, u8 *ptr, u32 len)
{
  return rxfifo_get(context->fifo_rx, context->dma_rx->NDTR, ptr, len);
}

static void uart_tx_try_start_dma(UART_CONTEXT *context, DMA_Stream_TypeDef *dma_stream_tx)
{
  u8 *memptr;
  u32 len;

  txfifo_dma_get_valid(context->fifo_tx, &memptr, &len);

  if ((len > 0) && ((dma_stream_tx->CR & (uint32_t)DMA_SxCR_EN) == 0))
  {
    dma_stream_tx->M0AR = (u32)memptr;
    dma_stream_tx->NDTR = len;
    dma_stream_tx->CR |= (uint32_t)DMA_SxCR_EN;
  }
}

static bool uart_write(UART_CONTEXT *context, u8 data)
{
  bool result;
  result = (1 == txfifo_put(context->fifo_tx, &data, 1));
  uart_tx_try_start_dma(context, context->dma_tx);
  return result;
}

static u32 uart_writes(UART_CONTEXT *context, const u8 *ptr, u32 len)
{
  u32 txed_len;
  txed_len = txfifo_put(context->fifo_tx, ptr, len);
  uart_tx_try_start_dma(context, context->dma_tx);
  return txed_len;
}

static void uart_txdma_tc_callback(UART_CONTEXT *context, USART_TypeDef *uart_type, DMA_Stream_TypeDef *dma_stream_tx)
{
  u8 *memptr;
  u32 len;

  txfifo_dma_get_valid(context->fifo_tx, &memptr, &len);

  if (len > 0)
  {
    dma_stream_tx->M0AR = (u32)memptr;
    dma_stream_tx->NDTR = len;
    dma_stream_tx->CR |= (uint32_t)DMA_SxCR_EN;
  }
}

#define DMA_TX_IRQH_INIT_FUNC(uart, dma, stream, channel)  \
  void DMA##dma##_Stream##stream##_IRQHandler(void) \
  { \
    extern UART_CONTEXT struct_##uart; \
    if (DMA_GetFlagStatus(DMA##dma##_Stream##stream, DMA_FLAG_TCIF##stream) != RESET) \
    { \
      DMA_ClearFlag(DMA##dma##_Stream##stream, DMA_FLAG_TCIF##stream); \
      uart_txdma_tc_callback(&struct_##uart, uart, DMA##dma##_Stream##stream); \
    } \
  } \
    \
  static void uart##_TXDMA_init(u32 memory_addr, u32 memory_size, u8 priority)  \
  { \
    extern UART_CONTEXT struct_##uart; \
    NVIC_InitTypeDef NVIC_InitStructure;  \
    DMA_InitTypeDef DMA_InitStructure;  \
    \
    struct_##uart.dma_tx = DMA##dma##_Stream##stream; \
    \
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA##dma, ENABLE);  \
    \
    DMA_Cmd(DMA##dma##_Stream##stream, DISABLE);  \
    \
    DMA_DeInit(DMA##dma##_Stream##stream);  \
    \
    DMA_InitStructure.DMA_Channel = DMA_Channel_##channel;  \
    DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)(&(uart##->DR)); \
    DMA_InitStructure.DMA_Memory0BaseAddr = memory_addr;  \
    DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral;  \
    DMA_InitStructure.DMA_BufferSize = (uint32_t)memory_size;  \
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;  \
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;  \
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;  \
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;  \
    DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;  \
    DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;  \
    DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Enable;  \
    DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;  \
    DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;  \
    DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;  \
    DMA_Init(DMA##dma##_Stream##stream, &DMA_InitStructure);  \
    \
    DMA_ClearFlag(DMA##dma##_Stream##stream, DMA_FLAG_TCIF##stream); \
    DMA_ITConfig(DMA##dma##_Stream##stream, DMA_IT_TC, ENABLE);  \
    \
    NVIC_InitStructure.NVIC_IRQChannel = DMA##dma##_Stream##stream##_IRQn;  \
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = priority >> 4;  \
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = priority & 0x0F;  \
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;  \
    NVIC_Init(&NVIC_InitStructure);  \
    \
    DMA_Cmd(DMA##dma##_Stream##stream, DISABLE);  \
  }

#define DMA_RX_INIT_FUNC(uart, dma, stream, channel)  \
  static void uart##_RXDMA_init(u32 memory_addr, u32 memory_size)  \
  { \
    extern UART_CONTEXT struct_##uart; \
    DMA_InitTypeDef DMA_InitStructure;  \
    \
    struct_##uart.dma_rx = DMA##dma##_Stream##stream; \
    \
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA##dma, ENABLE);  \
    \
    DMA_Cmd(DMA##dma##_Stream##stream, DISABLE);  \
    \
    DMA_DeInit(DMA##dma##_Stream##stream);  \
    \
    DMA_InitStructure.DMA_Channel = DMA_Channel_##channel;  \
    DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)(&(uart##->DR));  \
    DMA_InitStructure.DMA_Memory0BaseAddr = memory_addr;  \
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;  \
    DMA_InitStructure.DMA_BufferSize = memory_size;  \
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;  \
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;  \
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;  \
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;  \
    DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;  \
    DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;  \
    DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;  \
    DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;  \
    DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;  \
    DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;  \
    DMA_Init(DMA##dma##_Stream##stream, &DMA_InitStructure);  \
    DMA_Cmd(DMA##dma##_Stream##stream, ENABLE);  \
  }

#define UART_INIT(ops_ptr, uart, gpio_rx, pin_rx, fifosize_rx, gpio_tx, pin_tx, fifosize_tx, tx_dma_irq_priority)  \
  static UART_CONTEXT struct_##uart = {.uart_type = uart, }; \
  static void _##uart##_init(u32 bps)  \
  { \
    USART_InitTypeDef USART_InitStructure; \
    GPIO_InitTypeDef GPIO_InitStructure; \
    \
    static FIFO rxfifo, txfifo; \
    static u8 rxbuff[fifosize_rx], txbuff[fifosize_tx]; \
    \
    fifo_init(&rxfifo, rxbuff, fifosize_rx); \
    struct_##uart.fifo_rx = &rxfifo; \
    fifo_init(&txfifo, txbuff, fifosize_tx); \
    struct_##uart.fifo_tx = &txfifo; \
    \
    GPIO_PinAFConfig(gpio_tx, GPIO_PinSource##pin_tx, GPIO_AF_##uart); \
    \
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; \
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz; \
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; \
    GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_UP; \
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_##pin_tx; \
    GPIO_Init(gpio_tx, &GPIO_InitStructure); \
    \
    GPIO_PinAFConfig(gpio_rx, GPIO_PinSource##pin_rx, GPIO_AF_##uart); \
    GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_##pin_rx; \
    GPIO_Init(gpio_rx, &GPIO_InitStructure); \
    \
    USART_InitStructure.USART_BaudRate = bps; \
    USART_InitStructure.USART_WordLength = USART_WordLength_8b; \
    USART_InitStructure.USART_StopBits = USART_StopBits_1; \
    USART_InitStructure.USART_Parity = USART_Parity_No; \
    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; \
    USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; \
    USART_Init(uart, &USART_InitStructure); \
    \
    USART_DMACmd(uart, USART_DMAReq_Tx, ENABLE); \
    USART_DMACmd(uart, USART_DMAReq_Rx, ENABLE); \
    USART_Cmd(uart, ENABLE); \
    uart##_RXDMA_init((u32)(rxfifo.buffer), fifosize_rx); \
    uart##_TXDMA_init((u32)(txfifo.buffer), fifosize_tx, tx_dma_irq_priority); \
  } \
   \
  static u32 _##uart##_write_space(){return uart_write_space(&struct_##uart);} \
  static u32 _##uart##_read_valid_len(){return uart_read_valid_len(&struct_##uart);} \
  static bool _##uart##_read(u8 *ptr){return uart_read(&struct_##uart, ptr);} \
  static u32 _##uart##_reads(u8 *ptr, u32 len){return uart_reads(&struct_##uart, ptr, len);} \
  static bool _##uart##_write(u8 data){return uart_write(&struct_##uart, data);} \
  static u32 _##uart##_writes(const u8 *ptr, u32 len){return uart_writes(&struct_##uart, ptr, len);} \
  static UART_OPS _##uart##ops={ \
  .write_space = _##uart##_write_space, \
  .read_valid_len = _##uart##_read_valid_len, \
  .read = _##uart##_read, \
  .reads = _##uart##_reads, \
  .write = _##uart##_write, \
  .writes = _##uart##_writes, \
  .init = _##uart##_init}; \
  const UART_OPS *ops_ptr = &_##uart##ops;



沙发
whtwhtw| | 2017-3-20 10:20 | 只看该作者
支持一下

使用特权

评论回复
板凳
mmuuss586| | 2017-3-20 11:05 | 只看该作者

谢谢分享;

使用特权

评论回复
地板
nyszx| | 2017-3-20 11:22 | 只看该作者
谢谢分享。看来楼主对莫坛意见很大呀。

使用特权

评论回复
5
aspoke| | 2017-3-20 13:19 | 只看该作者

使用特权

评论回复
6
aspoke| | 2017-3-20 13:23 | 只看该作者
不用再中断执行了。

使用特权

评论回复
7
@若水| | 2017-3-20 18:21 | 只看该作者
我想问,你跟**的仇恨到底有多大?不过我也不喜欢**,当初想注册时,居然要钱,所以拒绝**,顶起……

使用特权

评论回复
8
@若水| | 2017-3-20 18:22 | 只看该作者
我想问,你跟amobbs的仇恨到底有多大?不过我也不喜欢amobbs,当初想注册时,居然要钱,所以拒绝amobbs,顶起……

使用特权

评论回复
9
戈卫东| | 2017-3-20 19:03 | 只看该作者
amobbs是什么?

使用特权

评论回复
10
Raeka| | 2017-3-20 22:48 | 只看该作者
顶红字加粗部分!!希望本论坛越来越好!!

使用特权

评论回复
11
pkuzhx| | 2017-3-21 09:07 | 只看该作者
第一句霸气

使用特权

评论回复
12
xyz549040622| | 2017-3-21 09:27 | 只看该作者
@21小喇叭 建议推荐。

使用特权

评论回复
13
boming| | 2017-3-21 14:46 | 只看该作者
收和发都用了中断吗?我做了个DMA的,但收和发都用了中断。能共享整个项目工程吗?还是看不懂

使用特权

评论回复
14
WZQ3| | 2017-3-21 22:43 | 只看该作者
谢谢分享!

使用特权

评论回复
15
suzhanhua| | 2017-3-21 23:19 | 只看该作者
跟着楼主多多学习一下。

使用特权

评论回复
16
suzhanhua| | 2017-3-21 23:21 | 只看该作者
不使用中断触发了吗?

使用特权

评论回复
17
gowow|  楼主 | 2017-3-24 10:04 | 只看该作者
@若水 发表于 2017-3-20 18:22
我想问,你跟amobbs的仇恨到底有多大?不过我也不喜欢amobbs,当初想注册时,居然要钱,所以拒绝amobbs,顶 ...

一个一直在用的账号名,应该是08年或更早注册的,被封了,说是密码简单,好,认了
后来注册的账号,有次交钱预定了春风电源,当初应该是说3个月后交货,后来应该是过了一年多,我问了问进度,没有辱骂没有质疑,我很尊重春风的,注意只是问了问进度,直接封账号,钱倒是退了,但是也白等了。其他等那些雕刻机之类的朋友,你们也知道怎么回事。
后来一个账号,也写过原创精华帖,被封了,理由是我三个月未回复帖子,认为我只会索取不会付出再后来就要注册费,我去你吗的,一分钱也不给你

使用特权

评论回复
18
gowow|  楼主 | 2017-3-24 10:10 | 只看该作者
boming 发表于 2017-3-21 14:46
收和发都用了中断吗?我做了个DMA的,但收和发都用了中断。能共享整个项目工程吗?还是看不懂 ...

收没有中断,直接放在循环的DMA接收里
发有中断,但是是一段字节的DMA中断

例如你先发送10个字节,DMA就开始发这10个字节。你在DMA发送过程中又分两次把20个字节和30个字节放进去FIFO里。那等最先的10个字节发送完,触发DMA完成中断,接着就会把余下的20+30=50个字节一次性用DMA发送出去。

按这个例子,就是本来要触发10+20+30=60次的UART字节发送中断,用这个驱动就变成了1+1次DMA发送中断。发的越多,发的越整块,效率就越高

使用特权

评论回复
19
gowow|  楼主 | 2017-3-24 10:14 | 只看该作者
把头文件保存到uart.h文件里,把C文件保存在到uart.c文件里

在uart.c最后用下面这样方式定义一个串口
//PC10  TX4                                DMA1.4.4
//PC11  RX4                                DMA1.2.4
DMA_TX_IRQH_INIT_FUNC(UART4, 1,4,4);
DMA_RX_INIT_FUNC(UART4, 1,2,4);
UART_INIT(uart_xxx, UART4, GPIOC, 11, 1024, GPIOC, 10, 1024, 1);


在uart.h文件里把handler声明一下
extern const UART_OPS *uart_xxx;

就可以在其他地方用这个uart_xxx句柄来调用uart驱动了,例如init和read
uart_xxx->init(57600);
uart_xxx->read(............

使用特权

评论回复
20
@若水| | 2017-3-24 11:21 | 只看该作者
gowow 发表于 2017-3-24 10:04
一个一直在用的账号名,应该是08年或更早注册的,被封了,说是密码简单,好,认了
后来注册的账号,有次 ...

这样说,真的好恶心,大家的付出,为amobbs铺了条发财路,如果没有大家的付出,里面哪来的资源?

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

43

主题

121

帖子

0

粉丝