fbpx

Zegar RTC, który znajdziesz w mikrokontrolerach STM32 służy nie tylko do prostego odmierzania czasu. Oprócz tej podstawowej funkcji posiada jeszcze kilka innych mniej lub bardziej użytecznych. Pokazałem Ci już rejestry backupowe, które można wykorzystać do przechowywania wrażliwych danych, czy zapamiętania daty w mniej zaawansowanym zegarze. Tym razem chciałbym pokazać Ci kilka innych ficzerów, które są dla nas dostępne.

W trakcie pisania wyszło mi całkiem sporo tekstu, więc podzieliłem ten wpis na dwie części 🙂

Dodatkowe funkcje RTC w STM32F4

Możesz pamiętać z poprzedniego wpisu, że w serii F4 jest naprawdę sporo rejestrów w samym module zegara czasu rzeczywistego. Spowodowane jest to między innymi tym, że mamy dodanych kilka ciekawych funkcji. Pokażę Ci dwie pierwsze związane z “włamaniami”:

  • Tamper
  • Time Stamp
  • Tamper + Time Stamp

Pozwól, że oprę się na płytce, którą wybrałem w poprzednim artykule. Jest to tzw. BlackPill z STM32F401 na pokładzie. Płytkę taką (lub nieco mocniejszą) możesz kupić u mnie.

stm32f411

Oprogramowanie, którego użyję to:

  • STM32CubeIDE v1.2.0
  • STM32CubeMX v5.5.0 wbudowany w IDE
  • HAL F4 v1.24.2

Będę również bazował na projekcie, który stworzyłem dla STM32F4.

Time Stamp w STM32F4

Tłumacząc to na polski będzie to Stempel Czasu (jak z Harry’ego Pottera kurcze). Funkcja ta polega na tym, że podczas “włamania”, czyli wykrycia na specjalnym pinie zbocza zatrzaśnięta zostaje data i czas. Wykonywane jest to “po cichu”, więc włamujący nie zorientuje się co się stało.

Takie zarejestrowane majsterkowanie możemy od razu zdalnie wysłać do centrali lub zapisać w logach w pamięci trwałej.

Po pierwsze musisz aktywować tę funkcję w Cube zaznaczając ją ptaszkiem.

Pojawi się skonfigurowany pin GPIO na PC13. To on będzie służył za źródło stempla.

W konfiguracji RTC pojawi się dodatkowa sekcja.

Wybierz tutaj które zbocze Cię interesuje. Ja wybrałem opadające. Na pin PC13 dodatkowo wrzuciłem rezystor podciągający do zasilania 10 kΩ. Nie można ustawić Pull Upa wewnętrznego dla pinu specjalnego.

Event Time Stamp generuje przerwanie. Oczywiście w HAL znajdziemy jego obsługę, którą możemy zaimplementować. Ja w niej odczytuję datę i godzinę zajścia oraz ustawiam własną flagę, której użyję w pętli głównej.

void HAL_RTCEx_TimeStampEventCallback(RTC_HandleTypeDef *hrtc)
{
  HAL_RTCEx_GetTimeStamp(hrtc, &RtcTimeStamp, &RtcDateStamp, RTC_FORMAT_BIN);
  MillisecondsStamp = ((RtcTime.SecondFraction-RtcTimeStamp.SubSeconds)/((float)RtcTime.SecondFraction+1) * 100);
	TimeStampFlag = 1;
}

Flagę tę wykorzystuję w pętli głównej, aby wyprintować datę i godzinę stempla. W pozostałym czasie co sekundę wysyłam dane z RTC na port szeregowy.

  /* USER CODE BEGIN WHILE */
  while (1)
  {
	  HAL_RTC_GetTime(&hrtc, &RtcTime, RTC_FORMAT_BIN);
	  Milliseconds = ((RtcTime.SecondFraction-RtcTime.SubSeconds)/((float)RtcTime.SecondFraction+1) * 100);
	  HAL_RTC_GetDate(&hrtc, &RtcDate, RTC_FORMAT_BIN);

	  if(RtcTime.Seconds != CompareSeconds)
	  {
		  MessageLen = sprintf((char*)Message, "Date: %02d.%02d.20%02d Time: %02d:%02d:%02d:%02d\n\r", RtcDate.Date, RtcDate.Month, RtcDate.Year, RtcTime.Hours, RtcTime.Minutes, RtcTime.Seconds, Milliseconds);
		  HAL_UART_Transmit(&huart2, Message, MessageLen, 100);
		  CompareSeconds = RtcTime.Seconds;
	  }

	  if(TimeStampFlag == 1)
	  {
		  MessageLen = sprintf((char*)Message, "TimeStamp! Date: %02d.%02d.20%02d Time: %02d:%02d:%02d:%02d\n\r", RtcDateStamp.Date, RtcDateStamp.Month,
				  RtcDateStamp.Year, RtcTimeStamp.Hours, RtcTimeStamp.Minutes, RtcTimeStamp.Seconds, MillisecondsStamp);
		  HAL_UART_Transmit(&huart2, Message, MessageLen, 100);

		  TimeStampFlag = 0;
	  }

	  if(GPIO_PIN_RESET == HAL_GPIO_ReadPin(TEST_GPIO_Port, TEST_Pin))
	  {
		 while(GPIO_PIN_RESET == HAL_GPIO_ReadPin(TEST_GPIO_Port, TEST_Pin))
		 {}
		 SetRTC();
	  }
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }

