• 基于STM32的简化版智能手表


    一、前言

    本文的OLED多级菜单UI为一个综合性的STM32小项目,使用多传感器与OLED显示屏实现智能终端的效果。项目中的多级菜单UI使用了较为常见的结构体索引法去实现功能与功能之间的来回切换,搭配DHT11,RTC,LED,KEY等器件实现高度智能化一体化操作。

    后期自己打板设计结构,可以衍生为智能手表等小玩意。目前,项目属于裸机状态(CPU占用率100%),后期可能会加上RTOS系统。

    二、硬件实物图

    图片

    温度计:

    图片

    图片

    游戏机:

    图片

    图片

    三、硬件引脚图

    1. OLED模块:
    2. VCC --> 3.3V
    3. GND --> GND
    4. SCL --> PB10
    5. SDA --> PB11
    6. DHT11模块:
    7. DATA --> PB9
    8. VCC --> 3.3V
    9. GND --> GND
    10. KEY模块(这部分笔者直接使用了正点原子精英板上的):
    11. KEY0 --> PE4
    12. KEY1 --> PE3
    13. KEY_UP --> PA0

    四、多级菜单

    随着工业化和自动化的发展,如今基本上所有项目都离不开显示终端。而多级菜单更是终端显示项目中必不可少的组成因素,其实 TFT-LCD 屏幕上可以借鉴移植很多优秀的开源多级菜单(GUI,比如:LVGL),而0.96寸的OLED屏幕上通常需要自己去适配和编程多级菜单。

    图片

    网上的普遍采用的多级菜单的方案是基于索引或者结构树,其中,索引法居多。索引法的优点:可阅读性好,拓展性也不错,查找的性能差不多是最优,就是有点占用内存空间。

    4.1 索引法多级菜单实现

    网上关于索引法实现多级菜单功能有很多基础教程,笔者就按照本项目中的具体实现代码过程给大家讲解一下索引法实现多级菜单。特别说明:本项目直接使用了正点原子的精英板作为核心板,所以读者朋友复现代码还是很简单的。

    首先,基于索引法实现多级菜单的首要条件是先确定项目中将使用到几个功能按键(比如:向前,向后,确定,退出等等)本项目中,笔者使用到了3个按键:下一个(next),确定(enter),退出(back)。所以,接下首先定义一个结构体,结构体中一共有5个变量(3+2),分别为:当前索引序号(current),向下一个(next),确定(enter),退出(back),当前执行函数(void)。其中,标红的为需要设计的按键(笔者这里有3个),标绿的则为固定的索引号与该索引下需要执行的函数。

    1. typedef struct
    2. {
    3.     u8 current;     //当前状态索引号
    4.     u8 next;   //向下一个
    5.     u8 enter;      //确定
    6.     6u8 back;   //退出
    7.     void (*current_operation)(void); //当前状态应该执行的操作
    8. } Menu_table;

    接下来就是定义一个数组去决定整个项目菜单的逻辑顺序(利用索引号)

    1. Menu_table  table[30]=
    2. {
    3.     {0,0,1,0,(*home)}, //一级界面(主页面) 索引,向下一个,确定,退出
    4.   
    5.     {1,2,5,0,(*Temperature)}, //二级界面 温湿度
    6.     {2,3,6,0,(*Palygame)}, //二级界面 游戏
    7.     {3,4,7,0,(*Setting)}, //二级界面 设置
    8.     {4,1,8,0,(*Info)}, //二级界面 信息
    9.   
    10.     {5,5,5,1,(*TestTemperature)},  //三级界面:DHT11测量温湿度
    11.     {6,6,6,2,(*ControlGame)},    //三级界面:谷歌小恐龙Dinogame
    12.     {7,7,9,3,(*Set)},        //三级界面:设置普通外设状态 LED
    13.     {8,8,8,4,(*Information)},    //三级界面:作者和相关项目信息
    14.  
    15.     {9,9,7,3,(*LED)},  //LED控制
    16. };

    这里解释一下这个数组中各元素的意义,由于我们在前面先定义了Menu_table结构体,结构体成员变量分别与数组中元素对应。比如:{0,0,1,0,(*home)},代表了索引号为0,按向下键(next)转入索引号为0,按确定键(enter)转入索引号为1,按退出键(back)转入索引号为0,索引号为0时执行home函数。

    在举一个例子帮助大家理解一下,比如,我们当前程序处在索引号为2(游戏界面),就会执行Playgame函数。此时,如果按下next按键,程序当前索引号就会变为3,并且执行索引号为3时候的Setting函数。如果按下enter按键,程序当前索引号就会变为6,并且执行索引号为6时候的ControlGame函数。如果按下back按键,程序当前索引号就会变为0,并且执行索引号为0时候的home函数。

    再接下就是按键处理函数:

    1. uint8_t  func_index = 0//主程序此时所在程序的索引值
    2.  
    3. void  Menu_key_set(void)
    4. {
    5.   if((KEY_Scan(1) == 1) && (func_index != 6))        //屏蔽掉索引6下的情况,适配游戏
    6.   { 
    7.     func_index=table[func_index].next; //按键next按下后的索引号
    8.     OLED_Clear(); 
    9.   }
    10.  
    11.   if((KEY_Scan(1) == 2) && (func_index != 6))
    12.   {
    13.     func_index=table[func_index].enter; //按键enter按下后的索引号
    14.     OLED_Clear();
    15.   }
    16.  
    17.  if(KEY_Scan(1) == 3)
    18.   {
    19.     func_index=table[func_index].back; //按键back按下后的索引号
    20.     OLED_Clear(); 
    21.   }
    22.  
    23.   current_operation_index=table[func_index].current_operation; //执行当前索引号所对应的功能函数
    24.   (*current_operation_index)();//执行当前操作函数
    25. }
    26.  
    27.  
    28. //按键函数
    29. u8 KEY_Scan(u8 mode)
    30. {
    31.  static u8 key_up=1;
    32.  if(mode)key_up=1
    33.  if(key_up&&(KEY0==0||KEY1==0||WK_UP==1))
    34.  {
    35.   HAL_Delay(100);  //消抖
    36.   key_up=0;
    37.   if(KEY0==0)return 1;
    38.   else if(KEY1==0)return 2;
    39.   else if(WK_UP==1)return 3;
    40.  }else if(KEY0==1&&KEY1==1&&WK_UP==0)key_up=1
    41.  return 0;
    42. }

    说明2点:

    (1)由于是目前本项目是裸机状态下运行的,所以CPU占用率默认是100%的,所以这里使用按键支持连按时,对于菜单的切换更好些。

    (2)可能部分索引号下的执行函数,需要使用到已经定义的3个按键(比如,本项目中的DInogame中)。所以,可以在需要差别化的索引号下去屏蔽原先的按键功能。如下:

    1.   if((KEY_Scan(1) == 1) && (func_index != 6))        //屏蔽掉索引6下的情况,适配游戏
    2.   { 
    3.     func_index=table[func_index].next; //按键next按下后的索引号
    4.     OLED_Clear(); 
    5.   }
    6.  
    7.   if((KEY_Scan(1) == 2) && (func_index != 6))        //屏蔽掉索引6下的情况,适配游戏
    8.   {
    9.     func_index=table[func_index].enter; //按键enter按下后的索引号
    10.     OLED_Clear();
    11.   }

    (3)笔者这里是使用全屏刷新去切换功能界面,同时,没有启用高级算法去加速显示,所以可能在切换界面的时候效果一般。读者朋友可以试试根据自己的UI情况使用局部刷新,这样可能项目会更加丝滑一点。

    本项目中的菜单索引图:

    图片

    4.2 内部功能实现(简化智能手表)

    OLED就是正常的驱动与显示,有能力的读者朋友可以使用高级算法去加速OLED屏幕的刷新率,可以使自己的多级菜单切换起来更丝滑。

    唯一需要注意的点就是需要去制作菜单里面的UI图标(注意图片大小是否合适):

    图片

    如果是黑白图片的话,可以直接使用PCtoLCD2002完美版进行取模:

    图片

    4.3 KEY按键

    KEY按键注意消抖(建议裸机情况下支持连续按动),同时注意自己实际硬件情况去进行编程(电阻是否存在上拉或者下拉)。

    图片

    4.4 DinoGame实现

    图片

    谷歌公司最近比较流行的小游戏,笔者之前有文章进行了STM32的成功复刻。博客地址:基于STM32的小游戏——谷歌小恐龙(Chrome Dino Game)_混分巨兽龙某某的博客-CSDN博客_谷歌恐龙

    4.5 LED控制和DHT11模块

    LED和DHT11模块其实都属于外设控制,这里读者朋友可以根据自己的实际情况去取舍。需要注意的是尽可能适配一下自己多级菜单(外设控制也需要注意一下按键安排,可以参考笔者项目的设计)。

    五、CubeMX配置

    1、RCC配置外部高速晶振(精度更高)——HSE;

    图片

    2、SYS配置:Debug设置成Serial Wire(否则可能导致芯片自锁);

    图片

    3、I2C2配置:这里不直接使用CubeMX的I2C2,使用GPIO模拟(PB10:CLK;PB11:SDA)

    图片

    4、RTC配置:年月日,时分秒;

    图片

    图片

    5、TIM2配置:由上面可知DHT11的使用需要us级的延迟函数,HAL库自带只有ms的,所以需要自己设计一个定时器;

    图片

    6、KEY按键配置:PE3,PE4和PA0设置为端口输入(开发板原理图)

    图片

    7、时钟树配置:

    图片

    8、文件配置

    图片

    六、代码

    6.1 OLED驱动代码

    此部分OLED的基本驱动函数,笔者使用的是I2C驱动的0.96寸OLED屏幕。所以,首先需要使用GPIO模拟I2C通讯。随后,使用I2C通讯去驱动OLED。(此部分代码包含了屏幕驱动与基础显示)

    oled.h:

    1. #ifndef __OLED_H
    2. #define __OLED_H
    3.  
    4. #include "main.h"
    5.  
    6. #define u8 uint8_t
    7. #define u32 uint32_t
    8.  
    9. #define OLED_CMD  0 //写命令
    10. #define OLED_DATA 1 //写数据
    11.  
    12. #define OLED0561_ADD 0x78  // OLED I2C地址
    13. #define COM    0x00  // OLED 
    14. #define DAT    0x40  // OLED 
    15.  
    16. #define OLED_MODE 0
    17. #define SIZE 8
    18. #define XLevelL  0x00
    19. #define XLevelH  0x10
    20. #define Max_Column 128
    21. #define Max_Row  64
    22. #define Brightness 0xFF
    23. #define X_WIDTH  128
    24. #define Y_WIDTH  64
    25.  
    26.  
    27. //-----------------OLED IIC GPIO进行模拟----------------
    28.  
    29. #define OLED_SCLK_Clr() HAL_GPIO_WritePin(GPIOB, GPIO_PIN_10, GPIO_PIN_RESET) //GPIO_ResetBits(GPIOB,GPIO_Pin_10)//SCL
    30. #define OLED_SCLK_Set() HAL_GPIO_WritePin(GPIOB, GPIO_PIN_10, GPIO_PIN_SET) //GPIO_SetBits(GPIOB,GPIO_Pin_10)
    31.  
    32. #define OLED_SDIN_Clr() HAL_GPIO_WritePin(GPIOB, GPIO_PIN_11, GPIO_PIN_RESET) // GPIO_ResetBits(GPIOB,GPIO_Pin_11)//SDA
    33. #define OLED_SDIN_Set() HAL_GPIO_WritePin(GPIOB, GPIO_PIN_11, GPIO_PIN_SET) // GPIO_SetBits(GPIOB,GPIO_Pin_11)
    34.  
    35.  
    36. //I2C GPIO模拟
    37. void IIC_Start();
    38. void IIC_Stop();
    39. void IIC_WaitAck();
    40. void IIC_WriteByte(unsigned char IIC_Byte);
    41. void IIC_WriteCommand(unsigned char IIC_Command);
    42. void IIC_WriteData(unsigned char IIC_Data);
    43. void OLED_WR_Byte(unsigned dat,unsigned cmd);
    44.  
    45.  
    46. //功能函数
    47. void OLED_Init(void);
    48. void OLED_WR_Byte(unsigned dat,unsigned cmd);
    49.  
    50. void OLED_FillPicture(unsigned char fill_Data);
    51. void OLED_SetPos(unsigned char x, unsigned char y);
    52. void OLED_DisplayOn(void);
    53. void OLED_DisplayOff(void);
    54. void OLED_Clear(void);
    55. void OLED_On(void);
    56. void OLED_ShowChar(u8 x,u8 y,u8 chr,u8 Char_Size);
    57. u32 oled_pow(u8 m,u8 n);
    58. void OLED_ShowNum(u8 x,u8 y,u32 num,u8 len,u8 size2);
    59. void OLED_ShowString(u8 x,u8 y,u8 *chr,u8 Char_Size);
    60.  
    61. #endif

    oled.c:

    1. #include "oled.h"
    2. #include "asc.h"    //字库(可以自己制作)
    3. #include "main.h"
    4.  
    5.  
    6.  
    7. /********************GPIO 模拟I2C*******************/
    8. //注意:这里没有直接使用HAL库中的模拟I2C
    9. /**********************************************
    10. //IIC Start
    11. **********************************************/
    12. void IIC_Start()
    13. {
    14.  
    15.  OLED_SCLK_Set() ;
    16.  OLED_SDIN_Set();
    17.  OLED_SDIN_Clr();
    18.  OLED_SCLK_Clr();
    19. }
    20.  
    21. /**********************************************
    22. //IIC Stop
    23. **********************************************/
    24. void IIC_Stop()
    25. {
    26.  OLED_SCLK_Set() ;
    27.  OLED_SDIN_Clr();
    28.  OLED_SDIN_Set();
    29.  
    30. }
    31.  
    32. void IIC_WaitAck()
    33. {
    34.  OLED_SCLK_Set() ;
    35.  OLED_SCLK_Clr();
    36. }
    37. /**********************************************
    38. // IIC Write byte
    39. **********************************************/
    40.  
    41. void IIC_WriteByte(unsigned char IIC_Byte)
    42. {
    43.  unsigned char i;
    44.  unsigned char m,da;
    45.  da=IIC_Byte;
    46.  OLED_SCLK_Clr();
    47.  for(i=0;i<8;i++)
    48.  {
    49.    m=da;
    50.   // OLED_SCLK_Clr();
    51.   m=m&0x80;
    52.   if(m==0x80)
    53.   {OLED_SDIN_Set();}
    54.   else OLED_SDIN_Clr();
    55.    da=da<<1;
    56.   OLED_SCLK_Set();
    57.   OLED_SCLK_Clr();
    58.  }
    59.  
    60.  
    61. }
    62. /**********************************************
    63. // IIC Write Command
    64. **********************************************/
    65. void IIC_WriteCommand(unsigned char IIC_Command)
    66. {
    67.    IIC_Start();
    68.    IIC_WriteByte(0x78);            //Slave address,SA0=0
    69.  IIC_WaitAck();
    70.    IIC_WriteByte(0x00);   //write command
    71.  IIC_WaitAck();
    72.    IIC_WriteByte(IIC_Command);
    73.  IIC_WaitAck();
    74.    IIC_Stop();
    75. }
    76. /**********************************************
    77. // IIC Write Data
    78. **********************************************/
    79. void IIC_WriteData(unsigned char IIC_Data)
    80. {
    81.    IIC_Start();
    82.    IIC_WriteByte(0x78);   //D/C#=0; R/W#=0
    83.  IIC_WaitAck();
    84.    IIC_WriteByte(0x40);   //write data
    85.  IIC_WaitAck();
    86.    IIC_WriteByte(IIC_Data);
    87.  IIC_WaitAck();
    88.    IIC_Stop();
    89. }
    90.  
    91. void OLED_WR_Byte(unsigned dat,unsigned cmd)
    92. {
    93.  if(cmd)
    94.  {
    95.   IIC_WriteData(dat);
    96.  }
    97.  else
    98.  {
    99.   IIC_WriteCommand(dat);
    100.  }
    101. }
    102.  
    103. void OLED_Init(void)
    104. {
    105.  HAL_Delay(100);  //这个延迟很重要
    106.  
    107.  OLED_WR_Byte(0xAE,OLED_CMD);//--display off
    108.  OLED_WR_Byte(0x00,OLED_CMD);//---set low column address
    109.  OLED_WR_Byte(0x10,OLED_CMD);//---set high column address
    110.  OLED_WR_Byte(0x40,OLED_CMD);//--set start line address
    111.  OLED_WR_Byte(0xB0,OLED_CMD);//--set page address
    112.  OLED_WR_Byte(0x81,OLED_CMD); // contract control
    113.  OLED_WR_Byte(0xFF,OLED_CMD);//--128
    114.  OLED_WR_Byte(0xA1,OLED_CMD);//set segment remap
    115.  OLED_WR_Byte(0xA6,OLED_CMD);//--normal / reverse
    116.  OLED_WR_Byte(0xA8,OLED_CMD);//--set multiplex ratio(1 to 64)
    117.  OLED_WR_Byte(0x3F,OLED_CMD);//--1/32 duty
    118.  OLED_WR_Byte(0xC8,OLED_CMD);//Com scan direction
    119.  OLED_WR_Byte(0xD3,OLED_CMD);//-set display offset
    120.  OLED_WR_Byte(0x00,OLED_CMD);//
    121.  
    122.  OLED_WR_Byte(0xD5,OLED_CMD);//set osc division
    123.  OLED_WR_Byte(0x80,OLED_CMD);//
    124.  
    125.  OLED_WR_Byte(0xD8,OLED_CMD);//set area color mode off
    126.  OLED_WR_Byte(0x05,OLED_CMD);//
    127.  
    128.  OLED_WR_Byte(0xD9,OLED_CMD);//Set Pre-Charge Period
    129.  OLED_WR_Byte(0xF1,OLED_CMD);//
    130.  
    131.  OLED_WR_Byte(0xDA,OLED_CMD);//set com pin configuartion
    132.  OLED_WR_Byte(0x12,OLED_CMD);//
    133.  
    134.  OLED_WR_Byte(0xDB,OLED_CMD);//set Vcomh
    135.  OLED_WR_Byte(0x30,OLED_CMD);//
    136.  
    137.  OLED_WR_Byte(0x8D,OLED_CMD);//set charge pump enable
    138.  OLED_WR_Byte(0x14,OLED_CMD);//
    139.  
    140.  OLED_WR_Byte(0xAF,OLED_CMD);//--turn on oled panel
    141.  HAL_Delay(100); 
    142.  OLED_FillPicture(0x0);
    143.  
    144. }
    145.  
    146.  
    147. /********************************************
    148. // OLED_FillPicture
    149. ********************************************/
    150. void OLED_FillPicture(unsigned char fill_Data)
    151. {
    152.  unsigned char m,n;
    153.  for(m=0;m<8;m++)
    154.  {
    155.   OLED_WR_Byte(0xb0+m,0);  //page0-page1
    156.   OLED_WR_Byte(0x00,0);  //low column start address
    157.   OLED_WR_Byte(0x10,0);  //high column start address
    158.   for(n=0;n<128;n++)
    159.    {
    160.     OLED_WR_Byte(fill_Data,1);
    161.    }
    162.  }
    163. }
    164.  
    165. //坐标设置
    166. void OLED_SetPos(unsigned char x, unsigned char y)
    167. {  OLED_WR_Byte(0xb0+y,OLED_CMD);
    168.  OLED_WR_Byte(((x&0xf0)>>4)|0x10,OLED_CMD);
    169.  OLED_WR_Byte((x&0x0f),OLED_CMD);
    170. }
    171. //开启OLED显示
    172. void OLED_DisplayOn(void)
    173. {
    174.  OLED_WR_Byte(0X8D,OLED_CMD);  //SET DCDC命令
    175.  OLED_WR_Byte(0X14,OLED_CMD);  //DCDC ON
    176.  OLED_WR_Byte(0XAF,OLED_CMD);  //DISPLAY ON
    177. }
    178. //关闭OLED显示
    179. void OLED_DisplayOff(void)
    180. {
    181.  OLED_WR_Byte(0X8D,OLED_CMD);  //SET DCDC命令
    182.  OLED_WR_Byte(0X10,OLED_CMD);  //DCDC OFF
    183.  OLED_WR_Byte(0XAE,OLED_CMD);  //DISPLAY OFF
    184. }
    185. //清屏函数,清完屏,整个屏幕是黑色的!和没点亮一样!!!
    186. void OLED_Clear(void)
    187. {
    188.  u8 i,n;
    189.  for(i=0;i<8;i++)
    190.  {
    191.   OLED_WR_Byte (0xb0+i,OLED_CMD);    //设置页地址(0~7)
    192.   OLED_WR_Byte (0x00,OLED_CMD);      //设置显示位置—列低地址
    193.   OLED_WR_Byte (0x10,OLED_CMD);      //设置显示位置—列高地址
    194.   for(n=0;n<128;n++)OLED_WR_Byte(0,OLED_DATA);
    195.  } //更新显示
    196. }
    197. void OLED_On(void)
    198. {
    199.  u8 i,n;
    200.  for(i=0;i<8;i++)
    201.  {
    202.   OLED_WR_Byte (0xb0+i,OLED_CMD);    //设置页地址(0~7)
    203.   OLED_WR_Byte (0x00,OLED_CMD);      //设置显示位置—列低地址
    204.   OLED_WR_Byte (0x10,OLED_CMD);      //设置显示位置—列高地址
    205.   for(n=0;n<128;n++)OLED_WR_Byte(1,OLED_DATA);
    206.  } //更新显示
    207. }
    208. //在指定位置显示一个字符,包括部分字符
    209. //x:0~127
    210. //y:0~63
    211. //mode:0,反白显示;1,正常显示
    212. //size:选择字体 16/12
    213. void OLED_ShowChar(u8 x,u8 y,u8 chr,u8 Char_Size)
    214. {
    215.  unsigned char c=0,i=0;
    216.   c=chr-' ';//得到偏移后的值
    217.   if(x>Max_Column-1){x=0;y=y+2;}
    218.   if(Char_Size ==16)
    219.    {
    220.    OLED_SetPos(x,y);
    221.    for(i=0;i<8;i++)
    222.    OLED_WR_Byte(F8X16[c*16+i],OLED_DATA);
    223.    OLED_SetPos(x,y+1);
    224.    for(i=0;i<8;i++)
    225.    OLED_WR_Byte(F8X16[c*16+i+8],OLED_DATA);
    226.    }
    227.    else {
    228.     OLED_SetPos(x,y);
    229.     for(i=0;i<6;i++)
    230.     OLED_WR_Byte(F6x8[c][i],OLED_DATA);
    231.  
    232.    }
    233. }
    234.  
    235. //m^n函数
    236. u32 oled_pow(u8 m,u8 n)
    237. {
    238.  u32 result=1;
    239.  while(n--)result*=m;
    240.  return result;
    241. }
    242.  
    243. //显示2个数字
    244. //x,y :起点坐标
    245. //len :数字的位数
    246. //size:字体大小
    247. //mode:模式 0,填充模式;1,叠加模式
    248. //num:数值(0~4294967295);
    249. void OLED_ShowNum(u8 x,u8 y,u32 num,u8 len,u8 size2)
    250. {
    251.  u8 t,temp;
    252.  u8 enshow=0;
    253.  for(t=0;t
    254.  {
    255.   temp=(num/oled_pow(10,len-t-1))%10;
    256.   if(enshow==0&&t<(len-1))
    257.   {
    258.    if(temp==0)
    259.    {
    260. //    OLED_ShowChar(x+(size2/2)*t,y,' ',size2);
    261.     OLED_ShowChar(x+(size2/2)*t,y,'0',size2);
    262.     continue;
    263.    }else enshow=1;
    264.  
    265.   }
    266.    OLED_ShowChar(x+(size2/2)*t,y,temp+'0',size2);
    267.  }
    268. }
    269.  
    270. //显示一个字符号串
    271. void OLED_ShowString(u8 x,u8 y,u8 *chr,u8 Char_Size)
    272. {
    273.  unsigned char j=0;
    274.  while (chr[j]!='\0')
    275.  {  OLED_ShowChar(x,y,chr[j],Char_Size);
    276.    x+=8;
    277.   if(x>120){x=0;y+=2;}
    278.    j++;
    279.  }
    280. }

    6.2 谷歌小恐龙游戏图形绘制代码

    该部分为整个项目代码的核心部分之一,任何一个游戏都是需要去绘制和构建游戏的图形以及模型的。好的游戏往往都具有很好的游戏模型和精美UI,很多3A大作都具备这样的特性。

    dinogame.h:

    1. #ifndef __DINOGAME_H
    2. #define __DINOGAME_H
    3.  
    4. void OLED_DrawBMP(unsigned char x0, unsigned char y0,unsigned char x1, unsigned char y1,unsigned char BMP[]);
    5. void OLED_DrawBMPFast(const unsigned char BMP[]);
    6. void oled_drawbmp_block_clear(int bx, int by, int clear_size);
    7. void OLED_DrawGround();
    8. void OLED_DrawCloud();
    9. void OLED_DrawDino();
    10. void OLED_DrawCactus();
    11. int OLED_DrawCactusRandom(unsigned char ver, unsigned char reset);
    12. int OLED_DrawDinoJump(char reset);
    13. void OLED_DrawRestart();
    14. void OLED_DrawCover();
    15.  
    16. #endif

    dinogame.c代码:

    1. #include "oled.h"
    2. #include "oledfont.h"
    3. #include "stdlib.h"
    4.  
    5. /***********功能描述:显示显示BMP图片128×64起始点坐标(x,y),x的范围0~127,y为页的范围0~7*****************/
    6. void OLED_DrawBMP(unsigned char x0, unsigned char y0,unsigned char x1, unsigned char y1,unsigned char BMP[])
    7. {
    8.  unsigned int j=0;
    9.  unsigned char x,y;
    10.  
    11.   if(y1%8==0) y=y1/8;
    12.   else y=y1/8+1;
    13.  for(y=y0;y
    14.  {
    15.   OLED_SetPos(x0,y);
    16.   for(x=x0;x
    17.      {
    18.       OLED_WR_Byte(BMP[j++],OLED_DATA);
    19.      }
    20.  }
    21. }
    22.  
    23.  
    24.  
    25. // 快速绘制图像
    26. void OLED_DrawBMPFast(const unsigned char BMP[])
    27. {
    28.  unsigned int j = 0;
    29.  unsigned char x, y;
    30.  
    31.  for (y = 0; y < 8; y++)
    32.  {
    33.   OLED_SetPos(0, y);
    34.   IIC_Start();
    35.   IIC_WriteByte(0x78);
    36.   IIC_WaitAck();
    37.   IIC_WriteByte(0x40);
    38.   IIC_WaitAck();
    39.   for (x = 0; x < 128; x++)
    40.   {
    41.    IIC_WriteByte(BMP[j++]);
    42.    IIC_WaitAck();
    43.   }
    44.   IIC_Stop();
    45.  }
    46. }
    47.  
    48. void oled_drawbmp_block_clear(int bx, int by, int clear_size)
    49. {
    50.  unsigned int i;
    51.  OLED_SetPos(bx, by);
    52.  IIC_Start();
    53.  IIC_WriteByte(0x78);
    54.  IIC_WaitAck();
    55.  IIC_WriteByte(0x40);
    56.  IIC_WaitAck();
    57.  
    58.  for (i = 0; i < clear_size; i++)
    59.  {
    60.   if (bx + i>128break;
    61.   IIC_WriteByte(0x0);
    62.   IIC_WaitAck();
    63.  }
    64.  IIC_Stop();
    65. }
    66.  
    67. void OLED_DrawGround()
    68. {
    69.  static unsigned int pos = 0;
    70.  unsigned char speed = 5;
    71.  unsigned int ground_length = sizeof(GROUND);
    72.  unsigned char x;
    73.  
    74.  OLED_SetPos(07);
    75.  IIC_Start();
    76.  IIC_WriteByte(0x78);
    77.  IIC_WaitAck();
    78.  IIC_WriteByte(0x40);
    79.  IIC_WaitAck();
    80.  for (x = 0; x < 128; x++)
    81.  {
    82.   IIC_WriteByte(GROUND[(x+pos)%ground_length]);
    83.   IIC_WaitAck();
    84.  }
    85.  IIC_Stop();
    86.  
    87.  pos = pos + speed;
    88.  //if(pos>ground_length) pos=0;
    89. }
    90.  
    91.  
    92. // 绘制云朵
    93. void OLED_DrawCloud()
    94. {
    95.  static int pos = 128;
    96.  static char height=0;
    97.  char speed = 3;
    98.  unsigned int i=0;
    99.  int x;
    100.  int start_x = 0;
    101.  int length = sizeof(CLOUD);
    102.  unsigned char byte;
    103.  
    104.  //if (pos + length <= -speed) pos = 128;
    105.  
    106.  if (pos + length <= -speed)
    107.  {
    108.   pos = 128;
    109.   height = rand()%3;
    110.  }
    111.  if(pos < 0)
    112.  {
    113.   start_x = -pos;
    114.   OLED_SetPos(01+height);
    115.  }
    116.  else
    117.  {
    118.   OLED_SetPos(pos, 1+height);
    119.  }
    120.  
    121.  IIC_Start();
    122.  IIC_WriteByte(0x78);
    123.  IIC_WaitAck();
    124.  IIC_WriteByte(0x40);
    125.  IIC_WaitAck();
    126.  for (x = start_x; x < length + speed; x++)
    127.  {
    128.   if (pos + x > 127break;
    129.   if (x < length) byte = CLOUD[x];
    130.   else byte = 0x0;
    131.  
    132.   IIC_WriteByte(byte);
    133.   IIC_WaitAck();
    134.  }
    135.  IIC_Stop();
    136.  
    137.  pos = pos - speed;
    138. }
    139.  
    140. // 绘制小恐龙
    141. void OLED_DrawDino()
    142. {
    143.  static unsigned char dino_dir = 0;
    144.  unsigned int j=0;
    145.  unsigned char x, y;
    146.  unsigned char byte;
    147.  
    148.  dino_dir++;
    149.  dino_dir = dino_dir%2;
    150.  for(y=0; y<2; y++)
    151.  {
    152.   OLED_SetPos(166+y);
    153.   IIC_Start();
    154.   IIC_WriteByte(0x78);
    155.   IIC_WaitAck();
    156.   IIC_WriteByte(0x40);
    157.   IIC_WaitAck();
    158.   for (x = 0; x < 16; x++)
    159.   {
    160.    j = y*16 + x;
    161.    byte = DINO[dino_dir][j];
    162.  
    163.    IIC_WriteByte(byte);
    164.    IIC_WaitAck();
    165.   }
    166.   IIC_Stop();
    167.  }
    168. }
    169.  
    170. // 绘制仙人掌障碍物
    171. void OLED_DrawCactus()
    172. {
    173.  char speed = 5;
    174.  static int pos = 128;
    175.  int start_x = 0;
    176.  int length = sizeof(CACTUS_2)/2;
    177.  
    178.  unsigned int j=0;
    179.  unsigned char x, y;
    180.  unsigned char byte;
    181.  
    182.  if (pos + length <= 0)
    183.  {
    184.   oled_drawbmp_block_clear(06, speed);
    185.   pos = 128;
    186.  }
    187.  
    188.  for(y=0; y<2; y++)
    189.  {
    190.   if(pos < 0)
    191.   {
    192.    start_x = -pos;
    193.    OLED_SetPos(06+y);
    194.   }
    195.   else
    196.   {
    197.    OLED_SetPos(pos, 6+y);
    198.   }
    199.  
    200.   IIC_Start();
    201.   IIC_WriteByte(0x78);
    202.   IIC_WaitAck();
    203.   IIC_WriteByte(0x40);
    204.   IIC_WaitAck();
    205.  
    206.   for (x = start_x; x < length; x++)
    207.   {
    208.    if (pos + x > 127break;
    209.    j = y*length + x;
    210.    byte = CACTUS_2[j];
    211.    IIC_WriteByte(byte);
    212.    IIC_WaitAck();
    213.   }
    214.   IIC_Stop();
    215.  }
    216.  oled_drawbmp_block_clear(pos + length, 6, speed); // 清除残影
    217.  pos = pos - speed;
    218. }
    219.  
    220.  
    221. // 绘制随机出现的仙人掌障碍物
    222. int OLED_DrawCactusRandom(unsigned char ver, unsigned char reset)
    223. {
    224.  char speed = 5;
    225.  static int pos = 128;
    226.  int start_x = 0;
    227.  int length = 0;
    228.  
    229.  unsigned int i=0, j=0;
    230.  unsigned char x, y;
    231.  unsigned char byte;
    232.  if (reset == 1)
    233.  {
    234.   pos = 128;
    235.   oled_drawbmp_block_clear(06, speed);
    236.   return 128;
    237.  }
    238.  if (ver == 0) length = 8//sizeof(CACTUS_1) / 2;
    239.  else if (ver == 1) length = 16//sizeof(CACTUS_2) / 2;
    240.  else if (ver == 2 || ver == 3) length = 24;
    241.  
    242.  for(y=0; y<2; y++)
    243.  {
    244.   if(pos < 0)
    245.   {
    246.    start_x = -pos;
    247.    OLED_SetPos(06+y);
    248.   }
    249.   else
    250.   {
    251.    OLED_SetPos(pos, 6+y);
    252.   }
    253.  
    254.   IIC_Start();
    255.   IIC_WriteByte(0x78);
    256.   IIC_WaitAck();
    257.   IIC_WriteByte(0x40);
    258.   IIC_WaitAck();
    259.  
    260.   for (x = start_x; x < length; x++)
    261.   {
    262.    if (pos + x > 127break;
    263.  
    264.    j = y*length + x;
    265.    if (ver == 0) byte = CACTUS_1[j];
    266.    else if (ver == 1) byte = CACTUS_2[j];
    267.    else if(ver == 2) byte = CACTUS_3[j];
    268.    else byte = CACTUS_4[j];
    269.  
    270.    IIC_WriteByte(byte);
    271.    IIC_WaitAck();
    272.   }
    273.   IIC_Stop();
    274.  }
    275.  
    276.  oled_drawbmp_block_clear(pos + length, 6, speed);
    277.  
    278.  pos = pos - speed;
    279.  return pos + speed;
    280. }
    281.  
    282.  
    283.  
    284.  
    285. // 绘制跳跃小恐龙
    286. int OLED_DrawDinoJump(char reset)
    287. {
    288.  char speed_arr[] = {113344567};
    289.  static char speed_idx = sizeof(speed_arr)-1;
    290.  static int height = 0;
    291.  static char dir = 0;
    292.  //char speed = 4;
    293.  
    294.  unsigned int j=0;
    295.  unsigned char x, y;
    296.  char offset = 0;
    297.  unsigned char byte;
    298.  if(reset == 1)
    299.  {
    300.   height = 0;
    301.   dir = 0;
    302.   speed_idx = sizeof(speed_arr)-1;
    303.   return 0;
    304.  }
    305.  if (dir==0)
    306.  {
    307.   height += speed_arr[speed_idx];
    308.   speed_idx --;
    309.   if (speed_idx<0) speed_idx = 0;
    310.  }
    311.  if (dir==1)
    312.  {
    313.   height -= speed_arr[speed_idx];
    314.   speed_idx ++;
    315.   if (speed_idx>sizeof(speed_arr)-1) speed_idx = sizeof(speed_arr)-1;
    316.  }
    317.  if(height >= 31)
    318.  {
    319.   dir = 1;
    320.   height = 31;
    321.  }
    322.  if(height <= 0)
    323.  {
    324.   dir = 0;
    325.   height = 0;
    326.  }
    327.  if(height <= 7) offset = 0;
    328.  else if(height <= 15) offset = 1;
    329.  else if(height <= 23) offset = 2;
    330.  else if(height <= 31) offset = 3;
    331.  else offset = 4;
    332.  
    333.  for(y=0; y<3; y++) // 4
    334.  {
    335.   OLED_SetPos(165- offset + y);
    336.  
    337.   IIC_Start();
    338.   IIC_WriteByte(0x78);
    339.   IIC_WaitAck();
    340.   IIC_WriteByte(0x40);
    341.   IIC_WaitAck();
    342.   for (x = 0; x < 16; x++) // 32
    343.   {
    344.    j = y*16 + x; // 32
    345.    byte = DINO_JUMP[height%8][j];
    346.  
    347.    IIC_WriteByte(byte);
    348.    IIC_WaitAck();
    349.   }
    350.   IIC_Stop();
    351.  }
    352.  if (dir == 0oled_drawbmp_block_clear(168- offset, 16);
    353.  if (dir == 1oled_drawbmp_block_clear(164- offset, 16);
    354.  return height;
    355. }
    356.  
    357. // 绘制重启
    358. void OLED_DrawRestart()
    359. {
    360.  unsigned int j=0;
    361.  unsigned char x, y;
    362.  unsigned char byte;
    363.  //OLED_SetPos(0, 0);
    364.  for (y = 2; y < 5; y++)
    365.  {
    366.   OLED_SetPos(52, y);
    367.   IIC_Start();
    368.   IIC_WriteByte(0x78);
    369.   IIC_WaitAck();
    370.   IIC_WriteByte(0x40);
    371.   IIC_WaitAck();
    372.   for (x = 0; x < 24; x++)
    373.   {
    374.    byte = RESTART[j++];
    375.    IIC_WriteByte(byte);
    376.    IIC_WaitAck();
    377.   }
    378.   IIC_Stop();
    379.  }
    380.  OLED_ShowString(103"GAME"16);
    381.  OLED_ShowString(863"OVER"16);
    382. }
    383. // 绘制封面
    384. void OLED_DrawCover()
    385. {
    386.  OLED_DrawBMPFast(COVER);
    387. }

    6.3 谷歌小恐龙的运行控制代码

    control.h:

    1. #ifndef __CONTROL_H
    2. #define __CONTROL_H
    3.  
    4. int get_key();
    5. void Game_control();
    6.  
    7. #endif****

    control.c:

    1. #include "control.h"
    2. #include "oled.h"
    3. #include "dinogame.h"
    4. #include "stdlib.h"
    5.  
    6. unsigned char key_num = 0;
    7. unsigned char cactus_category = 0;
    8. unsigned char cactus_length = 8;
    9. unsigned int score = 0;
    10. unsigned int highest_score = 0;
    11. int height = 0;
    12. int cactus_pos = 128;
    13. unsigned char cur_speed = 30;
    14. char failed = 0;
    15. char reset = 0;
    16.  
    17.  
    18. int get_key()
    19. {
    20.  if(HAL_GPIO_ReadPin(GPIOE, GPIO_PIN_4)==0)
    21.  {
    22.   HAL_Delay(10);            //延迟
    23.   if(HAL_GPIO_ReadPin(GPIOE, GPIO_PIN_4)==0)
    24.   {
    25.   return 2;
    26.   }
    27.  }
    28.  
    29.  if(HAL_GPIO_ReadPin(GPIOE, GPIO_PIN_3)==0)
    30.  {
    31.   HAL_Delay(10);            //延迟
    32.   if(HAL_GPIO_ReadPin(GPIOE, GPIO_PIN_3)==0)
    33.   {
    34.   return 1;
    35.   }
    36.  }
    37.  
    38.  if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0)==1)
    39.  {
    40.   HAL_Delay(10);            //延迟
    41.   if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0)==1)
    42.   {
    43.   return 3;
    44.   }
    45.  }
    46.  
    47.  return 0;
    48. }
    49.  
    50. void Game_control()
    51. {
    52.  
    53.  while(1)
    54.  {
    55.  
    56.   if(get_key() == 3)  //wk_up按键按下强制退出一次循环
    57.   {
    58.    break;
    59.   }
    60.   
    61.    if (failed == 1)
    62.   {
    63.    OLED_DrawRestart();
    64.  
    65.    key_num = get_key();
    66.    if (key_num == 2)
    67.    {
    68.     if(score > highest_score) highest_score = score;
    69.     score = 0;
    70.     failed = 0;
    71.     height = 0;
    72.     reset = 1;
    73.     OLED_DrawDinoJump(reset);
    74.     OLED_DrawCactusRandom(cactus_category, reset);
    75.     OLED_Clear();
    76.    }
    77.    continue;
    78.   }
    79.  
    80.  
    81.   score ++;
    82.   if (height <= 0) key_num = get_key();
    83.  
    84.   OLED_DrawGround();
    85.   OLED_DrawCloud();
    86.  
    87.   if (height>0 || key_num == 1) height = OLED_DrawDinoJump(reset);
    88.   else OLED_DrawDino();
    89.  
    90.   cactus_pos = OLED_DrawCactusRandom(cactus_category, reset);
    91.   if(cactus_category == 0) cactus_length = 8;
    92.   else if(cactus_category == 1) cactus_length = 16;
    93.   else cactus_length = 24;
    94.  
    95.   if (cactus_pos + cactus_length < 0)
    96.   {
    97.     cactus_category = rand()%4;
    98.    OLED_DrawCactusRandom(cactus_category, 1);
    99.   }
    100.  
    101.   if ((height < 16) && ( (cactus_pos>=16 && cactus_pos <=32) || (cactus_pos + cactus_length>=16 && cactus_pos + cactus_length <=32)))
    102.   {
    103.    failed = 1;
    104.   }
    105.  
    106.   
    107.   OLED_ShowString(350"HI:"12);
    108.   OLED_ShowNum(580, highest_score, 512);
    109.   OLED_ShowNum(980, score, 512);
    110.  
    111.  
    112.   reset = 0;
    113.  
    114.   cur_speed = score/20;
    115.   if (cur_speed > 29) cur_speed = 29;
    116.   HAL_Delay(30 - cur_speed);
    117. //  HAL_Delay(500);
    118.   key_num = 0;
    119.  
    120.  }
    121.  
    122. }

    6.4 多级菜单核心代码:

    menu.h:

    1. #ifndef __MENU_H
    2. #define __MENU_H
    3.  
    4. #include "main.h"
    5. #define  u8 unsigned char
    6.  
    7. //按键定义
    8. #define KEY0 HAL_GPIO_ReadPin(GPIOE, GPIO_PIN_4)  //低电平有效   KEY0
    9. #define KEY1 HAL_GPIO_ReadPin(GPIOE, GPIO_PIN_3)  //低电平有效
    10. #define WK_UP HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0)  //高电平有效
    11.  
    12.  
    13. typedef struct
    14. {
    15.     u8 current; //当前状态索引号
    16.     u8 next;   //向下一个
    17.     u8 enter;  //确定
    18.   u8 back;   //退出
    19.     void (*current_operation)(void); //当前状态应该执行的操作
    20. } Menu_table;
    21.  
    22. //界面UI
    23. void home();
    24. void Temperature();
    25. void Palygame();
    26. void Setting();
    27. void Info();
    28.  
    29.  
    30. void  Menu_key_set(void);
    31. u8 KEY_Scan(u8 mode);
    32.  
    33. void TestTemperature();
    34. void ConrtolGame();
    35. void Set();
    36. void Information();
    37.  
    38. void LED();
    39. void RTC_display();
    40.  
    41. #endif

    menu.c:

    1. #include "menu.h"
    2. #include "oled.h"
    3. #include "gpio.h"
    4. #include "dinogame.h"
    5. #include "control.h"
    6. #include "DHT11.h"
    7. #include "rtc.h"
    8.  
    9. RTC_DateTypeDef GetData;  //获取日期结构体
    10.  
    11. RTC_TimeTypeDef GetTime;   //获取时间结构体
    12.  
    13.  
    14. //UI界面
    15. //主页
    16. /****************************************************/
    17. //UI库
    18.  
    19. /****************************************************/
    20.  
    21. void (*current_operation_index)();  
    22.  
    23. Menu_table  table[30]=
    24. {
    25.     {0,0,1,0,(*home)}, //一级界面(主页面) 索引,向下一个,确定,退出
    26.   
    27.     {1,2,5,0,(*Temperature)}, //二级界面 温湿度
    28.     {2,3,6,0,(*Palygame)}, //二级界面 游戏
    29.     {3,4,7,0,(*Setting)}, //二级界面 设置
    30.     {4,1,8,0,(*Info)}, //二级界面 信息
    31.   
    32.   {5,5,5,1,(*TestTemperature)},  //三级界面:DHT11测量温湿度
    33.   {6,6,6,2,(*ConrtolGame)},    //三级界面:谷歌小恐龙Dinogame
    34.   {7,7,9,3,(*Set)},        //三级界面:设置普通外设状态 LED
    35.   {8,8,8,4,(*Information)},    //三级界面:作者和相关项目信息
    36.  
    37.   {9,9,7,3,(*LED)},  //LED控制
    38. };
    39.  
    40. uint8_t  func_index = 0//主程序此时所在程序的索引值
    41.  
    42. void  Menu_key_set(void)
    43. {
    44.   if((KEY_Scan(1) == 1) && (func_index != 6))
    45.   { 
    46.     func_index=table[func_index].next; //按键next按下后的索引号
    47.     OLED_Clear(); 
    48.   }
    49.  
    50.   if((KEY_Scan(1) == 2) && (func_index != 6))
    51.   {
    52.     func_index=table[func_index].enter; //按键enter按下后的索引号
    53.     OLED_Clear();
    54.   }
    55.  
    56.  if(KEY_Scan(1) == 3)
    57.   {
    58.     func_index=table[func_index].back; //按键back按下后的索引号
    59.     OLED_Clear(); 
    60.   }
    61.  
    62.   current_operation_index=table[func_index].current_operation; //执行当前索引号所对应的功能函数
    63.   (*current_operation_index)();//执行当前操作函数
    64. }
    65.  
    66.  
    67. void home()
    68. {
    69.  RTC_display();
    70.  OLED_DrawBMP(0,0,20,3,signal_BMP);
    71.  OLED_DrawBMP(20,0,36,2,gImage_bulethouch);
    72.  OLED_DrawBMP(112,0,128,2,gImage_engery);
    73.  OLED_DrawBMP(4,6,20,8,gImage_yes);
    74.  OLED_DrawBMP(12,4,28,6,gImage_left);
    75.  OLED_DrawBMP(40,2,88,8,gImage_home);
    76.  OLED_DrawBMP(99,4,115,6,gImage_right);
    77.  OLED_DrawBMP(107,6,123,8,gImage_back);
    78. }
    79.  
    80. void Temperature()
    81. {
    82.  RTC_display();
    83.  OLED_DrawBMP(0,0,20,3,signal_BMP);
    84.  OLED_DrawBMP(20,0,36,2,gImage_bulethouch);
    85.  OLED_DrawBMP(112,0,128,2,gImage_engery);
    86.  OLED_DrawBMP(4,6,20,8,gImage_yes);
    87.  OLED_DrawBMP(12,4,28,6,gImage_left);
    88.  OLED_DrawBMP(40,2,88,8,gImage_temp);
    89.  OLED_DrawBMP(99,4,115,6,gImage_right);
    90.  OLED_DrawBMP(107,6,123,8,gImage_back);
    91. }
    92.  
    93. void Palygame()
    94. {
    95.  RTC_display();
    96.  OLED_DrawBMP(0,0,20,3,signal_BMP);
    97.  OLED_DrawBMP(20,0,36,2,gImage_bulethouch);
    98.  OLED_DrawBMP(112,0,128,2,gImage_engery);
    99.  OLED_DrawBMP(4,6,20,8,gImage_yes);
    100.  OLED_DrawBMP(12,4,28,6,gImage_left);
    101.  OLED_DrawBMP(40,2,88,8,gImage_playgame);
    102.  OLED_DrawBMP(99,4,115,6,gImage_right);
    103.  OLED_DrawBMP(107,6,123,8,gImage_back);
    104. }
    105.  
    106. void Setting()
    107. {
    108.  RTC_display();
    109.  OLED_DrawBMP(0,0,20,3,signal_BMP);
    110.  OLED_DrawBMP(20,0,36,2,gImage_bulethouch);
    111.  OLED_DrawBMP(112,0,128,2,gImage_engery);
    112.  OLED_DrawBMP(4,6,20,8,gImage_yes);
    113.  OLED_DrawBMP(12,4,28,6,gImage_left);
    114.  OLED_DrawBMP(40,2,88,8,gImage_setting);
    115.  OLED_DrawBMP(99,4,115,6,gImage_right);
    116.  OLED_DrawBMP(107,6,123,8,gImage_back);
    117. }
    118.  
    119. void Info()
    120. {
    121.  RTC_display();
    122.  OLED_DrawBMP(0,0,20,3,signal_BMP);
    123.  OLED_DrawBMP(20,0,36,2,gImage_bulethouch);
    124.  OLED_DrawBMP(112,0,128,2,gImage_engery);
    125.  OLED_DrawBMP(4,6,20,8,gImage_yes);
    126.  OLED_DrawBMP(12,4,28,6,gImage_left);
    127.  OLED_DrawBMP(40,2,88,8,gImage_info);
    128.  OLED_DrawBMP(99,4,115,6,gImage_right);
    129.  OLED_DrawBMP(107,6,123,8,gImage_back);
    130. }
    131.  
    132.  
    133. //按键函数,不支持连按
    134. u8 KEY_Scan(u8 mode)
    135. {
    136.  static u8 key_up=1;
    137.  if(mode)key_up=1
    138.  if(key_up&&(KEY0==0||KEY1==0||WK_UP==1))
    139.  {
    140.   HAL_Delay(100);  //消抖
    141.   key_up=0;
    142.   if(KEY0==0)return 1;
    143.   else if(KEY1==0)return 2;
    144.   else if(WK_UP==1)return 3;
    145.  }else if(KEY0==1&&KEY1==1&&WK_UP==0)key_up=1
    146.  return 0;
    147. }
    148.  
    149. void TestTemperature()
    150. {
    151.  DHT11();
    152. }
    153.  
    154. void ConrtolGame()
    155. {
    156.  Game_control();
    157. }
    158.  
    159. void Set()
    160. {
    161.  OLED_ShowString(0,0,"Peripherals: Lights",16);
    162.  OLED_ShowString(0,2,"Status: Closed",16);
    163.  HAL_GPIO_WritePin(GPIOB, GPIO_PIN_5, GPIO_PIN_SET);
    164. }
    165.  
    166. void Information()
    167. {
    168.  OLED_ShowString(0,0,"Author:Sneak",16);
    169.  OLED_ShowString(0,2,"Date:2022/8/23",16);
    170.  OLED_ShowString(0,4,"Lab: Multi-level menu",16);
    171. }
    172.  
    173. void LED()
    174. {
    175.  OLED_ShowString(0,0,"Peripherals: Lights",16);
    176.  OLED_ShowString(0,2,"Status: Open",16);
    177.  HAL_GPIO_WritePin(GPIOB, GPIO_PIN_5, GPIO_PIN_RESET);
    178. }
    179.  
    180.  
    181.  
    182. void RTC_display()    //RTC????
    183. {
    184.    /* Get the RTC current Time */
    185.    HAL_RTC_GetTime(&hrtc, &GetTime, RTC_FORMAT_BIN);
    186.       /* Get the RTC current Date */
    187.     HAL_RTC_GetDate(&hrtc, &GetData, RTC_FORMAT_BIN);
    188.  
    189.   /* Display date Format : yy/mm/dd */
    190.  
    191.         /* Display time Format : hh:mm:ss */
    192.   OLED_ShowNum(40,0,GetTime.Hours,2,16);    //hour
    193.   OLED_ShowString(57,0,":",16); 
    194.   OLED_ShowNum(66,0,GetTime.Minutes,2,16);   //min
    195.   OLED_ShowString(83,0,":",16); 
    196.   OLED_ShowNum(93,0,GetTime.Seconds,2,16);   //seconds
    197. }

    七、总结与代码开源

    总结:本项目目前还处于最初代版本,十分简易,后期笔者将抽时间去精进优化该多级菜单项目。其中,UI界面中的电池与信号目前都还处于贴图状态,后期笔者会加上库仑计测量电池电量等。文章中指出了需要注意的地方与可以改进的点,感兴趣的朋友可以彼此交流交流。

  • 相关阅读:
    Docker 仓库与注册表: 构建可靠的容器镜像生态系统
    (附源码)springboot宠物管理系统 毕业设计 121654
    9. JVM-方法区
    关于一篇“范式详解”博文的批注
    Knative部署应用以及应用的更新、应用的分流(三)
    Vue3项目创建+组合式API使用+组件通信+渲染微博热搜+打包上线
    Java泛型
    protobuf 编译proto文件
    穿越风波,“长红”的直播电商依然扎根产业和消费者
    postgresql简单导出数据与手动本地恢复(小型数据库)
  • 原文地址:https://blog.csdn.net/m0_61687959/article/details/132912544