Top.Mail.Ru

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

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

Теоретическая часть.

Обзор модуля UART.

Что такое UART в целом, и зачем он нужен думаю объяснять не надо 🙂 Так что перейдем сразу к реализации в STM32. Предлагаю сначала посмотреть, какие регистры за что отвечают, а потом уже набросать какой-нибудь проект для наглядности.

Небольшое лирическое отступление... Как вы уже догадываетесь, модуль USART в STM32, как и все остальное в этих контроллерах, имеет множество настроек и режимов. Тут тебе и обычный прием/передача и поддержка LIN (об этом протоколе как-нибудь поговорим отдельно). И если в AVR частенько приходилось реализовывать программные UART’ы, поскольку аппаратных просто не хватало, то, к примеру, в STM32F103CB их как минимум три штуки! А это уже немало. Итак, начинаем копаться в даташите.

Регистр USART_SR.

Статусный регистр. Тут содержатся флаги, отражающие текущее состояние дел, то есть текущее состояние модуля USART. Поглядим на некоторые из них поближе:

  • LBD - Lin Break Detection flag. Выставляется при обнаружении брейка при использовании LIN (о том, что это такое обязательно расскажу в статье про LIN).
  • TXE - Transmit data register empty. Регистр данных пуст, пора его заполнить.
  • TC - Transmission complete. Передача завершена.
  • RXNE - Read data register not empty. Приемный регистр не пуст, надо срочно прочитать данные.

Есть там еще 4 флага для классификации ошибок – ошибка кадра, наличие шума, ошибка переполнения и т. д.

Регистр USART_DR.

Регистр данных. Именно тут мы будем забирать ценнейшую информацию, которая к нам пришла извне, сюда же будем записывать свои данные.

Регистр USART_BRR.

Здесь настраивается скорость обмена, которую мы хотим получить.

Регистр USART_CR1.

Регистр контроля – контролирует весь процесс. Биты по порядку:

  • UE - разрешение работы USART
  • M - длина посылки: 0 – 8 бит данных, 1 – 9 бит данных
  • Wake - будильник для USART’a – то есть метод его пробуждения
  • PCE - контроль паритета – ВКЛ или ВЫКЛ
  • PS - тип четности: 0 – четный, 1 – нечетный
  • PEIE - разрешение прерывания при обнаружении ошибки четности
  • TXEIE - прерывание от TXE
  • RXNEIE - прерывание от RXNE

Ну и там еще парочка битов, разрешающих/запрещающих прием/передачу, а также бит для отправки брейка. В общем, это все есть в даташите, все наглядно, красиво и понятно. Поэтому подробно углубляться не буду, нет в этом смысла.

Регистр USART_CR2 - в этом регистре такие биты как включение/выключение режима работы по LIN, установка количества стоп-битов, настройки clock’а, установка параметров LIN брейка и некоторые другие.

Регистр USART_CR3 - а здесь биты для использования DMA и SmartCard.

Регистр USART_GTPR - биты, отвечающие за предделитель.

В общем, вот они, 7 регистров, которые контролируют весь модуль USART в STM32. У меня описаны довольно поверхностно, только основное, если что, спрашивайте в комментариях, буду рад помочь.

Плавно переходим к практической части в виде небольшого базового примера. Забегая вперед скажу, что в данном случае проект будет действительно несложный, ознакомительный, а вот еще парочка материалов для STM32 с использованием UART:

Практическая часть.

Инициализация в STM32CubeMx.

Сегодня же для тестирования реализуем следующее - будем передавать данные при помощи USART2, а принимать посредством USART1. Таким образом, сравнив отправляемые через один и принятые другим модулем данные, мы сможем убедиться, правильно ли функционирует приемопередатчик. Я буду использовать отладочную плату STM32F4Discovery, но это не особо важно, так как механизм взаимодействия с CubeMx по сути один и тот же для любого контроллера и семейства.

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

STM32 UART пример программы.

В итоге коммутируем так:

  • PA2 (USART2_TX) - PA10 (USART1_RX)
  • PA3 (USART2_RX) - PA9 (USART1_TX)

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

Настройка USART в STM32CubeMx

