電子產(chǎn)業(yè)一站式賦能平臺(tái)

PCB聯(lián)盟網(wǎng)

搜索
查看: 699|回復(fù): 0
收起左側(cè)

基于WiFi IoT套件開發(fā)的猜數(shù)字小游戲代碼分享

[復(fù)制鏈接]

2607

主題

2607

帖子

7472

積分

高級(jí)會(huì)員

Rank: 5Rank: 5

積分
7472
跳轉(zhuǎn)到指定樓層
樓主
發(fā)表于 2020-12-7 13:56:04 | 只看該作者 回帖獎(jiǎng)勵(lì) |倒序?yàn)g覽 |閱讀模式
基于WiFi IoT套件開發(fā)的猜數(shù)字小游戲代碼分享, 猜數(shù)字是一個(gè)很經(jīng)典的小游戲,也是編程開發(fā)入門的典型,以下為基于WiFi IoT套件開發(fā)的猜數(shù)字小游戲的具體開發(fā)過程和效果。




基本規(guī)則:由甲方(玩家)默想一個(gè)1-99(包含)內(nèi)的任意數(shù)字(整數(shù)),然后由乙方進(jìn)行猜測(cè),并詢問甲方猜測(cè)的數(shù)字是大了還是小了,甲方根據(jù)實(shí)際情況進(jìn)行回復(fù),則乙方最多問6個(gè)問題,就一定能夠猜中甲方默想的數(shù)字。




基本原理:乙方問最多6次,包括最后一次說出猜中的數(shù)字,實(shí)際上乙方最多有7次猜測(cè)的機(jī)會(huì)。

而使用二分進(jìn)行查找,2^7=128,則99以內(nèi)的數(shù)字,完全可以覆蓋,因此乙方絕對(duì)可以猜中。




實(shí)現(xiàn)概述:以上的基本規(guī)則和基本原理明確了,我們要在WiFi IoT套件上實(shí)現(xiàn),并且甲方需要參與,需要處理以下三個(gè)部分:



  • 猜數(shù)字的主邏輯
      
  • 使用oled屏幕顯示提示信息,讓玩家進(jìn)行互動(dòng)操作:我們需要在屏幕上顯示漢字,進(jìn)行玩家當(dāng)前猜測(cè)的數(shù)字,以及玩家按鍵后告知玩家結(jié)果
      
  • 使用按鍵接收玩家操作(大了或者小了等):在這個(gè)實(shí)例中,我們使用了ADC方式來讀取按鍵信息,從而獲得玩家具體操作。所使用的按鍵為核心板上的USR按鍵,和OLED板上的S1,S2按鍵。使用ADC方式讀取的時(shí)候,他們所使用的輸入端口為GPIO5/ADC2,具體的按鍵作用如下:


      

         
    • USR:開始游戲,或者確認(rèn)
        
         
    • S1:如果猜小了,則玩家按S1告知
        
         
    • S2:如果猜大了,則玩家按S2告知

        
        




原始代碼修改處理:【代碼基礎(chǔ)為code-1.0.tar.gz】

  • 開啟I2C:vendor/hisi/hi3861/hi3861/build/config/usr_config.mk


      

         

    •    

             
      • ## BSP Settings
          
             
      • #
          
             
      • # CONFIG_I2C_SUPPORT is not set
          
             
      • CONFIG_I2C_SUPPORT=y
          
             
      • # CONFIG_I2S_SUPPORT is not set
           
      復(fù)制代碼

        
        

  • I2C復(fù)用端口設(shè)置:vendor/hisi/hi3861/hi3861/app/wifiiot_app/init/app_io_init.c


      

         


    •    
      1
      2
      3
      4
      5
      6
      7
      #ifdef CONFIG_I2C_SUPPORT
           /* I2C IO復(fù)用也可以選擇3/4; 9/10,根據(jù)產(chǎn)品設(shè)計(jì)選擇 */
           // hi_io_set_func(HI_IO_NAME_GPIO_0, HI_IO_FUNC_GPIO_0_I2C1_SDA);
           // hi_io_set_func(HI_IO_NAME_GPIO_1, HI_IO_FUNC_GPIO_1_I2C1_SCL);
           hi_io_set_func(HI_IO_NAME_GPIO_13, HI_IO_FUNC_GPIO_13_I2C0_SDA);
           hi_io_set_func(HI_IO_NAME_GPIO_14, HI_IO_FUNC_GPIO_14_I2C0_SCL);
      #endif




        
        




主邏輯代碼:guess.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
#include <stdio.h>
#include <unistd.h>

