STM32. Прием и передача данных по USART в STM32CubeMx.

USART в STM32Cube

Продолжаем настраивать и использовать все периферийные модули микроконтроллеров STM32 при помощи STM32CubeMx. Вот ссылка на все статьи курса – ссылка – а сегодня у нас на очереди инициализация, а также прием и передача данных при помощи SMT32 USART.

Для тестирования реализуем следующее – будем передавать данные при помощи USART2, а принимать посредством USART1. Таким образом, проверив отправляемые через один модуль и принятые другим модулем данные, мы сможем убедиться, правильно ли мы настроили приемопередатчик USART. Проект будем создавать для STM32F4, соответственно, для экспериментов используем отладочную плату STM32F4Discovery.

Для того, чтобы принять отправленные данные нам надо соединить выводы Rx/Tx USART1 с аналогичными выводами USART2. Только не забываем их перекрестить:

Настройка USART

У микроконтроллера STM32F407VGT6 выводы расположены следующим образом:

Подключение модулей USART1USART2 микроконтроллера STM32

Получается, что нам надо соединить выводы так:

  • PA2 – PA10
  • PA3 – PA9

С электрическими подключениями разобрались, переходим к работе непосредственно в STM32CubeMx. Создаем новый проект и активируем нужные нам модули USART1 и USART2:

Прием и передача данных по USART

Никаких дополнительных настроек тут не требуется, так что переходим на вкладку Configuration. Здесь мы можем настроить стандартные параметры USART – скорость передачи данных, количество бит, четность, количество стоп-бит и другие. В данном примере давайте поменяем только скорость, поставим 9600. Кроме того, включим прерывания:

Прерывания в STM32Cube

На этом работу с 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]:

Прием данных

Принятые данные в точности соответствуют отправленным, соответственно наша программа работает абсолютно правильно! На этом и закончим сегодняшнюю статью 🙂

Поделиться!

Подписаться
Уведомление о
guest
52 Комментарий
старее
новее большинство голосов
Inline Feedbacks
View all comments
Дмитрий
Дмитрий
5 лет назад

Добрый день! Подскажите а как принимать данные побайтно в прерывании?

Дмитрий
Дмитрий
5 лет назад

Еще вопросик) а как узнать что прием 32 байт уже закончен?
Заранее благодарен.

Сергей
Сергей
Reply to  Дмитрий
3 лет назад

если по хорошему, то надо переопределить функции

void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart);
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart);

они автоматически вызываются из прерываний USART, когда обработан последний байт из числа указанных в поле длинны. (в статье это 32).

Еще раз отмечу что функции вызываются обработчиком прерываний
HAL_UART_IRQHandler(UART_HandleTypeDef *huart); поэтому код в колл-бэках должен быть минимальный. Выставляйте глобальные флаги или выдавайте семафоры в случае с работай с ОС.

Дмитрий
Дмитрий
5 лет назад

Спасибо) как непривычно после Standard Peripheral Library

Денис
Денис
4 лет назад

А не подскажите как запустить debug?
Компилируя проект в CubeMX отладчик отваливается.
BOOT_0 подтянут к земле, разрешается запуск программы, если собрать обычный проект, то отладчику пофиг запущена программа или в режиме загрузчика.
Если собираю в CubeMX, то в дальнейшем загрузку можно сделать только через загрузчик, т.е. убрать перемычку с BOOT_0. Соотвественно и отладка не работает.
Камень STM32F107RB

RusikOk
RusikOk
Reply to  Денис
3 лет назад

а вы попробуйте назначить программатору его законные ноги в контроллере при инициализации. пункт из дерева SYS

Евгений
Евгений
4 лет назад

Прошу совета. Проблема связана с применением УАРТа для передачи данных с АЦП. Результат при вызове HAL_ADC_GetValue 32х битный (из которых я беру 16), а аргумент pData в HAL_UART_Transmit – 8ми битный. Передаю побайтно. В итоге в терминале на ПК мешанина из подряд идущих байт, которую нужно делить на парные байты. Может как-то можно по-людски организовать так, чтобы в терминале было, к примеру не 00000111 11100110 а соответствующее число 2022?

Евгений
Евгений
4 лет назад

00000111 11100110 – это одно число, двухбайтовое (или как там его правильно BCD что-ли), Так что это как раз 2022. Т.е. это надо найти прогу-терминал которая это правильно интерпретирует?

Евгений
Евгений
4 лет назад

все, нашел подходящий терминал

Дмитрий
Дмитрий
4 лет назад

