Skocz do zawartości

Opoznienie w us


Arturs

Pomocna odpowiedź

Witam

Jak można zrealizować opóźnienie w us dla STM32 ? Dla ms jest stworzona funkcja w bibliotece HAL ale dla us nic takiego nie znalazłem.

Oprogramowuje czujnik z którym komunikacja nie jest możliwa za pomocą SPI, I2C lub innym znanym interfejsem.

Trzeba wystawiać sygnał zegarowy i potem dane odczytywać. Problem jest taki, że STM32 jest za szybki 🙂 (wcześniej przy 8 bitowcach się z tym nie spotkałem bo za wolno było). Po wystawieniu zegara muszę poczekać na wystawienie danych przez czujnik pomiarowy a to trwa kilka us i procek musi zaczekać. Myślę jak to można jakoś sensownie rozwiązać.

Pozdrawiam

Artur

Link do komentarza
Share on other sites

Czy możesz po prostu napisać co to za czujnik? Znając szczegóły protokołu będzie nam łatwiej radzić. Pisanie komunikacji na opóźnieniach to jednak chała.

Dla µs znalazłem dwa rozwiązania oparte na DWT - specjalnym bloku licznika cykli procesora:

https://kbiva.wordpress.com/2013/03/25/microsecond-delay-function-for-stm32/

https://community.st.com/thread/13838#comment-151160

Link do komentarza
Share on other sites

Ojej, tak się obawiałem. Wciąż nie mogę zrozumieć dlaczego zrobili tam taki idiotyczny interfejs. Jakby ktoś czytał coś kiedyś o I2C, potem zapomniał a potem nagle próbował to odtworzyć z pamięci, porażka.

Hm, metoda opóźnieniowa tu się sprawdzi, bo nie ma szczególnych wymagań na czasy maksymalne. Nawet jeśli przyjdzie jakieś przerwanie albo scheduler zabierze czas procesora to transmisja co najwyżej na chwilę się zamrozi, ale nie zwiśnie.

Z drugiej strony może bardziej eleganckie byłoby zrobienie tego na automacie wykonywanym w przerwaniu okresowym od timera? Gdybyś miał np. jakiś system tick, powiedzmy 1ms, to realizując tam fsm obsługujący kolejne zdarzenia protokołu wykonywałbyś tę transmisję w sposób przezroczysty dla reszty kodu. Kolejne zbocza zegara generowane byłyby w odstępach 1ms więc całość komunikacji trwałaby dziesiątki ms, ale to niczemu nie przeszkadza. Czujnik wilgotności i tak jest wolny a odczyty z niego nie są potrzebne częściej jak raz na minutę.

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

Fakt ten interfejs to porażka. Jeden jego plus to, że nie Time- out, więc ciężko transmisje zawiesić. Rozwiązanie z delay mi się tak samo jak Tobie nie podoba, a alternatywa przez Ciebie zaproponowana jest dużo efektywniejsza. Tylko muszę ogarnąć jak ten kod podzielić, żeby tylko kawałek w przerwaniu wykonywać.

Myślisz, że można do tego Systick użyć, czy inny dowolny Timer ?

Jestem początkujący w STM32 i nie jestem jeszcze biegły w zasobach tego procesora więc każda podpowiedź jest istotna.

Link do komentarza
Share on other sites

I tak zwykle potrzebujesz w systemie podstawy czasu, choćby do wyzwalania jakichś procesów okresowych typu pomiary, odczyty czujników, mruganie diodkami itp. tak więc przerwanie od timera/systick'a na pewno się przyda.

Tutaj wynajdujesz w protokole zdarzenia i na tym opierasz pracę automatu. Przykładowo normalnie stoi on w stanie IDLE i czeka na zapalenie przez kogoś innego flagi będącej żądaniem wykonania określonej operacji, np. START_ODCZYTU. Po jej wykryciu zapala flagę BUSY, ustawia sobie (statyczne) liczniki bitów i bajtów, wypełnia bufory danych itp. i rozpoczyna transmisję. Od tej pory w każdym kolejnym przebiegu (przerwaniu) wykonuje operacje które akurat powinien zrobić: zmienia stan linii zegara i/lub danych, odczytuje stan linii danych, przesuwa wskaźniki danych, odlicza liczniki itp. Po zakończeniu wysyłania/odbierania gasi flagę BUSY informując tym samym "klienta", że proces się zakończył i że dane są gotowe gdzieś w pamięci a sam przeszedł z powrotem do stanu IDLE. Komunikacja między klientem a procesem obsługi czujnika może odbywać się na wiele sposobów. Żeby uprościć automat można np. samemu wypełniać pola danych nadawanych oraz ustawiać wszystkie liczniki i dopiero wtedy odpalać proces "Zrób komunikację". Wtedy automat nie musi nawet rozumieć tego co robi, ma tylko wysłać n bajtów i odebrać m bajtów.