#include <ohos_init.h>
#include <cmsis_os2.h>
#include <hiview_config.h>
#include <hiview_log.h>
#include <wifiiot_watchdog.h>
#include <hi_task.h>

#include “button/button.h“
#include “oled/oled.h“

/*
0123456789
請(qǐng)?jiān)谛闹心胍粋(gè)1~99的整數(shù),我能在6個(gè)問題之內(nèi)猜出這個(gè)數(shù)
想好了就按【USER】開始游戲吧,【RST】重啟
小了按【S1】,大了按【S2】,正確按【USER】
第?個(gè)問題,是這個(gè)數(shù)嗎:??
大了啊!那我再猜小一點(diǎn)
小了!那我再猜大一點(diǎn)
哈哈,我猜到了吧!
按【USER】再玩一次(請(qǐng)先默想一個(gè)1~99的整數(shù))
你默想的數(shù)一定是??

//  開始:0,長(zhǎng)度10
//  開始:10,長(zhǎng)度30
//  開始:40,長(zhǎng)度24
//  開始:64,長(zhǎng)度25
//  開始:89,長(zhǎng)度14
//  開始:103,長(zhǎng)度11
//  開始:114,長(zhǎng)度11
//  開始:125,長(zhǎng)度9
//  開始:134,長(zhǎng)度26
//  開始:160,長(zhǎng)度10
*/
char *str[] = {
    “0123456789“,
    “請(qǐng)?jiān)谛闹心胍粋(gè)1~99的整數(shù),我能在6個(gè)問題之內(nèi)猜出這個(gè)數(shù)“,
    “想好了就按【USER】開始游戲吧,【RST】重啟“,
    “小了按【S1】,大了按【S2】,正確按【USER】“,
    “第?個(gè)問題,是這個(gè)數(shù)嗎:??“,
    “大了!那我再猜小一點(diǎn)“,
    “小了啊!那我再猜大一點(diǎn)“,
    “哈哈,我猜到了吧!“,
    “按【USER】再玩一次(請(qǐng)先默想一個(gè)1~99的整數(shù))“,
    “你默想的數(shù)一定是??“
};

int pos[][2] = {
    {0, 10},
    {10, 30},
    {40, 24},
    {64, 25},
    {89, 14},
    {103, 11},
    {114, 11},
    {125, 9},
    {134, 26},
    {160, 10}
};

void display_string(int idx,int delay,int num1, int num2){
    int start=0;
    int len=0;

    start = pos[idx][0];
    len = pos[idx][1];
    if(idx==4 && num2==100) {
        len = len +1;
    }
    u8 no[len];
    for(int i=0;i<len;i++){
        no = start+i;
    }
    // 4 “第?個(gè)問題,是這個(gè)數(shù)嗎:??“,
    if(idx==4) {
        no[1] = num1;
        if(num2==100) {
            no[len-3] = 1;
            no[len-2] = 0;
            no[len-2] = 0;
        } else {
            no[len-2] = num2/10;
            no[len-1] = num2%10;
        }
    }
    OLED_Clear();
    OLED_ShowChineseString(0,0,no,len,16);        
    usleep(delay*1000*1000);   
}

