Pętla nieskończona w ESP NONSDK

Język C dla mikrokontrolerów Tensilic
ODPOWIEDZ
Awatar użytkownika
squeez
GRafik
Posty: 150
Rejestracja: 16 paź 2017, 23:52

Pętla nieskończona w ESP NONSDK

Post autor: squeez »

Mam taki dylemat :)

Wcześniej jak coś robiłem na ESP było to na zasadzie zdarzeń i callback jakie dostarcza SDK. Czyli np. jak nastapi połączenie z wifi wywoływany jest calback, z funkcją do obsłużenia, podobnie jak przychodzą dane po TCP czy UDP. Ewentualnie zdarzenia cykliczny wywoływane przez os_timer (tak na marginesie również callbacka).

Teraz napisałem obsługę RC5 na przerwaniach, nie chcę w wywołaniu przerwania walić całej obsługi (wywoływania zdarzeń dla poszczególnych kodów itp.) obsługa przerwań ma tylko zdekodować kod i to robi.

Więc wymyśliłem sobie że do zdarzenia RC5 wykorzystam system_os_task przypisując funkcję jak ma być wywołana a po zdekodowaniu kodu w przerwaniu wywołam system_os_post() no i tak zrobiłem i działa ale po chwili przyszła mi refleksja, że tych zadań dla użytkownika jest tylko 3. Więc jak będę chciał coś bardziej kluczowego zrobić to może być cienko.

Wymyśliłem więc że zrobię coś na wzór płetli nieskończonej jak w klasycznym podejściu na uc. Czyli zrobiłem task systemowy nie do obsługi RC5 ale taki co wywoływany jest w kółko, sam przez siebie, działający niejako pętla while()

Kod: Zaznacz cały

void loop (os_event_t *e)
{
    // Tu obsługa zadań użytkownika
    switch( e->sig ) {
    case RC5_SIG:
    	// Tutaj obsługa kodów RC5
    	// Do kodu RC5 dostajemy się przez e->par;
    	// ...
    	
    	// Na koniec "czyścimy", żeby ponownie nie wywołać tego same zdarzenia.
    	e->sig = 0;
    	e->par = 0;
    break;
    
    }
    // Kolejne wywołanie loop z parametrami.
    system_os_post(0, e->sig, e->par);
}
natomiast w funkcji obsługi przerwania, gdzie dekodowany jest kod RC5 gdy już mam całą ramkę, mniej więcej coś takiego

Kod: Zaznacz cały

#define RC5_SIG 1
uint16_t rc5data; // zmienna zawierajaca ramkę RC5

// to jest zmienna globalna (na razie potem, będzie to bardziej eleganckie)
os_event_t *sys_e;
sys_e = (os_event_t*)os_malloc(siezeof(os_event_t)*1);

// Tu jest funkcja obsługi przerwań ...
// Jeśli ramka poprawne wysłanie jej do przetworzenia
if(rc5_cnt > 13) {
	sys_e->sig = RC5_SIG;
	sys_e->par = rc5data;
}
Czy takie podejście jest "poprawne" bo działać działa poprawnie :) W takiej "sztucznej" pętli można obsługiwać inne zdarzenia wysyłane w podobny sposób, nadając im inny SIG i dane w PAR, czy to bezpośrednio same dane czy wskaźnik na coś większego niż 32 bity bo takiej wielkości zmienna w strukturze os_event_t.

Jeśli stosuje się jakieś inne konwencje prosiłbym o wskazanie kierunku :)
Awatar użytkownika
SunRiver
Użytkownik
Posty: 948
Rejestracja: 08 paź 2017, 11:27
Lokalizacja: Festung Oppeln
Kontakt:

Re: Pętla nieskończona w ESP NONSDK

Post autor: SunRiver »

Semantycznie w programistyce uważa się rozwiązanie za poprawne gdy:

1. Nie koliduje z semantyką języka
2. Nie koliduje i nie wywołuje ostrzeżeń prekompilera
3. Nie powoduje błędów semantycznych
4. Nie wywołuje błędów podczas kompilacji
5. Wykonuje kod w sposób zamierzony przez programistę
6. Działanie jest prawidłowe

Tak wiec jest to po prostu niejako dopełnienie powiedzonka że rozwiązań 1 problemu jest tyle ilu programistów.
Niektórzy wynieśli manierę niejako ze szkoły czy innych kursów czy książek , że trzeba pisać tak czy siak program
Tym czasem na prawdę sprawa wygląda inaczej i jest prostsza niż się wydaje ...

JA stosuję się tylko do wytycznych w kwestii składni , oraz sposobu formatowania kodu (ze względu na przejrzystość)
oraz do poprawności kolejności tworzenia części logicznych programu.

w pozostałych przypadkach panuje pewna dowolność kombinacji ...
Awatar użytkownika
squeez
GRafik
Posty: 150
Rejestracja: 16 paź 2017, 23:52

