#夏日挑战赛# 小凌派手势操作切换OLED菜单 原创 精华

物联风景
发布于 2022-7-2 18:44
浏览
3收藏

本文正在参加星光计划3.0—夏日挑战赛

一.前言

上一篇帖子演示了小凌派手势操作模块搭配OLED的基本效果,文章在这:
小凌派玩一玩OLED+手势操作
这次玩点有意思的,用手势操作OLED屏的菜单

二.制作中文字体

如果要实现手势菜单.就不能使用以前的方式来操作中文了,除非你能记住所有的中文字的位置,那这种情况下要如果进行呢,请看法宝:

typedef struct  Chinese_Font_data{
    char text[3];
    uint8_t data[32];

}Chinese_Font_data;

typedef struct ChineseFont{
   
    uint8_t font_size;
    uint8_t font_data_len;
    // uint8_t keep;
    Chinese_Font_data *font_data;

}ChineseFont;
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.

这里创建两个结构体,结构体里面就是存的字体信息和字体数据,那内容是啥样的呢,这里举几个例子:

Chinese_Font_data xingkai_data_16[]={

/*---------------子----------------*/
{"子",
{0x00,0x00,0x00,0x00,0x01,0xF0,0x0F,0x70,0x00,0x60,0x01,0xC0,0x01,0xFE,0x3F,0xFE,
0x78,0xC0,0x00,0xC0,0x00,0xC0,0x00,0xC0,0x00,0xC0,0x07,0xC0,0x03,0x80,0x01,0x00}},

/*---------------菜----------------*/
{"菜",
{0x00,0x00,0x06,0x70,0x06,0x7C,0x3F,0xFC,0x12,0xC0,0x03,0xC0,0x07,0x30,0x0D,0xE0,
0x05,0x70,0x3F,0xF8,0x17,0xC0,0x0D,0x60,0x19,0x38,0x71,0x1F,0x03,0x00,0x01,0x00}},

/*---------------单----------------*/
{"单",
{0x00,0x00,0x06,0x70,0x07,0x60,0x02,0x70,0x0F,0xF8,0x0D,0xF8,0x0F,0xF0,0x07,0xF0,
0x07,0xA0,0x01,0xFE,0x7F,0xFC,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80}},

/*---------------选----------------*/
{"选",
{0x00,0x00,0x00,0x60,0x00,0x60,0x19,0xE0,0x09,0xF8,0x01,0xF0,0x02,0x7C,0x3F,0xF8,
0x3A,0xA0,0x19,0xA0,0x19,0x24,0x0A,0x3E,0x7F,0x1C,0x01,0xFF,0x00,0x3C,0x00,0x00}},

/*---------------项----------------*/
{"项",
{0x00,0x00,0x00,0x00,0x00,0x1E,0x01,0xFC,0x0E,0x60,0x3C,0xFC,0x0C,0x8C,0x0C,0xAC,
0x0E,0xAC,0x3F,0xAC,0x71,0xEC,0x01,0xFC,0x00,0xDC,0x01,0x8C,0x02,0x04,0x00,0x00}},
}
  • 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.

三.中文字体的显示
处理完这个之后,就可以编写中文显示的方法了:


