Skocz do zawartości

Uniwersalna biblioteka obsługi menu wielopoziomowego - jak to ugryźć?


_LM_

Pomocna odpowiedź

Miało być bez maszyny stanów, łatwo modyfikowalne, zajmujące niewiele pamięci i niezależne od wejścia/wyjścia.

Teraz weź  swój kod i zmień tak, że na wejściu jest enkoder, a na wyjściu syntezator mowy z uwzględnieniem "where am i". A jak już to zrobisz, to dodaj na szybko dwie pozycje gdzieś na trzecim poziomie a na drugim połącz pozycję 3 i 5 w jedną.

  • Lubię! 1
Link do komentarza
Share on other sites

(edytowany)

Stwierdziłem że łatwiej będzie jak po kolei będę tutaj przedstawiał swoje pomysły w formie kodu. W przygotowaniu jest struktura którą będzie musiał uzupełnić użytkownik:

.h

typedef void(*inputCallback)(void(*f)(void));

typedef struct{
	inputCallback callback; // f wejsciowa
}IOmenu_t;

.c

void menuIO_Init(IOmenu_t *IOmenu,inputCallback * iCallback)
{
IOmenu->callback = iCallback;

}

W main:

IOmenu_t menu;

oraz funkcja init

menuIO_Init(&menu, &register_callback_sw_function);

funkcja register_callback_sw_function jako argument przyjmuje wskaźnik na funkcję która ma się wykonać po kliku, przyjmuje też wskaźnik na strukturę opisującą przyciski. Wrzucam kod tej biblioteki

.h

/*
 * button.h
 *
 *  Created on: 8 gru 2021
 *
 */
#ifndef BUTTON_H_
#define BUTTON_H_

typedef enum{
	IDLE = 0,
	DEBOUNCE,
	PRESSED,
	KLIK,
	REPEAT
}BTN_STATE_t;

typedef void(*fptr_t)(void);


typedef struct{
	BTN_STATE_t state;
	volatile uint8_t * dtaReadBitState;
	uint8_t nBit;
    volatile uint8_t *tmr;
	uint8_t repeat;
	uint8_t debounce_time;
	fptr_t	btn_pressed;
	fptr_t  btn_repeat;
}btn_t;

void btn_setup(btn_t * key, volatile uint8_t *btn,uint8_t nBit,volatile uint8_t  *timer);
void register_callback_sw_function(btn_t * key, fptr_t bPressed, fptr_t bRepeat);
void btn_process(btn_t * key);


#endif /* BUTTON_H_ */

.c

/*
 * button.c
 *
 *  Created on: 7 gru 2021
 *
 */
#include <avr/io.h>

#include "button.h"
#define TIMEDEBOUNCE 20



static uint8_t getState(volatile uint8_t * data,uint8_t nbit){
	return *data & (1 << nbit);
}

void btn_setup(btn_t * key,volatile uint8_t *btn,uint8_t nBit, volatile uint8_t  *timer)
{
	key->state = IDLE;
	key->dtaReadBitState = btn;
	key->nBit = nBit;
	key->tmr = timer;
	key->debounce_time = TIMEDEBOUNCE;
}

void btn_process(btn_t * key)
{

	if(0 == getState(key->dtaReadBitState, key->nBit) && key->state == IDLE){ // first
		*key->tmr = key->debounce_time; // timer przyjmuje wartosc czasu debounce
		key->state = DEBOUNCE;
		return;
	}

	if(key->state == DEBOUNCE && *key->tmr == 5){ // kiedy tmr zliczy
		if(0 == getState(key->dtaReadBitState, key->nBit)) // i jesli nadal wcisniety to
		{
			key->state = PRESSED;
		}
		else
		{
			key->state = IDLE; // jesli zaklocenie
		}
}

	if(key->state == PRESSED){
		if(key->btn_pressed)key->btn_pressed(); // wykonaj callback jesli funkcja zarejestrowana(wskaznik != NULL)
		key->state = KLIK;
		}

	if(key->state == KLIK && getState(key->dtaReadBitState, key->nBit))key->state = IDLE;

}

