前言

前面我们进行了串口的收发测试,以及串口最大速度测试。现在我们就来基于fifo实现一个更方便使用的串口驱动。

实现

添加如下文件

233529cyd4dmajno5jea0d

FIFO实现参考公众号文章

https://mp.weixin.qq.com/s/MvL9eDesyuxD60fnbl1nag

串口驱动实现参考上述公众号文章其他文章。

Fifo.c如下

#include <string.h>

#include "fifo.h"

#define FIFO_PARAM_CHECK 0

/**

* in为写入索引 0~(buffer_len-1)。

* out为读出索引 0~(buffer_len-1)。

* in == out时可能是满,也可能是空,可以通过len有效数据长度来确认。

* 写数据in增加,直到追赶到out则满。

* 读数据则out增加,直到追赶到in则空。

* in大于out时则[out,in)区间是有效数据。

* in小于out时则[out,buffer_len)和[0,in)区间是有效数据。

***********************************************************

*     0                                 buffer_len-1 buffer_len

*     (1)开始 in和out都是0

*     |                                             |

*     in(0)

*     out(0)

*     len = 0

*     (2)写入n字节数据 in变为n和out还是0 对应in大于out的情况

*     |                                             |

*     out(0)————————————>in(n)                      |   

*     len = n

*     (3)读出m字节数据(m<n) in还是n和out变为m 对应in大于out的情况

*     |                                             |

*             out(m)————>in(n)

*     len = n-m

*     (4)继续写入数据,绕回到开头,对应in小于out的情况

*     |                                             |

*             out(m)————————————————————————————————>

*     ——>in(k)

*     len = k + buffer_len-m

*/

uint32_t fifo_in(fifo_st* dev, uint8_t* buffer, uint32_t len)

{

  uint32_t space = 0;  /* 用于记录空闲空间大小 */

  /* 参数检查 */

  #if FIFO_PARAM_CHECK

  if((dev == 0) || (buffer == 0) || (len == 0))

  {

    return 0;

  }

  if(dev->buffer == 0)

  {

    return 0;

  }

  #endif

  /* 限制len的最大长度为buffer大小 */

  if(len > dev->buffer_len)

  {

    len = dev->buffer_len;

  }

  /* 计算空闲空间大小

   * 正常dev->len不应该大于dev->buffer_len

   */

  if(dev->buffer_len >= dev->len)

  {

    space = dev->buffer_len - dev->len;

  }

  else

  {

    /* 这里不应该出现, 出现则是异常 */

    dev->len = 0;

    space = dev->buffer_len;

  }

  /* 计算待写入大小, 如果len大于剩余空间则只写入剩余空间大小 */

  len = (len >= space) ? space : len;  

  if(len == 0)

  {

    return 0; /* 这里有可能无剩余空间,直接返回 */

  }

  /* 计算len的长度是否需要有绕回,需要分次写入 */

  space = dev->buffer_len - dev->in; /* 当前写入位置in到缓存末尾剩余可写入空间 */

  if(space >= len)

  {

    /* 当前写入位置in到缓存末尾足够一次写入 */

    memcpy(dev->buffer+dev->in,buffer,len);

  }

  else

  {

    /* 当前写入位置in到缓存末尾不够,还需要绕回到前面写 */

    memcpy(dev->buffer+dev->in,buffer,space);    /* 先写入tail部分  */

    memcpy(dev->buffer,buffer+space,len-space);  /* 再写入绕回头部分 */

  }

  /* 更新写入索引和有效数据长度 */

  dev->in += len;

  if(dev->in >= dev->buffer_len)

  {

    dev->in -= dev->buffer_len;  /* 判断加减法 替代 dev->in %= dev->buffer->len */

  }

  dev->len += len;  /* dev->len最大dev->buffer->len,无需%= dev->buffer->len */

  return len;

}

uint32_t fifo_out(fifo_st* dev, uint8_t* buffer, uint32_t len)

{

  uint32_t space = 0;

  /* 参数检查 */

  #if FIFO_PARAM_CHECK

  if((dev == 0) || (buffer == 0) || (len == 0))

  {

    return 0;

  }

  if(dev->buffer == 0)

  {

    return 0;

  }

  #endif


  /* 判断是否有数据 */

  if(dev->len == 0)

  {

    return 0;

  }

  /* 可读出数据量取需要的和有的之间的小值 */

  len = (dev->len) > len ? len : dev->len;

  /* 计算len的长度是否需要有绕回,需要分次读出 */

  space = dev->buffer_len - dev->out; /* 当前读出位置out到缓存末尾剩余可读出空间 */

  if(space >= len)

  {

    /* 当前读出位置out到缓存末尾足够一次读出 */

    memcpy(buffer,dev->buffer+dev->out,len);

  }

  else

  {

    /* 当前读出位置out到缓存末尾不够,还需要绕回到前面读 */

    memcpy(buffer,dev->buffer+dev->out,space);    /* 先读出tail部分  */

    memcpy(buffer+space,dev->buffer,len-space);   /* 再读出绕回头部分 */

  }

  /* 更新读出索引和有效数据长度 */

  dev->out += len;

  if(dev->out >= dev->buffer_len)

  {

    dev->out -= dev->buffer_len;  /* 判断加减法 替代 dev->out %= dev->buffer->len */

  }

  dev->len -= len;   /* 这里dev->len 不可能小于len,不会溢出 */

  return len;

}

uint32_t fifo_getlen(fifo_st* dev)

