按键扫描函数(基于FIFO思想)

多按键扫描事件处理程序设计

基于FIFO原理的多按键扫描程序设计。
支持多按键的单击、双击、三连击、、、长按等事件处理。

1.按键FIFO

1.1原理

FIFO(First In First Out)是一种先进先出的数据缓存器,顺序写入数据,顺序的读出数据。
假设我们在内存存上面开辟一个10字节的内存做FIFO的数据缓存器
开始时,未发生任何按键读写事件、Write=0,Read=0
我们按下三个不同的按键事件,按键1单击、按键2双击、按键3长按,将三个按键事件更新到按键FIFO的缓存器中不进行读,此时Write = 3,Read = 0,表示有三个按键事件未处理。
我们每读取一个按键事件Read+1
当Read=Write时,所有事件处理完毕。
FIFO空间满时Write重置,原有未处理事件会被覆盖。
在这里插入图片描述

1.2代码实现

1.定义按键FIFO缓冲区及按键事件代码

#define KEY_FIFO_SIZE 10
typedef struct
{
    uint8_t Buf[KEY_FIFO_SIZE]; /* FIFO缓冲区 */
    uint8_t Read; 				/* 缓冲区读指针 */
    uint8_t Write; 				/* 缓冲区写指针 */
}KEY_FIFO_STRUCT;
static KEY_FIFO_STRUCT m_sKeyFifo;

typedef enum
{
    KEY_NONE = 0,

    KEY_1_ONE,    /* 单击 */
    KEY_1_DOUBLE, /* 双击  */
    KEY_1_THREE,  /* 三击  */
    KEY_1_LONG,   /* 长按 */

    KEY_2_ONE,
    KEY_2_DOUBLE,
    KEY_2_THREE,
    KEY_2_LONG,

    KEY_3_ONE,
    KEY_3_DOUBLE,
    KEY_3_THREE,
    KEY_3_LONG,  
}KEY_STATE_ENUM;

2.写按键事件值到FIFO缓冲区

/**
 * 将按键事件压入FIFO缓冲区
 * handle: 按键事件
*/
void KeyPutFifo(uint8_t handle)
{
    m_sKeyFifo.Buf[m_sKeyFifo.Write] = handle;
    if (++m_sKeyFifo.Write >= KEY_FIFO_SIZE)
    {
    	m_sKeyFifo.Write = 0;
    }
} 

3.读取按键FIFO缓冲区的数值

/**
 * 2.按键 FIFO 缓冲区读取一个键值。
*/
uint8_t KeyGetState(void)
{
  uint8_t ret;
  if (m_sKeyFifo.Read == m_sKeyFifo.Write)
  {
    return KEY_NONE;
  }
  else
  {
    ret = m_sKeyFifo.Buf[m_sKeyFifo.Read];
    if (++m_sKeyFifo.Read >= KEY_FIFO_SIZE)
    {
      m_sKeyFifo.Read = 0;
    }
    return ret;
  }
} 

2.按键扫描

按键扫描需要注意:
1.硬件初始化,对应IO配置为输入
2.按键按下时,对应IO的状态应该是高电平还是低电平?
3.同时按下了几个按键?
4.按键滤波时长。
5.按键按下多久是长按?连续按下按键的间隔是多少才合适?

2.1按键IO定义初始化

//  PB13 
//  PC12 
//  PD2  
typedef struct 
{
  uint32_t      Pin;    /*  GPIO引脚  */
  GPIO_TypeDef  *Port;  /*  GPIO端口  */
  GPIO_PinState State;  /*  按下状态  */
  
}KEY_GPIO_STRUCT;

KEY_GPIO_STRUCT g_sGpioList[KEY_NUM] = 
{
  {GPIO_PIN_2,  GPIOD, GPIO_PIN_RESET}, /*	按键1	*/
  {GPIO_PIN_13, GPIOB, GPIO_PIN_RESET}, /*	按键2	*/
  {GPIO_PIN_12, GPIOC, GPIO_PIN_RESET}, /*	按键3	*/
};

