|
|
  |
USART2(приём по прерывании RXNE)+USART3(DMA на передачу), STM32F2+FreeRTOS |
|
|
|
Jun 21 2016, 11:28
|
Частый гость
 
Группа: Участник
Сообщений: 121
Регистрация: 27-04-09
Из: Украина
Пользователь №: 48 342

|
Прошу помощи у сообщества. Понимаю, что тема избитая, перечитал, что нашёл на этом форуме, но увы, не работает как задумано. Вкратце алгоритм работы такой: - приём из USART2 работает по прерыванию RXNE в массив; - по прерыванию IDLE определяю, что приём закончен и переключаю на второй массив (что бы первый неспешно обработать, в данной реализации, для отработки алгоритма, просто выдать в USART3); - накопленный массив выдаю в USART3 по DMA. И вот тут грабли, выдача происходит рваная: Код $GPGGA,111415.00,4952.1238,E,1,11,01.2,120.4,M,26.7,M,SV,00,40,29,15018*79 $GLGSV,231,73,65,152,38,14,040,35*66 $GLGSV,2,2,06,83,720,32*66 $GNGSA,29,32,,,,,,,01.$GNGSA,A,3,83,73,0 $PORZD,A,004.1*39 1A $PORZD,A,004.1*39 $GPGGA,111417.00,49,M,,*55 $GPRMC,111417.00,A,4912.361,8 $GPGSV,3,28*,00,40,29,1501806,66,10,006,,1,29,38,240,37,18,235,00,37,33,1V,2 ,2,06,83,720,32*66 $GNGSALG,32,,,,,,,01.$GNGSA,A,3,83,73,2938 .0*38 0,038,39,84,50,2,A,3,02,06,12,259,01.,M,,*5A $GPRMC,111418.00,A,4912.3692,N,03152.1238,E,00.00,000.5,210616,,,A*58 $GPGSV,3,1,10,02,54,108,36,06,31,051,33,12,75,081,38,25,61,300,38*7E $GPGSV,3,2,10,29,38,240,37,32,14,274,31,33,18,235,00,37,33,193,00*7E $GPGSV,3,3,10,39,33,189,00,40,29,150,00*79 $GLGSV,2,1,06,66,10,006,30,73,65,152,38,75,05,323,01,82,14,040,34*66 $GLGSV,2,2,06,83,70,038,38,84,50,220,31*64 $GNGSA,A,3,02,06,12,25,29,32,,,,,,,01.9,01.2,01.5*19 $GNGSA,A,3,83,73,84,82,66,,,,,,,,01.9,01.2,01.5*1A $PORZD,A,004.0*38 а теперь код, собственно как это всё у меня реализовано: Код //============================================================================= if ( USART_GetITStatus ( USART2, USART_IT_IDLE ) == SET ) { DMA_Cmd ( DMA1_Stream3, DISABLE );
USART_ReceiveData ( USART2 );// сброс PE,FE,NE,ORE,IDLE bits in USART_SR
if ( BufIDLE.currentBuf == buf1 ) { BufIDLE.currentBuf = buf2; } else { BufIDLE.currentBuf = buf1; }
xSemaphoreGiveFromISR ( xSemaphore_usart2IDLE, &xHigherPriorityTaskWoken ); }
//============================================================================= if ( USART_GetITStatus ( USART2, USART_IT_RXNE ) == SET ) { ISRcChar2 = ( uint8_t ) ( ( USART_ReceiveData ( USART2 ) ) & (uint8_t)0xFF );
if ( BufIDLE.currentBuf == buf1 ) { BufIDLE.buf1 [BufIDLE.buf1_idx++] = ISRcChar2; } else { BufIDLE.buf2 [BufIDLE.buf2_idx++] = ISRcChar2; } }
void DMA1_Stream3_IRQHandler ( void ) { portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE;
/* Test on DMA Stream Transfer Complete interrupt */ if ( DMA_GetITStatus ( DMA1_Stream3, DMA_IT_TCIF3 ) == SET ) { /* Clear DMA Stream Transfer Complete interrupt pending bit */ DMA_ClearITPendingBit ( DMA1_Stream3, DMA_IT_TCIF3 );
DMA_Cmd ( DMA1_Stream3, DISABLE );
xSemaphoreGiveFromISR ( xSemaphore_usart3TX, &xHigherPriorityTaskWoken ); } } задача, выдающая в юсарт3: Код void EchoTask_usart2 ( void *pvParameters ) { #define size_local buf_SIZE
uint8_t *USART2_Buf_local;
USART2_Buf_local = pvPortMalloc ( size_local );
uint16_t USART2_Idx_local = 0;
for(;; ) { xSemaphoreTake ( xSemaphore_usart2IDLE, portMAX_DELAY );
//обнуляем локальный буфер memset ( USART2_Buf_local, 0, size_local ); USART2_Idx_local = 0;
if ( BufIDLE.currentBuf == buf1 ) { USART2_Idx_local = BufIDLE.buf2_idx; memcpy ( USART2_Buf_local, BufIDLE.buf2, USART2_Idx_local ); memset ( BufIDLE.buf2, 0, buf_SIZE ); BufIDLE.buf2_idx = 0; } else { USART2_Idx_local = BufIDLE.buf1_idx; memcpy ( USART2_Buf_local, BufIDLE.buf1, USART2_Idx_local ); memset ( BufIDLE.buf1, 0, buf_SIZE ); BufIDLE.buf1_idx = 0; }
xSemaphoreTake ( xSemaphore_usart3TX, portMAX_DELAY );
DMA1toUSART3TX_Put ( USART2_Buf_local, USART2_Idx_local ); } } Буду благодарен за конструктивные советы.
|
|
|
|
|
Jun 23 2016, 09:16
|