Здесь же доступны для изменения классические параметры UART:

  • скорость передачи данных
  • длина пакета
  • четность
  • количество стоп-битов
  • направление обмена данными
  • over sampling

Для нашего тестового проекта это не очень важно, важно лишь, чтобы настройки для USART1 и USART2 были одинаковыми. Поэтому аналогичным же образом конфигурируем и второй модуль. На вкладке "NVIC_Settings" также активируем прерывание:

Прерывание UART в STM32

На этом работу с STM32CubeMx заканчиваем, генерируем код и открываем проект. Вся инициализация портов и модулей USART уже включена в функцию main():

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

Кроме того, CubeMx сгенерировал обработчики прерываний, в которых реализованы механизмы обмена данными и сброса всех нужных флагов - все это находится внутри функции HAL_UART_IRQHandler(). Сами же обработчики в файле stm32f4xx_it.c:

/**
* @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 */
}

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

Для приема и отправки данных в HAL реализованы следующие функции:

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

Поскольку в нашем проекте кроме работы с UART больше ничего не будет, особо оценить преимущества работы на прерываниях мы не сможем, но тем не менее осуществим обмен именно таким образом. Кроме того, объявим в файле main.c массивы (transmitBuffer[], receiveBuffer[]) и их размер (BUFFER_SIZE) для отправляемых и принимаемых данных и в итоге получаем следующее:

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
#define BUFFER_SIZE                                                     32

/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/
UART_HandleTypeDef huart1;
UART_HandleTypeDef huart2;

/* USER CODE BEGIN PV */
uint8_t transmitBuffer[BUFFER_SIZE];
uint8_t receiveBuffer[BUFFER_SIZE];

/* 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 */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */

/* 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_USART1_UART_Init();
  MX_USART2_UART_Init();
  /* USER CODE BEGIN 2 */

  for (unsigned char i = 0; i < BUFFER_SIZE; i++)
  {
          transmitBuffer[i] = i + 1;
          receiveBuffer[i] = 0;
  }

  HAL_UART_Receive_IT(&huart1, receiveBuffer, BUFFER_SIZE);
  HAL_UART_Transmit_IT(&huart2, transmitBuffer, BUFFER_SIZE);

  /* USER CODE END 2 */

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

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

Собираем проект, зашиваем контроллер и проверяем работоспособность программы. Для этого запускаем отладку и смотрим, что у нас попадает в буфер receiveBuffer[]:

Принятые данные в точности соответствуют отправленным👍 Для того, чтобы отследить момент окончания передачи, либо приема существуют callback-функции:

  • HAL_UART_TxCpltCallback()
  • HAL_UART_RxCpltCallback()

Их можно, к примеру, переопределить прямо в main.c:

/* USER CODE BEGIN 4 */
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
  if (huart->Instance == USART1)
  {
    // USART1 завершил прием данных
  }
}

void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{
  if (huart->Instance == USART2)
  {
    // USART2 завершил отправку данных
  }
}

/* USER CODE END 4 */

В эти функции программа попадет автоматически при возникновении соответствующих событий, то есть окончания процесса отправки, либо приема - типичный механизм для библиотеки HAL.

И на этом на сегодня завершаем деятельность, знакомство с UART в STM32 осуществлено, спасибо за внимание, и до скорого 🤝

Подписаться
Уведомить о
guest

132 комментариев
Старые
Новые
Межтекстовые Отзывы
Посмотреть все комментарии
Дмитрий
Дмитрий
9 лет назад

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

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

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

Сергей
Сергей
Ответ на комментарий  Дмитрий
7 лет назад

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

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

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

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

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

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

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

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

RusikOk
RusikOk
Ответ на комментарий  Денис
7 лет назад

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Снова у меня вопрос. Ситуация все та же. 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 контролировать скорость отправки данных на конкретной частоте или достаточно сконфигурировать скорость передачи данных
Заранее спасибо

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

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

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

Ребята, не могу настроить 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. Хочу что бы постоянно передавались данные из массива. Подскажите в чем ошибка

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

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

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

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

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

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