void register_callback_sw_function(btn_t * key, fptr_t bPressed, fptr_t bRepeat)
{
	key->btn_pressed = bPressed;
	key->btn_repeat  = bRepeat;
}

Wystąpiły następujące problemy: niezgodność typów z tymi które oczekuje funkcja callback, tu się zacząłem zastanawiać w jaki sposób przekazać wskaźnik na funkcję z nieznaną ilością argumentów, czy jest to możliwe?

  • Jakie pola powinna obsługiwać struktura IOmenu_t?
  • Myślę że trzeba zaimplementować strumień wyjściowy
  • Czy od razu też wskazać miejsce ewentualnego zapisu danych.
  • No na pewno przydałby się timer który automatycznie zamknie menu po czasie bezczynności

Póki co tyle pytań, działam dalej 

Edytowano przez _LM_
Link do komentarza
Share on other sites

Zarejestruj się lub zaloguj, aby ukryć tę reklamę.
Zarejestruj się lub zaloguj, aby ukryć tę reklamę.

jlcpcb.jpg

jlcpcb.jpg

Produkcja i montaż PCB - wybierz sprawdzone PCBWay!
   • Darmowe płytki dla studentów i projektów non-profit
   • Tylko 5$ za 10 prototypów PCB w 24 godziny
   • Usługa projektowania PCB na zlecenie
   • Montaż PCB od 30$ + bezpłatna dostawa i szablony
   • Darmowe narzędzie do podglądu plików Gerber
Zobacz również » Film z fabryki PCBWay

Dlaczego nie chcesz jakiejś funkcji typu getCommand() tylko bawisz się w buttony, debouncingi i jakieś niskopoziomowe pierdoły które w żannym przypadku nie powinny być częścią tej biblioteki? Co jak będzie trzeba zastosować enkoder? Albo co gorsza zrobić coś co obsługuje albo to albo to w zależności np. od stanu jakiegoś pinu przy starcie?

Link do komentarza
Share on other sites

 

3 minuty temu, ethanak napisał:

Dlaczego nie chcesz jakiejś funkcji typu getCommand() tylko bawisz się w buttony, debouncingi i jakieś niskopoziomowe pierdoły które w żannym przypadku nie powinny być częścią tej biblioteki?

Aaaha już rozumiem. A libsa z buttonami podesłałem żeby nie było wątpliwości co mam na myśli (z resztą działa bez problemu ale nie o tym temat). Ok jakie twoim zdaniem powinna mieć pola strukturka IOmenu?

Link do komentarza
Share on other sites

(edytowany)
18 minut temu, ethanak napisał:

Dlaczego nie chcesz jakiejś funkcji typu getCommand()

Taki getCommand() musiałby przewidzieć wszystkie możliwe przejścia menu czyli góra, dół, lewo,prawo zapisz itd. Nawet jeśli części tych instrukcji użytkownik aktualnie nie będzie wykorzystywał

Edytowano przez _LM_
Link do komentarza
Share on other sites

@_LM_ poczekasz do wieczora kiedy będę miał dostęp do kompa (na telefonie nawet chorderem dość ciężko się kod pisze)?

W międzyczasie poszukaj czegoś na temat "poziomów abstrakcji", zrozumienie ich niesamowicie ułatwia życie

 

  • Lubię! 1
Link do komentarza
Share on other sites

Anegdota nie bardzo trafiona, a i krytyka kol. @FlyingDutch przesadzona. W końcu każdy w mniejszym lub wiekszym stopniu używa gotowych rozwiązań (inaczej trzeba by było pisać wszystko a assemblerze) - jak nie z FSM, to z gotowego parsera Json.

Jednak jako hobbyści wybieramy sobie coś, co nas zainteresuje i nawet jak są na ten temat gotowe rozwiązania, to i tam robimy to po swojemu. Może lepiej, a może i nie, ale satysfakcja jest bezcenna.

Wszystko to, o czym tu dyskutujemy zostało już gdzieś, przez kogoś wymyślone, więc postępu ogólnoświatowego i tak z tego nie będzię.

  • Lubię! 1
Link do komentarza
Share on other sites

3 minuty temu, jand napisał:

