Инструменты пользователя

Инструменты сайта


develepment:clang:embedded_menu

Меню для устройства

Для OLED экрана 16х02 потребовалось реализовать меню. В качестве основы был взят код MicroMenu. Пару дней походив вокруг него, набросал следующий код:

# menu.h
	
#ifndef MENU_H
#define MENU_H
 
/**
 * Реализация меню для STM32. Основана на коде MicroMenu.
 * http://www.avrfreaks.net/index.php?module=Freaks%20Files&func=viewFile&id=2178&showinfo=1
 */
 
typedef enum {SELECT, BTN_MENU=4, BTN_UP, BTN_DOWN, BTN_OK} MESSAGE_T;
typedef void (*pfunc_t)(MESSAGE_T);
 
typedef struct {
  void* parent;    // указатель на родительский элемент меню
  void* ok;        // указатель на дочерний элемент меню
  void* next;      // указатель на следующий соседний элемент меню
  void* prev;      // указатель на предыдущий соседний элемент меню
  pfunc_t handler; // функция-обработчик элемента меню
} MENU_T;
 
extern MENU_T null_menu;
extern MENU_T* current_menu;
 
#define NE null_menu
#define NF (void*) 0
#define NULL_LABEL 0x00
 
#define PARENT  *((MENU_T*) current_menu->parent)
#define OK      *((MENU_T*) current_menu->ok)
#define NEXT    *((MENU_T*) current_menu->next)
#define PREV    *((MENU_T*) current_menu->prev)
#define HANDLER *((pfunc_t*) current_menu->handler)
 
#define MENU_MAKE(name, parent, ok, next, prev, handler) \
  extern MENU_T parent;                                  \
  extern MENU_T ok;                                      \
  extern MENU_T next;                                    \
  extern MENU_T prev;                                    \
  MENU_T name = {                                        \
    (void*) &parent,                                     \
    (void*) &ok,                                         \
    (void*) &next,                                       \
    (void*) &prev,                                       \
    (pfunc_t) handler                                    \
  }
 
#define MENU_SELECT(x) menu_select((MENU_T*) &x);
 
void menu_select(MENU_T* menu);
 
#endif /* MENU_H */
# menu.c
	
#include "menu.h"
 
MENU_T null_menu = {(void*) 0, (void*) 0, (void*) 0, (void*) 0, (void*) 0};
MENU_T* current_menu;
 
 
void menu_select(MENU_T* menu) {
  if ((void*) menu == (void*) &NE)
    return;
 
  current_menu = menu;
  current_menu->handler(SELECT);
}

Использовать его крайне просто — надо лишь описать структуру меню:

MENU_MAKE(m_idle, m_clock, NE, NE, NE, menu_idle_handler);
MENU_MAKE(m_clock, m_idle, m_clock_hour, m_date, m_flash, menu_clock_handler);
MENU_MAKE(m_clock_hour, m_clock, m_clock_min, NE, NE, menu_clock_hour_handler);
MENU_MAKE(m_clock_min, m_clock, m_clock_sec, NE, NE, menu_clock_minute_handler);
MENU_MAKE(m_clock_sec, m_clock, m_clock, NE, NE, menu_clock_second_handler);
...

И для каждого элемента меню реализовать функцию-обработчик, например ``menu_idle_handler``:

void menu_idle_handler(MESSAGE_T msg) {
  switch(msg) {
  case SELECT:
    // Настройка экрана для этого элемента меню.
    ws0010_clear();
    ws0010_string("M: MENU", LINE0, 0);
    ws0010_string("Status: IDLE", LINE1, 0);
    rtc_mode = RTC_TIME;
    break;
  case BTN_MENU:
    // Гасим часы и переходим на первый уровень меню.
    rtc_mode = RTC_HIDE;
    ws0010_string("      ", LINE1, 10);
    MENU_SELECT(PARENT);
    break;
  case BTN_OK:
    // Переключаем состояние часов.
    if (RTC_TIME == rtc_mode) {
      rtc_mode = RTC_DATE;
    } else {
      rtc_mode = RTC_TIME;
    }
    break;
  default:
    break;
  }
}

Такой подход позволил мне реализовать меню произвольной вложенности с дополнительной логикой в любом элементе меню.

https://vk.com/video19523249_169098839?hash=f963c9588b0cb4a2

develepment/clang/embedded_menu.txt · Последние изменения: 2014/10/29 16:04 — Ruslan Popov