static void gpioInit(void)  //  按键GPIO口初始化
{
    GPIO_InitTypeDef GPIO_Init;
  
    /* 第 1 步:打开 GPIO 时钟 */
    __HAL_RCC_GPIOB_CLK_ENABLE();
    __HAL_RCC_GPIOC_CLK_ENABLE();
    __HAL_RCC_GPIOD_CLK_ENABLE();

    /* 第 2 步:配置所有的按键 GPIO 为浮动输入模式*/
    GPIO_Init.Mode = GPIO_MODE_INPUT;
    GPIO_Init.Pull = GPIO_NOPULL;
    GPIO_Init.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
  
    for (uint8_t i = 0; i < KEY_NUM; i++)
    {
        GPIO_Init.Pin = g_sGpioList[i].Pin;
        HAL_GPIO_Init(g_sGpioList[i].Port, &GPIO_Init);
    }
}

2.2判断按键是否按下

/* 
* 判断按键是否按下
* 返回值 1 表示按下, 0 表示未按下
*/
static uint8_t KeyPinActive(uint8_t id)
{
  GPIO_PinState level = HAL_GPIO_ReadPin(g_sGpioList[id].Port, g_sGpioList[id].Pin);
  if(level == g_sGpioList[id].State)
  {
    return 1;
  }
  else
  {
    return 0;
  }
} 

/**
 * 按键是否按下,限制当前只有一个按键事件
 * 1 表示按下, 0 表示未按下
*/
static uint8_t KeyIsDownFunc(uint8_t id)
{
  if (id < KEY_NUM)  /* 实体单键 */
  {
    uint8_t i;
    uint8_t count = 0;
    uint8_t save = 255;
    /* 判断有几个键按下 */
    for (i = 0; i < KEY_NUM; i++)
    {
      if (KeyPinActive(i))
      {
        count++;
        save = i;
      }
    }
    if (count == 1 && save == id)
    {
      return 1; /* 只有 1 个键按下时才有效 */
    }
    return 0;
  }
  return 0;
}  

2.3按键检测事件结构体、定义、初始化

#define KEY_FILTER_TIME 20  /*  滤波时间,按下200ms才做处理  单位 10ms*/
#define KEY_LONG_TIME   100 /*  长按事件时间 1S 单位 10ms  */
#define KEY_NUM         3   /* 按键个数*/
typedef struct
{
    uint8_t  (*IsKeyDownFunc)(void); /* 按键按下的判断函数,1 表示按下 */
    uint8_t  Count;       /* 滤波器计数器 */
    uint16_t LongCount;   /* 长按计数器 */
    uint16_t LongTime;    /* 按键按下持续时间, 0 表示不检测长按 */
    uint16_t LongState;   /* 长按状态  */
    uint8_t  State;       /* 按键当前状态 */
    uint8_t  DownState;   /* 按键按下状态 */
    uint8_t  DownCount;   /* 按键按下次数 */
    uint8_t  RepeatSpeed; /* 连续按键周期 */
    uint8_t  RepeatCount; /* 连续按键计数器 */
}KEY_CHECK_STRUCT;
static KEY_CHECK_STRUCT g_sKeyCheck[KEY_NUM];  /* 按键检测  */ 

static void KeyVarInit(void)  //  初始化按键变量
{
    uint8_t i;
    m_sKeyFifo.Read = 0;
    m_sKeyFifo.Write = 0;
    for (i = 0; i < KEY_NUM; i++)
    {
        g_sKeyCheck[i].LongTime = KEY_LONG_TIME; /* 长按时间 0 表示不检测长按键事件 */
        g_sKeyCheck[i].Count =    KEY_FILTER_TIME / 2; /* 计数器设置为滤波时间的一半 */
        g_sKeyCheck[i].State = 0;       /* 按键缺省状态, 0 为未按下 */
        g_sKeyCheck[i].RepeatSpeed = 0; /* 按键连发的速度, 0 表示不支持连发 */
        g_sKeyCheck[i].RepeatCount = 0; /* 连发计数器 */
    }
}
void keyInit(void)  //  初始化按键
{
    KeyVarInit(); 	/* 初始化按键变量 */
    gpioInit();    /* 初始化按键硬件 */
}

