Урок 14
HAL. USART. Прием данных
На прошлом занятии мы разобрались с технологией передачи данных по интерфейсу USART, попробовали это на практике.
Во-первых, нужно обрабатывать прерывание от USART, вы ведь не угадаем, когда именно нам принимать.
Во-вторых, где-то и как-то это надо отображать.
Вот тут-то на помощь нам приходит наш добрый и старый дисплей 20х4.
Подключим его по той же 4-битной схеме (нажмите на картинку для увеличения изображения)
В связи с этим новый проект USART_RECEIVE мы создадим из другого проекта — MYLCD80
Запускаем вновь созданный проект в CUBE, включаем аналогичным образом USART2 – Asynchronous.
Те же самые настройки. как и в проекте с передачей данных должны быть и в Configuration.
Только единственная разница, в USART в Configuration на USART2 включим прерывания
Генерируем проект. Открываем его.
Добавляем файл 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, на дисплей ничего не выведется)
В будущих проектах мы обязательно вернемся к режиму приёма данных по USART с использованием прерываний, правда не очень скоро, но вернёмся.
Предыдущий урок Программирование МК STM32 Следующий урок
Переходник USB-TTL лучше купить такой (сейчас у меня именно такой и он мне больше нравится)
Смотреть ВИДЕОУРОК
Можете показать пример как определять размер сообщеия автоматичемкт?
Была у меня проблема приема пакета неизвестной длины. Один контроллер другому посылает команды по 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 и найти решение именно для моей задачи. Надеюсь, кому-нибудь мой комментарий сэкономит время.
некорректно ушел комментарий. дублирую пункты:
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 символов
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.
Я сделал проще,читал по одному байту в буфер и добавил счётчик позиции,после чтения сбрасывал позицию на 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 . Видел примеры с кольцевым буфером,но смысла в нём не вижу
Капец какой-то, посылаю одно, на сайте другое… дубль 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.
какой-то глюк с комментарием. про DMA вся суть криво.
кому надо — пишите chechetkin.v.v@mail.ru
Ага, у меня тоже куда — то делсть часть инфы
Ребята, надо, чтобы, терминал v 1.9b получал пакеты произвольной длины(желательно, по прерываниям. то есть, я ввожу то что мне надо и нажимаю SEND). Я так понял, HAL для этого совершенно не годится? Перепробовал все, принимает совершенно не то что нужно. Плюс, символы '$' и '#' не обрабатываются. Вместо них . Весь код по книге, только, размер уменьшен до одного символа и вместо LCD HAL_UART_Transmit_IT(…). Ведь, по — идее, функция, HAL_UART_Receive_IT(…) должна взять первый символ и послать его HAL_UART_Transmit_IT, очистить буфер, перейти ко второму символу и также нередать его в верхнюю часть терминала? Или я ошибаюсь?
Помогите новичку !!!
У меня вопрос по функции передачи. Есть задача — передать код и в зависимости от него, включать/отключать светодиод. Вроде всё просто, но есть проблема, почему-то игнорируются и не обрабатываются 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);}
Сам еще разбираюсь, но по ходу у тебя вот эта строчка бесконечно вызывает прерывание на прием,
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;
тут далнейший код условий и морганий
}