打印
[蓝牙芯片]

CH58x串口环形FIFO数据处理---以BLE_UART工程为例

[复制链接]
3733|2
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
tpgf|  楼主 | 2024-7-3 07:56 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
1. 串口数据处理的几个问题
考虑几个问题:

读取速度大于写入速度时,会读取到空数据
当写入速度大于读取速度时,会出现数据覆盖的情况(这是不允许的)
2. 环形FIFO图示
假设定义一个 size 为10 的环形FIFO缓冲区,下图分别表示:

初始化
写入5个字节
读取4个字节
写入9个字节



fifo缓冲区的长度

fifo->end - fifo->begin

3. 代码思路解析
本文以南京沁恒微电子公司的 CH583 提供的 SDK中的 BLE_UART 工程为例,解析串口数据解析的过程, .c 和 .h 置于文末供参考备份。

3.1 数据流向过程解析
1️⃣ : 定义fifo 缓冲区以及发送接收数组

typedef struct Fifo_s
{
    uint16_t begin;
    uint16_t end;
    uint8_t *data;
    uint16_t size;
    uint16_t size_mask;
} app_drv_fifo_t;

static uint8_t app_uart_tx_buffer[APP_UART_TX_BUFFER_LENGTH] = {0};   // 512
static uint8_t app_uart_rx_buffer[APP_UART_RX_BUFFER_LENGTH] = {0};   // 2048

static app_drv_fifo_t app_uart_tx_fifo;
static app_drv_fifo_t app_uart_rx_fifo;


初始化:



2️⃣ :在串口接收中断中,将接收FIFO寄存器中的数据写入 app_uart_rx_fifo,并将接收一帧数据完成的标志位置1;

void UART3_IRQHandler(void){
    switch(UART3_GetITFlag())
    {
         case UART_II_RECV_TOUT:  /*接收fifo一段时间内没有数据,产生超时中断*/
            /*将R8_UART3_RBR 寄存器中的数据,长度为 R8_UART3_RFC ,写入 rx_fifo  */
            error = app_drv_fifo_write_from_same_addr(&app_uart_rx_fifo, (uint8_t *)&R8_UART3_RBR, R8_UART3_RFC);
            uart_rx_flag = true;   /*表示接口完成一次数据*/
            break;     
    }
}


app_drv_fifo_write_from_same_addr : 将硬件 rx fifo 寄存器中的数据读出,存储到 rx_fifo中, push data to fifo

app_drv_fifo_read_to_same_addr : 将硬件 tx fifo 寄存器中的数据读出,存储到 tx_fifo 中 , pop data from fifo

3.2 buffer_size_mask 解析
3.3【fifo初始化】
typedef struct Fifo_s
{
    uint16_t begin;
    uint16_t end;
    uint8_t *data;
    uint16_t size;
    uint16_t size_mask;   
} app_drv_fifo_t;

// size_mask = size - 1
// 通过判断  size & (size -1 )  == 0  限制,size大小必须为 2 的整数次幂

// 比如初始化一个 fifo ,长度为 16 ;
uint8_t uart_buffer[16] = {0};   // 512
static app_drv_fifo_t uart_fifo;

uart_fifo.begin = uart_fifo.end = 0;
uart_fifo.size =16;
uart_fifo.size_mask = 16 -1;
uart_fifo.data = &uart_buffer[0];


3.4【状态判断】
// 刷新 fifo
void app_drv_fifo_flush(app_drv_fifo_t *fifo){
    fifo->begin = 0;
    fifo->end = 0;
}
// 判断已经占用的长度
uint16_t app_drv_fifo_length(app_drv_fifo_t *fifo){
    uint16_t tmp = fifo->begin;
    return fifo->end - tmp;
}

// 判断空
bool app_drv_fifo_is_empty(app_drv_fifo_t *fifo){
    return (fifo->begin == fifo->end);
}

// 判断满
bool app_drv_fifo_is_full(app_drv_fifo_t *fifo){       
    return ((fifo->end - fifo->begin) == fifo->size);
}


3.5【数据写入与读出】
关于唤醒FIFO防止数组索引溢出,有时候还采用 , (begin + size - out ) % size

/*将一组数据 push到fifo中*/
void app_drv_fifo_push(app_drv_fifo_t *fifo, uint8_t data){
    uint16_t index = fifo->end & fifo->size_mask; /*将数组的index***限制在 0:size-1 范围内,不会越界*/
    fifo->data[index] = data;  /*为了保证数组不越界 */
    fifo->end++;
}

