Метод реализации многоуровневого меню STM32+0.96

0

Выбор аппаратного обеспечения

  • Микроконтроллер STM32F103C8T6
  • Дисплей 0,96 Oled
  • 5 кнопок

Подключение

  • SCL —- PA1
  • SDA —- PA2
  • KEY_UP —- PA4
  • KEY_DOWN —- PA5
  • KEY_LEFT —- PA3
  • KEY_RIGHT —- PA6
  • KEY_OK —- PA7

Ссылка на исходный код:

Программа:

/**
 * @brief Функция сканирования клавиш
 * 
 * @param mode Режим 1 означает непрерывное сканирование, а режим 0 означает одиночное сканирование.
 */
void KeyScan(u8 mode)
{
    static int keyCount = 0;
    static int keyState = 0;
    if(mode == 1) keyState=0;
    if (keyState == 0 && (KEY_UP == 0||KEY_DOWN == 0||KEY_LEFT == 0||KEY_RIGHT == 0||KEY_OK == 0))
    {    
        keyCount++;
        if(keyCount>2)
        {
            keyState = 1;
            keyCount=0;
            if(KEY_UP == 0) KeyUp();
            else if(KEY_DOWN == 0) KeyDown();
            else if (KEY_LEFT == 0) KeyLeft();
            else if (KEY_RIGHT == 0) KeyRight();
            else if (KEY_OK == 0) KeyOk();
        }
    }else if (KEY_UP == 1 && KEY_DOWN == 1 && KEY_LEFT == 1 && KEY_RIGHT == 1 && KEY_OK == 1)
    {
        keyState = 0;
    }
}

void KeyUp()
{
	if(isKeyUp == 0)
		isKeyUp=1;
	LED=!LED;
}

Многоуровневый дизайн меню:

//Структура параметров страницы меню
struct MenuProperty_t
{
	u8 MenuLen;//Общее количество пунктов меню на текущей странице меню
	u8 scrollBarLen;//Длина полосы прокрутки, поскольку все они используют 16 символов размера, на странице меню может быть до четырех пунктов меню, а полоса прокрутки для пяти пунктов меню равна 1.
};

//Структура пункта прейскуранта
struct Menu_t{
	struct MenuProperty_t *MenuProperty;//Параметры страницы меню, на которой расположен текущий пункт меню
	u8 displayString[15];//Символ текущего пункта меню
	void (*func1) (void);//Функция текущего пункта меню
	void (*func2) (void);//Функция текущего пункта меню
	struct Menu_t *fatherMenu;//Родительский пункт меню текущего пункта меню
	struct Menu_t *childrenMenu;//Дочерний пункт меню текущего пункта меню	
};

Определение страницы меню

Главное меню интерфейса считается меню первого уровня. Основной интерфейс обычно можно использовать для рисования некоторых забавных дизайнов пользовательского интерфейса. То, что я делаю в этом проекте, – это дизайн часов.На этой странице меню есть только один пункт меню, а полоса прокрутки равна 0.Поскольку инициализация не может быть заполнена сначала неинициализированными данными, инициализации его элемента подменю сначала присваивается значение NULL.

//Основной пользовательский интерфейс
struct MenuProperty_t MainUIProperty={1,0};
struct Menu_t MainUI=
{&MainUIProperty,"MainUI       " ,NULL,NULL,NULL};

Главное меню – это дополнительное меню, которое используется для классификации элементов данных, которые я хочу отобразить. Родительским меню является MainUI, а элемент подменю сначала инициализируется значением NULL. Обратите внимание, что строка должна быть написана как можно короче из 15 символов, а пробелы также следует использовать для заполнения свободного места, чтобы последующие данные было легко обновить. На этой странице меню есть четыре пункта меню, а полоса прокрутки равна 0.

//Главное меню
struct MenuProperty_t menuMainProperty={4,0};
struct Menu_t menuMain[4]=
{
	{&menuMainProperty,"last menu     ", NULL,NULL, &MainUI,NULL},
	{&menuMainProperty,"Animal        ", NULL,NULL, &MainUI,NULL},
	{&menuMainProperty,"Pid           ", NULL,NULL, &MainUI,NULL},
	{&menuMainProperty,"Time set      ", NULL,TimeSetInit, &MainUI,NULL}
};

