STM Урок 14. HAL. USART. Прием данных



 

Урок 14

 

HAL. USART. Прием данных

 

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

Во-первых, нужно обрабатывать прерывание от USART, вы ведь не угадаем, когда именно нам принимать.

Во-вторых, где-то и как-то это надо отображать.

Вот тут-то на помощь нам приходит наш добрый и старый дисплей 20х4.

Подключим его по той же 4-битной схеме (нажмите на картинку для увеличения изображения)

 

image00_0500

 

В связи с этим новый проект USART_RECEIVE мы создадим из другого проекта — MYLCD80

Запускаем вновь созданный проект в CUBE, включаем аналогичным образом USART2 – Asynchronous.

 

 

Те же самые настройки. как и в проекте с передачей данных должны быть и в Configuration.

Только единственная разница, в USART в Configuration на USART2 включим прерывания

 

image01

 

Генерируем проект. Открываем его.

Добавляем файл lcd.c.

Собираем, прошиваем, смотрим, чтобы убедиться, что дисплей у нас работает.

Если дисплей работает, то мы работаем с кодом.

По дисплею оставляем в коде только это

 

/* USER CODE BEGIN 2 */

        LCD_ini();

  sprintf(str, «Stm32F407VG»);

        LCD_String(str);

        LCD_SetPos(10, 2);

  sprintf(str, «ARM mc»);

        LCD_String(str);

  /* USER CODE END 2 */

 

Остальное убираем, из бесконечного цикла также всё убираем, и уберем переменную i.

 

 

