你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

【经验分享】STM32开发项目:FIFO数据模型库

[复制链接]
STMCU小助手 发布时间:2022-4-14 10:31
背景说明
先进先出队列(简称队列)是一种基于先进先出(FIFO)策略的集合类型。对于数据传输速度不同步的应用场景里,采用FIFO队列作为数据缓冲是十分有必要的。在单片机开发的项目中,笔者遇见应用FIFO缓冲的两个典型场景分别是串口数据收发与ADC高速采样。

FIFO队列最重要的指标之一就是队列长度问题,因为队列长度是有限的,有可能被填满,这就涉及到该机制的丢弃原则。常见的一个丢弃原则叫做Tail Drop机制,简单地说就是该队列如果已经满了,那么后续进入的报文被丢弃。还有一种原则是覆盖机制,简单的说就是如果队列已经满了,那么后续进入的数据将覆盖最前面的数据,队列中的数据始终保持着最新。

两种机制有各自的应用场景,但当队列已满时,都有不可避免的数据丢失。因此,在硬件资源足够的前提下,可以适当增大队列的深度,提高读操作的速度,以尽量避免队列满的情况。本文设计的FIFO队列模型采用了覆盖机制,读者可以根据需要将它改成Tail Drop机制。

库源码
头文件

  1. /*
  2. * fifo.h
  3. *
  4. *  Created on: 2020年9月18日
  5. *      Author: Tao
  6. */

  7. #ifndef SOURCE_ALWHALESLIB_SYSEXTEND_INC_FIFO_H_
  8. #define SOURCE_ALWHALESLIB_SYSEXTEND_INC_FIFO_H_

  9. #include "stm32f4xx.h"
  10. #include "stm32f4xx_conf.h"

  11. /**
  12. * 考虑到FIFO数据成员可能会是不同类型的数据,
  13. * 因此在此处重命名一个数据类型,
  14. * 并将此数据类型作为FIFO数据结构库的成员类型。
  15. * 这么做的好处是可以方便的修改FIFO的成员数据类型。
  16. */
  17. typedef uint16_t FIFO_DataType;

  18. typedef struct
  19. {
  20.         uint32_t BufferSize;                                //数据缓存容量
  21.         FIFO_DataType *Buffer;                                //数据缓存
  22.         FIFO_DataType *Read_P;                                //读指针
  23.         FIFO_DataType *Write_P;                                //写指针
  24.         uint32_t DataCount;                                        //剩余数据量
  25. } FIFO_Struct;


  26. #define FIFO_DATA_NUM                1
  27. #define FIFO_DATA_SIZE                10000

  28. extern FIFO_Struct FIFO_Data[FIFO_DATA_NUM];

  29. void FIFO_Init(FIFO_Struct *FIFO_Data);
  30. void FIFO_WriteData(FIFO_Struct *FIFO_Data,FIFO_DataType *Data, uint32_t length);
  31. void FIFO_WriteOneData(FIFO_Struct *FIFO_Data,FIFO_DataType Data);
  32. void FIFO_ReadData(FIFO_Struct *FIFO_Data,FIFO_DataType *Data, uint32_t length);
  33. uint16_t FIFO_ReadOneData(FIFO_Struct *FIFO_Data);
  34. uint8_t FIFO_IsDataFull(FIFO_Struct *FIFO_Data);
  35. uint8_t FIFO_IsDataEmpty(FIFO_Struct *FIFO_Data);
  36. uint32_t FIFO_GetDataCount(FIFO_Struct *FIFO_Data);

  37. #endif /* SOURCE_ALWHALESLIB_SYSEXTEND_INC_FIFO_H_ */
复制代码