Приветствую уважаемые знатоки. Решаю похожую задачу, только с SPI. Пытаюсь связать два микроконтроллера STM32. Интересует следующее, хочу проверить, попадает ли контроллер в прерывание при приеме данных. Видимо оно называется SPI1_IRQHandler. Не ясен механизм. При при обработке прерывания таймера (TIM3_IRQHandler к примеру) понятно, что тело функции пишем мы сами. А тут оно как-бы уже должно быть, дак где же посмотреть его тело и соответственно воткнуть туда точку останова для проверки. Извиняюсь за примитивные вопросы, заранее спасибо за помощь.

Дмитрий
Дмитрий
4 лет назад

Спасибо, нашел. Тогда еще вопрос. Пытаюсь на более простом примере еще разобраться. Плата discoveryf4, настраиваю SPI1 как master, SPI2 как slave. Посмотрел пример. Там сначала вне бесконечного цикла инициируется прерывание для SPI2 , а в цикле идет передача данных по SPI1. И в таком виде ничего не работает. Если беру инициализацию прерывания и добавляю в бесконечный цикл начинает что-то работать. В чем тут смысл и как вообще это правильно настроить, чтоб понятно было, что за чем следует. Заранее спасибо

Дмитрий
Дмитрий
4 лет назад

А зачем нужна вот эта конструкция? Видел ее в примере.
void HAL_SPI_RxCpltCallback(SPI_HandleTypeDef *hspi)
{
if (hspi->Instance == hspi2.Instance)
{
HAL_SPI_Receive_IT(&hspi2, (uint8_t*) data_get, 1);
}
}
С нем все работает вообще дико странно. Результат получается половина от того, что я отправлял. Как такое может быть?

Максим
Максим
4 лет назад

Сталкивался с этим: куб не включает выводы арма для jtag – ручками в куб-проекте отредактируй их как используемые для jtag. Тогда отладка и программирование заработает. А прошить можешь так: зажать ресет, запустить программирование и отпустить ресет.

Дмитрий
Дмитрий
4 лет назад

Снова у меня вопрос. Ситуация все та же. STM32f4 – Master, STM32f100 – Slave. Настроил передачу данных Slave. Но удалось заставить принимать правильные данные только воткнув в цикле задержку после отправки данных. В связи с этим вопросы
1) Что за задержка тогда 10000 в функции HAL_SPI_Transmit(&hspi1, (uint8_t*) data_send, BITS, 10000) и чего она дает
2) Что вообще является событием для прерывания:
приход данных по SLK или NSS, или же вообще по MOSI.
3) Какова правильная последовательность действий при приеме и передаче данных между Slave и Master. Должно ли это обязательно ли быть прерывание или нет и что за чем идет. Кто и за кем обращается к сдвиговому регистру
4) Нужно ли Master контролировать скорость отправки данных на конкретной частоте или достаточно сконфигурировать скорость передачи данных
Заранее спасибо

Дмитрий
Дмитрий
4 лет назад

Всем здравствуйте. Имею такой вопрос. Настроил SPI между двумя stm32. Master — f4, slave — f100. Вопрос следующий. Отсылаю значение одной 16 битовой переменной. При приеме данных слейвом в первый раз младший бит всегда теряется. Отправляю 4 (b100), получаю 2 (b10). При приеме данных мастером все наоборот, появляется лишний бит. Отправил 2 (b10), получаю 4 (b100). При повторном приеме, передаче, приходят правильные данные и больше такое не возникает. В чем причина? Подскажите, заранее спасибо

Алексей
Алексей
4 лет назад

Ребята, не могу настроить USART DMA в circular mode через Cube. Достаточно ли в Cube поставить галочку в DMA request setting – mode circular? Запускаю командой HAL_UART_Transmit_DMA (&huart1, uartTX, 16) в main. Первый раз при запуске передает данные, потом висит, пока снова не запускаю HAL_UART_Transmit_DMA (&huart1, uartTX, 16) в цикле while. Хочу что бы постоянно передавались данные из массива. Подскажите в чем ошибка

Виктор
Виктор
4 лет назад

Здравствуйте!
А если не известна заранее длина принимаемых данных? Как в таком случае организовать прием?

Тимофей
Тимофей
4 лет назад

Зачем счётчик, есть callback-функция HAL_UART_RxCpltCallback?

Ефим
Ефим
4 лет назад

static void MX_GPIO_Init(void);
static void MX_USART1_UART_Init(void);
static void MX_USART2_UART_Init(void);
на эти три строки ругается компилятор
да и вообще что то не могу проследить изменение receiveBuffer[32] правда использую эклипс

Efim25
Efim25
4 лет назад

перегенерировал код кубом, этих строк куб не генерит

Ефим
Ефим
4 лет назад

в общем один буфер заполняется нулями, другой числами, и потом ничего (зависает на определенном адресе), где ошибка хз

В.В.
В.В.
4 лет назад