Добавим код в main()

 

  /* USER CODE BEGIN WHILE */

        str[8]=0;

        HAL_UART_Receive_IT(&huart2,(uint8_t*) str,8);

  while (1)

  {

                HAL_Delay(100);

                if(huart2.RxXferCount==0)

                {

                        LCD_SetPos(0, 3);

                        LCD_String(str);

                        str[8]=0;

                        HAL_UART_Receive_IT(&huart2,(uint8_t*) str,8);

                }

  /* USER CODE END WHILE */

 

Собираем, прошиваем, смотрим.

С терминальной программы пытаемся вводить символы (обязательно ровно по 8, ну, либо по-другому, но пока не наберется 8, на дисплей ничего не выведется)

 

image02

 

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

 

 

Предыдущий урок Программирование МК STM32 Следующий урок

 

 

Исходный код

 

 

Терминальная программа

 

STM32F4-DISCOVERY

Переходник USB-TTL лучше купить такой (сейчас у меня именно такой и он мне больше нравится)

 

 

Смотреть ВИДЕОУРОК

 

STM32 HAL. USART. Передача данных

14 комментариев на “STM Урок 14. HAL. USART. Прием данных
  1. Юриц:

    Можете показать пример как определять размер сообщеия автоматичемкт? 

  2. Владимир:

    Была у меня проблема приема пакета неизвестной длины. Один контроллер другому посылает команды по UART. У каждой команды свой набор параметров, и соответственно свой размер пакета передаваемых данных. Да и, кроме того, контроллер занят другими боле важными задачами, в том числе отработкой конвейера поступающих по UART команд. День потратил на поиск и отработку глюков, но нашёл решения. Может кому-то пригодится, ибо в уроке не все тонкости раскрыты.
    1. Приём Rx через прерывания.
    Функция HAL_UART_Receive_IT определяет буфер, в который ведётся запись, а также 2 параметра huart2.RxXferSize — это длина принимаемого пакета, и huart2.RxXferCount — счетчик принимаемых байт. Естественно, буфер должен быть готов принять пакет максимальной длины и функция HAL_UART_Receive_IT принимает именно его. А вот с более короткими пакетами надо немного включить мозг. После вызова функции HAL_UART_Receive_IT, переменные RxXferCount=RxXferSize (счётчик равен размеру ожидаемых данных). Но когда начинается прием данных, то RxXferCountInstance->CNDTR
    только вот условие if ((huart2.hdmarx->Instance->CNDTR)Instance->CNDTR;
    if (cnt < huart.RxXferSize)
    Вот так условие заработало.
    функция HAL_UART_AbortReceive(&huart2) работает и для режима приема по прерываниям, и для режима приема через DMA.

    3. Ну вот если надо принять пакет вообще никак не известной заранее длины, то в режиме прерываний надо использовать таймауты (определять интервал времени между принятыми байтами, не забывая вычищать буфер), а в режиме DMA есть такой момент
    if(hdma_usart2_rx.State==HAL_DMA_STATE_READY_HALF_MEM0) — есть загрузка половины пакета – вычищаем буфер, потом HAL_UART_AbortReceive(&huart2); HAL_UART_Receive_DMA(&huart2, (uint8_t*) str, максимальный_размер_пакета)

    P.S. я очень плохо знаю язык си, и еще хуже знаю STM32 и KeiluVision5. Не кидайтесь тапками если чё не так. Просто потратил много времени на решение данной задачи, а на технических форумах все специалисты пишут что HAL==кал, и все равно надо изучать ReferenceManual на контроллер и CMSIS. По крайней мере, благодаря видеоурокам Владимира, я понял, как начать, а потом отследить через Watch1 и найти решение именно для моей задачи. Надеюсь, кому-нибудь мой комментарий сэкономит время.

  3. Владимир:

    некорректно ушел комментарий. дублирую пункты:
    1. Приём Rx через прерывания.
    Функция HAL_UART_Receive_IT определяет буфер, в который ведётся запись, а также 2 параметра huart2.RxXferSize — это длина принимаемого пакета, и huart2.RxXferCount — счетчик принимаемых байт. Естественно, буфер должен быть готов принять пакет максимальной длины и функция HAL_UART_Receive_IT принимает именно его. А вот с более короткими пакетами надо немного включить мозг. После вызова функции HAL_UART_Receive_IT, переменные RxXferCount=RxXferSize (счётчик равен размеру ожидаемых данных). Но когда начинается прием данных, то RxXferCount<RxXferSize. Если выполняется это условие, то смотрим первый байт в буфере приёма str[0] и определяем какая должна быть длина пакета. Далее проверяем весь ли пакет принят?
    if (длина_пакета==(RxXferSize-RxXferCount)) если нет, то ничего не делаем (в следующем цикле проверим), а если да, то переписываем принятый пакет в другую переменную для последующей обработки команды, а вот с UART'ом следующий порядок действий такой:
    HAL_UART_AbortReceive(&huart2); //внеплановое завершение приёма по правилам HAL
    HAL_UART_Receive_IT(&huart2, str, размер_максимального_пакета); //запуск приёма следующего пакета

    • Исходный кот:

      Hu is длина пакета?
      Откуда я знаю, какой длины пакет придет? Вернее, Я ЭТО знаю, а, как узнает про это устройство, когда у него только две линии Rx и Tx?

    • Исходный кот:

      На GPIO вешать ничего нельзя… Так бы можно было бы оттырить прино 6 GPIO, все равно их полно и болтаются они как …в проруби. Но, По ТХ, работаем только по Rx и Tx!Команды не более 30 символов

  4. Владимир:

    2. Приём в Rx через DMA.
    Ну суть та же, только счётчик принятых данных не huart2.RxXferCount, а huart2.hdmarx->Instance->CNDTR
    только вот условие if ((huart2.hdmarx->Instance->CNDTR)Instance->CNDTR;
    if (cnt < huart.RxXferSize)
    Вот так условие заработало.
    функция HAL_UART_AbortReceive(&huart2) работает и для режима приема по прерываниям, и для режима приема через DMA.

    • Владимир:

      Блин, да что так все криво-то отправляется?
      2. Приём в Rx через DMA.
      Ну суть та же, только счётчик принятых данных не huart2.RxXferCount, а huart2.hdmarx->Instance->CNDTR
      только вот условие if ((huart2.hdmarx->Instance->CNDTR)Instance->CNDTR;
      if (cnt < huart.RxXferSize)
      Вот так условие заработало.
      функция HAL_UART_AbortReceive(&huart2) работает и для режима приема по прерываниям, и для режима приема через DMA.

    • Zanis:

      Я сделал проще,читал по одному байту в буфер и добавил счётчик позиции,после чтения сбрасывал позицию на 0.
      char usart1_rx_buf[USART1_RX_BUFFER_SIZE] = {0,};
      volatile uint8_t usart1_byte;
      volatile uint8_t usart1_byte_count= 0;
      void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
      {
      if(huart == &huart1)
      {
      if(usart1_byte_count 0)// если пришли байты
      {
      // просто эхо принятых данных
      HAL_UART_Transmit_IT(&huart1,(uint8_t*) usart1_rx_buf, usart1_byte_count);
      HAL_Delay(10);// обязательно задержка
      usart1_byte_count=0;// сброс позиции
      }
      Это подходит для простых задач,для простого обмена данными.Например с компьютера настроить время в RTC . Видел примеры с кольцевым буфером,но смысла в нём не вижу

  5. Владимир:

    Капец какой-то, посылаю одно, на сайте другое… дубль 3:
    2. Приём в Rx через DMA.
    Ну суть та же, только счётчик принятых данных не huart2.RxXferCount, а huart2.hdmarx->Instance->CNDTR
    только вот условие
    if ((huart2.hdmarx->Instance->CNDTR)Instance->CNDTR;
    if (cnt < huart.RxXferSize)
    Вот так условие заработало.
    функция HAL_UART_AbortReceive(&huart2) работает и для режима приема по прерываниям, и для режима приема через DMA.

  6. Исходный кот:

    Ребята, надо, чтобы, терминал v 1.9b получал пакеты произвольной длины(желательно, по прерываниям. то есть, я ввожу то что мне надо и нажимаю SEND). Я так понял, HAL для этого совершенно не годится? Перепробовал все, принимает совершенно не то что нужно. Плюс, символы '$' и '#' не обрабатываются. Вместо них . Весь код по книге, только, размер уменьшен до одного символа и вместо LCD HAL_UART_Transmit_IT(…). Ведь, по — идее, функция, HAL_UART_Receive_IT(…) должна взять первый символ и послать его HAL_UART_Transmit_IT, очистить буфер, перейти ко второму символу и также нередать его в верхнюю часть терминала? Или я ошибаюсь?

  7. posehn:

    Помогите новичку !!!
    У меня вопрос по функции передачи. Есть задача — передать код и в зависимости от него, включать/отключать светодиод. Вроде всё просто, но есть проблема, почему-то игнорируются и не обрабатываются IF-условия, которые идут после выполнения функции передачи. Подскажите, что я сделал не так ?

    Вот фрагмент
    char str1[60];
    char my_comm[]=»00000000";
    char switchon[]=»11111111";
    char switchoff[]=»22222222";

    HAL_UART_Receive_IT(&huart1,(uint8_t*)my_comm,8);
    sprintf(str1,»%s»,my_comm);
    HAL_UART_Transmit(&huart1,(uint8_t*)str1,strlen(str1),0x1000);
    HAL_Delay(100);

    // Send Command from button
    if (my_comm[0] == switchon[0])
    {HAL_GPIO_WritePin(GPIOB, GPIO_PIN_4, GPIO_PIN_SET);}

    if (my_comm[0] == switchoff[0])
    {HAL_GPIO_WritePin(GPIOB, GPIO_PIN_4, GPIO_PIN_RESET);}

  8. Fly:

    Сам еще разбираюсь, но по ходу у тебя вот эта строчка бесконечно вызывает прерывание на прием,
    HAL_UART_Receive_IT(&huart1,(uint8_t*)my_comm,8);
    и ни чего не дает получить. И после нее все постоянно выполняется в холостуюНадо бы в коллбэке флаг по приему задать а ее выполнять в условии типа
    if (flag==1)
    {
    HAL_UART_Receive_IT(&huart1,(uint8_t*)my_comm,8);
    flag=0;
    тут далнейший код условий и морганий
    }

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

*