// 主任務(wù)
static void *GuessTask(const char *arg){
    (void)arg;

    gpio_button_init();
    oled_display_init();

    OLED_Clear();
    printf(“請(qǐng)?jiān)谛闹心胍粋(gè)1~100的整數(shù),我能在6個(gè)問題之內(nèi)猜出這個(gè)數(shù)是什么:\n“);
    display_string(1,2,0,0);
    printf(“想好了就按【USER】開始游戲吧,【RST】重啟\n“);
    display_string(2,2,0,0);
    printf(“小了按【S1】,大了按【S2】,正確按【USER】\n“);
    display_string(3,0,0,0);

    key_event_t zf; //聲明char類型來存放輸入的字符
    char number; //電腦猜測(cè)的數(shù)字

    while ((zf = gpio_button_get())!=KEY_EVENT_NONE)
    {
        // getchar();//忽略回車
        char min_shu = 1;   // 1是初始最小數(shù)。
        char max_shu = 100; // 100是初始最大數(shù)。

        if (zf == KEY_EVENT_USER)
        {
            int jishu = 1; // 計(jì)數(shù)用的,6個(gè)問題以內(nèi)嘛。
            while (1)   // 條件一直為真,死循環(huán),能用break跳出循環(huán),或用return跳出整個(gè)函數(shù)。
            {
                number = (min_shu + max_shu) / 2; // 最小數(shù)和最大數(shù)的和除2 ,意思就是取它們的中間值。
                printf(“\n第%d個(gè)問題,是這個(gè)數(shù)嗎:%d“, jishu, number);
                display_string(4,0,jishu, number);
                zf = gpio_button_get();
                // getchar();//忽略回車

                if (zf == KEY_EVENT_S2)
                {
                    printf(“\n大了!那我再猜小一點(diǎn)\n“);
                    display_string(5,2,0,0);
                    max_shu = number - 1; //如果是大了,那最大值至少比目前的數(shù)小1。
                    jishu++;              //回答次數(shù)加1 ,如果你回答了電腦6次問題,電腦還沒有猜對(duì),那電腦就輸了。
                }
                if (zf == KEY_EVENT_S1)
                {
                    printf(“\n小了!那我再猜大一點(diǎn)\n“);
                    display_string(6,2,0,0);
                    min_shu = number + 1; //如果是小了,那最小值至少比目前的數(shù)大1。
                    jishu++;              //同上面,計(jì)數(shù)加1
                }
                if (zf == KEY_EVENT_USER)
                {
                    // printf(“y\n“);
                    printf(“\n哈哈,我猜到了吧!\n“);
                    display_string(7,2,0,0);
                    printf(“按【USER】再玩一次(請(qǐng)?jiān)谛闹邢饶胍粋(gè)1~100的整數(shù)),【RST】重啟\n“);
                    display_string(8,0,0,0);
                    break;
                }
                if (jishu == 7)
                {
                    printf(“\n你默想的數(shù)一定是%d“,(min_shu + max_shu) / 2);
                    display_string(9,2,0,0);
                    printf(“\n按【USER】再玩一次(請(qǐng)?jiān)谛闹邢饶胍粋(gè)1~100的整數(shù)),【RST】重啟\n“);
                    display_string(8,0,0,0);
                    break;
                }
            }
        }
        else {
            printf(“\n按鍵無效,請(qǐng)重新選擇(按【USER】開始,【RST】重啟):“);
        }
    }
    return NULL;
}

// 程序入口
static void GuessEntry(void)
{
    osThreadAttr_t attr;
    WatchDogDisable();
    SetLogLevel(HILOG_LV_ERROR);

    attr.name = “GuessTask“;
    attr.attr_bits = 0U;
    attr.cb_mem = NULL;
    attr.cb_size = 0U;
    attr.stack_mem = NULL;
    attr.stack_size = 1024;
    attr.priority = osPriorityNormal;

    if (osThreadNew((osThreadFunc_t)GuessTask, NULL, &attr) == NULL) {
        printf(“[GuessNum] Falied to create GuessTask!\n“);
    }
}

SYS_RUN(GuessEntry);








主邏輯代碼說明:

  • 因?yàn)樵贠LED上面顯示字符(包括漢字),需要預(yù)先取得漢字的字模點(diǎn)陣數(shù)據(jù);在這個(gè)實(shí)例中,會(huì)有不同的提示語(yǔ)出現(xiàn),且未中文,為了方便處理,我將每句話的字模點(diǎn)陣數(shù)據(jù)單獨(dú)取出,所以定義了str[],pos[][2],以及display_string(),用于顯示對(duì)應(yīng)的語(yǔ)句。其最終調(diào)用oled/oled.c中的OLED_ShowChineseString()來將漢字輸出到OLED屏幕;特別的,語(yǔ)句4“第?個(gè)問題,是這個(gè)數(shù)嗎:??”需要處理具體數(shù)字,所以進(jìn)行了特殊的處理。
      
  • 獲取按鍵的部分,在button/button.c中的gpio_button_get(),代碼隨后展示,用于獲取按鍵的狀態(tài)

      



OLED部分代碼:【以下為oled/oled.h,oled/oled.c和字模數(shù)據(jù)oled/oledfont.h請(qǐng)查看附件】

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#ifndef __OLED_H
#define __OLED_H


#define OLED_MODE 0
#define SIZE 8
#define XLevelL     0x00
#define XLevelH     0x10
#define Max_Column  128
#define Max_Row     64
#define Brightness  0xFF
#define X_WIDTH     128
#define Y_WIDTH     64      


#define OLED_CMD  0 //寫命令
#define OLED_DATA 1 //寫數(shù)據(jù)


#define u8 unsigned char
#define u16 unsigned short
#define u32 unsigned int

