Продолжаем настраивать и использовать все периферийные модули микроконтроллеров STM32 при помощи STM32CubeMx. Вот ссылка на все статьи курса – ссылка – а сегодня у нас на очереди инициализация, а также прием и передача данных при помощи SMT32 USART.
Для тестирования реализуем следующее – будем передавать данные при помощи USART2, а принимать посредством USART1. Таким образом, проверив отправляемые через один модуль и принятые другим модулем данные, мы сможем убедиться, правильно ли мы настроили приемопередатчик USART. Проект будем создавать для STM32F4, соответственно, для экспериментов используем отладочную плату STM32F4Discovery.
Для того, чтобы принять отправленные данные нам надо соединить выводы Rx/Tx USART1 с аналогичными выводами USART2. Только не забываем их перекрестить:
У микроконтроллера STM32F407VGT6 выводы расположены следующим образом:
Получается, что нам надо соединить выводы так:
- PA2 – PA10
- PA3 – PA9
С электрическими подключениями разобрались, переходим к работе непосредственно в STM32CubeMx. Создаем новый проект и активируем нужные нам модули USART1 и USART2:
Никаких дополнительных настроек тут не требуется, так что переходим на вкладку Configuration. Здесь мы можем настроить стандартные параметры USART – скорость передачи данных, количество бит, четность, количество стоп-бит и другие. В данном примере давайте поменяем только скорость, поставим 9600. Кроме того, включим прерывания:
На этом работу с Cube заканчиваем, генерируем проект и переходим в IAR. Вся инициализация портов и модуля USART уже включена в функцию main():
/* Initialize all configured peripherals */ MX_GPIO_Init(); MX_USART1_UART_Init(); MX_USART2_UART_Init();
Кроме того, Cube сгенерировал обработчики прерываний, в которых уже реализованы механизмы обмена данными и сброса всех нужных флагов – все это находится внутри функции HAL_UART_IRQHandler():
/** * @brief This function handles USART2 global interrupt. */ void USART2_IRQHandler(void) { /* USER CODE BEGIN USART2_IRQn 0 */ /* USER CODE END USART2_IRQn 0 */ HAL_UART_IRQHandler(&huart2); /* USER CODE BEGIN USART2_IRQn 1 */ /* USER CODE END USART2_IRQn 1 */ } /** * @brief This function handles USART1 global interrupt. */ void USART1_IRQHandler(void) { /* USER CODE BEGIN USART1_IRQn 0 */ /* USER CODE END USART1_IRQn 0 */ HAL_UART_IRQHandler(&huart1); /* USER CODE BEGIN USART1_IRQn 1 */ /* USER CODE END USART1_IRQn 1 */ }
Как же нам реализовать отправку и прием данных? А все просто, для этого в HAL Driver есть функции:
HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout); HAL_StatusTypeDef HAL_UART_Receive(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout); HAL_StatusTypeDef HAL_UART_Transmit_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size); HAL_StatusTypeDef HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size); HAL_StatusTypeDef HAL_UART_Transmit_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size); HAL_StatusTypeDef HAL_UART_Receive_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size);
Из их названия в принципе уже понятно, какие механизмы они будут использовать:
- прием и передача в обычном режиме
- прием и передача с использованием прерываний
- прием и передача при помощи DMA
Поскольку в нашем проекте кроме работы с USART больше ничего не будет, особо оценить преимущества работы на прерываниях мы не сможем, но тем не менее реализуем обмен именно таким образом 🙂 Кроме того, объявим в файле main.c массивы для отправляемых и принимаемых данных и в итоге получим следующее:
/* Includes ------------------------------------------------------------------*/ #include "stm32f4xx_hal.h" /* USER CODE BEGIN Includes */ /* USER CODE END Includes */ /* Private variables ---------------------------------------------------------*/ UART_HandleTypeDef huart1; UART_HandleTypeDef huart2; /* USER CODE BEGIN PV */ uint8_t transmitBuffer[32]; uint8_t receiveBuffer[32]; /* USER CODE END PV */ /* Private function prototypes -----------------------------------------------*/ void SystemClock_Config(void); static void MX_GPIO_Init(void); static void MX_USART1_UART_Init(void); static void MX_USART2_UART_Init(void); /* USER CODE BEGIN PFP */ /* 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_USART1_UART_Init(); MX_USART2_UART_Init(); /* USER CODE BEGIN 2 */ for (unsigned char i = 0; i < 32; i++) { transmitBuffer[i] = i + 1; receiveBuffer[i] = 0; } HAL_UART_Receive_IT(&huart1, receiveBuffer, 32); HAL_UART_Transmit_IT(&huart2, transmitBuffer, 32); /* USER CODE END 2 */ /* USER CODE BEGIN 3 */ /* Infinite loop */ while (1) { } /* USER CODE END 3 */ }
Собираем проект, программируем микроконтроллер и проверяем работоспособность программы. Для этого запускаем отладку и смотрим, что у нас попадает в буфер receiveBuffer[32]:
Принятые данные в точности соответствуют отправленным, соответственно наша программа работает абсолютно правильно! На этом и закончим сегодняшнюю статью 🙂