A oto efekt działania.

Po pojawieniu się zbocza opadającego pojawia się przerwanie i jest to wyprintowane na serial. Proste 🙂 Pełny kod przykładu znajdziesz tutaj.

Tamper w STM32F4

O tej funkcji już wspominałem przy okazji artykułów o F1 bo tam również ona występuje. Jej działanie polega na czyszczeniu rejestrów backupowych po wykryciu zbocza na specjalnym pinie. Jest to taki antywłamywacz. Gdy ktoś chce się włamać do urządzenia i otwiera obudowę, możemy automatycznie skasować wrażliwe dane z rejestrów podtrzymywanych bateryjnie.

Aby ustawić Tamper wystarczy zaznaczyć odpowiedni ptaszek w Cube.

Tutaj też pojawi się skonfigurowany pin na rysunku mikrokontrolera.

Oraz oczywiście pojawiły się nowe opcje w konfiguracji RTC

Są to głównie opcje zachowania pinu.

  • Filter – możesz zdecydować, po którym zboczy rejestry się wyczyszczą.
  • Sampling Frequency – jak często pin ma być sprawdzany. Widoczne ustawienie oznacza sprawdzenie pinu co sekundę.
  • Precharge Duration – wstępne ładowanie pinu do stanu wysokiego. Taki pull-up bez rezystora, jeśli dobrze to rozumiem. Uwaga – do PC13 w BlackPill podłączona jest dioda, która od razu ten ładunek rozładuje!
  • Tamper PullUp – czy używać tego wstępnego naładowania.
  • Time Stamp On Tamper Detection – połączenie z Time Stamp
  • Tamper 1 Trigger – na które zbocze ma reagować moduł Tamper.

Zdarzenie, które wywołuje Tamper nie tylko kasuje zawartość rejestrów backupowych, ale też może wyzwolić przerwanie. W przykładzie wykorzystuję je do ponownego ustawienia Tampera.

  while (1)
  {
	  HAL_RTC_GetTime(&hrtc, &RtcTime, RTC_FORMAT_BIN);
	  Milliseconds = ((RtcTime.SecondFraction-RtcTime.SubSeconds)/((float)RtcTime.SecondFraction+1) * 100);
	  HAL_RTC_GetDate(&hrtc, &RtcDate, RTC_FORMAT_BIN);

	  if(RtcTime.Seconds != CompareSeconds)
	  {
		  MessageLen = sprintf((char*)Message, "Date: %02d.%02d.20%02d Time: %02d:%02d:%02d:%02d\n\r", RtcDate.Date, RtcDate.Month, RtcDate.Year, RtcTime.Hours, RtcTime.Minutes, RtcTime.Seconds, Milliseconds);
		  HAL_UART_Transmit(&huart2, Message, MessageLen, 100);
		  CompareSeconds = RtcTime.Seconds;
	  }

	  if(TamperFlag == 1)
	  {

		  HAL_RTCEx_GetTimeStamp(&hrtc, &RtcTimeTimeStamp, &RtcDateTimeStamp, RTC_FORMAT_BIN);
		  Milliseconds = ((RtcTime.SecondFraction-RtcTimeTimeStamp.SubSeconds)/((float)RtcTime.SecondFraction+1) * 100);

		  MessageLen = sprintf((char*)Message, "Tamper detected at Time Stamp %02d.%02d.20%02d Time: %02d:%02d:%02d:%02d!\n\r",RtcDateTimeStamp.Date, RtcDateTimeStamp.Month, RtcDateTimeStamp.Year,
				  RtcTimeTimeStamp.Hours, RtcTimeTimeStamp.Minutes, RtcTimeTimeStamp.Seconds, Milliseconds);
		  HAL_UART_Transmit(&huart2, Message, MessageLen, 100);

		  MessageLen = sprintf((char*)Message, "Backup Registers cleared:\n\r");
		  HAL_UART_Transmit(&huart2, Message, MessageLen, 100);

		  for(int i = 0; i < BACKUP_COUNT; i++)
		  {
			  uint32_t tmp = HAL_RTCEx_BKUPRead(&hrtc, aBKPDataReg[i]);
			  MessageLen = sprintf((char*)Message, "%X, ", tmp);
			  HAL_UART_Transmit(&huart2, Message, MessageLen, 100);
		  }

		  MessageLen = sprintf((char*)Message, "\n\r");
		  HAL_UART_Transmit(&huart2, Message, MessageLen, 100);

		  MessageLen = sprintf((char*)Message, "Write example data to regs:\n\r");
		  HAL_UART_Transmit(&huart2, Message, MessageLen, 100);

		  for(int i = 0; i < BACKUP_COUNT; i++)
		  {
			  HAL_RTCEx_BKUPWrite(&hrtc, aBKPDataReg[i], (i * 32));
			  uint32_t tmp = HAL_RTCEx_BKUPRead(&hrtc, aBKPDataReg[i]);
			  MessageLen = sprintf((char*)Message, "%X, ", tmp);
			  HAL_UART_Transmit(&huart2, Message, MessageLen, 100);
		  }

		  MessageLen = sprintf((char*)Message, "\n\rWait for Tamper\n\r\n\r");
		  HAL_UART_Transmit(&huart2, Message, MessageLen, 100);

		  RTC_TamperSet();
		  TamperFlag = 0;
	  }

	  if(GPIO_PIN_RESET == HAL_GPIO_ReadPin(TEST_GPIO_Port, TEST_Pin))
	  {
		 while(GPIO_PIN_RESET == HAL_GPIO_ReadPin(TEST_GPIO_Port, TEST_Pin))
		 {}
		 SetRTC();
	  }
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }

