verlaty

Stm32: Cubemx+Uart_Dma

68 сообщений в этой теме

verlaty    0

Есть такая задача. Надо организовать двухсторонний обмен данными между двумя устройствами по радиоканалу. Радиомодемы подключаются по UART. Решил использовать UART DMA режим. Настройку и сборку проекта делал через Cube. Проблема заключается в следующем: после некоторого времени работы устройства прекращается прием данных. В тоже время если на одном устройстве оставить только передающую, а на другом приемную часть программы – все прекрасно работает.

Прошу помощи в решении этого вопроса (к сожалению большого опыта в программировании этих процессоров не имею). Может подскажите готовое решение для двухстороннего обмена данными по UART.

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

Инициализация UART

/* USART1 init function */
void MX_USART1_UART_Init(void)
{
huart1.Instance = USART1;
huart1.Init.BaudRate = 19200;
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;
huart1.Init.OneBitSampling = UART_ONEBIT_SAMPLING_DISABLED ;
huart1.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
HAL_UART_Init(&huart1);}

Инициализация DMA

void MX_DMA_Init(void)
{
/* DMA controller clock enable */
__DMA1_CLK_ENABLE();
__DMA2_CLK_ENABLE();
/* DMA interrupt init */
HAL_NVIC_SetPriority(DMA1_Channel4_IRQn, 0, 0); // DMA1_Channel4 - передача uart
HAL_NVIC_EnableIRQ(DMA1_Channel4_IRQn);
HAL_NVIC_SetPriority(DMA1_Channel5_IRQn, 0, 0); // DMA1_Channel5 – прием UART
HAL_NVIC_EnableIRQ(DMA1_Channel5_IRQn);
HAL_NVIC_SetPriority(DMA2_Channel1_IRQn, 0, 0);// DMA2_Channel1 – ADC
HAL_NVIC_EnableIRQ(DMA2_Channel1_IRQn);}

В main делаю отправку данных вот так:

if(TX_Ready == 1)
{
TX_Ready = 0;
HAL_UART_Transmit_DMA(&huart1, (uint8_t *)uartTX, 9);
}

Флаг сбрасывается вот здесь TX_Ready:

void HAL_UART_TxCpltCallback(UART_HandleTypeDef *UartHandle)
{
TX_Ready = 1;
}

Прием данных делаю вот здесь:

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *UartHandle)
{
if(huart1.RxXferSize == 1)
{
if(RXbyte[0] == START_BYTE)
HAL_UART_Receive_DMA(&huart1, (uint8_t *)uartRX, 3);
else
HAL_UART_Receive_DMA(&huart1, (uint8_t *)RXbyte, 1);
}
else
{
if (uartRX[2] == 2*START_BYTE)
IRdist = (uartRX[1] << 8) | uartRX[0];
HAL_UART_Receive_DMA(&huart1, (uint8_t *)RXbyte, 1);
}
}

Немного опишу теорию приема.

Запускаю прием одного байта командой HAL_UART_Receive_DMA(&huart1, (uint8_t *)RXbyte, 1).

Если принятый байт равен стартовому байту в отправленной пачке, то принимаю пачку из 3 байт HAL_UART_Receive_DMA(&huart1, (uint8_t *)uartRX, 3). Если не равен, снова принимаю байт (ожидаю нужный мне байт)

Если принял пачку и контрольный третий байт равен ожидаемому, то формирую сигнал IRdist и снова принимаю один байт. if(huart1.RxXferSize == 1) этим смотрю что я принимал один байт или пачку.

В ответном устройстве аналогично принимается пачка из 9 байт, а отправляется 3 байта.

Изменено пользователем verlaty

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
artos5    290
Если принятый байт равен стартовому байту в отправленной пачке, то принимаю пачку из 3 байт HAL_UART_Receive_DMA(&huart1, (uint8_t *)uartRX, 3). Если не равен, снова принимаю байт (ожидаю нужный мне байт)

Если принял пачку и контрольный третий байт равен ожидаемому, то формирую сигнал IRdist и снова принимаю один байт. if(huart1.RxXferSize == 1) этим смотрю что я принимал один байт или пачку.

В ответном устройстве аналогично принимается пачка из 9 байт, а отправляется 3 байта.

Зачем Вы так усложняете? Скорее всего у вас ошибка в коде.

Я обрабатываю немного не так данные:

Принимаю данные по ЮАРТ через прерывание , если принял то что надо - выполняю обработку и очищаю буфер приема. Если принял не то что ожидал - просто очищаю буфер , и выдаю ошибку.

Так у меня реализованы АТ команды .

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
verlaty    0