源文件
  1. /*
  2. * fifo.c
  3. *
  4. *  Created on: 2020年9月18日
  5. *      Author: Tao
  6. */
  7. #include "fifo.h"

  8. static FIFO_DataType FIFO_DataBuffer[FIFO_DATA_NUM][FIFO_DATA_SIZE];

  9. FIFO_Struct FIFO_Data[FIFO_DATA_NUM] =
  10. {
  11.                 {
  12.                                 .BufferSize = FIFO_DATA_SIZE,
  13.                                 .Buffer = FIFO_DataBuffer[0],
  14.                 },
  15. };


  16. /**
  17. * @brief 初始化FIFO队列数据:清空数据、复位读写指针
  18. */
  19. void FIFO_Init(FIFO_Struct *FIFO_Data)
  20. {
  21.         FIFO_Data->DataCount = 0;
  22.         FIFO_Data->Read_P = FIFO_Data->Buffer;
  23.         FIFO_Data->Write_P = FIFO_Data->Buffer;
  24. }


  25. /**
  26. * @brief 将数据写入FIFO队列,当数据超出缓冲区的大小时,停止写入数据
  27. * @param FIFO_Data: 需要操作的FIFO队列数据指针
  28. * @param Data: 需要写入的数据指针
  29. * @param length: 需要写入的数据长度
  30. */
  31. void FIFO_WriteData(FIFO_Struct *FIFO_Data,FIFO_DataType *Data, uint32_t length)
  32. {
  33.         //将数据依次写入到队列缓冲区
  34.         for(uint32_t index = 0; index < length; index++)
  35.         {
  36.                 //如果计数器大于等于缓冲容量
  37.                 if(FIFO_Data->DataCount > FIFO_Data->BufferSize -1)
  38.                 {
  39.                         //退出写数据
  40.                         return;
  41.                 }

  42.                 //写入一个数据
  43.                 *FIFO_Data->Write_P = *Data;
  44.                 //写指针移动一位
  45.                 FIFO_Data->Write_P++;
  46.                 //数据缓存指针移动一位
  47.                 Data++;
  48.                 //计数器自增1
  49.                 FIFO_Data->DataCount++;

  50.                 //如果写指针已经到达缓冲区边界
  51.                 if(FIFO_Data->Write_P >= FIFO_Data->Buffer + FIFO_Data->BufferSize)
  52.                 {
  53.                         //使写指针回到缓冲区起点
  54.                         FIFO_Data->Write_P = FIFO_Data->Buffer;
  55.                 }
  56.         }
  57. }


  58. /**
  59. * @brief 向FIFO队列中写入一个数据
  60. * @param FIFO_Data: 需要操作的FIFO队列数据指针
  61. * @param Data: 要写入的数据
  62. */
  63. void FIFO_WriteOneData(FIFO_Struct *FIFO_Data,FIFO_DataType Data)
  64. {
  65.         FIFO_WriteData(FIFO_Data, &Data, 1);
  66. }


  67. /**
  68. * @brief 将数据从FIFO队列中读出,当缓冲区为空时,停止读出数据
  69. * @param FIFO_Data: 需要操作的FIFO队列数据指针
  70. * @param Data: 用来存放读出数据的指针
  71. * @param length: 需要读出的数据长度
  72. */
  73. void FIFO_ReadData(FIFO_Struct *FIFO_Data,FIFO_DataType *Data, uint32_t length)
  74. {
  75.         //将缓冲区数据依次读出到Data数组中
  76.         for(uint32_t index = 0; index < length; index++)
  77.         {
  78.                 //缓冲区数据计数器为0时
  79.                 if(FIFO_Data->DataCount == 0)
  80.                 {
  81.                         //退出读数据
  82.                         return;
  83.                 }

  84.                 //如果数据计数器大于写指针减去缓冲区起始位置(说明写入的数据已经到达过缓冲区边界)
  85.                 if(FIFO_Data->Write_P - FIFO_Data->Buffer < FIFO_Data->DataCount)
  86.                 {
  87.                         //确定数据初始位置,并传递给读指针
  88.                         FIFO_Data->Read_P = FIFO_Data->BufferSize - FIFO_Data->DataCount + FIFO_Data->Write_P;
  89.                 }
  90.                 //写入的数据还未到达过缓冲区边界
  91.                 else
  92.                 {
  93.                         //确定数据初始位置,并传递给读指针
  94.                         FIFO_Data->Read_P = FIFO_Data->Write_P - FIFO_Data->DataCount;
  95.                 }

  96.                 //读出一个数据
  97.                 *Data = *FIFO_Data->Read_P;
  98.                 //读指针移动一位
  99.                 FIFO_Data->Read_P++;
  100.                 //数据缓存指针移动一位
  101.                 Data++;
  102.                 //计数器自减1
  103.                 FIFO_Data->DataCount--;

  104.                 //如果读指针已经到达缓冲区边界
  105.                 if(FIFO_Data->Read_P >= FIFO_Data->Buffer + FIFO_Data->BufferSize)
  106.                 {
  107.                         //使读指针回到缓冲区起点
  108.                         FIFO_Data->Read_P = FIFO_Data->Buffer;
  109.                 }
  110.         }
  111. }


  112. /**
  113. * @brief 从FIFO队列中读出一个数据
  114. * @param FIFO_Data: 需要操作的FIFO队列数据指针
  115. * @retval 读出的数据
  116. */
  117. FIFO_DataType FIFO_ReadOneData(FIFO_Struct *FIFO_Data)
  118. {
  119.         FIFO_DataType tempData;

  120.         FIFO_ReadData(FIFO_Data, &tempData, 1);

  121.         return tempData;
  122. }


  123. /**
  124. * @brief 判断队列数据是否已满
  125. * @param FIFO_Data: 需要操作的FIFO队列数据指针
  126. * @retval 指示队列是否已满
  127. *                 @arg 0: 队列未满
  128. *                 @arg 1: 队列已满
  129. */
  130. uint8_t FIFO_IsDataFull(FIFO_Struct *FIFO_Data)
  131. {
  132.         if(FIFO_Data->DataCount >= FIFO_Data->BufferSize)
  133.         {
  134.                 return 1;
  135.         }
  136.         else
  137.         {
  138.                 return 0;
  139.         }
  140. }


  141. /**
  142. * @brief 判断队列数据是否为空
  143. * @param FIFO_Data: 需要操作的FIFO队列数据指针
  144. * @retval 指示队列是否为空
  145. *                 @arg 0: 队列不空
  146. *                 @arg 1: 队列为空
  147. */
  148. uint8_t FIFO_IsDataEmpty(FIFO_Struct *FIFO_Data)
  149. {
  150.         if(FIFO_Data->DataCount == 0)
  151.         {
  152.                 return 1;
  153.         }
  154.         else
  155.         {
  156.                 return 0;
  157.         }
  158. }


  159. /**
  160. * @brief 获取队列数据的数量
  161. * @param FIFO_Data: 需要操作的FIFO队列数据指针
  162. * @retval 队列数据的数量
  163. */
  164. uint32_t FIFO_GetDataCount(FIFO_Struct *FIFO_Data)
  165. {
  166.         return FIFO_Data->DataCount;
  167. }