void ssd1306_DrawChinese(char *ch, ChineseFont *font,SSD1306_COLOR color)
{
    // printf("%d %d\n",sizeof(xingkai_16),(sizeof(xingkai_16[0])));
    // printf("%d,, draw chinese:%s\n",xingkai_16_font_len,ch);
    IF(font == &xingkai_16){

        for(int ch_idx=0;ch_idx<xingkai_16_font_len;ch_idx++){
            // printf("%d === %s\n",i,font[i].text);
            Chinese_Font_data *font_data =&font->font_data[ch_idx];
            if(strncmp(ch,font_data->text,sizeof(font_data->text))==0){
  
                {
                    int x=SSD1306.CurrentX,y=SSD1306.CurrentY;
                    int w=font->font_size,h=font->font_size;
                    int stride = 0;
                    if (x + w > SSD1306_WIDTH || y + h > SSD1306_HEIGHT || w * h == 0) {
                        printf("%dx%d @ %d,%d out of range or invalid!\r\n", w, h, x, y);
                        return;
                    }

                    w = (w <= SSD1306_WIDTH ? w : SSD1306_WIDTH);
                    h = (h <= SSD1306_HEIGHT ? h : SSD1306_HEIGHT);
                    stride = (stride == 0 ? w : stride);

                    uint8_t rows = font->font_data_len * 8 / stride;
                    for (uint8_t i = 0; i < rows; i++) {
                        uint32_t base = i * stride / 8;
                        for (uint8_t j = 0; j < w; j++) {
                            uint32_t idx = base + (j / 8);
                            uint8_t byte = idx < font->font_data_len ? font_data->data[idx] : 0;
                            uint8_t bit  = byte & (0x80 >> (j % 8));
                            ssd1306_DrawPixel(x + j, y + i, bit ? !color : color);
                        }
                    }
                }

                // ssd1306_DrawRegion(SSD1306.CurrentX,SSD1306.CurrentY,font->font_size,
                // font->font_size, font_data->data,font->font_data_len,0);
                SSD1306.CurrentX += font->font_size;

                break;
            }
        }

    }

   
}

void ssd1306_Text(char *string,uint8_t string_len,uint8_t font_size,uint8_t mode)
{
    int i=0;

    // printf("Text:%x %x %x %x %x %x %d %d %d\n",string[0],string[1],string[2],string[3],string[4],string[5],
    //                             string[3],string[4],string[5]);
   printf("ssd1306_Text %d:%s\n",string_len,string);
    while(i<string_len){
        if(string[i] < 0)
        {
            // printf("[%d ]%d %x for chinese\n",i,string[i],string[i]);
            ssd1306_DrawChinese(&string[i],&xingkai_16,mode);
            
            
            i+=3;
        }
        else
        {
            // printf("[%d ]%d %x for char\n",i,string[i],string[i]);
            ssd1306_DrawChar(string[i],Font_7x10,!mode);
            i+=1;
        }
    }
}
  • 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.

这里面的一个逻辑就是判断字符是中文还是英文,如果是中文的话char值是小于0的,但是不是一个字符能表示,中文使用UTF-8,最多会使用3个字符表示一个中文字,所以当出现中文的时候,char数组里长度需要增加3,如果是英文字的话就只需要1个字符了.
这工作完成之后就可以测试中文和英文数字等的的显示了,随便写几个中文字,调用接口显示:

ssd1306_Text("1.你好 2.Hello",strlen("你好世界"),16,0);
  • 1.

可以观察显示,不得不提一句,这地方花费了大量的时间在编译上,因为编译系统不完善.不能支持增量编译,希望厂家团队尽快解决.

四.制作菜单框架
其实这里演示的菜单框架比较简单,因为菜单会有子菜单项,所以需要用多叉树的结构来表现,请看法宝:

typedef struct Menu{
    // char name[8];
    char text[14];
    char state;
    void *param;
    uint8_t sub_len;
    struct Menu **subMenu;
    void (*enteRFunc)(struct Menu *menu);
    void (*exitFunc)(struct Menu *menu);

}Menu;
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.

然后就制作菜单的数据,这里举例:

#include "menu.h"
#include <stddef.h>

Menu hoguo={
    .text="火锅",

};

Menu BBQ={
    .text="BBQ",
};

Menu western={
    .text="西餐",
};

extern Menu mainMenu;
Menu *eating_subs[]={&mainMenu,&hoguo,&BBQ,&western};


Menu eating={
    .text="聚餐",
    .sub_len = 3,
    .subMenu=eating_subs
};

Menu comedy={
    .text="喜剧片",
    .sub_len=0,
};

Menu Thriller={
    .text="惊悚片"
};