2.4按键检测
需要定时10ms调用一次void KeyScan10ms(void)

/*
* 检测一个按键。
* IO 的 id, 从 0 开始编码
*/
static void KeyDetectKey(uint8_t i)
{
  KEY_CHECK_STRUCT *pBtn;
  pBtn = &g_sKeyCheck[i];
  if (KeyIsDownFunc(i)) /*  1.执行按键按下的处理  */
  {
    if (pBtn->Count < KEY_FILTER_TIME) /*  2.用于按键滤波前给 Count 设置一个初值 */
    {
      pBtn->Count = KEY_FILTER_TIME;
    }
    else if (pBtn->Count < (KEY_FILTER_TIME + 10)) /* 这里实现 KEY_FILTER_TIME 时间长度的延迟 */
    {
      pBtn->Count++;
    } /* 这里实现 KEY_FILTER_TIME 时间长度的延迟 */
    else
    {
      if (pBtn->State == 0)
      {
        pBtn->State = 1;
      }

      if(pBtn->DownState == 1)
      {
        pBtn->DownState = 0;
        if(pBtn->DownCount < 3)
        {
          pBtn->DownCount += 1;
        }
      }

      if (pBtn->LongTime > 0)
      {
        if (pBtn->LongCount < pBtn->LongTime)
        {
          /* 发送按钮持续按下的消息 */
          if (++pBtn->LongCount == pBtn->LongTime)
          {
            KeyPutFifo((uint8_t)(4 * i + 4)); /* 键值放入按键 FIFO */
            pBtn->LongState = 1;
          }
        }
        else
        {
          if (pBtn->RepeatSpeed > 0)
          {
            if (++pBtn->RepeatCount >= pBtn->RepeatSpeed)
            {
              pBtn->RepeatCount = 0;
             
              KeyPutFifo((uint8_t)(4 * i + 4));
            }
          }
        }
      }
    }
  }
  else //	处理按键松手,或按键没有按下的处理
  {
    if (pBtn->Count > KEY_FILTER_TIME)
    {
      pBtn->Count = KEY_FILTER_TIME;
    }
    else if (pBtn->Count != 0)
    {
      pBtn->Count--;
    }
    else
    {
      if (pBtn->State == 1)
      {
        pBtn->State = 0;
        if (pBtn->LongState == 0)
        {
          KeyPutFifo((uint8_t)(4 * i + pBtn->DownCount)); /* 发送按钮弹起的消息 */
        }
        else
        {
          pBtn->LongState = 0;
        }
        pBtn->DownCount = 0;
      }
    }
    
    pBtn->DownState = 1;
    pBtn->LongCount = 0;
    pBtn->RepeatCount = 0;
  }
}
/**
 * 10ms调用一次此函数,轮询按键
*/
void KeyScan10ms(void)
{
    uint8_t i;
    for (i = 0; i < KEY_NUM; i++)
    {
    	KeyDetectKey(i);
    }
}

代码

KEY.c

#include "../HARDWARE/key/key.h"
#include "main.h"
#include "string.h"

//  PB13 
//  PC12 
//  PD2  

/**
 * 1.将 1 个键值压入按键 FIFO 缓冲区。可用于模拟一个按键
 * _KeyCode : 按键代码
*/
void KeyPutFifo(uint8_t handle)
{
    m_sKeyFifo.Buf[m_sKeyFifo.Write] = handle;
    if (++m_sKeyFifo.Write >= KEY_FIFO_SIZE)
    {
    	m_sKeyFifo.Write = 0;
    }
} 