复制代码

应用指南
声明并初始化FIFO队列数据
根据项目的需要可以选择声明一个FIFO数据类型的变量或者数组,并初始化。可以依次为每一个成员赋值以完成初始化,或者是为FIFO结构体类型变量分配缓存区后,通过void FIFO_Init(FIFO_Struct *FIFO_Data)来完成其他成员的初始化。

  1. #define FIFO_DATA_NUM                2
  2. #define FIFO_DATA_SIZE                10000

  3. extern FIFO_Struct FIFO_Data[FIFO_DATA_NUM];

  4. FIFO_Struct FIFO_Data[FIFO_DATA_NUM] =
  5. {
  6.         {
  7.                 .BufferSize = FIFO_DATA_SIZE,
  8.                 .Buffer = FIFO_DataBuffer[0],
  9.         },
  10.         {
  11.                 .BufferSize = FIFO_DATA_SIZE,
  12.                 .Buffer = FIFO_DataBuffer[1],
  13.         },
  14. };

  15. void User_Init()
  16. {
  17.         /*省略无关代码*/
  18.         FIFO_Init(&FIFO_Data[0]);
  19.         FIFO_Init(&FIFO_Data[1]);
  20.         /*省略无关代码*/
  21. }
复制代码

在需要写入数据的地方
根据写入数据的数量,可以选择调用void FIFO_WriteData(FIFO_Struct *FIFO_Data,FIFO_DataType *Data, uint32_t length)或者void FIFO_WriteOneData(FIFO_Struct *FIFO_Data,FIFO_DataType Data)。例如:

  1. /**
  2. * @brief 在外部中断14的服务函数中周期性的写入AD7606的高速采样数据
  3. */
  4. void EXTI14Triggered()
  5. {
  6.         if(User_StartDAQ != 0)
  7.         {
  8.                 AD7606_RefreshData();
  9.                 FIFO_WriteOneData(&FIFO_Data[0], AD7606_RawData[0]);
  10.         }
  11. }
复制代码

在需要读出数据的地方
根据读出数据的数量,可以选择调用void FIFO_ReadData(FIFO_Struct *FIFO_Data,FIFO_DataType *Data, uint32_t length)或者uint16_t FIFO_ReadOneData(FIFO_Struct *FIFO_Data)。例如:

  1. /**
  2. * @brief This function is called by timer update interrupt handler, which will be executed periodically.
  3. */
  4. void User_RefreshData()
  5. {
  6.         //注意SensorData数组顺序与ADS1115数据通道顺序不一致
  7.         SensorData[0].value = ADS1115_Data[1];
  8.         SensorData[1].value = ADS1115_Data[0];

  9.         //注意SensorData数组顺序与AD7606数据通道顺序不一致
  10.         SensorData[2].value = (int16_t)AD7606_RawData[1]/32768.0*5000;
  11.         SensorData[3].value = (int16_t)FIFO_ReadOneData(&FIFO_Data[0])/32768.0*5000;

  12.         User_SetOutputCurrent();
  13.         User_RefreshSingalSwitch();
  14. }
复制代码

FIFO队列的数据类型
由于C语言不支持模板特性,因此采用typedef关键字重命名数据类型,并将此别名用作FIFO队列的数据成员类型。根据不同项目的不同需求,可以更改数据类型:

  1. typedef float FIFO_DataType;
复制代码



收藏 评论0 发布时间:2022-4-14 10:31

举报

0个回答

所属标签

相似技术帖

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版