Link do komentarza
Share on other sites

Jestem w trakcie pisania obsługi tego czujnika tak aby kawałek kodu wykonywał się w przerwaniu, które występuje co 1ms. Zdecydowałem się na użycie instrukcji switch ale tak naprawdę im dalej w to brnę to jestem coraz mniej przekonany o poprawności mojego wyboru.

Do tej pory tak to wygląda:

void SHT_Read(void)
{

static uint8_t ticks;
static uint8_t Check_Bits = 0x80;

if (!SHT_STATUS.busy) return;


switch (next_states)
{

case SCK_HIGH:
	HAL_GPIO_WritePin(SHT_CLK_GPIO_Port, SHT_CLK_Pin, 1);
	if (SHT_STATUS.start_transmission) next_states = DATA_WRITE;
	else next_states = SCK_LOW;

	break;

case SCK_LOW:
	HAL_GPIO_WritePin(SHT_CLK_GPIO_Port, SHT_CLK_Pin, 0);
	if (SHT_STATUS.read_data) next_states = DATA_READ;
	if (SHT_STATUS.start_transmission) next_states = SCK_HIGH;
	if (SHT_STATUS.send_command) next_states = DATA_WRITE;
	if (SHT_STATUS.read_data || SHT_STATUS.read_crc) next_states = DATA_READ;
       break;

case DATA_READ:
	HAL_GPIO_ReadPin(SHT_DATA_GPIO_Port, SHT_DATA_Pin);
	next_states = SCK_HIGH;
	break;

case DATA_WRITE:

	if (SHT_STATUS.start_transmission && ticks == 1) 
	{

		HAL_GPIO_WritePin(SHT_DATA_GPIO_Port, SHT_DATA_Pin, 0);	
		next_states = SCK_LOW;

	}
	if (SHT_STATUS.start_transmission && ticks == 4)
	{

		HAL_GPIO_WritePin(SHT_DATA_GPIO_Port, SHT_DATA_Pin, 1);	
		next_states = SCK_LOW;
	}


	if (SHT_STATUS.send_command)
	{


		if (SHT_STATUS.command & Check_Bits)
		{
			HAL_GPIO_WritePin(SHT_DATA_GPIO_Port, SHT_DATA_Pin, 1);			
		}
		else
		{
			HAL_GPIO_WritePin(SHT_DATA_GPIO_Port, SHT_DATA_Pin, 0);	
		}

		Check_Bits = Check_Bits >> 1;
		next_states = SCK_HIGH;
	}
	break;

case WAIT_FOR_MEASURE:


	if (!(HAL_GPIO_ReadPin(SHT_DATA_GPIO_Port, SHT_DATA_Pin)))
	{
		next_states = SCK_HIGH;
		SHT_STATUS.read_data = 1;

	}
	else ticks--;

	break;



default:

	break;

}

ticks++;
if (ticks == 5)
{
	SHT_STATUS.start_transmission = 0;
	SHT_STATUS.send_command = 1;
}
if (ticks == 30)
{
	SHT_STATUS.send_command = 0;
	SHT_DATA_GPIO_INPUT_Init();
	next_states = SCK_HIGH;

}
if (ticks == 32)
{
	next_states = WAIT_FOR_MEASURE;

}

if (ticks == 50 )
{

	SHT_DATA_GPIO_OUTPUT_Init();
	HAL_GPIO_WritePin(SHT_DATA_GPIO_Port, SHT_DATA_Pin, 0);
	next_states = SCK_HIGH;

}
if (ticks == 52)
{

	SHT_DATA_GPIO_INPUT_Init();
	next_states = SCK_HIGH;

}
}

Jeśli to jedyna droga to napisze to do końca 🙂

Link do komentarza
Share on other sites

Bądź aktywny - zaloguj się lub utwórz konto!

Tylko zarejestrowani użytkownicy mogą komentować zawartość tej strony

Utwórz konto w ~20 sekund!

Zarejestruj nowe konto, to proste!

Zarejestruj się »

Zaloguj się

Posiadasz własne konto? Użyj go!

Zaloguj się »
×
×
  • 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.