【沁恒蓝牙mesh】CH58x串口环形FIFO数据处理---以BLE_UART工程为例

本文章主要针对沁恒科技的CH58x芯片,以 BLE_UART 工程为依托,介绍串口数据的接收与处理。
该工程中 串口数据的处理用到了环形FIFO机制,可作为其他开发工具

📋 个人简介

1. 串口数据处理的几个问题

考虑几个问题:

  • 读取速度大于写入速度时,会读取到空数据
  • 当写入速度大于读取速度时,会出现数据覆盖的情况(这是不允许的)

2. 环形FIFO图示

假设定义一个 size 为10 的环形FIFO缓冲区,下图分别表示:

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

image-20230810170844358

  • 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;

初始化:

image-20230811085912949

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_notifyapp_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;
}

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
AXI4-Stream FIFO的`s_axi_awready`信号需要在AXI接口的主设备和从设备之间协商来控制,以确保数据能够正确地传输。要拉高`s_axi_awready`信号,需要满足以下条件: 1. 主设备(例如CPU)发出AXI写请求(`s_axi_awvalid`为高电平)。 2. 从设备(例如AXI4-Stream FIFO)准备好接受写数据,即FIFO的剩余空间足够,同时也需要考虑FIFO的输出(如果有的话)是否占用了FIFO的空间。 3. 从设备将`s_axi_awready`信号拉高,表示准备好接受数据。 如果从设备没有准备好接收数据,则从设备将`s_axi_awready`保持低电平,主设备将继续等待设备准备好。 在FIFO的实现中,可以使用一个计数器来跟踪FIFO中的剩余空间,以便决定何时准备好接收新的写入数据。当FIFO满时,`s_axi_awready`将保持低电平,直到FIFO中有数据被读取并腾出空间。 下面是一个示例代码片段,其中展示了如何使用计数器来控制`s_axi_awready`信号: ```vhdl -- FIFO深度计数器 signal count : integer range 0 to MAX_DEPTH-1 := 0; -- 当FIFO有空间时,准备接收新的写入数据 if (s_axi_awvalid = '1' and count < MAX_DEPTH and not fifo_full) then s_axi_awready <= '1'; else s_axi_awready <= '0'; end if; -- 当FIFO有数据时,准备输出 if (s_axis_tready = '1' and count > 0) then s_axis_tvalid <= '1'; else s_axis_tvalid <= '0'; end if; -- 当FIFO有空间时,准备接收新的写入数据 if (s_axi_awvalid = '1' and count < MAX_DEPTH and not fifo_full) then -- 将写入数据存储到FIFOfifo_mem(count) <= s_axi_wdata; -- 计数器加1 count <= count + 1; -- 响应写请求 s_axi_bvalid <= '1'; else -- 响应写请求 s_axi_bvalid <= '0'; end if; ``` 在上面的代码中,`fifo_full`是一个标志,用于指示FIFO是否已满。如果FIFO已满,则不会接受新的写入数据,并且`s_axi_awready`将保持低电平,直到FIFO中有数据被读取并腾出空间。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

积跬步、至千里

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值