Skocz do zawartości

Pomocna odpowiedź

Napisano

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

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ę.

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.

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.

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 🙂

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...