Efim25
Efim25
8 лет назад

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

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

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

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

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

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

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

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

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

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

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

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

Приветствую. Понадобилось освоить 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 даже не компилит, видит что он тут не используется, хотя именно этот колбек по идее и должен работать в таком режиме передачи. Что я делаю нет так?

Дмитрий
Дмитрий
Ответ на комментарий  Aveal
8 лет назад

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

Дмитрий
Дмитрий
Ответ на комментарий  Aveal
8 лет назад

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

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

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

Слава
Слава
Ответ на комментарий  Aveal
8 лет назад

Спасибо

Alexander
Alexander
7 лет назад

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

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

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

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

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

FaFa
FaFa
1 год назад

Собираю устройство, скажем так прибор, состоящий из 2-х плат и 2-х микроконтроллеров.
На первой плате МК STM32F103 - принимает данные с датчиков по I2C, выполняет расчеты, сохраняет все настройки во внешнюю EEPROM по SPI.
На второй плате МК STM32F401 - работает на вывод графики на дисплей по интерфейсу 8080-параллельному.
Хочу соединить МК-1 с МК-2, думаю использовать UART, но также думаю и про SPI.
Чтобы передавать команды от МК-1 для отрисовки на дисплее с помощью МК-2.
Если использовать UART, то у первого МК(F103) - не хочу "занимать" нативные ноги TX/RX(PA10/PA9).
Как понял их статьи, UART 2(PA3/PA2) - без проблем.
А что касается UART 3, могу задействовать его для передачи данных?
И вообще, стоит ли делать связь между МК по UART?
Или лучше взять для передачи команд управления от МК-1 к МК-2 через SPI?
Подскажите ваше видение как лучше, или как сделали бы вы?
P.S. Есть такая марка дисплеев DWIN - так они используют UART для управления дисплеем от МК.

Антон
Антон
1 год назад

В receive buffer ничего не приходит (пробовал реализовать на stm32f103rbt6) В чем может быть проблема? P. s. Нужно для диплома,а так я не силен в этом

Антон
Антон
Ответ на комментарий  Aveal
1 год назад

Использую отладочник stm32f103 nucleо - 64, все вилки в норме, проверял

Антон
Антон
Ответ на комментарий  Aveal
1 год назад

А есть исходник этого кода? Мб у меня чего-то не хватает,хотя все по вашему гайду делал

Антон
Антон
Ответ на комментарий  Aveal
1 год назад

Да,куда выложить?

Антон
Антон
Ответ на комментарий  Aveal
1 год назад

