#夏日挑战赛# 小凌派手势操作切换OLED菜单 原创 精华
物联风景
发布于 2022-7-2 18:44
浏览
3收藏
一.前言
上一篇帖子演示了小凌派手势操作模块搭配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.
这里的逻辑就是把菜单标题和子菜单都显示出来,效果如图:
六.结合手势传感器传的数据
把手势传感器的数据接收到,然后做相应的逻辑处理,这里有一个问题,就是目前菜单因为屏幕的原因,只能显示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
回复
分享
微博
QQ
微信
举报
举报
5
3
3
微信扫码分享
删除帖子
删除 取消
相关推荐
视频地址:https://ost.51cto.com/person/posts/3185339?tabIndex=2
给大佬鼓掌~
感谢[感谢[