{

  #if FIFO_PARAM_CHECK

  if(dev == 0)

  {

    return 0;

  }

  #endif

  return dev->len;

}

void fifo_clean(fifo_st* dev)

{

  #if FIFO_PARAM_CHECK

  if(dev == 0)

  {

    return 0;

  }

  #endif

  dev->len = 0;

  dev->in = 0;

  dev->out = 0;

}

uint32_t fifo_getfree(fifo_st* dev)

{

  if(dev == 0)

  {

    return 0;

  }

  return dev->buffer_len - dev->len;

}

Fifo.h如下

#ifndef FIFO_H

#define FIFO_H

#ifdef __cplusplus

extern "C" {

#endif


#include <stdint.h>

/**

* \struct fifo_st

* FIFO缓冲区结构.

*/

typedef struct

{

  uint32_t in;          /**< 写入索引        */

  uint32_t out;         /**< 读出索引        */

  uint32_t len;         /**< 有效数据长度    */

  uint32_t buffer_len;  /**< 有效长度        */

  uint8_t* buffer;      /**< 缓存,用户分配   */

} fifo_st;


/**

* \fn fifo_in

* 往fifo里写数据

* \param[in] dev \ref fifo_st

* \param[in] buffer 待写入的数据

* \param[in] len 待写入的长度

* \retval 返回实际写入的数据量

*/

uint32_t fifo_in(fifo_st* dev, uint8_t* buffer, uint32_t len);

/**

* \fn fifo_out

* 从fifo读出数据

* \param[in] dev \ref fifo_st

* \param[in] buffer 存读出的数据

* \param[in] len 需要读出的数据长度

* \retval 返回实际读出的数据量

*/

uint32_t fifo_out(fifo_st* dev, uint8_t* buffer, uint32_t len);

uint32_t fifo_getlen(fifo_st* dev);

void fifo_clean(fifo_st* dev);

uint32_t fifo_getfree(fifo_st* dev);

#ifdef __cplusplus

}

#endif

#endif

串口驱动实现如下

Uart.c

#include "me32g030.h"

#include "me32g030_uart.h"

#include "me32g030_sys.h"

#include "me32g030_ioconfig.h"

#include "uart.h"

#include "fifo.h"

#define CriticalAlloc()

#define EnterCritical()        __disable_irq()

#define ExitCritical()  __enable_irq()

uint8_t s_uart_rx_buffer[16];

static fifo_st s_uart_fifo_dev[1]=

{

        {

         .in = 0,

         .len = 0,

         .out = 0,

         .buffer = s_uart_rx_buffer,

         .buffer_len = sizeof(s_uart_rx_buffer),

        },

};

void uart_rx_cb(int id, uint8_t* buff, uint32_t len)

{

        fifo_in(&(s_uart_fifo_dev[id]), buff, len);

}

void uart_init(int id, uint32_t baud)

{

        //init UART pin

        PA2_INIT(PA2_UART1_TX);

        PA3_INIT(PA3_UART1_RX);

        //initial UART1

        UART_Open(UART1,baud,UART_NO_PARITY,UART_TRIGGER_LEVEL_4_BYTES);

        UART_EnableInt(UART1, UART_RX_INT);

        NVIC_EnableIRQ(UART1_IRQn);

}

void UART1_IRQHandler(void)

{

        uint8_t byte_data;

        if (UART1->LSR_b.RDR)

        {

                byte_data = UART1->RBR;

                uart_rx_cb(0, &byte_data, 1);

        }

}

uint32_t uart_send(int id, uint8_t* buffer, uint32_t len)

{

                for(uint32_t i=0;i<len;i++)

                {

                        //send a test data

                        while(UART1->LSR_b.THRE == 0);

                        UART1->THR=buffer;

                }

    return len;

}

uint32_t uart_read(int id, uint8_t* buffer, uint32_t len)

{

    uint32_t rlen;

    CriticalAlloc();

    EnterCritical();

    rlen = fifo_out(&(s_uart_fifo_dev[id]), buffer, len);

    ExitCritical();

    return rlen;

}

Uart.h

#ifndef UART_H

#define UART_H

#ifdef __cplusplus

extern "C" {

#endif


#include <stdint.h>

void uart_init(int id, uint32_t baud);

uint32_t uart_send(int id, uint8_t* buffer, uint32_t len);

uint32_t uart_read(int id, uint8_t* buffer, uint32_t len);

#ifdef __cplusplus

}

#endif

#endif

测试

Main.c中

先打印HelloWorld然后收到数据原样返回。

#include <string.h>

#include "me32g030.h"

#include "me32g030_sys.h"

#include "uart.h"

uint8_t str[] = "Hello World";

int main(void)

{

  SYS_SetAHBClkDivider(1);

  uart_init(0, 3000000);

        uint8_t rx_buf[64];


        uart_send(0, str, strlen((const char*)str));

        while(1)

        {

                uint32_t len = uart_read(0, rx_buf, sizeof(rx_buf));

                if(len > 0){

                        uart_send(0, rx_buf, len);

                }

        }

}

使用串口调试助手,这里使用3M波特率,进行发送,可以看到开发板原样返回,无数据丢失,说明我们的驱动可以可靠稳定的实现3M的速度传输。

233529iign6gdx6rqx8q88

总结

以上基于FIFO实现了串口驱动,提供了简单的接口给应用层使用,测试可以看出很稳定,3M速率也完全没有问题,后面就可以方便的使用串口了。