Re: Pętla nieskończona w ESP NONSDK

Post autor: squeez »

OK tu się zgodzę mi chodzi tylko czy takie zapętlenie taska nie ma jakichś negatywnych skutków, teraz przykład jest trywialny i nie zauważyłe by coś było nie tak. Timery programowe (systemowe) odpalają się poprawnie.

No dobra przetestuję takie rozwiązanie z większą ilością zdarzeń i zobaczymy jak to będzie chodzić.
Awatar użytkownika
squeez
GRafik
Posty: 150
Rejestracja: 16 paź 2017, 23:52

Re: Pętla nieskończona w ESP NONSDK

Post autor: squeez »

Jak by ktoś chciał do zabawy i testów odbiór RC5 w przerwaniu.

Kod: Zaznacz cały

#define RC5_PIN		14
#define RC5_SIG		1
#define RC5_1T		889
#define RC5_2T		1778
#define RC5_ERR		220
#define RC5_MAX		50000
#define RC5_MIN		500

void ICACHE_FLASH_ATTR rc5init(void) {
	// GPIO14 - input pullup
	PIN_FUNC_SELECT( PERIPHS_IO_MUX_MTMS_U, FUNC_GPIO14 );
	GPIO_DIS_OUTPUT( GPIO_ID_PIN( RC5_PIN ) );

	// Enbale interrupt
	ETS_GPIO_INTR_DISABLE();
	gpio_pin_intr_state_set(GPIO_ID_PIN( RC5_PIN ), GPIO_PIN_INTR_ANYEDGE);
	ETS_GPIO_INTR_ENABLE();
}

void ICACHE_FLASH_ATTR rc5event(uint16_t code) {
	uint8_t tdata;
	tdata = (uint8_t)(code >> 11) & (0x01);
	os_printf("Ramka RC5: %X Tog: %d\n\r", code, tdata);

	if( (uint8_t)(code & 0x3F) == 1 ) {
		SetRelay(1, 1);
	}

	if( (uint8_t)(code & 0x3F) == 2 ) {
		SetRelay(1, 0);
	}
}

void rc5interrupt(uint8_t state) {

	static uint32_t ltime = 0;
	static uint16_t data = 0;
	static uint8_t cnt = 1;
	static uint8_t halftime = 2;
	uint32_t ttime = system_get_time();
	uint16_t time;

	time = ttime - ltime;

	// IF 1T
	if( time > (RC5_1T - RC5_ERR) && time < (RC5_1T + RC5_ERR) ) {
		halftime--;
	}

	// IF T2 or 2*T1
	if( ( time > (RC5_2T - RC5_ERR) && time < (RC5_2T + RC5_ERR) ) || !halftime ) {
		cnt++;
		data <<= 1;
		data |= state;
		halftime = 2;
	}

	if( cnt > 13 ) {
		sys_ev->par =~data;
		sys_ev->sig = RC5_SIG;
		cnt = 0;
	}

	if( cnt == 2 && data ) {
		cnt = 0;
	}

	if( time > RC5_MAX || time < RC5_MIN || !cnt ) {
		cnt = 1;
		data = 0;
		halftime = 2;
	}

	ltime = ttime;
}
obsługa przerwania

Kod: Zaznacz cały

void ICACHE_FLASH_ATTR gpio_init(void) {
	// Ustawienia GPIO ...
	
	// Innterrupt function
	ETS_GPIO_INTR_ATTACH(gpio_int_handler, NULL);
}
void gpio_int_handler(void *arg) {

	uint32_t gpio_status = GPIO_REG_READ(GPIO_STATUS_ADDRESS);
	GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, gpio_status);

	// GPIO14 - RC5
	if( gpio_status & (BIT( RC5_PIN )) ) {
		rc5interrupt( (uint8_t)GPIO_INPUT_GET( GPIO_ID_PIN( RC5_PIN ) ) );
	}
}
obsługa eventów w poolingu

Kod: Zaznacz cały

os_event_t *sys_ev;

void ICACHE_FLASH_ATTR loop(os_event_t *e) {

	switch( e->sig ) {
		case RC5_SIG:
			rc5event( (uint16_t)e->par );
		break;
	}

	if( e->sig ) {
		e->sig = 0;
	}

	system_os_post(USER_TASK_PRIO_0, sys_ev->sig, sys_ev->par);
}

void ICACHE_FLASH_ATTR user_init(void) {
	system_init_done_cb(app_init);
	gpio_init();
	rc5init();
	sys_ev = (os_event_t*)os_malloc(sizeof(os_event_t)*1);
	system_os_task(loop, USER_TASK_PRIO_0, sys_ev, 1);
	system_os_post(USER_TASK_PRIO_0, 0, 0);
}
tak mniej więcej to wygląda, i działa, nie ma obsługi bitu togle ale można dorobić.
ODPOWIEDZ

Wróć do „C dla Tensilic”