Выложу позже, пока выяснил,что hal_uart_transmit_it и hal_uart_receive_it в состояние hal_busy сразу встают(

Антон
Антон
Ответ на комментарий  Aveal
1 год назад

https://disk.yandex.ru/d/0MLfpQQZ3XDG-g Ссылка на диск с файлом

Антон
Антон
Ответ на комментарий  Aveal
1 год назад

Да,соединено, что именно прописать для флагов?

Антон
Антон
Ответ на комментарий  Aveal
1 год назад

Спасибо за дополнение к коду,но у меня все равно hal transmit на hal busy уходит,поэтому ничего не может передать( Я уже даже не знаю в чем проблема

Антон
Антон
Ответ на комментарий  Aveal
1 год назад

Да

Антон
Антон
Ответ на комментарий  Aveal
1 год назад

То же самое

Антон
Антон
Ответ на комментарий  Aveal
1 год назад

У меня все еще не работает, мб дело в отладочнике stm32f103rb nucleo64?

Антон
Антон
Ответ на комментарий  Aveal
1 год назад

А что может быть не так? И какие параметры должны быть установлены?

Антон
Антон
Ответ на комментарий  Aveal
1 год назад

Все точно также, контроллер выбран правильно

Kirill
Kirill
1 год назад

У меня не заработало, прошил микроконтроллер (в дебаг режиме), массив transmitBuffer заполнился, много раз нажал следующий шаг, у цикла while, а в receiveBuffer одни нули:(

Правильно понял, что шаг "В итоге коммутируем так:", не означает действие в проекте? (Хотя пины именно так и расположились)

Также функции:

  • HAL_UART_TxCpltCallback()
  • HAL_UART_RxCpltCallback()

не отработали

Kirill
Kirill
Ответ на комментарий  Aveal
1 год назад

О, понял, да я в целом этот термин так себе и представил, только вот у меня плата с stm32 есть и там уже все пины направлены в нужные места - видимо поэтому у меня receive и не приходит на usart1, так как у меня комутация не выполнена!

Скоро приедет Nucleo, там попробую!

Спасибо за ответ, очень приятно!)

Kirill
Kirill
Ответ на комментарий  Kirill
1 год назад

Я почему-то подумал, что мы программно можем пины направить друг на друга, но видимо как раз таки и нет, а вот именно физической связи у меня нет, поэтому и не получилось)

Kirill
Kirill
1 год назад

Увидел последний комментарий (от Антона), тоже не работало у человека. Добавил рекомендации от Админа, отличный код, от автора получился таким:

while (1)
{
HAL_Delay(100);
HAL_UART_Receive_IT(&huart1, receiveBuffer, BUFFER_SIZE);
HAL_UART_Transmit_IT(&huart2, transmitBuffer, BUFFER_SIZE);
HAL_Delay(100);
}

Также добавил две переменные, которые становятся true, когда случился Transive / Receive, переменная isTransived становится True, а вот isReceived остается false;

Подождал 10 циклов, isReceived остается false:(

Kirill
Kirill
1 год назад

Доброго времени суток! Приехала Nucleo, выполнил проект 1 в 1, комутацию выполнил, не работает:(

В комментариях прикреплю что есть, если нужно что-то специфичное, попросите, я пришлю.

Думается мне что на 1 устройстве нельзя usart -> usart передачи делать, но может, я ошибаюсь.

Kirill
Kirill
Ответ на комментарий  Kirill
1 год назад

Комутация на плате, слева пины закрепленные от внешнего ст-линка, плата рабочая (проверил миганием светодиода)

Справа - сверху вниз
PC5 - USART1 RX
PC4 - USART1 TX
PA2 - USART2 TX
PA3 - USART2 RX
(ioc проекта, в следующем комментарии)

nucleo_commutation
Kirill
Kirill
Ответ на комментарий  Kirill
1 год назад

ioc проекта (usart идентичны, настройки не менял, они изначально одинаковы, на обоих включены EXTI)

Kirill
Kirill
Ответ на комментарий  Kirill
1 год назад

ioc проекта (usart идентичны, настройки не менял, они изначально одинаковы, на обоих включены NVIC)

ioc
Kirill
Kirill
Ответ на комментарий  Kirill
1 год назад

расположение пинов на моей nucleo

nucleo-pins
Kirill
Kirill
Ответ на комментарий  Aveal
1 год назад

Да могу, сейчас выложу, спасибо большое за обратную связь, коллеги на работе тоже не особо понимают почему не работают, я постараюсь сейчас на гитхаб выложить

Я пробовал передачу/прием в обычном режиме;
Менял местами Receive/Transmit
BUFFER_SIZE менял, но кажется твои 32 так и не ставил ни разу, 16, 8, 4, 2 проверял, receive не реагирует

Kirill
Kirill
Ответ на комментарий  Aveal
1 год назад

Там лишний код имеется, отличный от того что в гайде, надеюсь не запутаешься)
https://github.com/b0r1ngx/usart-to-usart-stm32

Kirill
Kirill
Ответ на комментарий  Aveal
1 год назад

В HAL_UART_RxCpltCallback - не попадает

В USART1_IRQHandler - сейчас гляну

Kirill
Kirill
Ответ на комментарий  Aveal
1 год назад

В USART1_IRQHandler() - приходит!

Добавил переменную которая переключается когда туда заходит, сразу после выполнения строчки
HAL_UART_Receive_IT(&huart1, receiveBuffer, BUFFER_SIZE);

она обновляется

Kirill
Kirill
Ответ на комментарий  Kirill
1 год назад

При том обновляется 3 раза)

Потом сижу в цикле, выполняю действия, а receive и переменная эта, всеравно не срабатывает

Kirill
Kirill
Ответ на комментарий  Aveal
1 год назад

Не знаю что это за функция (UART_RxISR_8BIT) и где ее искать, сейчас буду гуглить

Kirill
Kirill
Ответ на комментарий  Aveal
1 год назад

Нашел ее, мне нужно в этот файл изменения внести?

ekrana-2023-11-22-v-4.45.27 pm
Kirill
Kirill
Ответ на комментарий  Aveal
1 год назад

да верняк, щас так и сделаю а то я обычно step_over нажимал, спасибо!

Kirill
Kirill
Ответ на комментарий  Aveal
1 год назад

Так, буду в общем тогда, обо всем писать тут, в первое место куда попал это HAL_UART_Receive_IT(), здесь дошел до return - все ок значит

ekrana-2023-11-22-v-4.56.24 pm
Kirill
Kirill
Ответ на комментарий  Aveal
1 год назад

Хм, при инициализации 109 строки - HAL_UART_Receive_IT(&huart1, receiveBuffer, BUFFER_SIZE) - я вот захожу во внутрь, попадаю в функцию HAL_UART_Receive_IT() и UART_Start_Receive_IT(), они успешно инициализируются и я возвращаюсь в main() функцию - и все, дальше до цикла (while) дохожу, ну и собственно ни в UART_RxISR_8BIT, ни в HAL_UART_RxCpltCallback() - не попадаем

Kirill
Kirill
Ответ на комментарий  Kirill
1 год назад

На 3244 строке huart->RxISR = UART_RxISR_8BIT;
я вижу эту функцию UART_RxISR_8BIT, но я так понимаю это установка колбэк функции с название RxISR в адрес объекта huart - если я правильно синтаксис понял (я в С/STM32 начинающий совсем)

Kirill
Kirill
Ответ на комментарий  Kirill
1 год назад

Вот скрин, про что говорю, забыл прикрепить

ekrana-2023-11-22-v-5.08.13 pm
Kirill
Kirill
Ответ на комментарий  Kirill
1 год назад

Так вот еще один скрин того же самого, но с именем файла и номером строк (нельзя фотографии редактировать, поэтому так)

ekrana-2023-11-22-v-5.13.41 pm
Kirill
Kirill
Ответ на комментарий  Kirill
1 год назад

Я до понедельника, или даже до вторника/среды следующей прервусь тут! Надеюсь еще спишемся! Возможно даже созвониться можно если ты не против!

Kirill
Kirill
Ответ на комментарий  Kirill
1 год назад

имел в виду что попробовать отладить как-то в этой функции, узнать где проблема?

Когда гайд увидел, смотрю шикарный короткий, а дает повзаимодействовать по USART на одном устройстве, а тут опять программистские проблемы, без них никуда... каждый день ошибки/баги фиксить приходится, тяжело жить, ахаха)