extern Menu movie;

Menu *movie_subs[]={&mainMenu, &comedy,
        &Thriller};

Menu movie={
    .text="看电影",
    .sub_len = 3,
    .subMenu=movie_subs
};


Menu yunnan={
    .text="云南",
    .sub_len=0,
};

Menu beijing={
    .text="北京"
};
Menu xizang={
    .text="西藏"
};
extern Menu trip;

Menu *trip_subs[]={&mainMenu,&yunnan, &beijing,
        &xizang};


Menu trip={
    .text="旅游",
    .sub_len = 4,
    .subMenu=trip_subs


};

Menu kge={
    .text="唱K",
    .sub_len=0,
};

Menu anmo={
    .text="按摩"
};
Menu xizao={
    .text="洗澡"
};
extern Menu relex;

Menu *releax_subs[]={&mainMenu,&kge, &anmo,
        &xizao};
Menu relax={
    .text="放松",
    .sub_len = 4,
    .subMenu=releax_subs


};

Menu *mainMenu_subs[5]={NULL,&eating,&trip,&movie,&relax};

Menu mainMenu={
   
        .text = "娱乐活动",
        .sub_len = 5,
        .subMenu=mainMenu_subs
   
};
  • 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.

菜单项制作好了之后,可以放到UI里去渲染

五.UI线程渲染菜单



                ssd1306_SetCursor(32, 0);

                ssd1306_Text(currentMenu->text,strlen(currentMenu->text),16,0);



                int show_idx=1;

                if(select_idx > max_sub){

                        show_idx+=(select_idx-max_sub);

                        // continue;

                }

                int cursor=1;

                for(int i=show_idx;i<currentMenu->sub_len;i++)

                {

                       

                       

                        ssd1306_SetCursor(0, 16*(cursor++));

                        Menu *currentSub=currentMenu->subMenu[i];

                        if(currentSub){



                                char text[24]={0};

                                sprintf(text,"%d.%s",i,currentSub->text);

                                printf("text = %s\n",text);

                                ssd1306_Text(text,strlen(text),16,select_idx==i);

                        }

                       

                }



                ssd1306_UpdateScreen();       
  • 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.

这里的逻辑就是把菜单标题和子菜单都显示出来,效果如图:
#夏日挑战赛# 小凌派手势操作切换OLED菜单-鸿蒙开发者社区

六.结合手势传感器传的数据

把手势传感器的数据接收到,然后做相应的逻辑处理,这里有一个问题,就是目前菜单因为屏幕的原因,只能显示3个菜单,所以需要做一个机制,当选择的菜单超过当前长度的时候往下滑一格,
代码如下:



static void *OLedTask(const char *arg)

