本文章主要针对沁恒科技的CH58x芯片,以
BLE_UART
工程为依托,介绍串口数据的接收与处理。
该工程中 串口数据的处理用到了环形FIFO机制,可作为其他开发工具
📋 个人简介
- 💖 作者简介:大家好,我是喜欢记录零碎知识点的小菜鸟。😎
- 📝 个人主页:欢迎访问我的 Ethernet_Comm 博客主页🔥
- 🎉 支持我:点赞👍+收藏⭐️+留言📝
- 📣 系列专栏:沁恒蓝牙mesh二次开发 🍁
- 💬格言:写文档啊不是写文章,重要的还是直白!🔥
【沁恒蓝牙mesh】CH58x串口环形FIFO数据处理
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;
}