Подменю animal считается меню уровня 3. Это данные пункта меню animal, который вы действительно хотите отобразить. Родительским меню является menuMain, и для инициализации элемента подменю сначала устанавливается значение NULL. На этой странице меню есть шесть пунктов меню, а длина полосы прокрутки равна 2, поскольку с одной стороны отображается до 4 пунктов, прокручивайте вниз по одному за раз.

Примечание: Примечание: Если вы определяете один элемент, вы должны добавить & к адресу. Если вы определяете массив, вы можете использовать имя массива для получения первого адреса массива.

//anima Подменю l
struct MenuProperty_t setMenu1Property={6,2};
struct Menu_t setMenu1[6]=
{
	{&setMenu1Property,"last menu     ",NULL,NULL,menuMain,NULL},
	{&setMenu1Property,"bull          ",NULL,NULL,menuMain,NULL},
	{&setMenu1Property,"bird          ",NULL,NULL,menuMain,NULL},
	{&setMenu1Property,"dog           ",NULL,NULL,menuMain,NULL},
	{&setMenu1Property,"bow           ",NULL,NULL,menuMain,NULL},
	{&setMenu1Property,"fish          ",NULL,NULL,menuMain,NULL}
};

Функция обновления OLED-страницы

Обновите информацию о странице, если она есть на главной странице, просто очистите ее и нарисуйте картинку. если ее нет на главной странице, используйте наложение для достижения эффекта обновления.

void DisplayRefreash(struct Menu_t *nowMenu,u8 selectItem,u8 scrollBar)
{
	int i = 0;
	static u8 lastSelectItem=0;//Запишите последний индекс
	if(nowMenu==&MainUI)//Когда я возвращаюсь в главное меню, поскольку экран занят не полностью, все экраны очищаются, а затем окрашиваются.
	{
		OLED_Clear();
		MainUiSet();
	}else 
	{	
		OLED_ShowChar(0,lastSelectItem*16, ' ',16,1);//Снимите последнюю стрелку указателя
		OLED_ShowChar(0,selectItem*16,     '>',16,1);//На этот раз нарисуйте указательную стрелку
		for(i=0;i<(nowMenu->MenuProperty->MenuLen-nowMenu->MenuProperty->scrollBarLen);i++)
		{
			OLED_ShowString(8,i*16,nowMenu[i+scrollBar].displayString,16,1);
		}
	}
	OLED_Refresh();
	lastSelectItem = selectItem;
}

Функция обновления OLED-данных

Когда необходимо обновить данные каждой страницы, вам нужно только перезаписать последние данные в строке, поэтому вам придется каждый раз вводить полную строку символов, или вы можете достичь той же цели, имея одинаковую длину символов в каждой строке.

void DisplayRefreashData(struct Menu_t *nowMenu,u8 selectItem,u8 scrollBar)
{
	int i = 0;
	for(i=0;i<(nowMenu->MenuProperty->MenuLen-nowMenu->MenuProperty->scrollBarLen);i++)
	{
		OLED_ShowString(8,i*16,nowMenu[i+scrollBar].displayString,16,1);
	}
	OLED_Refresh();
}

Перед обновлением данных изменения данных могут быть использованы функцией Sprintf для переопределения отображаемой строки каждого пункта меню

void GuiDataDisplayRefresh()
{
	if(menuPoint == setMenu1)
	{
		sprintf((char*)setMenu1[1].displayString,"bull  %3d     ",count1);
		sprintf((char*)setMenu1[2].displayString,"bird  %3d     ",count2);
		sprintf((char*)setMenu1[3].displayString,"dog   %3d     ",count3);
		sprintf((char*)setMenu1[4].displayString,"bow   %3d     ",count4);
		sprintf((char*)setMenu1[5].displayString,"fish  %3d     ",count5);
		DisplayRefreashData(menuPoint,selectItem,scrollBar);
	}
	else if(menuPoint==&MainUI)
	{
		MainUiSet();
		OLED_Refresh();
	}
}

Инициализация меню

В основном это используется для инициализации подменю некоторых пунктов меню, а также указателя на текущее меню.

Глобальные переменные

menuPoint    Текущее меню указывает на адрес
selectItem   Текущий индекс 0-3
scrollBar    Местоположение текущей полосы прокрутки, верхняя часть равна 0
void GuiInit()
{
	MainUI.childrenMenu = menuMain;
	menuMain[1].childrenMenu = setMenu1;
	menuMain[2].childrenMenu = setMenu2;
	menuMain[3].childrenMenu = setMenu3;
	menuPoint = &MainUI;
	DisplayRefreash(menuPoint,selectItem,scrollBar);
}