Как то кривопопово работает HAL_UART_Receive_IT….
Никто не победил как ей сбросить в начало кольцевой буфер…
может появилось более менее человеческое описание ….

Ефим
Ефим
4 лет назад

у кого не получается проследить значение переменной receiveBuffer, строки
HAL_UART_Receive_IT(&huart1, receiveBuffer, 32);
HAL_UART_Transmit_IT(&huart2, transmitBuffer, 32);
ставим в цикл вайл, дли наглядности можно поставить чекпоинт

Дмитрий
Дмитрий
4 лет назад

Приветствую. Цикла для CubeMX под SPI нет, так что пишу сюда. Принцип по идее один и тот же.
Пытаюсь подружить два микроконтроллера STM32f103. Но как-то функции передачи непонятно работают. Отсюда вопросы:
1) Чем отличаются функции с препиской IT (как я понял, они прерывания вребают) от обычных бункций приема и передачи?
2) Что конкретно означает задержка Timeout – аргумент функции приемо-передачи без IT. Когда она вступает в силу, эта задержка?
3) Какова логика вызова колбеков и зачем они нужны. Почему, если сам не вызвал ни разу прерывание, то и колбек неактивен. И зачем он вообще нужен?
Заранее спасибо)

Дмитрий
Дмитрий
4 лет назад

Да, и еще вопросик:
4) Когда в дело вступает прерывание, доступное для мастера. Когда оно вообще используется?

Дмитрий
Дмитрий
4 лет назад

Приветствую. Понадобилось освоить USART в режиме Half Duplex. Подскажите пожалуйста, какая должна быть последовательность действий при отправке и приеме данных. Я действовал так. Само собой сначала соответствующим образом инициализируем интерфейс. Затем отправляем так:
HAL_HalfDuplex_EnableTransmitter(…);
HAL_UART_Transmit_IT(…);
И данные уходят, вижу их на осциллографе. После этого принимающий девайс шлет ответ и его я тоже вижу. Прием осуществляю так:
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{
HAL_HalfDuplex_EnableReceiver(…);
HAL_UART_Receive_IT(…);
}
И данных процессор в итоге не видит, хоть они и идут. Что странно, HAL_UART_TxHalfCpltCallback Keil даже не компилит, видит что он тут не используется, хотя именно этот колбек по идее и должен работать в таком режиме передачи. Что я делаю нет так?

Дмитрий
Дмитрий
Reply to  Aveal
4 лет назад

Компиляция то идет, но данный калбек не активен. Это видно, если запустить отладку кейла. Что не так то делаю?

Дмитрий
Дмитрий
Reply to  Aveal
4 лет назад

Колбек определен в моем файле, только дело явно не в этом, Другие то колбеки там же прописаны и работают. Подкажите сам алгоритм. Как в таком режиме USARTом управлять?

Слава
Слава
3 лет назад

Здравствуйте, спасибо за статью. Вы не могли пояснить процесс передачи. Почему вначале мы пишем функцию приема, а затем передачи?

Слава
Слава
Reply to  Aveal
3 лет назад

Спасибо

Alexander
Alexander
3 лет назад

Присоединяюсь к клубу теряющих последний байт. 🙁

ЭдМахалыч
ЭдМахалыч
2 лет назад

Здравствуйте! Немного не по теме. Сейчас поднимаю ModbussRTU на USARTe (портирую FreeModbuss). Вопрос, собственно, можно ли такой протокол передавать средствами DMA ? Еще не доконца разобрался с портированием и не могу понять как в DMA реализовать направление передачи (устройство будет мастером) или это скрыто под капотом?
Если вы сталкивались с подобными задачами – направите может.
В любом случае спасибо за ваш труд. По вашим урокам уже много полезных устройств трудится 🙂

ЭдМахалыч
ЭдМахалыч
2 лет назад

Спасибо за быстрый ответ! Выгода, думаю будет: на шине (rs-485) – 6 блоков InOut по восем каналов каждый + LCD 16×4 (то же на STM). Каждую итерацию главного цикла я должен обработать ввод-вывод. А в DMA привлекает прерывание обработки половины буфера. Т.е. он пока DMA обрабатывает первую половины, я работаю со второй и наоборот. Просто с МК я работаю только год (до этого я чистый (“десктопник”), и не уверен что в срок успею разобраться с портированием freeModBuss + DMA. Но попробую. Если положительно, то поделюсь. Думаю многим будет интересно (особенно когда сроки “жмуть” 🙂 )

Присоединяйтесь!

Profile Profile Profile Profile Profile
Vkontakte
Twitter

Язык сайта

Август 2020
Пн Вт Ср Чт Пт Сб Вс
 12
3456789
10111213141516
17181920212223
24252627282930
31  

© 2013-2020 MicroTechnics.ru