/**
 * 2.按键 FIFO 缓冲区读取一个键值。
*/
uint8_t KeyGetState(void)
{
  uint8_t ret;
  if (m_sKeyFifo.Read == m_sKeyFifo.Write)
  {
    return KEY_NONE;
  }
  else
  {
    ret = m_sKeyFifo.Buf[m_sKeyFifo.Read];
    if (++m_sKeyFifo.Read >= KEY_FIFO_SIZE)
    {
      m_sKeyFifo.Read = 0;
    }
    return ret;
  }
} 

typedef struct 
{
  uint32_t      Pin;    /*  GPIO引脚  */
  GPIO_TypeDef  *Port;  /*  GPIO端口  */
  GPIO_PinState State;  /*  按下状态  */
  
}KEY_GPIO_STRUCT;

KEY_GPIO_STRUCT g_sGpioList[KEY_NUM] = 
{
  {GPIO_PIN_2,  GPIOD, GPIO_PIN_RESET}, /*	按键1	*/
  {GPIO_PIN_13, GPIOB, GPIO_PIN_RESET}, /*	按键2	*/
  {GPIO_PIN_12, GPIOC, GPIO_PIN_RESET}, /*	按键3	*/
};

static void gpioInit(void)  //  按键GPIO口初始化
{
    GPIO_InitTypeDef GPIO_Init;
  
    /* 第 1 步:打开 GPIO 时钟 */
    __HAL_RCC_GPIOB_CLK_ENABLE();
    __HAL_RCC_GPIOC_CLK_ENABLE();
    __HAL_RCC_GPIOD_CLK_ENABLE();

    /* 第 2 步:配置所有的按键 GPIO 为浮动输入模式*/
    GPIO_Init.Mode = GPIO_MODE_INPUT;
    GPIO_Init.Pull = GPIO_NOPULL;
    GPIO_Init.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
  
    for (uint8_t i = 0; i < KEY_NUM; i++)
    {
        GPIO_Init.Pin = g_sGpioList[i].Pin;
        HAL_GPIO_Init(g_sGpioList[i].Port, &GPIO_Init);
    }
}

static void KeyVarInit(void)  //  初始化按键变量
{
    uint8_t i;
    m_sKeyFifo.Read = 0;
    m_sKeyFifo.Write = 0;
    for (i = 0; i < KEY_NUM; i++)
    {
        g_sKeyCheck[i].LongTime = KEY_LONG_TIME; /* 长按时间 0 表示不检测长按键事件 */
        g_sKeyCheck[i].Count =    KEY_FILTER_TIME / 2; /* 计数器设置为滤波时间的一半 */
        g_sKeyCheck[i].State = 0;       /* 按键缺省状态, 0 为未按下 */
        g_sKeyCheck[i].RepeatSpeed = 0; /* 按键连发的速度, 0 表示不支持连发 */
        g_sKeyCheck[i].RepeatCount = 0; /* 连发计数器 */
    }
}


void keyInit(void)  //  初始化按键
{
    KeyVarInit(); 	/* 初始化按键变量 */
    gpioInit(); 	  /* 初始化按键硬件 */
}


/* 
* 判断按键是否按下
* 返回值 1 表示按下, 0 表示未按下
*/
static uint8_t KeyPinActive(uint8_t id)
{
  GPIO_PinState level = HAL_GPIO_ReadPin(g_sGpioList[id].Port, g_sGpioList[id].Pin);
  if(level == g_sGpioList[id].State)
  {
    return 1;
  }
  else
  {
    return 0;
  }
}  


/**
 * 按键是否按下,限制当前只有一个按键事件
 * 1 表示按下, 0 表示未按下
*/
static uint8_t KeyIsDownFunc(uint8_t id)
{
  if (id < KEY_NUM)  /* 实体单键 */
  {
    uint8_t i;
    uint8_t count = 0;
    uint8_t save = 255;
    /* 判断有几个键按下 */
    for (i = 0; i < KEY_NUM; i++)
    {
      if (KeyPinActive(i))
      {
        count++;
        save = i;
      }
    }
    if (count == 1 && save == id)
    {
      return 1; /* 只有 1 个键按下时才有效 */
    }
    return 0;
  }
  return 0;
}  