Профессионал
    
Группа: Свой
Сообщений: 1 032
Регистрация: 13-03-08
Из: Маськва
Пользователь №: 35 877

|
Плохо задумано, к сожалению.
Idle - это пауза >= одному байту. Я никогда не интересовался, как обстоят дела с паузами у GNSS приёмников, но предположим, что плохо - паузы могут быть в произвольный момент времени.
Например, приходят пакеты 10 байт, пауза, 2 байта, пауза. Принимаем в буфер 1 10 байт (где проверка на переполнение, кстати?!), запускаем передачу. Принимаем в буфер 2 пару байт, запускаем передачу. Сколько чего отправилось из буфера 1, неизвестно - вероятнее всего, несколько байт уйти не успели.
--------------------
Тут обсуждается творческий порыв, а не соответствие каким-либо стандартам ©
|
|
|
|
|
Jun 29 2016, 11:31
|
Частый гость
 
Группа: Участник
Сообщений: 121
Регистрация: 27-04-09
Из: Украина
Пользователь №: 48 342

|
Изменил приём по IDLE+DMA, приём не происходит, хотя в прерывание ИДЛЕ - заходит с каждым принятым фреймом данных. Количество принятых байтов вычисляю вот так: Код // USART2IDLE2_Idx = buf_SIZE - DMA1_Stream5->NDTR; USART2IDLE2_Idx = buf_SIZE - DMA_GetCurrDataCounter ( DMA1_Stream5 ); Но USART2IDLE2_Idx - всегда нулевой, подскажите пожалуйста, где косяк?
|
|
|
|
|
Jul 4 2016, 11:52
|
Частый гость
 
Группа: Участник
Сообщений: 121
Регистрация: 27-04-09
Из: Украина
Пользователь №: 48 342

|
Зашевелилось. приём по ДМА+ИДЛЕ, передача ДМА. В прерывании ИДЛЕ происходит переключение принимающих массивов, а в задаче выдачи уже копирование из накопленного массива, в в локальный массив, который уже выдаётся наружу. Но к сожалению сбои не исчезли, что я делаю не так? Код void DMA1_Stream3_IRQHandler ( void ) { portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE;
/* Test on DMA Stream Transfer Complete interrupt */ if ( DMA_GetITStatus ( DMA1_Stream3, DMA_IT_TCIF3 ) == SET ) { /* Clear DMA Stream Transfer Complete interrupt pending bit */ DMA_ClearITPendingBit ( DMA1_Stream3, DMA_IT_TCIF3 );
DMA_Cmd ( DMA1_Stream3, DISABLE );
xSemaphoreGiveFromISR ( xSemaphore_usart3TX, &xHigherPriorityTaskWoken ); } } Код if ( USART_GetITStatus ( USART2, USART_IT_IDLE ) == SET ) { USART_ReceiveData ( USART2 );// сброс PE,FE,NE,ORE,IDLE bits in USART_SR
DMA_Cmd ( DMA1_Stream5, DISABLE );
if ( CurrentBuf == buf1 ) { USART2IDLE1_Idx = buf_SIZE - DMA1_Stream5->NDTR;
CurrentBuf = buf2; } else { USART2IDLE2_Idx = buf_SIZE - DMA1_Stream5->NDTR;
CurrentBuf = buf1; }
RxDMA1_Stream5 ();
xSemaphoreGiveFromISR ( xSemaphore_usart2IDLE, &xHigherPriorityTaskWoken ); } Код void EchoTask_usart2 ( void *pvParameters ) { #define size_local 4096
uint8_t USART2_Buf_local [ size_local ];
volatile uint16_t USART2_Idx_local = 0;
for(;; ) { xSemaphoreTake ( xSemaphore_usart2IDLE, portMAX_DELAY );
//обнуляем локальный буфер memset ( USART2_Buf_local, 0, size_local ); USART2_Idx_local = 0;
if ( CurrentBuf == buf1 ) { USART2_Idx_local = USART2IDLE2_Idx;
memcpy ( USART2_Buf_local, USART2_Buf2_IDLE, USART2_Idx_local );
memset ( USART2_Buf2_IDLE, 0, buf_SIZE ); USART2IDLE2_Idx = 0; } else { USART2_Idx_local = USART2IDLE1_Idx;
memcpy ( USART2_Buf_local, USART2_Buf1_IDLE, USART2_Idx_local );
memset ( USART2_Buf1_IDLE, 0, buf_SIZE ); USART2IDLE1_Idx = 0; }
xSemaphoreTake ( xSemaphore_usart3TX, portMAX_DELAY );
DMA1toUSART3TX_Put ( USART2_Buf_local, USART2_Idx_local ); } }
|
|
|
|
|
Jul 4 2016, 16:55
|