Да такую цель и преследовал. Принимать по байту, а если приходит нужный ( это или команда управления устройством или команда настройки устройства) то обрабатываю уже по типу входящей информации. Если я знаю что должно прийти 9 байт, то зачем по каждому байту обрабатывать в прерывании. Если есть код, покажи как ты принимаешь через прерывания. Повторюсь что в одну сторону обмен идет без проблем, а прием передача - подвисает.

Изменено пользователем verlaty

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
mail_robot    1 407

а если к примеру один байт из пачки пропадет? Сколько времени приемник будет ждать завершения посылки?

Если пачка окажется битой по последовательности (а с радиомодемами это более чем реально), то вся канитель с приемом повиснет если не навсегда, то очень надолго. Надо мутить флуш по таймауту и ресетить всю процедуру принимающей стороны

И я бы еще не разрешал DMA принимать по 3 байта. Пускай лучше всегда по одному тянет. Так проще таймаут будет отработать если то.

Да и вообще при таких коротких посылках проще использовать функцию приема по прерываниям. Я понимаю если бы там скорость была мегабита 3-4 или поток, но тут то... 3 копейки на хромой телеге

Изменено пользователем mail_robot

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
artos5    290

Вот откопал код. Когда то пробовал , работало в железе замечательно.

В архиве , полный рабочий проект под кейл. А вот "куски" кода:

void USART1_IRQHandler()
{
   //Проверяем, действительно ли прерывание вызвано приемом нового байта
char vuart_data;
if (USART1->SR & USART_SR_RXNE) { // ... не пришло ли что-то в UART ?
vuart_data=USART1->DR;

rx_buffer[rx_wr_index++]=vuart_data;
  if (rx_wr_index == RX_BUFFER_SIZE) rx_wr_index=0;
}
}
void USART2_IRQHandler()
{
   //Проверяем, действительно ли прерывание вызвано приемом нового байта
char vuart_data;
if (USART2->SR & USART_SR_RXNE) { // ... не пришло ли что-то в UART ?
vuart_data=USART2->DR;

rx_buffer2[rx_wr_index2++]=vuart_data;
  if (rx_wr_index2 == RX_BUFFER_SIZE) rx_wr_index2=0;
}
}
void clear_buffer()
{
for(unsigned char x=0; x<RX_BUFFER_SIZE; x++){rx_buffer[x]=0; rx_buffer2[x]=0;}
rx_wr_index=0;
rx_wr_index2=0;
}
char string_cmp(char *str1,char *str2)
{
unsigned int x=0;
str1+=0;
str2+=0;
while(str1[x]!=0)
{
if(str1[x] != str2[x])
{
 return 0;
}
x++;
}
return 1;
}
// принимаем строку , и передаем строку:

if(string_cmp("test_data", rx_buffer2))
{
Usart2_Send_String("test send data1!");
clear_buffer();
led_blink(3, 200);
}
else if(string_cmp("data send", rx_buffer2))
{
Usart2_Send_String("test send data2!");
clear_buffer();
led_blink(3, 200);
}

Таймаут пол секунды (настраивается индивидуально под размер принимаемой строки) примерно .

TEST_F100RBT6.rar

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
verlaty    0

а если к примеру один байт из пачки пропадет? Сколько времени приемник будет ждать завершения посылки?

Если пачка окажется битой по последовательности (а с радиомодемами это более чем реально), то вся канитель с приемом повиснет если не навсегда, то очень надолго. Надо мутить флуш по таймауту и ресетить всю процедуру принимающей стороны

И я бы еще не разрешал DMA принимать по 3 байта. Пускай лучше всегда по одному тянет. Так проще таймаут будет отработать если то.

Да и вообще при таких коротких посылках проще использовать функцию приема по прерываниям. Я понимаю если бы там скорость была мегабита 3-4 или поток, но тут то... 3 копейки на хромой телеге

если пропадет.... Он принимает допустим 9 байт, и 9 - это контрольный, если пачка целай и не битая он ее дешифрирует, иначе просто принимает след байт, в этом случае программа не зависнет. Но суть то вопроса в том что он просто зависает. Я принимал и в прерываниях и в дма....результат один и тот же. А протокол обмена - это просто одно из решений.

Вот откопал код. Когда то пробовал , работало в железе замечательно.

....

Спасибо. Попробую что нить использовать из него

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
mail_robot    1 407

а если байт потеряет? примет то 8, а ДМА будет ждать 9-ый. Следующая пачка даже целая примется как поломанная и так далее. Для контроллера это будет фактически зависон, так как условий правильного пакета уже долго не будет, пока не накопится 9 ошибок.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
verlaty    0