/*
* 检测一个按键。
* IO 的 id, 从 0 开始编码
*/
static void KeyDetectKey(uint8_t i)
{
  KEY_CHECK_STRUCT *pBtn;
  pBtn = &g_sKeyCheck[i];
  if (KeyIsDownFunc(i)) /*  1.执行按键按下的处理  */
  {
    if (pBtn->Count < KEY_FILTER_TIME) /*  2.用于按键滤波前给 Count 设置一个初值 */
    {
      pBtn->Count = KEY_FILTER_TIME;
    }
    else if (pBtn->Count < (KEY_FILTER_TIME + 10)) /* 这里实现 KEY_FILTER_TIME 时间长度的延迟 */
    {
      pBtn->Count++;
    } /* 这里实现 KEY_FILTER_TIME 时间长度的延迟 */
    else
    {
      if (pBtn->State == 0)
      {
        pBtn->State = 1;
      }

      if(pBtn->DownState == 1)
      {
        pBtn->DownState = 0;
        if(pBtn->DownCount < 3)
        {
          pBtn->DownCount += 1;
        }
      }

      if (pBtn->LongTime > 0)
      {
        if (pBtn->LongCount < pBtn->LongTime)
        {
          /* 发送按钮持续按下的消息 */
          if (++pBtn->LongCount == pBtn->LongTime)
          {
            KeyPutFifo((uint8_t)(4 * i + 4)); /* 键值放入按键 FIFO */
            pBtn->LongState = 1;
          }
        }
        else
        {
          if (pBtn->RepeatSpeed > 0)
          {
            if (++pBtn->RepeatCount >= pBtn->RepeatSpeed)
            {
              pBtn->RepeatCount = 0;
             
              KeyPutFifo((uint8_t)(4 * i + 4));
            }
          }
        }
      }
    }
  }
  else //	处理按键松手,或按键没有按下的处理
  {
    if (pBtn->Count > KEY_FILTER_TIME)
    {
      pBtn->Count = KEY_FILTER_TIME;
    }
    else if (pBtn->Count != 0)
    {
      pBtn->Count--;
    }
    else
    {
      if (pBtn->State == 1)
      {
        pBtn->State = 0;
        if (pBtn->LongState == 0)
        {
          KeyPutFifo((uint8_t)(4 * i + pBtn->DownCount)); /* 发送按钮弹起的消息 */
        }
        else
        {
          pBtn->LongState = 0;
        }
        pBtn->DownCount = 0;
      }
    }
    
    pBtn->DownState = 1;
    pBtn->LongCount = 0;
    pBtn->RepeatCount = 0;
  }
}
/**
 * 10ms调用一次此函数,轮询按键
*/
void KeyScan10ms(void)
{
    uint8_t i;
    for (i = 0; i < KEY_NUM; i++)
    {
    	KeyDetectKey(i);
    }
}

void KeyTest(KEY_STATE_ENUM KeyHander)
{
  if(KeyHander == 0)
    return;
    char str[50];
    strcpy(str,"0");
    switch (KeyHander)
    {
    case KEY_1_ONE:
      sprintf(str, "KEY 1 ONE");
      break;

    case KEY_1_DOUBLE:
      sprintf(str, "KEY 1 DOUBLE");
      break;

    case KEY_1_THREE:
      sprintf(str, "KEY 1 THREE");
      break;

    case KEY_1_LONG:
      sprintf(str, "KEY 1 LONG");
      break;

    case KEY_2_ONE:
      sprintf(str, "KEY 2 ONE");
      break;

    case KEY_2_DOUBLE:
      sprintf(str, "KEY 2 DOUBLE");
      break;

    case KEY_2_THREE:
      sprintf(str, "KEY 2 THREE");
      break;

    case KEY_2_LONG:
      sprintf(str, "KEY 2 LONG");
      break;

    case KEY_3_ONE:
      sprintf(str, "KEY 3 ONE");
      break;

    case KEY_3_DOUBLE:
      sprintf(str, "KEY 3 DOUBLE");
      break;

    case KEY_3_THREE:
      sprintf(str, "KEY 3 THREE");
      break;

    case KEY_3_LONG:
      sprintf(str, "KEY 3 LONG");
      break;

    default:
      break;
    }
    clearScreen();
    DisplayFont(1, 4, 0, str);
//    usb_printf(str);
}

