[HAL] Uart, przerwania, buforowanie

Tu możesz pisać o swoich problemach z pisaniem programów w języku C/C++ dla STM.
Awatar użytkownika
Antystatyczny
Geek
Geek
Posty: 1168
Rejestracja: czwartek 03 wrz 2015, 22:02

[HAL] Uart, przerwania, buforowanie

Postautor: Antystatyczny » poniedziałek 02 maja 2016, 06:03

Witam.

Powalczyłem trochę z uartem w STM32 i przy okazji udało mi się opracować własną implementację bufora cyklicznego, który za chwilę zaprezentuję.
Moduł ten (plik źródłowy oraz nagłówkowy) udostępniam na licencji GPL v3. Jego przykładowy sposób użycia pokażę wraz z odbiorem danych przez USART2. Nie jest to przykład wzorcowy, ani napisany na czysto. To jedynie pętla zwrotna, która wysyła z powrotem na uart wszystko, co wpadnie do bufora cyklicznego. Po wygenerowaniu kodu przez CubeMX należy go uzupełnić o kilka istotnych "drobiazgów". Przede wszystkim należy włączyć przerwania dla modułu USART2. Ale co ja będe gadał... Zerknijcie na kod:

Kod: Zaznacz cały

/**
  ******************************************************************************
  * File Name          : main.c
  * Description        : Main program body
  ******************************************************************************
  *
  * COPYRIGHT(c) 2016 STMicroelectronics
  *
  * Redistribution and use in source and binary forms, with or without modification,
  * are permitted provided that the following conditions are met:
  *   1. Redistributions of source code must retain the above copyright notice,
  *      this list of conditions and the following disclaimer.
  *   2. Redistributions in binary form must reproduce the above copyright notice,
  *      this list of conditions and the following disclaimer in the documentation
  *      and/or other materials provided with the distribution.
  *   3. Neither the name of STMicroelectronics nor the names of its contributors
  *      may be used to endorse or promote products derived from this software
  *      without specific prior written permission.
  *
  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
  ******************************************************************************
  */
/* Includes ------------------------------------------------------------------*/
#include "stm32l1xx_hal.h"
#include "usart.h"
#include "gpio.h"

/* USER CODE BEGIN Includes */
#include "stdbool.h"
/* USER CODE END Includes */

/* Private variables ---------------------------------------------------------*/

/* USER CODE BEGIN PV */
/* Private variables ---------------------------------------------------------*/
uint8_t txt[] = "Hello from STM32L152RE!\r\n";
/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);

/* USER CODE BEGIN PFP */
/* Private function prototypes -----------------------------------------------*/

/* USER CODE END PFP */

/* USER CODE BEGIN 0 */

/* USER CODE END 0 */

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();

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

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_USART2_UART_Init();

  /* USER CODE BEGIN 2 */
  HAL_NVIC_SetPriority(USART2_IRQn, 0, 1);
  HAL_NVIC_EnableIRQ(USART2_IRQn);

  /* Greeting via uart2 */
  HAL_UART_Transmit_IT(&huart2, txt, sizeof(txt) - 1);
  /* USER CODE END 2 */

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

  /* USER CODE BEGIN 3 */

     UART_Task(&huart2);
  }
  /* USER CODE END 3 */

}

/** System Clock Configuration
*/
void SystemClock_Config(void)
{

  RCC_OscInitTypeDef RCC_OscInitStruct;
  RCC_ClkInitTypeDef RCC_ClkInitStruct;

  __HAL_RCC_PWR_CLK_ENABLE();

  __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);

  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.HSICalibrationValue = 16;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI;
  RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL6;
  RCC_OscInitStruct.PLL.PLLDIV = RCC_PLL_DIV3;
  HAL_RCC_OscConfig(&RCC_OscInitStruct);

  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
  HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_1);

  HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/1000);

  HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);

  /* SysTick_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(SysTick_IRQn, 0, 0);
}

/* USER CODE BEGIN 4 */

/* USER CODE END 4 */