Профессионал
    
Группа: Свой
Сообщений: 1 032
Регистрация: 13-03-08
Из: Маськва
Пользователь №: 35 877

|
Почитал-почитал, и ничего не понял.
Я бы сделал: - буфер FIFO с методами Put(байт) и Get(байт). - прерывание Usart2_RXNE, которое делает Put() - прерывание Usart3_TXE, которое делает Get()
Дальше немного магии с разрешением прерывания от уарта3: - в обработчике Usart3_TXE, если FIFO пустой, TXE выключаем; - в обработчике Usart2_RXNE всегда включаем TXE
И никаких переливаний из пустого в порож..., простите, я хотел сказать "никаких memcpy". Ах, да, freertos'а в моём решении тоже нет :-)
--------------------
Тут обсуждается творческий порыв, а не соответствие каким-либо стандартам ©
|
|
|
|
|
Jul 4 2016, 19:15
|
Частый гость
 
Группа: Участник
Сообщений: 121
Регистрация: 27-04-09
Из: Украина
Пользователь №: 48 342

|
Цитата(esaulenka @ Jul 4 2016, 19:55)  Почитал-почитал, и ничего не понял.
Я бы сделал: - буфер FIFO с методами Put(байт) и Get(байт). - прерывание Usart2_RXNE, которое делает Put() - прерывание Usart3_TXE, которое делает Get()
Дальше немного магии с разрешением прерывания от уарта3: - в обработчике Usart3_TXE, если FIFO пустой, TXE выключаем; - в обработчике Usart2_RXNE всегда включаем TXE
И никаких переливаний из пустого в порож..., простите, я хотел сказать "никаких memcpy". Ах, да, freertos'а в моём решении тоже нет :-) По прерываниям RXNE+TXE у меня работает, но задача состоит в том, что бы не прерываться на каждый принятый байт, а всю посылку целиком принять. Псоле приёма неспешно обработать и выдать в другой порт тоже по ДМА, так как есть ещё чем занятся процессору, да и скорости в 921600 бод тоже к этому подталкивают.
|
|
|
|
|
Jul 6 2016, 08:53
|

Профессионал
    
Группа: Свой
Сообщений: 1 032
Регистрация: 13-03-08
Из: Маськва
Пользователь №: 35 877

|
Ну хорошо, решение на DMA. Но с тем же FIFO.
- настраиваем DMA на приём USART2 в circular mode, постоянно складывать данные в буфер. Этот DMA будет работать вечно. - делаем в FIFO указатель put (из регистра приёмного DMA->NDTR) и указатель get (программный). - делаем функцию запуска DMA на передачу в USART3. Надо рассмотреть два варианта: put "впереди" get - обычная передача, и get впереди put - буфер "закольцевался", надо передать данные двумя кусочками. - периодически проверяем, что указатели FIFO не равны, и запускаем передающий DMA (если не запущен). Простейший вариант - по таймауту (не более времени заполнения FIFO). Более сложно - по флагам из прерываний "Tx DMA complete" и "Rx Idle".
А манипуляции аж с тремя буферами выглядят странно... И опрос NDTR надо вылечить. Как-то очень подозрительно, что туда пишется USART2_Idx_local (который всегда разный, но меньше buf_SIZE), а читается всегда buf_SIZE.
--------------------
Тут обсуждается творческий порыв, а не соответствие каким-либо стандартам ©
|
|
|
|
|
  |
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|