Сейчас попробую объяснить идею. Я принимаю по одному байту. Если принятый байт будет равен START_BYTE, тогда принимаем пачку из трех байт. Если третий контрольный байт равен стоповому байту ( для упрощения я сделал так: if(uartRX[2]==2*START_BYTE, на самом деле этот контрольный байт можно рассчитывать по передаваемой информации) , то мы дешифрируем пачку. Допустим мы пропустили байт и третий принятый байт не равен стоповому байту, тогда мы начинаем снова ловить стартовый байт и просто пропускаем битую пачку. программа не зависнет. В моем проекте идет дистанционное управление исполнительным устройством, то есть постоянно передается информация для управления несколькими механизмами. Если я потеряю одну пачку, ничего страшного не произойдет. Дело в том что я пробовал все это реализовать и через прерывания и складывать в буфер по одному байту а уже в основной программе разбирать принятую информацию, но косячек все равно вылазил. Сейчас попробую работать напрямую с регистрами, так как посоветовал artos5. Повторюсь, если вы знаете уже готовые варианты двухстороннего обмена по уарт (что бы не изобретать велосипед), буду очень признателен.

Изменено пользователем verlaty

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
mail_robot    1 407

можно порыть в кодах, но на ночь глядя долговато это все выковыривать

а что если поискать причину зависона? Скажем делать эхо всех принимаемых данных в отладочный уарт? Просто так же блоки контроллера не останавливаются. Будет понятно на каком месте затык, потому как данные там и там по идее пропасть должны. А если в отладочном не пропадут, то хоть будет видно что именно принимается. Я для таких целей всегда свободный уарт держу (железный ессно), благо их в стм-ке предостаточно

Вообще всегда предпочитаю дебажить через уарт + STLink. Гораздо удобнее выходит, особенно когда переменка вне области видимости стандартного отладчика. Слегка конечно догружает ядро, но ни разу небыло так чтобы существенно.

Изменено пользователем mail_robot

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
artos5    290

Приду домой - покажу как это делается .

Хотя , приведенный мною пример может это решить .

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
verlaty    0

Да, именно зависон и хочется найти, Разобраться с причиной, а не заново переписывать. Спасибо Вам за помощь и советы.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
mail_robot    1 407

кстати очень поддерживаю, что используется именно HAL. Правильной дорогой идете, товарищ!

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
verlaty    0

кстати очень поддерживаю, что используется именно HAL. Правильной дорогой идете, товарищ!

.

Я занялся STM буквально месяца 3 назад. Пришлось перейти с ARDUINO, так как STM гораздо функциональнее. И не смотря на то что многие Профи плюются на HAL, и Cube и HAL мне очень сильно упростили знакомство. Наверное UART первая проблема, которая заставила меня капнуть куда то глубже. В проекте использую I2C, USB, ADC, PWM, переписывал некоторые библиотеки с ARDUINO и в принципе посидев денек другой все нормально запускалось и работало. Без сомнения в дальнейшем придется разбираться и капать глубже.

Для отладки использую STLink (на базе STM32F3-Discovery) и STM Studio. А для проекта изготовил свои платы и устройства на том же процессоре.

Изменено пользователем verlaty

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
mail_robot    1 407

я тоже сначала настороженно отнесся к HAL, но потом как то взялся и покопал либы. Пришел к выводу, что ничего там опасного нет, а вот удобства мегаочевидны. Просто вся возня с регистрами и зависимостями заменена макросами. Ошибки были в первых версиях, и изза них собсна и все вопли, но к версии 1.3.0 их почти все вычистили. Юзаю HAL уже примерно пол года и до их пор проблем не наблюдал. Все работает. Хотя по первости было трудновато привыкнуть к синтаксису и принципам работы. Колбэки вообще поначалу в ступор вводили, пока не нашел их обьявления и не выяснил механизм вызова.

И да - на сегодняшний день это единственная 100% поддерживаемая ST библиотека, остальное они сворачивают

Кстати, только обратил внимание. В коде приема функция HAL_UART_Receive_DMA вызывается без предварительной остановки DMA. Если DMA настроен в режим CIRCULAR, то это может вызывать ошибки. Самого блока настройки DMA не видно

Изменено пользователем mail_robot

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
verlaty    0

Он не в CIRCULAR работает.

void HAL_UART_MspInit(UART_HandleTypeDef* huart)
{
 GPIO_InitTypeDef GPIO_InitStruct;
 if(huart->Instance==USART1)
 {
 /* USER CODE BEGIN USART1_MspInit 0 */
 /* USER CODE END USART1_MspInit 0 */
   /* Peripheral clock enable */
   __USART1_CLK_ENABLE();

   /**USART1 GPIO Configuration   
   PA9	 ------> USART1_TX
   PA10	 ------> USART1_RX
   */
   GPIO_InitStruct.Pin = GPIO_PIN_9|GPIO_PIN_10;
   GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
   GPIO_InitStruct.Pull = GPIO_PULLUP;
   GPIO_InitStruct.Speed = GPIO_SPEED_HIGH;
   GPIO_InitStruct.Alternate = GPIO_AF7_USART1;
   HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
   /* Peripheral DMA init*/

   hdma_usart1_rx.Instance = DMA1_Channel5;
   hdma_usart1_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;
   hdma_usart1_rx.Init.PeriphInc = DMA_PINC_DISABLE;
   hdma_usart1_rx.Init.MemInc = DMA_MINC_ENABLE;
   hdma_usart1_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
   hdma_usart1_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
   hdma_usart1_rx.Init.Mode = DMA_NORMAL;
   hdma_usart1_rx.Init.Priority = DMA_PRIORITY_VERY_HIGH;
   HAL_DMA_Init(&hdma_usart1_rx);
   __HAL_LINKDMA(huart,hdmarx,hdma_usart1_rx);
   hdma_usart1_tx.Instance = DMA1_Channel4;
   hdma_usart1_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;
   hdma_usart1_tx.Init.PeriphInc = DMA_PINC_DISABLE;
   hdma_usart1_tx.Init.MemInc = DMA_MINC_ENABLE;
   hdma_usart1_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
   hdma_usart1_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
   hdma_usart1_tx.Init.Mode = DMA_NORMAL;
   hdma_usart1_tx.Init.Priority = DMA_PRIORITY_HIGH;
   HAL_DMA_Init(&hdma_usart1_tx);
   __HAL_LINKDMA(huart,hdmatx,hdma_usart1_tx);
 /* Peripheral interrupt init*/
   HAL_NVIC_SetPriority(USART1_IRQn, 0, 0);
   HAL_NVIC_EnableIRQ(USART1_IRQn);
 /* USER CODE BEGIN USART1_MspInit 1 */
 /* USER CODE END USART1_MspInit 1 */
 }
}

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
verlaty    0

Приду домой - покажу как это делается .

Хотя , приведенный мною пример может это решить .

Решил принимать один байт через прерывания, а распознав принятый байт как стартовый - принимать пачку через DMA. Передавать пачку тоже через DMA. За основу взял статьи http://dss-nk.ru/node/20 и http://dss-nk.ru/node/21. Твой пример похож на решение из этой статьи.Возник такой вопрос. При компиляции ругается на USART1->SR, USART_SR_RXNE и USART1->DR. В чем может быть проблема?

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
mail_robot    1 407

подключение заголовочных файлов CMSIS надо проверить. Эти определения должны быть там

вообще затея так себе. Мешанина может быть из определений

Изменено пользователем mail_robot

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
verlaty    0

Искал через поиск где лежат эти слова в папке проекта. Так вот в своем проекте их вообще не нашел.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
mail_robot    1 407

Ну он HAL и использует. Из другого проекта выдернуть CMSIS и подключить к своему, тогда должно собраться

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
artos5    290

Приду домой - покажу как это делается .

Хотя , приведенный мною пример может это решить .

Решил принимать один байт через прерывания, а распознав принятый байт как стартовый - принимать пачку через DMA. Передавать пачку тоже через DMA. За основу взял статьи http://dss-nk.ru/node/20 и http://dss-nk.ru/node/21. Твой пример похож на решение из этой статьи.Возник такой вопрос. При компиляции ругается на USART1->SR, USART_SR_RXNE и USART1->DR. В чем может быть проблема?

Попробуй откомпилировать кейлом мой проект . Скорее всего в твоем проце по-другому регистры называются...

Кубом сгенерируй проект, и добавь функции , и все. Делов на вечер .Я за вечер смог читать / искать строки в буфере UART. Главное , буфер по больше выделить.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
verlaty    0

Попробуй откомпилировать кейлом мой проект . Скорее всего в твоем проце по-другому регистры называются...

Кубом сгенерируй проект, и добавь функции , и все. Делов на вечер .Я за вечер смог читать / искать строки в буфере UART. Главное , буфер по больше выделить.

Твой проект компилириуется. Видать вопрос в том, что у меня другой проц. Буду искать решение....

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Ваша публикация должна быть проверена модератором

Гость
Вы не авторизованы. Если у вас есть аккаунт, пожалуйста, войдите.
Ответить в тему...

×   Вставлено в виде отформатированного текста.   Восстановить форматирование

  Разрешено не более 75 смайлов.

×   Ваша ссылка была автоматически встроена.   Отобразить как ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставить изображения напрямую. Загрузите или вставьте изображения по ссылке.

Загрузка...