Kirill
Kirill
Ответ на комментарий  Aveal
1 год назад

Попробовал повзаимодействовать с USART2 и USART3 (т.к в мануале, не увидел информации о первом USART), пины поменялись, провода переключил, тоже ничего не получилось

При этом смена, отправка (transive) с USART3 работает, и USART2 не принимает

nucleo-usart-manual
Алексей
Алексей
8 месяцев назад

Доброго дня, спасите, помогите, уже 3 дня бьюсь и бестолку.

Создал проект в МХ, тактирование HSE чип отправляет по UART число с инкрементом.
В терминале компа получаю хрень типа (<0x15> <break>) грешил на тактирование, перестроился на HSI внутренний то точно не врёт.
На приёме тоже самое, пробовал разные скорости, без результата.
Грешил на железо, прошил ардуиной иде, всё в норме, данные корректны.
аА вот КУБ победить никак не получается.
Куда копать. не пойму.

Алексей
Алексей
Ответ на комментарий  Aveal
8 месяцев назад

Да проекта пока нет, изучаю возможности куба, переезжаю с ардуины.
А, так, вот, что есть.

 while (1) {
    if (HAL_GetTick()-flash > 500){
           ++count;
           if(count >= 5){count = 0;}
          HAL_UART_Transmit_IT(&huart1,count, 1);
          flash = HAL_GetTick();
          HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_8);

терминал кидает это

ekrana-ot-2024-03-18-13-39-53
Алексей
Алексей
Ответ на комментарий  Aveal
8 месяцев назад

Вот, что с адресом.

ekrana-ot-2024-03-18-13-50-50
Алексей
Алексей
Ответ на комментарий  Aveal
8 месяцев назад

Ай, вот я туканул, СПАСИБО!!!!!!!!!!!!!

Алексей
Алексей
Ответ на комментарий  Aveal
8 месяцев назад

Раз уж мне с вами так повезло, подскажите как передать не 16ое число, а 10е

Алексей
Алексей
Ответ на комментарий  Aveal
8 месяцев назад

🙂 да, точно.
Вообще как оказалось долго на ардуине сидеть нельзя, она слишком много за человека делает, всё забыл и расслабился.но тебе спасибо, ещё неделю бы сидел.
и ведь нигде нет инфы, что перед данными адрес ставить нужно, нигде не встречал.
был бы хоть один сайт для бывших ардуинщиков, жить стало бы легче.

Амир
Амир
4 месяцев назад

Доброго дня, ругается на for (unsigned char i = 0; i < BUFFER_SIZE; i++)
  {
      transmitBuffer[i] = i + 1;
      receiveBuffer[i] = 0;
  }
  HAL_UART_Receive_IT(&huart1, receiveBuffer, BUFFER_SIZE);
  HAL_UART_Transmit_IT(&huart2, transmitBuffer, BUFFER_SIZE); и uint8_t transmitBuffer[BUFFER_SIZE];
uint8_t receiveBuffer[BUFFER_SIZE];
/* USER CODE BEGIN Header */
/**
 ******************************************************************************
 * @file      : main.c
 * @brief     : Main program body
 ******************************************************************************
 * @attention
 *
 * Copyright (c) 2024 STMicroelectronics.
 * All rights reserved.
 *
 * This software is licensed under terms that can be found in the LICENSE file
 * in the root directory of this software component.
 * If no LICENSE file comes with this software, it is provided AS-IS.
 *
 ******************************************************************************
 */
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */

/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
#define BUFFER_SIZE
/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/
UART_HandleTypeDef huart1;
UART_HandleTypeDef huart2;

/* USER CODE BEGIN PV */
uint8_t transmitBuffer[BUFFER_SIZE];
uint8_t receiveBuffer[BUFFER_SIZE];
/* 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 */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */

/* 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_USART1_UART_Init();
 MX_USART2_UART_Init();
 /* USER CODE BEGIN 2 */
 for (unsigned char i = 0; i < BUFFER_SIZE; i++)
  {
      transmitBuffer[i] = i + 1;
      receiveBuffer[i] = 0;
  }
  HAL_UART_Receive_IT(&huart1, receiveBuffer, BUFFER_SIZE);
  HAL_UART_Transmit_IT(&huart2, transmitBuffer, BUFFER_SIZE);
 /* 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 SystemClock_Config(void)
{
 RCC_OscInitTypeDef RCC_OscInitStruct = {0};
 RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

 /** Initializes the RCC Oscillators according to the specified parameters
 * in the RCC_OscInitTypeDef structure.
 */
 RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
 RCC_OscInitStruct.HSIState = RCC_HSI_ON;
 RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
 RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
 if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
 {
  Error_Handler();
 }

 /** Initializes the CPU, AHB and APB buses clocks
 */
 RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
               |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
 RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI;
 RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
 RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
 RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

 if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK)
 {
  Error_Handler();
 }
}

