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

本文详细介绍了在沁恒科技的CH58x芯片上,使用BLE_UART工程如何通过环形FIFO机制处理串口数据,包括数据流向分析、buffer_size_mask解释、初始化与状态判断,以及与蓝牙数据收发的接口实现。
摘要由CSDN通过智能技术生成

本文章主要针对沁恒科技的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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

积跬步、至千里

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

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

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

打赏作者

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

抵扣说明:

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

余额充值