//OLED控制用函數(shù)
void delay_ms(unsigned int ms);
void OLED_ColorTurn(u8 i);
void OLED_DisplayTurn(u8 i);
void OLED_WR_Byte(u8 dat,u8 cmd);
void OLED_Set_Pos(u8 x, u8 y);
void OLED_Display_On(void);
void OLED_Display_Off(void);
void OLED_Clear(void);
void OLED_ShowChar(u8 x,u8 y,u8 chr,u8 sizey);
u32 oled_pow(u8 m,u8 n);
void OLED_ShowNum(u8 x,u8 y,u32 num,u8 len,u8 sizey);
void OLED_ShowString(u8 x,u8 y,char *chr,u8 sizey);
void OLED_ShowChinese(u8 x,u8 y,u8 no,u8 sizey);
void OLED_ShowChineseString(u8 x,u8 y,u8 no[],u8 length,u8 sizey);
void OLED_Direct_ShowString(u8 x,u8 y,char *chr,u8 sizey);
void OLED_DrawBMP(u8 x,u8 y,u8 sizex, u8 sizey,u8 BMP[]);
void OLED_Init(void);
void oled_display_init(void);
#endif








OLED漢字字模數(shù)據(jù)獲取方式:

  • 在OLED上面顯示字符(包括漢字),本質(zhì)上是描點(diǎn),所以獲取對(duì)應(yīng)字符的點(diǎn)陣數(shù)據(jù)即可。
      
  • 生成字模數(shù)據(jù)的工具為PCToLCD,設(shè)置為字符模式和C51格式;這個(gè)工具還可以用于取圖片的點(diǎn)陣數(shù)據(jù)。
      
  • 具體獲取方式如下:


      







按鍵部分代碼:【以下為button/button.h,button/button.c請(qǐng)查看附件】

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#ifndef __BUTTON_H
#define __BUTTON_H

#include <hi_types_base.h>

#define APP_DEMO_ADC
#define ADC_TEST_LENGTH 64
#define VLT_MIN 100
#define STATUS_LEN 4

// 按鍵狀態(tài)定義
typedef enum
{
    KEY_EVENT_NONE = 0,
    KEY_EVENT_S1,
    KEY_EVENT_S2,
    KEY_EVENT_USER
} key_event_t;
// 按鍵電壓范圍值定義
float vlt_val_scopes[STATUS_LEN][2]={
    { 3.0, 9.0},    //KEY_EVENT_NONE
    { 0.4, 0.6},    //KEY_EVENT_S1
    { 0.8, 1.1},    //KEY_EVENT_S2
    { 0.01,0.3},    //KEY_EVENT_USER
};

// 按鍵狀態(tài)列表
key_event_t key_status_list[STATUS_LEN] = {
    KEY_EVENT_NONE,
    KEY_EVENT_S1,
    KEY_EVENT_S2,
    KEY_EVENT_USER,
};
//獲取當(dāng)前按鍵
key_event_t get_key_event(void);

// ADC轉(zhuǎn)換
hi_void convert_to_voltage(hi_u32 data_len);

// ADC獲取
void button_adc_test(void);

// 設(shè)置 按鍵中斷響應(yīng)
void gpio_button_init(void);

// 獲取需要的按鍵狀態(tài)
key_event_t gpio_button_get(void);

#endif








按鍵部分代碼說明:當(dāng)使用ADC方式來讀取按鍵狀態(tài)的時(shí)候,本質(zhì)上,是讀取了ADC輸入端口的數(shù)據(jù),這個(gè)數(shù)據(jù)進(jìn)過一定的轉(zhuǎn)換,能夠化為對(duì)應(yīng)的電壓數(shù)據(jù)。而不同的按鍵按下后,ADC端口讀取的電壓是不同的,并且是在一定范圍內(nèi)波動(dòng)的,對(duì)應(yīng)按鍵的電壓范圍在上述vlt_val_scopes中進(jìn)行了定義。我們獲取到了對(duì)應(yīng)的電壓數(shù)據(jù),然后與vlt_val_scopes每個(gè)范圍數(shù)據(jù)進(jìn)行對(duì)比,從而據(jù)此得到對(duì)應(yīng)的按鍵信息。




實(shí)際結(jié)果演示:




視頻地址: 鏈接: https://pan.baidu.com/s/1RtT8Wh3ZPbasJ-dK7x1QRg 提取碼: vkyh




完整代碼:

下載地址: https://pan.baidu.com/s/1RtT8Wh3ZPbasJ-dK7x1QRg 提取碼: vkyh

發(fā)表回復(fù)

本版積分規(guī)則


聯(lián)系客服 關(guān)注微信 下載APP 返回頂部 返回列表