{

        (void)arg;

        uint32_t ret=0;

       

        ssd1306_Init();

        printf("OLedTask queueId=%d\n",queueId);



        //ssd1306_DrawString("Hello HarmonyOS!", Font_7x10, White);



        int text_area_width = 64;

        int text_area_height = 64;



        int text_per_line_num = text_area_height / font_width;



        int mod = hope_num % text_per_line_num;

        int text_line_num = hope_num / text_per_line_num;

        if (mod)

        {

                text_line_num += 1;

        }

        printf("title_addr=%p,dir_addr=%p\n", title_arr,dir_arr);

        int text_alredy_show = 0;



        int x = 0;

        int y = 0;

        int select_idx = 1;



        ssd1306_Fill(Black);

        ssd1306_SetCursor(0, 0);



        Menu *currentMenu = &mainMenu;

        int max_sub=3;

        int start=0;

       

        while(1){

                uint32_t flag = 0;

                uint32_t rLen =4;

                if(start){

                               

                        ret = LOS_QueueReadCopy(queueId,

                                                                        &flag,

                                                                        &rLen,

                                                                        LOS_WAIT_FOREVER);

                        // LOS_Msleep(OLED_INTERVAL_TIME_US);

                        printf("READ ret = %d,FLAG =%02x\n",ret,flag);

                }

                start =1;



                ssd1306_Fill(Black);

                ssd1306_SetCursor(0, 0);

               

                int idx=0;

                for(;idx<9;idx++){

                        if(flag&(1<<idx)){

                                break;

                        }

                }

                printf("idx=%d,select_idx=%d\n",idx,select_idx);



                switch(idx){

                        case 0:

                        if(select_idx > 1){

                                select_idx--;

                        }

                        break;

                        case 1:

                        if(select_idx < currentMenu->sub_len-1){

                                select_idx++;

                        }

                        break;

                        case 2:

                        {

                                Menu *currentSub=currentMenu->subMenu[0];

                                if(currentSub){

                                        currentMenu=currentSub;

                                        select_idx = 1;

                                        start = 0;

                                        continue;

                                }



                        }break;

                        case 3:

                        {

                                        Menu *currentSub=currentMenu->subMenu[select_idx];

                                        if(currentSub){

                                                currentMenu=currentSub;

                                                select_idx = 1;

                                                start = 0;

                                                continue;

                                        }



                        }break;

                       

                        break;



                }

                printf(">>idx=%d,select_idx=%d\n",idx,select_idx);

               

                ssd1306_SetCursor(32, 0);

                ssd1306_Text(currentMenu->text,strlen(currentMenu->text),16,0);



                int show_idx=1;

                if(select_idx > max_sub){

                        show_idx+=(select_idx-max_sub);

                        // continue;

                }

                int cursor=1;

                for(int i=show_idx;i<currentMenu->sub_len;i++)

                {

                       

                       

                        ssd1306_SetCursor(0, 16*(cursor++));

                        Menu *currentSub=currentMenu->subMenu[i];

                        if(currentSub){



                                char text[24]={0};

                                sprintf(text,"%d.%s",i,currentSub->text);

                                printf("text = %s\n",text);

                                ssd1306_Text(text,strlen(text),16,select_idx==i);

                        }

                       

                }



                ssd1306_UpdateScreen();       

        }


  • 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.
  • 189.
  • 190.
  • 191.
  • 192.
  • 193.
  • 194.
  • 195.
  • 196.
  • 197.
  • 198.
  • 199.
  • 200.
  • 201.
  • 202.
  • 203.
  • 204.
  • 205.
  • 206.
  • 207.
  • 208.
  • 209.
  • 210.
  • 211.
  • 212.
  • 213.
  • 214.
  • 215.
  • 216.
  • 217.
  • 218.
  • 219.
  • 220.
  • 221.
  • 222.
  • 223.
  • 224.
  • 225.
  • 226.
  • 227.
  • 228.
  • 229.
  • 230.
  • 231.
  • 232.
  • 233.
  • 234.
  • 235.
  • 236.
  • 237.
  • 238.
  • 239.
  • 240.
  • 241.
  • 242.
  • 243.
  • 244.
  • 245.
  • 246.
  • 247.
  • 248.
  • 249.
  • 250.
  • 251.
  • 252.
  • 253.
  • 254.
  • 255.

具体效果可以查看附件的视频

七.总结

其实手势控制菜单最重要的就是思路,再结合一点编程技巧的话,事情就很容易了,费时的地方就是字库的制作和菜单的制作.
希望还能给大家带来更多作品.

©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
小凌派手势操作菜单.zip 7.77M 17次下载
5
收藏 3
回复
举报
5
3
3
3条回复
按时间正序
/
按时间倒序
物联风景
物联风景

视频地址:https://ost.51cto.com/person/posts/3185339?tabIndex=2

回复
2022-7-2 18:47:37
鸿蒙活动小助手
鸿蒙活动小助手

给大佬鼓掌~

回复
2022-7-21 14:54:02
物联风景
物联风景 回复了 鸿蒙活动小助手
给大佬鼓掌~

感谢[感谢[

回复
2022-7-21 21:19:26


回复
    相关推荐