Skocz do zawartości

Pierwsze projekty w STM32CubeIDE - UART


yukimikoto

Pomocna odpowiedź

Dzień dobry :)

 

Po niestety wielu nieudanych projektach, przesiadłam się na CubeMxIDE, gdyż jest wg mnie łatwiej zacząć. Jestem już na lekcji poświęconej UART. Mam kilka pytań:

1. Jak mają się Callbacki do przerwań, co one robią w przerwaniach i dlaczego się je stosuje?

2. Mam następującą funkcję:

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)

Czy

UART_HandleTypeDef 

jest typem danych (jeżeli tak, jakim?), natomiast

*huart

jest deklaracją wskaźnika? Nie rozumiem zapisu w tym nawiasie. Wiem tylko, że ten Callback pozwala odebrać dane z mikrokontrolera i w nim określamy, co chcemy z tymi danymi zrobić.

3. Mam także pytanie, mając kod:

HAL_UART_Receive_IT(&huart2, &znak, 1);

używamy tutaj wskaźnika do zmiennej znak, a więc wskazujemy na adres, pod którą się ona znajduje. Natomiast wysyłając dane:

HAL_UART_Transmit_IT(&huart2, komunikat, dl_kom);

Nie używamy tutaj wskaźników, a odwołujemy się bezpośrednio do zmiennych. Czy chodzi o to, że w powyższym przykładzie wskazujemy na miejsce, gdzie ma wysłać to, co odbierze program z mikrokontrolera, natomiast w Transmit odwołujemy się nie do miejsca, a do wartości zmiennej?

4. Dlaczego w kodzie jest wymagane, by funkcję HAL_UART_Receive_IT(&huart2, &znak, 1); wywołać dwa razy - raz w funkcji main(), a drugi w Callbacku?

uint8_t znak, komunikat[20];
uint16_t dl_kom;
/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_USART2_UART_Init();
  /* USER CODE BEGIN 2 */
HAL_UART_Receive_IT(&huart2,&znak,1);

  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

/**
  * @brief System Clock Configuration
  * @retval None
  */

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
	if (huart->Instance == USART2)
	{
	if(znak=='e')
	{
		sprintf(komunikat, "\nLubie cie");
		dl_kom = 9;
	}
	else if (znak=='d')
	{
		sprintf(komunikat, "\nnie lubie cie");
		dl_kom = 13;

	}
	else
	{
		sprintf(komunikat, "\nzly znak");
		dl_kom = 8;
	}
	HAL_UART_Receive_IT(&huart2, &znak, 1);
	HAL_UART_Transmit_IT(&huart2, komunikat, dl_kom);
}
}

Pozdrawiam i proszę o wyrozumiałość, jestem na poziomie elementarnym , dopiero się uczę, ale chcę uczyć się z pełnym zrozumieniem, nie na zasadzie "wykuć kawałek kodu na blachę".

Pozdrawiam

 

 

Link do komentarza
Share on other sites

@yukimikoto

Ad 1.

Gdy wygenerowane zostanie przerwanie np. na wciśnięcie przycisku to działający program wstrzumuje pracę, którą w chwili wystąpienia przerwania wykonywał i dokonuje skoku do tzw. procedury obsługi tego konkretnego przerwania - gdzie program przechodzi do wywołania jakiejś funkcji - callbacka. Callback to nic innego jak zdefiniowany kawałek kodu, który określa co się ma zadziać, gdy wystąpi przerwanie X. W tym konkretnym przypadku w callbacku zazwyczaj odczytujemy odebrany znak i coś z nim robimy.

Ad 2.

UART_HandleTypeDef jest to po prostu struktura z wieloma polami, które każde ma jakieś określone znaczenie - nazywa się te struktury uchwytami (handle) i są podstawą frameworka od ST. Obiekty takich struktur (poprzez wskaźniki) są używane w większości funkcji dostarczonych przez HAL. Tutaj (rozdział 3.2, strona 10) znajdziesz dłuższe wyjaśnienie od ST.

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) - ten callback/funkcja przyjmuje jako argument wskaźnik do obiektu typu UART_HandleTypeDef (powyższej struktury). Więc *huart nie jest deklaracją wskaźnika, tylko argumentem i ten argument ma postać wskaźnika do typu UART_HandleTypeDef - czyli mówiąc inaczej możemy do tej funkcji przekazywać obiekty typu UART_HandleTypeDef np. huart1 (UART1) i coś z tymi obiektami w tej funkcji zrobić. Funkcja oczekuje wskaźnika więc w wywołaniu przekazywany jest adres obiektu - HAL_UART_Receive_IT(&huart2, &znak, 1);. Jest to klasyczne wykorzystanie wskaźników i struktur w C.

Ad 3.

Jak zobaczysz na deklaracje obu tych funkcji:

HAL_StatusTypeDef HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
HAL_StatusTypeDef HAL_UART_Transmit_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)

to w obu przypadkach lista argumentów jest identyczna. Więc sposób wywołania tych funkcji będzie taki sam.

Natomiast u Ciebie różnica polega na tym, że w pierwszym przypadku - HAL_UART_Receive_IT(&huart2, &znak, 1); - przekazujesz jedną zmienną typu uint8_t - jeden znak - a funkcja w argumencie oczekuje wskaźnika do uint8_t, więc przekazany jest adres, pod którym znajduje się zmienna znak -> &znak.

Z kolei w drugim przypadku, wysyłasz więcej niż jedną zmienną typu uint8_t - de facto tablicę uint8_t (które w tym kontekście nazywa się buforami). I teraz sztuczka polega na tym, że w C i C++ nazwa tablicy jest jednocześnie adresem pierwszego elementu tej tablicy, a więc pisząc HAL_UART_Transmit_IT(&huart2, komunikat, dl_kom); korzystasz właśnie z tej zasady -> komunikat jest adresem na pierwszy element tej tablicy. Można to zapisać też w inny równoważny sposób: &komunikat[0] i będzie oznaczało to to samo co komunikat.

Podsumowując w obu przypadkach przekazany został adres na konkretną zmienną / konkretny element tablicy. Natomiast w pierwszym efektem wywołania tego callbacku będzie zapisanie pod adresem zmiennej znak odebranego przez UART znaku, natomiast w drugim przypadku efektem będzie wysłanie tekstu w postaci znaków umieszczonych w tablicy komunikat (począwszy od pierwszego elementu tej tablicy), a sama tablica została przekazana przez nazwę tablicy komunikat (lub równoważnie &komunikat[0]).

Ad 4.

Callback HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) jest wywoływany gdy odebrana zostanie określona liczba znaków. Ale żeby stało się to faktem to trzeba przynajmniej raz wywołać funkcję HAL_UART_Receive_IT -> "chce żeby callback HAL_UART_RxCpltCallback wywołał się za każdym razem gdy odebrany zostanie 1 znak". Dlatego pierwszy raz w funkcji main, a potem za każdym odebranym znakiem w callbacku. Bez wywołania HAL_UART_Receive_IT w main callback HAL_UART_RxCpltCallback nigdy by się nie wywołał.

Edytowano przez Matthew11
poprawa formatowania, opisów i literówek
  • Lubię! 1
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.