Ключевая функция управления

Кнопки вверх и вниз в основном используются для переключения текущего индекса и полосы прокрутки

Левая и правая клавиши в основном используются для реализации функциональных функций

void GuiControl()
{
	if(isKeyUp==1)//Нажмите кнопку вверх
	{
		isKeyUp=0;//Флаг снят
		selectItem--;//Указатель текущего меню на странице текущего меню --
		if(selectItem<0&&scrollBar!=0)//Если оно меньше 0, но полоса прокрутки не равна 0, полоса прокрутки вычитается
		{
			selectItem = 0;
			scrollBar--;
		}else if(selectItem<0&&scrollBar==0)//Если оно меньше 0, а полоса прокрутки также равна 0, индекс будет перемещен в конец, и полоса прокрутки будет самой большой.
		{
			selectItem = menuPoint->MenuProperty->MenuLen-1-menuPoint->MenuProperty->scrollBarLen;
			scrollBar  = menuPoint->MenuProperty->scrollBarLen;
		}
		DisplayRefreash(menuPoint,selectItem,scrollBar);//Обновите дисплей
	}else if(isKeyDown==1)//Аналогично кнопке "Вверх"
	{
		isKeyDown=0;
		selectItem++;
		//Если индекс превышает максимальное значение, но полоса прокрутки не имеет максимального значения, оставьте индекс на максимальном значении, а полосу прокрутки ++
		if(selectItem>(menuPoint->MenuProperty->MenuLen-1-menuPoint->MenuProperty->scrollBarLen)&&scrollBar!=menuPoint->MenuProperty->scrollBarLen)
		{
			selectItem = menuPoint->MenuProperty->MenuLen-1-menuPoint->MenuProperty->scrollBarLen;
			scrollBar++;
		}
		//Если индекс больше максимального значения, полоса прокрутки имеет максимальное значение и перемещается в первую позицию
		else if(selectItem>(menuPoint->MenuProperty->MenuLen-1-menuPoint->MenuProperty->scrollBarLen)&&scrollBar==menuPoint->MenuProperty->scrollBarLen)
		{
			selectItem=0;
			scrollBar =0;
		}
		DisplayRefreash(menuPoint,selectItem,scrollBar);
	}else if(isKeyLeft==1)
	{
		//Если функция 1 текущего меню не пуста, выполните соответствующую функцию
		if(menuPoint[selectItem+scrollBar].func1!=NULL)
		{
			menuPoint[selectItem+scrollBar].func1();
		}
		isKeyLeft=0;
		DisplayRefreash(menuPoint,selectItem,scrollBar);
	}else if(isKeyRight==1)
	{
		if(selectItem==0 && scrollBar==0 && menuPoint[selectItem].fatherMenu!=NULL)//Если индекс равен нулю, а родительское меню не пустое, укажите на родительский указатель
		{
			menuPoint = menuPoint[selectItem].fatherMenu;
		}
		else if(menuPoint[selectItem+scrollBar].childrenMenu!=NULL)//Если страница индексного подменю не пуста, укажите на подменю
		{
			if(menuPoint[selectItem+scrollBar].func2!=NULL)//Если функция 2 текущего меню не пуста, выполните соответствующую функцию
			{
				menuPoint[selectItem+scrollBar].func2();
			}
			menuPoint = menuPoint[selectItem+scrollBar].childrenMenu;
			selectItem = 0;
		}
		else if(menuPoint[selectItem+scrollBar].func2!=NULL)//Если функция 2 текущего меню не пуста, выполните соответствующую функцию
		{
			menuPoint[selectItem+scrollBar].func2();
		}
		isKeyRight=0;
		DisplayRefreash(menuPoint,selectItem,scrollBar);
	}else if(isKeyOk==1)
	{
		isKeyOk=0;
		DisplayRefreash(menuPoint,selectItem,scrollBar);
	}
	GuiDataDisplayRefresh();
}
5 1 голос
Рейтинг статьи
Подписаться
Уведомить о
guest
0 комментариев
Межтекстовые Отзывы
Посмотреть все комментарии
0
Оставьте комментарий! Напишите, что думаете по поводу статьи.x