#include <U8g2lib.h>
#include <Wire.h>

#define buttun_1 2          //按钮1 的引脚号
#define buttun_2 3          //按钮2 的引脚号

U8G2_SH1106_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE);//该驱动是1.3寸的OLED屏幕。
//U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE, /* clock=*/ SCL, /* data=*/ SDA);  //0.9寸的OLED显示器的驱动使用硬件I2C

short x, x_trg; //x当前x坐标值,x_trg 目标x坐标值
short y = 15, y_trg = 15;     //y坐标,目标y坐标
int state;    //状态

byte CD_ChangDu;
u8g2_uint_t str_h;        //菜单字符串高度
u8g2_uint_t str_w;        //菜单字符串宽度
short frame_y , frame_y_trg; //方框的y坐标
short frame_len, frame_len_trg; //方框的宽度
char ui_select = 0;       //菜单成员序号

typedef struct      //定义别名为CAIDAN_LIEBIAO的结构
{
  char *str;        //指针变量用于指向菜单列表成员的地址
} CD_LIEBIAO;


CD_LIEBIAO LieBiao[] = {      //定义结构体数组来存放菜单列表
  {"abcd"}, {"a"}, {"ab"}, {"abc"}
};

typedef struct
{
  byte val;       //按钮键值
  byte last_val;  //上一次的按钮键值
} KEY_T;
KEY_T key[2] = {0}; //置0初始化


typedef struct
{
  byte id;            //按钮编号
  byte press;         //按钮状态
  byte update_flag;   //更新状态
  byte res;           //预留位
} KEY_MSG;              //按钮消息

KEY_MSG key_msg = {0};    //初始化变量值


/***------------获取按钮键值------***/
byte get_io_val (byte ch)
{
  if (ch == 0)          //参数为0返回 按钮1 的信号,否则返回 按钮2 的信号
  {
    return digitalRead(buttun_1);
  }
  else
  {
    return digitalRead(buttun_2);
  }
}
/***===================================================***/




/***------------按钮初始化函数------***/
void key_init(void)     //按钮初始化
{
  for (int i = 0; i < 2; i++)
  {
    key[i].val = key[i].last_val = get_io_val(i);
  }
}
/***===================================================***/

/***---------------------------按钮扫描函数----------------------***/
void key_scan (void)                  //扫描按钮状态
{
  for (int i = 0; i < 2; i++)             //遍历按钮成员
  {
    key[i].val  = get_io_val(i);          //获取当前按钮键值
    if (key[i].val != key[i].last_val)     //如果当前键值与上一次的键值不相等
    {
      key[i].last_val = key[i].val;       //将当前键值保存为上一次键值
      if (key[i].val == 0)               //如果按钮被按下,更新按钮消息
      {
        key_msg.id = i;
        key_msg.press = 1;
        key_msg.update_flag = 1;
      }
    }
  }
}
/***===================================================***/

/***---------------------------菜单执行函数----------------------***/
void ui_proc (void)
{
  if (key_msg.press && key_msg.update_flag)     //如果按钮被按下且已更新键值
  {
    key_msg.update_flag = 0;            //将更新键值状态置为0 (未更新键值)
    if (key_msg.id)                   //判断被按下的是哪个按钮,buttun_1向上移,buttun_2向下移
    {
      ui_select ++;                   //下一个菜单成员

      if (ui_select >= CD_ChangDu)
      {
        ui_select = CD_ChangDu - 1;
      } else
      {
        frame_y_trg += (str_h );
      }
    }
    else
    {
      ui_select --;

      if (ui_select < 0)
      {
        ui_select = 0;
      } else
      {
        frame_y_trg -= (str_h );
      }
    }
    frame_len_trg = u8g2.getUTF8Width(LieBiao[ui_select].str) + 4;       // 获取字符串的像素宽度
  }
  cd_show();
}


/***------------菜单移动(从当前坐标向目标坐标靠近)------***/
int cd_run(short *a, short *a_trg, byte temp) //两个指针变量分别指向代入坐标的地址,加减坐标的步长
{
  if (*a < *a_trg)          //取出两个地址中的数据(坐标值)进行比较,如果当前坐标小于目标坐标
  {
    temp = (*a_trg - *a) > temp ? temp : 1; //如果目标坐标-当前坐标小于步长,步长赋值为1,避免出现负数
    *a += temp;                //加大当前坐标值,向目标坐标靠近
  }
  else if ( *a > *a_trg)
  {
    temp = (*a_trg - *a) < temp ? temp : 1;
    *a -= temp;
  }
  else
  {
    return 0;             //如果两坐标相等,返回0
  }
  return 1;               //不相等,返回1
}
/***===================================================***/


/***-------------------------菜单显示--------------------------***/
void cd_show(void)
{
  u8g2.clearBuffer();         // 清除内部缓冲区
  // u8g2.setFont(u8g2_font_helvB14_tn);                        // 设置数字字体

  for (int i = 0; i < CD_ChangDu ; i++)       //遍历列表成员
  {
    u8g2.drawStr(x + 10, y + i * str_h, LieBiao[i].str); // 输出列表成员
  }
  u8g2.drawRFrame(x + 8, frame_y, frame_len, str_h + 2, 3);
  cd_run(&frame_y, &frame_y_trg, 3);
  cd_run(&frame_len, &frame_len_trg, 4);
  u8g2.sendBuffer();          // 将内存数据传输到显示器
}
/***===================================================***/

/***-------------------------菜单相关数据初始化--------------------------***/
void cd_init (void)
{
  u8g2.begin();                     //U8G2初始化

  u8g2.setFont(u8g2_font_6x13_mr);                     // 设置字体
  CD_ChangDu = sizeof(LieBiao) / sizeof(CD_LIEBIAO);  //计算菜单列表的成员数量
  str_h = u8g2.getAscent() - u8g2.getDescent() + 1;   // 获取字体高度(基准线以上-基准线以下)
  str_w = u8g2.getUTF8Width(LieBiao[0].str);          // 获取字符串的像素宽度
  frame_y = frame_y_trg = y + 1 - str_h;              //初始化方框的y坐标
  frame_len = frame_len_trg = str_w + 4;              //初始化方框的宽度
}

/***===================================================***/

void setup(void)
{
  pinMode(buttun_1, INPUT_PULLUP);
  pinMode(buttun_2, INPUT_PULLUP);
  key_init();     //按钮初始化
  cd_init();      //菜单初始化
  Serial.begin(115200);

}

void loop(void)
{
  key_scan();
  ui_proc();

}