/*将fifo中的最后一个元素 pop出来*/
uint8_t app_drv_fifo_pop(app_drv_fifo_t *fifo){
    uint16_t index = fifo->end & fifo->size_mask;
    fifo->data[index] = data;  /*为了保证数组不越界 */
    return data;
}

// 若 a = 2^n - 1   即 a = 3  7 15 这些数字
// 则 n & (a-1) 的结果就是,将n的结果分组:
// 假设 a = 16;  则 n & 15 的结果就是:

/*
        n = 0-15   结果为 0-15
        n = 16-31  结果为 0-15
*/




4. 数据收发接口
BLE_UART工程中,ch58x 模块作为蓝牙主机,通过PC端的串口实现 串口数据转蓝牙数据的收据收发

ch58x发送蓝牙数据:PC 发送串口指令,ch58x 蓝牙从机接收串口指令,将数据通过蓝牙协议发送至蓝牙主机
ch58x接收蓝牙数据:ch58x 蓝牙从机接收到蓝牙主机的消息,将蓝牙数据转为串口消息,通过串口3发送至PC端



4.1 ch58x接收蓝牙数据—>通过串口发出
Peripheral_Init 初始化时,注册回调函数 ble_uart_add_service(on_bleuartServiceEvt);
在回调函数中 on_bleuartServiceEvt,ch58x 接受到蓝牙消息,将蓝牙数据存储到 app_uart_tx_fifo中
然后在主循环中,通过查询的方式,将 app_uart_tx_fifo 中的数据传递给串口3的发送保持寄存器R8_UART3_THR进行数据的发送
/*1. 初始化中注册回调函数*/
void Peripheral_Init(){
        ble_uart_add_service(on_bleuartServiceEvt);
}

/*2. 在回调函数中,接受到蓝牙数据时,将数据存储在 app_uart_tx_fifo*/
void on_bleuartServiceEvt(uint16_t connection_handle, ble_uart_evt_t *p_evt)
{
         //ble to uart
      app_uart_tx_data((uint8_t *)p_evt->data.p_data, p_evt->data.length);
}

/*3. 在主循环中,通过查询的方式发送数据*/
void app_uart_process(void)
{
    //tx process
    if(R8_UART3_TFC < UART_FIFO_SIZE)
    {
        app_drv_fifo_read_to_same_addr(&app_uart_tx_fifo, (uint8_t *)&R8_UART3_THR, UART_FIFO_SIZE - R8_UART3_TFC);
    }
}





4.2 ch58x发送蓝牙数据–>将串口数据以蓝牙协议发出
串口3 的接收中断中,将接受到的串口消息存储在 app_uart_rx_fifo中,将标志位uart_rx_flag置位
在主循环中,开启任务 UART_TO_BLE_SEND_EVT
在 Peripheral_ProcessEvent 事件处理函数中,通过 ble_uart_notify 将 app_uart_rx_fifo中的数据以蓝牙协议发送出去








5. 代码示例
5.1 app_drv_fifo.h
/********************************** (C) COPYRIGHT *******************************
* File Name          : app_drv_fifo.h
* Author             : WCH
* Version            : V1.1
* Date               : 2022/01/19
* Description        :
*********************************************************************************
* Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd.
* Attention: This software (modified or not) and binary are used for
* microcontroller manufactured by Nanjing Qinheng Microelectronics.
*******************************************************************************/

#ifndef __APP_DRV_FIFO_H__
#define __APP_DRV_FIFO_H__

#include <stdbool.h>
#include <stdint.h>

#ifndef BV
  #define BV(n)    (1 << (n))
#endif

#ifndef BF
  #define BF(x, b, s)    (((x) & (b)) >> (s))
#endif

#ifndef MIN
  #define MIN(n, m)    (((n) < (m)) ? (n) : (m))
#endif

#ifndef MAX
  #define MAX(n, m)    (((n) < (m)) ? (m) : (n))
#endif

#ifndef ABS
  #define ABS(n)    (((n) < 0) ? -(n) : (n))
#endif

typedef enum
{
    APP_DRV_FIFO_RESULT_SUCCESS = 0,
    APP_DRV_FIFO_RESULT_LENGTH_ERROR,
    APP_DRV_FIFO_RESULT_NOT_FOUND,
    APP_DRV_FIFO_RESULT_NOT_MEM,
    APP_DRV_FIFO_RESULT_NULL,

} app_drv_fifo_result_t;

#ifndef NULL
  #define NULL    0
#endif

/*!
* FIFO structure
*/
typedef struct Fifo_s
{
    uint16_t begin;
    uint16_t end;
    uint8_t *data;
    uint16_t size;
    uint16_t size_mask;
} app_drv_fifo_t;