#ifdef USE_FULL_ASSERT

/**
   * @brief Reports the name of the source file and the source line number
   * where the assert_param error has occurred.
   * @param file: pointer to the source file name
   * @param line: assert_param error line source number
   * @retval None
   */
void assert_failed(uint8_t* file, uint32_t line)
{
  /* USER CODE BEGIN 6 */
  /* User can add his own implementation to report the file name and line number,
    ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
  /* USER CODE END 6 */

}

#endif

/**
  * @}
  */

/**
  * @}
*/

/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/


Jak widać tuż po inicjalizacji wygenerowanej przez CubeMX wpakowałem ustalenie priorytetu oraz włączenie przerwania dla USART2, a następnie wysyłam dla zabawy tekst powitalny. Wysyłany jest w bardzo surowy sposób. Myślę, że wysyłka danych wymaga opakowania, ale nie o tym mowa w tym wątku. Aby zmusić USART do współpracy z buforem cyklicznym musiałem się uciec do pewnego podstępu. Zadeklarowałem jednobajtowy buforek, do którego predefiniowana funkcja odbiorcza ładuje bajt odebrany przez USART. W związku z tym, że buforek jest jednobajtowy, callback wywoływany po zapełnieniu się buforka, wywołuje się po każdym odebranym bajcie... i o to właśnie m chodziło. W callbacku sprawdzam, czy bufor cykliczny nie jest czasem pełny i jeśli nie jest, to pakuje doń odebrany bajt. Zobaczcie sami:

Kod: Zaznacz cały

/* callback called each time, when rxData2 is full */
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
   /* check if circular buffer isn't full */
   if(BUFFER_IsFull(&hbuffer2) == BUFFER_STATE_NOT_FULL)
   {
      /* put received byte to the buffer */
      BUFFER_PutByte(&hbuffer2, rxData2);
      /* re-enable IT */
      HAL_UART_Receive_IT(huart, &rxData2, sizeof(rxData2));
   }
}


Nazwa callbacka jest predefiniowana i nie możemy jej zmienić. Gdy już bajt siedzi w buforze, włączamy ponownie przerwanie od odbioru danych (ono jest wyłączane automatycznie po każdym zapełnieniu buforka jednobajtowego) i opuszczamy callback. Sterowanie wraca do pętli głównej programu i natrafia na UART_Task(). Funkcja ta sprawdza, czy w buforze cyklicznym znajdują się jakieś dane, a jeśli tak, to wysyła je z powrotem na USART2. A tak wygląda jej ciało:

Kod: Zaznacz cały

void UART_Task(UART_HandleTypeDef* huart)
{
   /* temporary one-byte buffer */
   static uint8_t txData;

   /* check if is at least one byte to read from buffer */
   if(BUFFER_IsEmpty(&hbuffer2) == BUFFER_STATE_NOT_EMPTY)
   {
      /* get byte and store in txData */
      BUFFER_GetByte(&hbuffer2, &txData);
      /* test loopback - put received byte back via UART2 */
      HAL_UART_Transmit_IT(huart, &txData, sizeof(txData));
   }
}


Jak widać prostota straszna...ale chodziło tylko o test bufora cyklicznego. Aha, implementacja bufora cyklicznego umożliwia wykorzystanie go do jakiejkolwiek innej roboty. On jest ogólnego przeznaczenia. Wynik działania programu:
2016-05-02.png


Ok, na koniec kompletny projekt dla Atollic TrueSTUDIO. Projekt tworzyłem na procesor STM32L152RE z dołączonym kwarcem 8MHz.

CubeMXusart_IT.zip


Pozdrawiam.
Nie masz wymaganych uprawnień, aby zobaczyć pliki załączone do tego posta.
"The true sign of intelligence is not knowledge but imagination" Albert Einstein.

Wróć do „Programowanie STM w C/C++”

Kto jest online

Użytkownicy przeglądający to forum: Obecnie na forum nie ma żadnego zarejestrowanego użytkownika i 5 gości