key.h

#ifndef _KEY_H
#define _KEY_H

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

#define KEY_FIFO_SIZE 10
typedef struct
{
    uint8_t Buf[KEY_FIFO_SIZE]; /* 键值缓冲区 */
    uint8_t Read; 				      /* 缓冲区读指针 1 */
    uint8_t Write; 				      /* 缓冲区写指针 */
}KEY_FIFO_STRUCT;

static KEY_FIFO_STRUCT m_sKeyFifo;       /* 按键 FIFO 变量,结构体 */

typedef enum
{
    KEY_NONE = 0,

    KEY_1_ONE,    /* 单击 */
    KEY_1_DOUBLE, /* 双击  */
    KEY_1_THREE,  /* 三击  */
    KEY_1_LONG,   /* 长按 */

    KEY_2_ONE,
    KEY_2_DOUBLE,
    KEY_2_THREE,
    KEY_2_LONG,

    KEY_3_ONE,
    KEY_3_DOUBLE,
    KEY_3_THREE,
    KEY_3_LONG,  
}KEY_STATE_ENUM;

/*按键滤波时间 50ms, 单位 10ms。
只有连续检测到 50ms 状态不变才认为有效,包括弹起和按下两种事件
即使按键电路不做硬件滤波,该滤波机制也可以保证可靠地检测到按键事件
*/
#define KEY_FILTER_TIME 20  /*  滤波时间,按下200ms才做处理  单位 10ms*/
#define KEY_LONG_TIME   100 /*  长按事件时间 1S 单位 10ms  */
#define KEY_NUM         3   /* 按键个数*/
typedef struct
{
    uint8_t  (*IsKeyDownFunc)(void); /* 按键按下的判断函数,1 表示按下 */
    uint8_t  Count;       /* 滤波器计数器 */
    uint16_t LongCount;   /* 长按计数器 */
    uint16_t LongTime;    /* 按键按下持续时间, 0 表示不检测长按 */
    uint16_t LongState;   /* 长按状态  */
    uint8_t  State;       /* 按键当前状态 */
    uint8_t  DownState;   /* 按键按下状态 */
    uint8_t  DownCount;   /* 按键按下次数 */
    uint8_t  RepeatSpeed; /* 连续按键周期 */
    uint8_t  RepeatCount; /* 连续按键计数器 */
}KEY_CHECK_STRUCT;

static KEY_CHECK_STRUCT g_sKeyCheck[KEY_NUM];  /* 按键检测  */  