Wszystko to, o czym tu dyskutujemy zostało już gdzieś, przez kogoś wymyślone, więc postępu ogólnoświatowego i tak z tego nie będzię.

Powiedz to firmie Dyson i ich odkurzaczom cyklonowym - wszyscy ich olewali, bo przecież to nie ma sensu, a aktualne rozwiązania są najlepsze. Teraz jest to jedna z najlepszych firm w tej dziedzinie, a te co krzyczały "przecież to nie ma sensu" poupadały.

Sam odkurzacz powstał jako projekt hobbystyczny, bo coś można było zrobić lepiej. Wymagało to ogromu pracy, ale dzięki temu odkurzacz bardzo dobrze rozdziela powietrze i kurz, dzięki czemu nie wymaga worka, czyli nie trzeba go wymieniać. 

O to też chodziło w anegdocie z kamieniami - nie ważne co jest łatwiejsze teraz, ważne co będzie wygodniejsze w przyszłości. Nie chodziło o postęp ogólnoświatowy, tylko o to, by kolega zrobił to tak, by jemu wygodnie było z tego korzystać.

Link do komentarza
Share on other sites

5 minut temu, jand napisał:

Wszystko to, o czym tu dyskutujemy zostało już gdzieś, przez kogoś wymyślone, więc postępu ogólnoświatowego i tak z tego nie będzię.

Ech... przypomina mi się "Fundacja" Asimova i podejście pewnego archeologa który twierdził, że nie należy robić wykopalisk tylko korzystać z wcześniej napisanych artykułów,  najlepiej przez autorów którzy sami wykopalisk nie robili.

8 minut temu, jand napisał:

Jednak jako hobbyści

Hobbysta != amator.

A akurat takiej bardzo ogólnej biblioteki do menu nie widziałem, i jeśli koledze @_LM_ uda się ją stworzyć wpłynie to na postęp ogólnoświatowy 😉

  • Lubię! 1
Link do komentarza
Share on other sites

1 minutę temu, jand napisał:

Wszystko to, o czym tu dyskutujemy zostało już gdzieś, przez kogoś wymyślone, więc postępu ogólnoświatowego i tak z tego nie będzię.

Pomyślało setki tysięcy inżynierów kupujących byle co na Aliexpress, o efektach muszę przypominać? Poza tym @FlyingDutch pokazał coś co nie jest tematem tego wątku.

Szczerze mówiąc mam komercyjną bibliotekę i program do tworzenia menu, mógłbym skorzystać nawet tutaj o tym nie wspominając ale co z tego jak za jakiś czas znów padnie pytanie: jak zbudować menu? No i znów tysiące switch case, okraszone tu i ówdzie delayami tak że nieda się z tego korzystać. 

@jand bardzo proszę o trzymanie się tematu wątku

Link do komentarza
Share on other sites

2 godziny temu, ethanak napisał:

Miało być bez maszyny stanów, łatwo modyfikowalne, zajmujące niewiele pamięci i niezależne od wejścia/wyjścia.

Teraz weź  swój kod i zmień tak, że na wejściu jest enkoder, a na wyjściu syntezator mowy z uwzględnieniem "where am i". A jak już to zrobisz, to dodaj na szybko dwie pozycje gdzieś na trzecim poziomie a na drugim połącz pozycję 3 i 5 w jedną.

Cześć,

dlaczego bez maszyny stanów - to jest jeden z podstawowych wzorców projektowych w systemach embedded (i to bardzo dobrze sprawdzający się w praktyce - jest bardzo często stosowany w projektowaniu układów scalonych).

Przejscia miedzy pozycjami menu nie są w instrukcji switch - tylko w deklaracjach "transitions" - przejść miedzy stanami (łatwo można je modyfikować - są tam wskaźniki na wywoływane funkcje).. Lokalizację językową i inne interfejsy sprzętowe można łatwo dodać do tego kodu - wystarczy zadeklarować dodatkowe eventy, ja po prostu nie miałem takiej potrzeby. Wbrew pozorom napisałem sporo generatorów kodu (także opartych na XML, JSON i defiicji struktur baz danych w SQL), ale nigdy do takich trywialnych zadań  (np. do synchroizacji baz danych między serwerami). Widzę, że macie sporo wolnego czasu, ja niestety nie i wymagane są ode mnie bardzo krótkie terminy realizacji zadań, więc przeważnie staram się robić rzeczy jak najprościej , podobno nie jest to najgorszy pomysł - jest nawet takie dość popularne powiedzonko : KISS - keep it simple supid.