//__inline uint16_t app_drv_fifo_length(app_drv_fifo_t *fifo);

uint16_t app_drv_fifo_length(app_drv_fifo_t *fifo);

/*!
* Initializes the FIFO structure
*
* \param [IN] fifo   Pointer to the FIFO object
* \param [IN] buffer Buffer to be used as FIFO
* \param [IN] size   size of the buffer
*/
app_drv_fifo_result_t
app_drv_fifo_init(app_drv_fifo_t *fifo, uint8_t *buffer, uint16_t buffer_size);

/*!
* Pushes data to the FIFO
*
* \param [IN] fifo Pointer to the FIFO object
* \param [IN] data data to be pushed into the FIFO
*/
void app_drv_fifo_push(app_drv_fifo_t *fifo, uint8_t data);

/*!
* Pops data from the FIFO
*
* \param [IN] fifo Pointer to the FIFO object
* \retval data     data popped from the FIFO
*/
uint8_t app_drv_fifo_pop(app_drv_fifo_t *fifo);

/*!
* Flushes the FIFO
*
* \param [IN] fifo   Pointer to the FIFO object
*/
void app_drv_fifo_flush(app_drv_fifo_t *fifo);

/*!
* Checks if the FIFO is empty
*
* \param [IN] fifo   Pointer to the FIFO object
* \retval isEmpty    true: FIFO is empty, false FIFO is not empty
*/
bool app_drv_fifo_is_empty(app_drv_fifo_t *fifo);

/*!
* Checks if the FIFO is full
*
* \param [IN] fifo   Pointer to the FIFO object
* \retval isFull     true: FIFO is full, false FIFO is not full
*/
bool app_drv_fifo_is_full(app_drv_fifo_t *fifo);

app_drv_fifo_result_t
app_drv_fifo_write(app_drv_fifo_t *fifo, uint8_t *data,
                   uint16_t *p_write_length);

app_drv_fifo_result_t
app_drv_fifo_write_from_same_addr(app_drv_fifo_t *fifo, uint8_t *data,
                                  uint16_t write_length);

app_drv_fifo_result_t
app_drv_fifo_read(app_drv_fifo_t *fifo, uint8_t *data, uint16_t *p_read_length);

app_drv_fifo_result_t
app_drv_fifo_read_to_same_addr(app_drv_fifo_t *fifo, uint8_t *data,
                               uint16_t read_length);

#endif // __APP_DRV_FIFO_H__



5.2 app_drv_fifo.c
/********************************** (C) COPYRIGHT *******************************
* File Name          : app_drv_fifo.c
* Author             : WCH
* Version            : V1.1
* Date               : 2022/01/19
* Description        :
*********************************************************************************
* Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd.
* Attention: This software (modified or not) and binary are used for
* microcontroller manufactured by Nanjing Qinheng Microelectronics.
*******************************************************************************/

#include "app_drv_fifo.h"

static __inline uint16_t fifo_length(app_drv_fifo_t *fifo)
{
    uint16_t tmp = fifo->begin;
    return fifo->end - tmp;
}

uint16_t app_drv_fifo_length(app_drv_fifo_t *fifo)
{
    return fifo_length(fifo);
}

app_drv_fifo_result_t
app_drv_fifo_init(app_drv_fifo_t *fifo, uint8_t *buffer, uint16_t buffer_size)
{
    if(buffer_size == 0)
    {
        return APP_DRV_FIFO_RESULT_LENGTH_ERROR;
    }
    if(0 != ((buffer_size) & (buffer_size - 1)))
    {
        return APP_DRV_FIFO_RESULT_LENGTH_ERROR;
    }
    fifo->begin = 0;
    fifo->end = 0;
    fifo->data = buffer;
    fifo->size = buffer_size;
    fifo->size_mask = buffer_size - 1;
    return APP_DRV_FIFO_RESULT_SUCCESS;
}

void app_drv_fifo_push(app_drv_fifo_t *fifo, uint8_t data)
{
    fifo->data[fifo->end & fifo->size_mask] = data;
    fifo->end++;
}

uint8_t app_drv_fifo_pop(app_drv_fifo_t *fifo)
{
    uint8_t data = fifo->data[fifo->begin & fifo->size_mask];
    fifo->begin++;
    return data;
}

void app_drv_fifo_flush(app_drv_fifo_t *fifo)
{
    fifo->begin = 0;
    fifo->end = 0;
}

bool app_drv_fifo_is_empty(app_drv_fifo_t *fifo)
{
    return (fifo->begin == fifo->end);
}