void keyInit(void); /*  按键初始化 */
void KeyScan10ms(void);
uint8_t KeyGetState(void);
void KeyTest(KEY_STATE_ENUM KeyHander);
#endif
  • 3
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
1、设计按键FIFO的优点   要介绍实现按键FIFO的优点,首先要了解FIFO的一些基本概念。FIFO即First In First Out,是一种先进先出的数据缓存方式,例如在超市购物之后我们会提着满满的购物车来到收银台排在结账队伍的最后等待付款,先排队的客户先付款离开,后面排队的只有等待前面付款离开才能进行付款。说白了FIFO就是这样一种先进先出机制,先存入的数据在读取时最先被读取到。   设计按键FIFO注意有三个方面的优点(来自于安富莱电子Eric2013大佬总结):   1、可以有效记录按键事件的发生,特别是系统要实现记录按键按下、松开、长按时,使用FIFO来实现是一种不错的选择方式。   2、系统是阻塞的,这样系统在检测到按键按下的情况,由于机械按键抖动的原因不需要在这里等待一段时间,然后在确定按键是否正常按下。   3、按键FIFO程序在系统定时器中定时检测按键状态,确认按键按下后将状态写入FIFO中,不一定在主程序中一直做检测,这样可以有效降低系统资源的消耗。 2、按键的硬件设计   按键的原理图如上图所示,对于KEY0~KEY2这三个按键,一端接地,另一端连接stm32的GPIO端口。当按键按下时相应的IO口被拉低,如果把GPIO口配置为输入模式,此时读取相应的IO口电平,就可以检测到按键是否被按下。对于KEY_UP按键则是与前面三个按键相反,IO口配置为输入模式时,读取到高电平时表示按键按下。因为机械固有的物理特性,按键按下内部弹簧片在瞬间接触的时候会有力学的回弹,造成2-8毫秒内信号不稳定,所以在设计检测机械按键是否按下的程序时,应考虑到按键消抖问题。
下面是一个用结构体编写FIFO(First In First Out)函数的示例代码: ```c #include <stdio.h> #include <stdbool.h> #define MAX_SIZE 10 typedef struct { int buffer[MAX_SIZE]; // 存储数据的缓冲区 int head; // 队列头指针 int tail; // 队列尾指针 bool is_empty; // 标识队列是否为空 bool is_full; // 标识队列是否已满 } fifo; // 初始化FIFO队列 void fifo_init(fifo *f) { f->head = 0; f->tail = 0; f->is_empty = true; f->is_full = false; } // 向FIFO队列中插入一个元素 void fifo_push(fifo *f, int data) { if (f->is_full) { printf("FIFO is full, can't push data.\n"); return; } f->buffer[f->tail] = data; f->tail++; if (f->tail >= MAX_SIZE) { f->tail = 0; } if (f->tail == f->head) { f->is_full = true; } f->is_empty = false; } // 从FIFO队列中取出一个元素 int fifo_pop(fifo *f) { if (f->is_empty) { printf("FIFO is empty, can't pop data.\n"); return -1; } int data = f->buffer[f->head]; f->head++; if (f->head >= MAX_SIZE) { f->head = 0; } if (f->head == f->tail) { f->is_empty = true; } f->is_full = false; return data; } // 获取FIFO队列中元素的数量 int fifo_count(fifo *f) { if (f->is_empty) { return 0; } if (f->is_full) { return MAX_SIZE; } if (f->head < f->tail) { return f->tail - f->head; } else { return MAX_SIZE - f->head + f->tail; } } // 打印FIFO队列中的元素 void fifo_print(fifo *f) { if (f->is_empty) { printf("FIFO is empty.\n"); return; } printf("FIFO elements: "); int count = fifo_count(f); int i = f->head; while (count > 0) { printf("%d ", f->buffer[i]); i++; if (i >= MAX_SIZE) { i = 0; } count--; } printf("\n"); } int main() { fifo f; fifo_init(&f); fifo_push(&f, 1); fifo_push(&f, 2); fifo_push(&f, 3); fifo_push(&f, 4); fifo_push(&f, 5); fifo_print(&f); printf("fifo_pop: %d\n", fifo_pop(&f)); printf("fifo_pop: %d\n", fifo_pop(&f)); fifo_push(&f, 6); fifo_push(&f, 7); fifo_print(&f); return 0; } ``` 在上面的代码中,我们定义了一个名为`fifo`的结构体,用于存储FIFO队列的相关信息,包括一个存储数据的缓冲区`buffer`,一个队列头指针`head`,一个队列尾指针`tail`,以及标识队列是否为空和已满的变量`is_empty`和`is_full`。然后,我们实现了四个FIFO队列操作函数:`fifo_init`用于初始化队列,`fifo_push`用于向队列中插入一个元素,`fifo_pop`用于从队列中取出一个元素,`fifo_count`用于获取队列中元素的数量。最后,我们在`main`函数中使用这些函数来操作FIFO队列,并打印队列中的元素。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值