W pętli głównej ustawiam rejestry i czekam na zdarzenie Tamper. Do płytki podłączyłem sobie przycisk i zewnętrzny Pull Up, aby działał on poprawnie (nie ten precharged Pull Up bo jest LEDka). Po pojawieniu się przerwania Tamper ustawiam jedynie flagę i później po jej sprawdzeniu printuję zawartość rejestrów, ponownie je wypełniam oraz nastawiam jeszcze raz Tamper.

Oto efekt działania:

Rejestry pięknie są czyszczone 🙂 Pełny kod przykładu znajdziesz tutaj.

Tamper + Time Stamp w STM32F4

W ustawieniu Tampera było takie coś jak Time Stamp On Tamper Detection. Podczas normalnego działania Tampera kasujemy rejestry i tyle.

Czasem jednak może zajść taka potrzeba, że chciałbyś wiedzieć nieco więcej o tym włamaniu. Chociażby to kiedy to się stało. Właśnie to umożliwia połączenie tych dwóch funkcji – kasowanie i zapis daty i godziny zdarzenia.

Wystarczy ustawić ten jeden bit w konfiguracji Tampera, aby połączyć te dwie funkcje.

Następnie w obsłudze Tamper wystarczy odczytać specjalne rejestry Time Stamp, w których przechowywana jest informacja, kiedy to się stało i… tyle 🙂 Mamy dwie pieczenie na jednym ogniu. Proste, co nie?

Pełny kod przykładu znajdziesz tutaj.

Podsumowanie

Jak widzisz używanie funkcji dodatkowych wbudowanego RTC nie jest skomplikowane. Pamiętaj, że te funkcje podobnie jak cały moduł zegara działają podczas zasilania bateryjnego. Oznacza to że “włamanie” podczas działania bateryjnego jest odnotowane w rejestrach specjalnych przeznaczonych właśnie na te funkcje. Jest to bardzo cenna właściwość bo nie zawsze chcemy, aby MCU był na pełnych obrotach.

W następnym wpisie zajmę się funkcjami związanymi z budzeniem mikrokontrolera oraz… człowieka 🙂

Pełny projekt wraz z biblioteką znajdziesz jak zwykle na moim GitHubie: LINK

Jeśli zauważyłeś jakiś błąd, nie zgadzasz się z czymś, chciałbyś coś dodać istotnego lub po prostu uważasz, że chciałbyś podyskutować na ten temat, napisz komentarz. Pamiętaj, że dyskusja ma być kulturalna i zgodna z zasadami języka polskiego.

5/5 - (6 votes)

Podobne artykuły

.

5 komentarzy

Konrad · 09/06/2021 o 10:34

HAL_RTCEx_GetTimeStamp -> funkcja zwraca Year jako 0U co jest widoczne w logach na konsoli oraz linii 321 tej funkcji halowej, czy to jest efekt zamierzony czy blad biblioteki? Probowalem napisac wlasna funkcje HAL_RTCEx_GetTimeStampYear gdzie dodalem linje odczuty rejestru LAT -> sDate->Year = (uint8_t)((datetmpreg & (RTC_DR_YT | RTC_DR_YU)) >> 16U); ale efekt jest podobny, jedynie wpisanie do zmiennej roku wartosci z palca lub wartosci odczytanej z funkcji HAL_RTC_GetDate daje poprawny wynik czyli w moim przypadku 21.

Alfred · 03/04/2020 o 12:24

Świetnie opracowany materiał. Bardzo przejrzyście wytłumaczone.

    Mateusz Salamon · 03/04/2020 o 16:24

    Dzięki! 🙂

Daniel · 02/04/2020 o 06:40

Świetny artykuł ?

    Mateusz Salamon · 02/04/2020 o 20:07

    Dzięki! 🙂

Dodaj komentarz

Avatar placeholder

Twój adres e-mail nie zostanie opublikowany. Wymagane pola są oznaczone *