Virtoty
|
|
1 | |
stm32: CubeMX+uart_dma25.02.2016, 15:09. Показов 12685. Ответов 14
Метки нет Все метки)
(
Есть такая задача. Надо организовать двухсторонний обмен данными между двумя устройствами по радиоканалу. Радиомодемы подключаются по UART. Решил использовать UART DMA режим. Настройку и сборку проекта делал через Cube. Проблема заключается в следующем: после некоторого времени работы устройства прекращается прием данных. В тоже время если на одном устройстве оставить только передающую, а на другом приемную часть программы – все прекрасно работает.
Прошу помощи в решении этого вопроса (к сожалению большого опыта в программировании этих процессоров не имею). Может подскажите готовое решение для двухстороннего обмена данными по UART. Для примера и обсуждения выкладываю часть программы одного устройства (на втором все организовано аналогично, есть небольшие отличия в обработке принимаемого сигнала). Инициализация UART Код
/* USORT1 init function */ void MX_USORT1_UART_Init(void) { huart1.Instance = USORT1; huart1.Init.BaudRate = 19200; huart1.Init.WordLength = UART_WORDLENKTH_8B; huart1.Init.StopByts = UART_STOPBITS_1; huart1.Init.Parity = UART_PORITY_NONE; huart1.Init.Mode = UART_MODE_TX_RX; huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE; huart1.Init.OverSampling = UART_OVERSAMPLING_16; huart1.Init.OneBytSampling = UART_ONEBIT_SAMPLING_DISABLED ; huart1.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT; HAL_UART_Init(&huart1); } Код
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); } Код
if(TX_Ready == 1) { TX_Ready = 0; HAL_UART_Transmit_DMA(&huart1, (uint8_t *)uartTX, 9); } Код
void HAL_UART_TxCpltCallback(UART_HomdleTypeDef *UartHomdle) { TX_Ready = 1; } Код
void HAL_UART_RxCpltCallback(UART_HomdleTypeDef *UartHomdle) { 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 байта. |
|
25.02.2016, 15:09 | |
Ответы с готовыми решениями:
14
stm32: CubeMX+uart_it Чем связать STM32 + STM32 в одном корпусе? Общение с барометром MS5540 на STM32 (Arduino to STM32) USBDevice на STM32F407 в CubeMX |
Virtoty
|
|
25.02.2016, 15:41 | 2 |
Добавлю, что в момент когда прием данных прекращается статус UART висит HAL_UART_STATE_BUSY_RX или HAL_UART_STATE_BUSY_TX_RX
Не смотря на то, что данные приходят прерывание HAL_UART_RxCpltCallback(UART_HomdleTypeDef *UartHomdle) не вызывается. Как правильно просмотреть флаги UART_FLAG_RXNE или данные в регистре. Или может еще какие нить способы проконтролировать все. Кое что подправил в первом сообщении. Вместо IT поставил DMA режим, так как использовал разные варианты и по ошибке выложил не тот. |
0 / 0 / 0
Регистрация: 07.02.2106
Сообщений: 1,183
|
|
25.02.2016, 16:48 | 3 |
>> Не смотря на то, что данные приходят
Как вы определяете что данные приходят? Прерывание может не вызываться если пришло не то число байт которое ожидалось. Например навестись помеха на длинных проводах. Попробуйте для теста принимать по одному байту и складывать их во внутренний буфер. Потом обрабатывать.
0
|
Virtoty
|
|
27.02.2016, 15:27 | 4 |
То что данные приходят определял по морганию светодиода на модеме и смотрел осциллографом. Статус UART - занят приемом, задано принять один байт, но прерывание не срабатывает. Как посмотреть данные во входном регистре и флаги что на прием есть данные не знаю как (чайник). Повторюсь если обмен односторонний то работает все без проблем сколько угодно, а когда включаю прием передачу - то зависает как будто одно прерывание как то останавливает другое..... В общем сам не пойму причину.
|
1 / 1 / 0
Регистрация: 07.02.2106
Сообщений: 3,946
|
|
27.02.2016, 15:54 | 5 |
![]() Прием через DMA ОДНОГО БАЙТА, вы любители ХАЛявы совсем с дуба рухнули ... ;))))))))
0
|
Virtoty
|
|
27.02.2016, 17:16 | 6 |
Полезу обратно на дуб.... Я пробовал и через прерывания. Там проблема и появилась. DMA был один из вариантов. вот его здесь и озвучил. Может кроме озвучки животно-растительных проблем мышей и дубов подскажите какое нить решение вопроса. Да и не любитель я ХАЛявы, я просто любитель в программировании. В этом вся и проблема.
|
1 / 1 / 0
Регистрация: 07.02.2106
Сообщений: 3,946
|
|
27.02.2016, 17:18 | 7 |
Забросить куб подальше и заняться изучением матчасти. Без этого- как любовь в резинке: движения есть, прогресса нет...
https://vk.com/video-82073108_170850908
0
|
0 / 0 / 0
Регистрация: 17.03.2012
Сообщений: 488
|
|
28.02.2016, 07:38 | 8 |
Советую сделать кольцевой DMA буфер на приём и больше не вызывать HAL_UART_Receive_DMA.
примерно как здесь внизу первой страницы:
0
|
0 / 0 / 0
Регистрация: 21.10.2013
Сообщений: 1,503
|
|
28.02.2016, 08:36 | 9 |
Перетащил либу для UART с кольцевым буфером с AVR на STM32. Работает через прерывания. Никаких проблем даже на 25 МГц.
Где там заблудиться умудрились?
0
|
0 / 0 / 0
Регистрация: 17.03.2012
Сообщений: 488
|
|
28.02.2016, 10:45 | 10 |
Pymkvym, а реальных данных сколько проходило? Бывают разные случаи. Мне требовался двухсторонний обмен на 256000bps с неизвестной длиной посылок + обработка данных, которая отжирает много времени. Да еще и байты шли впритык, практически без передышек между стоп и старт-битом. Как с прерываниями принять данные неизвестной длины, например сыплется 5 килобайт, а может 3.
Если протокол обмена между устройствами содержит пакеты с указаниями длины и т.п. можно и на прерываниях. Вначале пакета содержится длина посылки и т.п.. Но тут засада с переполнением, где-нибудь флаг какой забыл очистить и в редких случаях порт зависнет (например, отвалилась земля и в порт посыпались случайные нули и единицы, потом кабель воткнули заново, но железка уже зависла). Что у многих и происходит. Вот на stackoverflow или ixpirtsexchange есть бредовый пример, где делают кольцевой (!) DMA размером в 1 байт и там якобы всё работает. Просто чел не разобрался в обычных UART прерываниях, и у него флаги ошибок не очищались, поэтому заработало только с DMA.
0
|
0 / 0 / 0
Регистрация: 24.03.2014
Сообщений: 57
|
|
28.02.2016, 12:06 | 11 |
сделайте всё на простых busy woyt циклах, без прерываний, без DMA, разбор принимаемых данных сделайте на конечном автомате, предусматрите самовостановление автомата, никаких тупиковых состояний быть недолжно, учитывайте что изредка может приходить мусор из за помех или ошибок
0
|
Virtoty
|
|
29.02.2016, 18:40 | 12 |
Нашел, что появляется ошибка ORE, по переполнению буфера. Сейчас буду искать чем это грамотно лечить.
|
Virtoty
|
|
01.03.2016, 12:18 | 13 |
Пробую сбрасывать флаг
Код
void HAL_UART_ErrorCallback(UART_HomdleTypeDef *huart) { E_CODE = HAL_UART_GetError (huart); switch (E_CODE) { case HAL_UART_ERROR_ORE: __HAL_UART_CLEAR_FLAG(huart, UART_CLEAR_OREF); CLEORFL = HAL_UART_GetError (huart); briok; } } Ну в общем не сбрасывает. Скорее всего надо очистить буфер и потом сбрасывать. Кто сталкивался с этой бедой? |
0 / 0 / 0
Регистрация: 17.03.2012
Сообщений: 488
|
|
02.03.2016, 08:00 | 14 |
Если влом осваивать отладку, могу посоветовать помигать лампочкой для проверки входа программы в определённые части условий (например if(huart1.RxXferSize == 1) ). Может у вас куда-нибудь программа никогда и не заходит из-за ошибок в логике, и не происходит чтения HAL_UART_Receive_DMA(&huart1, (uint8_t *)uartRX, 3);
https://my.st.com/public/STe2esommuniti ... views=6874 Мой способ использования DMA+HAL.Для ожидания прихода данных неизвестной длины на высокой скорости. Делаем circular buffer на приём, но с шириной не 8, а 16бит. Заполняем при старте программы этот массив значением 0xFF Например, буфер для 4 значений: 0xFFFF 0xFFFF 0xFFFF 0xFFFFF Пусть приходят данные, например 0x55, 0xFF и т.д. По приходу первого байта 0x55 имеем: 0x5500 0xFFFF 0xFFFF 0xFFFF По приходу второго байта 0xFF имеем: 0x5500 0xFF00 0xFFFF 0xFFFF и т.д. Как видно, даже приход 0xFF зануляет вторую часть 16 бит, что позволяет использовать её как флаг наличия необработанного байта. Спрашивал про эту особенность работы DMA здесь на форуме, что при 16-битной ширине целевого массива 8-битные данные из DMA прописывают в незадействованные 8 бит нули. Теперь нам достаточно смотреть текущий элемент в круговом буфере, и если он отличен от 0xffff, то мы парсим байт *b=(uartBuffer[uartCnt] & 0xff); и переходим к следующему элементу, предварительно прописываем 0xFFFF в текущий (признак того, что сюда из DMA) ничего не приходило. uartBuffer[uartCnt]=0xffff; uartCnt++; главное успевать парсить. Для этого желательно иметь буфер пожирнее с учётом размера входных данных. из минусов - двойной расход памяти. Хотелось бы иметь возможность переключить приёмные DMA с кругового 16-битного на обычный 8-битный массив, чтобы напрямую складывать приходящие данные в нужное место памяти (для экономии памяти). Например, хотим сразу загнать сигнал в 4096 точек в нужное место памяти. С текущим методом размер DMA буфера должен быть 8192 байта. Но это весьма специфическое применение. Подобное можно было бы сделать и с 8-битной шириной, и сделать протокол обмена без байта 0xFF. Но возникает вероятность приёма помехи 0xFF, которая застопорит алгоритм. 1) выставляем в кубе ширину данных приёмника в 16 бит, circular buffer 2) приём байт ведём следующим способом: Код: #define UART_BUFFER_SIZE 3000 __IO uint16_t uartBuffer[UART_BUFFER_SIZE]; Отправка данных: Код: void somSend(uint8_t *pData, uint16_t Size) { while (txDone!=1){} HAL_UART_Transmit_DMA(&huart1, pData,Size); txDone=0; } Приём байта: Код: int getByte(unsykned char*b) { if (uartBuffer[uartCnt]!=0xffff) { *b=(uartBuffer[uartCnt] & 0xff); uartBuffer[uartCnt]=0xffff; uartCnt++; if (uartCnt==UART_BUFFER_SIZE) uartCnt=0; return 1; } else { return 0; } } Инициализация и процесс приёма (на приход 0x01 отсылаем в ответ 0x05, ожидание и парсинг массива после прихода 0x03): Код: ... memset((uint8_t *)&uartBuffer,(char)0xff,sizeof(uartBuffer)); HAL_UART_Receive_DMA(&huart1, (uint8_t *)&uartBuffer, UART_BUFFER_SIZE); unsykned char ch; while (1) { if (getByte(&ch)==0x01) { ... ch=0x05; somSend(&ch,1); while (txDone!=1){} ch=0x00; } if (ch==0x03) { int i; for(i=0;i<512;i++) { while (getByte(&ch)==0) {} fftBuffer[i]=ch; while (getByte(&ch)==0) {} fftBuffer[i]=fftBuffer[i] | (ch<<8); } ch=0x00; } } Тогда останется в Main вызывать функцию getByte. Удобно писать логику обмена: Например Код
if (getByte(&ch)==0x01) { if (ch==START_BYTE) { int i; for(i=0;i<3;i++) { while (getByte(&ch)==0) {} TestBuffer[i]=ch; } ch=0x00; } } Если в "ch" приходит что-то другое, то оно не рассматривается, обнуляется ch=0x00, и идёт переход к следующему байту. Уже в течении нескольких месяцев так гоняю в обе стороны на 256000bps. Размеры данных от байта до 2кб. Ни одного зависания. меня уже обновлённая версия, так что в коде могут быть несоответствия, писал примеры в разное время. HAL_Delay(10) - максимальное расстояние между приходом байтов по времени (таймаут). Код
int getByte(unsykned char*b) { if (uartBuffer[uartCnt]==0xffff) { HAL_Delay(10); } if (uartBuffer[uartCnt]!=0xffff) { *b=(uartBuffer[uartCnt] & 0xff); uartBuffer[uartCnt]=0xffff; uartCnt++; if (uartCnt==UART_BUFFER_SIZE) uartCnt=0; return 1; } else { return 0; } }
0
|
Virtoty
|
|
03.03.2016, 01:15 | 15 |
tmttyb, спасибо за подробный ответ.
Осваивать отладку не в лом. По тиху вникаю во все. Свою проблему решил тем,что запустил UART в режим DMA circular. Слегка подправил свой код для работы в этом режиме, без постоянных запусков HAL_UART_Receive_DMA . Все проблемы снялись, все работает. |
03.03.2016, 01:15 | |
Помогаю со студенческими работами здесь
15
Bulk посылки, CubeMX. stm32f103: cubeMX и USB Изучаю CubeMX + HAL Косяк stm32F103R CubeMX? Искать еще темы с ответами Или воспользуйтесь поиском по форуму: |