/**
 * @brief USART1 Initialization Function
 * @param None
 * @retval None
 */
static void MX_USART1_UART_Init(void)
{

 /* USER CODE BEGIN USART1_Init 0 */

 /* USER CODE END USART1_Init 0 */

 /* USER CODE BEGIN USART1_Init 1 */

 /* USER CODE END USART1_Init 1 */
 huart1.Instance = USART1;
 huart1.Init.BaudRate = 115200;
 huart1.Init.WordLength = UART_WORDLENGTH_8B;
 huart1.Init.StopBits = UART_STOPBITS_1;
 huart1.Init.Parity = UART_PARITY_NONE;
 huart1.Init.Mode = UART_MODE_TX_RX;
 huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
 huart1.Init.OverSampling = UART_OVERSAMPLING_16;
 if (HAL_UART_Init(&huart1) != HAL_OK)
 {
  Error_Handler();
 }
 /* USER CODE BEGIN USART1_Init 2 */

 /* USER CODE END USART1_Init 2 */

}

/**
 * @brief USART2 Initialization Function
 * @param None
 * @retval None
 */
static void MX_USART2_UART_Init(void)
{

 /* USER CODE BEGIN USART2_Init 0 */

 /* USER CODE END USART2_Init 0 */

 /* USER CODE BEGIN USART2_Init 1 */

 /* USER CODE END USART2_Init 1 */
 huart2.Instance = USART2;
 huart2.Init.BaudRate = 115200;
 huart2.Init.WordLength = UART_WORDLENGTH_8B;
 huart2.Init.StopBits = UART_STOPBITS_1;
 huart2.Init.Parity = UART_PARITY_NONE;
 huart2.Init.Mode = UART_MODE_TX_RX;
 huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;
 huart2.Init.OverSampling = UART_OVERSAMPLING_16;
 if (HAL_UART_Init(&huart2) != HAL_OK)
 {
  Error_Handler();
 }
 /* USER CODE BEGIN USART2_Init 2 */

 /* USER CODE END USART2_Init 2 */

}