bool app_drv_fifo_is_full(app_drv_fifo_t *fifo)
{
    return (fifo_length(fifo) == fifo->size);
}

app_drv_fifo_result_t
app_drv_fifo_write(app_drv_fifo_t *fifo, uint8_t *data, uint16_t *p_write_length)
{
    if(fifo == NULL)
    {
        return APP_DRV_FIFO_RESULT_NULL;
    }
    if(p_write_length == NULL)
    {
        return APP_DRV_FIFO_RESULT_NULL;
    }
    //PRINT("fifo_length = %d\r\n",fifo_length(fifo));
    const uint16_t available_count = fifo->size - fifo_length(fifo);
    const uint16_t requested_len = (*p_write_length);
    uint16_t       index = 0;
    uint16_t       write_size = MIN(requested_len, available_count);
    //PRINT("available_count %d\r\n",available_count);
    // Check if the FIFO is FULL.
    if(available_count == 0)
    {
        return APP_DRV_FIFO_RESULT_NOT_MEM;
    }

    // Check if application has requested only the size.
    if(data == NULL)
    {
        return APP_DRV_FIFO_RESULT_SUCCESS;
    }

    for(index = 0; index < write_size; index++)
    {
        //push
        fifo->data[fifo->end & fifo->size_mask] = data[index];
        fifo->end++;
    }
    (*p_write_length) = write_size;
    return APP_DRV_FIFO_RESULT_SUCCESS;
}

app_drv_fifo_result_t
app_drv_fifo_write_from_same_addr(app_drv_fifo_t *fifo, uint8_t *data, uint16_t write_length)
{
    if(fifo == NULL)
    {
        return APP_DRV_FIFO_RESULT_NULL;
    }
    const uint16_t available_count = fifo->size_mask - fifo_length(fifo) + 1;
    const uint16_t requested_len = (write_length);
    uint16_t       index = 0;
    uint16_t       write_size = MIN(requested_len, available_count);

    // Check if the FIFO is FULL.
    if(available_count == 0)
    {
        return APP_DRV_FIFO_RESULT_NOT_MEM;
    }

    for(index = 0; index < write_size; index++)
    {
        //push
        fifo->data[fifo->end & fifo->size_mask] = data[0];
        fifo->end++;
    }
    return APP_DRV_FIFO_RESULT_SUCCESS;
}

app_drv_fifo_result_t
app_drv_fifo_read(app_drv_fifo_t *fifo, uint8_t *data, uint16_t *p_read_length)
{
    if(fifo == NULL)
    {
        return APP_DRV_FIFO_RESULT_NULL;
    }
    if(p_read_length == NULL)
    {
        return APP_DRV_FIFO_RESULT_NULL;
    }
    const uint16_t byte_count = fifo_length(fifo);
    const uint16_t requested_len = (*p_read_length);
    uint32_t       index = 0;
    uint32_t       read_size = MIN(requested_len, byte_count);

    if(byte_count == 0)
    {
        return APP_DRV_FIFO_RESULT_NOT_FOUND;
    }
    //PRINT("read size = %d,byte_count = %d\r\n",read_size,byte_count);
    for(index = 0; index < read_size; index++)
    {
        //pop
        data[index] = fifo->data[fifo->begin & fifo->size_mask];
        fifo->begin++;
    }

    (*p_read_length) = read_size;
    return APP_DRV_FIFO_RESULT_SUCCESS;
}

app_drv_fifo_result_t
app_drv_fifo_read_to_same_addr(app_drv_fifo_t *fifo, uint8_t *data, uint16_t read_length)
{
    if(fifo == NULL)
    {
        return APP_DRV_FIFO_RESULT_NULL;
    }
    const uint16_t byte_count = fifo_length(fifo);
    const uint16_t requested_len = (read_length);
    uint32_t       index = 0;
    uint32_t       read_size = MIN(requested_len, byte_count);

    for(index = 0; index < read_size; index++)
    {
        //pop
        data[0] = fifo->data[fifo->begin & fifo->size_mask];
        fifo->begin++;
    }
    return APP_DRV_FIFO_RESULT_SUCCESS;
}
————————————————

                            版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

原文链接:https://blog.csdn.net/qq_39217004/article/details/132393198

使用特权

评论回复
沙发
呐咯密密| | 2024-7-4 15:14 | 只看该作者
环形缓冲很实用

使用特权

评论回复
板凳
micoccd| | 2024-7-10 10:58 | 只看该作者
数据量大的时候唤醒缓冲会非常好用

使用特权

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

本版积分规则

2028

主题

15903

帖子

14

粉丝