Jeśli macie sporo czasu to można się bawić, nie widzę w tym nic złego.

BTW: w podanym przeze mnie kodzie nie ma żadnej instrukcji blokującej.

Pozdrawiam

Edytowano przez FlyingDutch
update
Link do komentarza
Share on other sites

11 minut temu, FlyingDutch napisał:

napisałem sporo generatorów kodu

Ja z paru korzystałem..

11 minut temu, FlyingDutch napisał:

ale nigdy do takich trywialnych zadań 

a ja właśnie do takich trywialnych (np. Glade).

13 minut temu, FlyingDutch napisał:

Widzę, że macie sporo wolnego czasu, ja niestety nie i wymagane są ode mnie bardzo krótkie terminy realizacji zadań

Rozróżnij to, co robimy (przynajmniej ja) w ramach pracy, a co robimy w ramach hobby (a to mogą być bardzo podobne rzeczy). Gdyby Thorvalds i paru innych kiedyś tam nie mieli czasu na jakieś tam pitoły dzisiaj wszyscy siedzieliby na Windowsach (co może byłoby do przełknięcia) a jedynym używanym serwerem www byłby jakiś IIS (a to raczej byłoby mało przyjemne).

Link do komentarza
Share on other sites

Ok więc nieco zmieniłem kwestię podkładania funkcji wejściowych (tak wiem @ethanak że miałem zaczekać) teraz to wygląda tak:

.h

typedef void(*ptrf)(void);

typedef struct{
	
	uint16_t timer;
	ptrf callback[]; // f wejsciowa
}IOmenu_t;

void menuInputInit(IOmenu_t *IOmenu,volatile uint16_t * menuTimer,ptrf up,ptrf dwn,ptrf left,ptrf right,ptrf enter,ptrf esc, ptrf mexit);

.c

void menuInputInit(IOmenu_t *IOmenu,volatile uint16_t * menuTimer,ptrf up,ptrf dwn,ptrf left,ptrf right,ptrf enter,ptrf esc, ptrf mexit)
{
	IOmenu->callback[0] = up;
	IOmenu->callback[1] = dwn;
	IOmenu->callback[2] = left;
	IOmenu->callback[3] = right;
	IOmenu->callback[4] = enter;
	IOmenu->callback[5] = esc;
	IOmenu->callback[6] = mexit;
	IOmenu->timer = *menuTimer;
}

w main

menuInputInit(&menu, &mnuTimer, b1click, b2click, NULL, NULL, NULL, NULL, NULL);

Ogólnie program będzie musiał mocno operować na strukturze IOmenu, teraz zastanawiam się jak obsłużyć strumień wyjściowy, będzie on zawarty w tej samej strukturze ale z osobną funkcją inicjalizującą aby nie robić bałaganu

Link do komentarza
Share on other sites

Dołącz do dyskusji, napisz odpowiedź!

Jeśli masz już konto to zaloguj się teraz, aby opublikować wiadomość jako Ty. Możesz też napisać teraz i zarejestrować się później.
Uwaga: wgrywanie zdjęć i załączników dostępne jest po zalogowaniu!

Anonim
Dołącz do dyskusji! Kliknij i zacznij pisać...

×   Wklejony jako tekst z formatowaniem.   Przywróć formatowanie

  Dozwolonych jest tylko 75 emoji.

×   Twój link będzie automatycznie osadzony.   Wyświetlać jako link

×   Twoja poprzednia zawartość została przywrócona.   Wyczyść edytor

×   Nie możesz wkleić zdjęć bezpośrednio. Prześlij lub wstaw obrazy z adresu URL.

×
×
  • Utwórz nowe...

Ważne informacje

Ta strona używa ciasteczek (cookies), dzięki którym może działać lepiej. Więcej na ten temat znajdziesz w Polityce Prywatności.