/**
 * @brief GPIO Initialization Function
 * @param None
 * @retval None
 */
static void MX_GPIO_Init(void)
{

 /* GPIO Ports Clock Enable */
 __HAL_RCC_GPIOA_CLK_ENABLE();

}

/* USER CODE BEGIN 4 */
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
 if (huart->Instance == USART1)
 {
  // USART1 завершил прием данных
 }
}
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{
 if (huart->Instance == USART2)
 {
  // USART2 завершил отправку данных
 }
}
/* USER CODE END 4 */

/**
 * @brief This function is executed in case of error occurrence.
 * @retval None
 */
void Error_Handler(void)
{
 /* USER CODE BEGIN Error_Handler_Debug */
 /* User can add his own implementation to report the HAL error return state */
 __disable_irq();
 while (1)
 {
 }
 /* USER CODE END Error_Handler_Debug */
}

#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 /* USE_FULL_ASSERT */
В чем может быть проблема?

Амир
Амир
Ответ на комментарий  Aveal
4 месяцев назад

спасибо!

Ярослав
Ярослав
3 месяцев назад

Пытаюсь реализовать двухстороннюю передачу UART-USB, приём в UART реализовал по одному байту(записывал в буфер), а завершение приёма было после байта окончания сообщения. Но что делать, если в конце сообщения нет байта окончания сообщения? Он же так и будет его ждать? Можно ли как-то реализовать некий таймер, по окончанию которого приём автоматически закончится? (желательно внутри этого коллбэка)

izobrazhenie_2024-08-01_160329480
Давид
Давид
2 месяцев назад

Добрый день, возникала проблема. Микроконтроллер stm32f407zet6 на плате запаял rs485 (конкретно adm3485) к ногам usart1 PA9/PA10 rx/tx настроил в асинхронном режиме, pa8 управляет режимами передачи/отправки данных. В CubeIDE выбираю нужные режимы, настроил тактирование по hse согласно мануалу. При отправке сообщения в терминале ком порта ничего не отображается, с железом проблем нет, с него все уходит (тыкал осцилографом). С чем может быть связана данная проблема ? 
  
 HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8, GPIO_PIN_SET);
 HAL_UART_Transmit(&huart1, (uint8_t*)"check UART\n", 11, 1000);

Давид
Давид
Ответ на комментарий  Aveal
2 месяцев назад

Добрый вечер, выходило всё хорошо. Ошибка была в том что на шине была предельная частота, ещё и скорость работы UART тоже высокая была, поэтому данные ломались. Спасибо, всё починил.

132
0
Оставьте комментарий! Напишите, что думаете по поводу статьи.x