Sergey_Aleksandrovi4 1 19 октября, 2009 Опубликовано 19 октября, 2009 · Жалоба Столкнулся с необъяснимой и в то же время весьма серьёзной (для меня) проблемой: пишу принятые из UART данные в память с помощью DMA в режиме автобуферизации. Сделано из-за того, что принятый макрос может обрабатываться весьма продолжительно, а DMA тем делом будет в фоне накидывать новые команды в буфер из которого по готовности я бы их вынимал и исполнял. Число вновьпринятых данных в буфере пытался определять при помощи регистра DMA6_CURR_X_COUNT, а также в качестве альтернативы, как разность регистра текущего адреса DMA6_CURR_ADDR и адреса первой ячейки приёмного буфера - не тут то было! Как только буфер DMA заполняется и счётный регистр должен вновь стать равен числу элементов буфера, а регистр текущего адреса должен стать равен адресу первого элемента - наблюдается аномальное поведение: текущий адрес = (адресу последней ячейки -- Х), а счётчик = (0 + Х), где Х - некое значение, зависящее как от адреса ячейки до начала приёма очередного блока данных, так и от длины этого блока. Т.е. процессор не успевает отображать число принятых данных, хотя сами данные корректно сливаются в буфер без потерь. Читал аномалилисты, там подобные грабли расписаны, однако для ревизии моего чипа (0.5) вроде как неактуальны, да и предложенные пути обхода не помогают. Процессор BlackFin-BF532 rev0.5, VisualDSP 4.5. Кусок индусского кода: #include <ccblkfn.h> #include <cdefBF532.h> #include <sys/exception.h> #include <stdio.h> #include "utils.h" #define RBUFLENGTH 1024 //Объём принимающего буфера UART //Приёмный буфер. Заполняется через UART по DMA (в циклическом режиме). Читается WaitNewMacros() unsigned char RBuff[RBUFLENGTH] = {0};//Буфер (циклический DMA), в который складываются принимаемые по UART байты макросов unsigned short RBuffHead = 0; //Указатель на начало принимаемого макроса в буфере приёма unsigned short RBuffTail = 0; //Указатель на конец (последний элемент) принимаемого макроса в буфере приёма //Буфер принятого макроса. В него переписывается принятый макрос из буфера приёма UART unsigned char MacroBuff[256] = {0}; //Буфер хранящий принятый для обработки макрос unsigned short MacroHead = 0; //Указатель на актуальную ячейку в буфере принятого макроа unsigned char CurMacroLength = 0; //Число элементов текущего макроса, которое должны принять и обработать . . . void main (void) { init(); while (1) { WaitNewMacros(); ExecuteMacros(); } } void init(void) { //----Настройка PLL SetupPLL((PLL_CLK/CLKIN), (PLL_CLK/MAIN_CLK), (PLL_CLK/SCLK)); //----Настройка прерываний системы *pILAT |= EVT_IVG9 | EVT_IVG10 | EVT_IVG12; // clear pending IVG9, IVG10 IVG5(программный флаг) interrupts *pSIC_IAR1 &= 0x00FFFFFF; *pSIC_IAR1 |= P15_IVG(10) | P14_IVG(9); // Назначение прерываний: передача UART (IVG=10), приём UART (IVG=9) ssync(); *pSIC_IMASK |= SIC_MASK(14); // Разрешены прерывания приёма UART(14), *pSIC_IMASK &= ~SIC_MASK(15); // Запретить прерывание "опустошение буфера передатчика UART" register_handler(ik_ivg9, ISR_UART_receive); register_handler(ik_ivg10, ISR_UART_transmit); //*pIMASK |= EVT_IVG9; // Разрешить событие с приоритетом 9 //*pIMASK &= ~EVT_IVG10; // Запретить событие с приоритетом 10 *pIMASK |= EVT_IVG9 | EVT_IVG10; //Разрешить прерывание событий 9(приём UART),10(передатчик UART пуст) //*pIMASK &= ~EVT_IVG10; ssync(); //--==== Настройка DMA приёмника UART на фоновый приём байтов макросов ====-- //----Остановить UART, т.к. он разрешается в загрузчике *pUART_LCR = 0; //Разрешить доступ к UART_THR UART_RBR UART_IER *pUART_GCTL = 0; //Прекратить работу UART *pUART_IER = 0; // Очистить регистры (R1C) приёма, статус линии, регистр идентификации прерыв short temp; temp = *pUART_RBR; temp = *pUART_LSR; temp = *pUART_IIR; //----Инициализация канала DMA приёмника UART *pDMA6_CONFIG = 0; ssync(); *pDMA6_START_ADDR = RBuff; *pDMA6_X_COUNT = RBUFLENGTH; /*!!*/ *pDMA6_CURR_ADDR = RBuff; //При старте до приёма первого байта в этом регистре мусор. НЕПОНЯТНО /*!!*/ *pDMA6_CURR_X_COUNT = RBUFLENGTH; //При старте до приёма первого байта в этом регистре мусор. НЕПОНЯТНО *pDMA6_X_MODIFY = 1; //Однобайтные слова *pDMA6_CONFIG = (1<<12) | WDSIZE_8 | WNR | DMAEN; //Одномерный ПРИЁМ (из UART в память) 8-ми битных слов с АВТОБУФЕРИЗАЦИЕЙ без прерываний //----Инициализация UART *pUART_LCR |= DLAB; // Разрешить доступ к DLL и DLH (бит 7 DLAB=1) // Настройка скорости передачи *pUART_DLL = UART_DIV & 0x00FF; // UART_DIV=SCLK/(SPEEDx16) (определено в GlobalParam.h) *pUART_DLH = UART_DIV>>8; // Формат кадра 8N1 *pUART_LCR = WLS(8); // WLS[1:0]=0#11 - 8 бит данных; формат 8N1; DLAB сброшен в 0 (доступ к THR,RBR,IER) ssync(); // Очистить регистры приёма, статус линии, регистр идентификации прерыв ЧТЕНИЕМ их содержимого (из примера) short temp; temp = *pUART_RBR; temp = *pUART_LSR; temp = *pUART_IIR; // Разрешение прерывания по приёму и передаче (опустошение буфера передачи) *pUART_IER = ERBFI | ETBEI; *pUART_GCTL |= UCEN; // Разрешить подачу тактового сигнала (бит UCEN=1) } //------------------------------------------------------------------- /*------------------------------------------------------------------- Функция ожидания принятия нового макроса по UART с помощью DMA. дописать кммент....*/ void WaitNewMacros(void) { unsigned int DMAByteCount; //Количество байт в буфере приёма DMA от начала этого буфера. Вычисляется как //...разность текушего адреса DMA и адреса первого элемента буфера bool ReceiveValidMacros = false; //Как только будет принят макрос с правильным началом и концом и будет переписан в буфер хранения - выйдем while (1) { //Ждать принятия хотя бы 2-х новых байт (начало макроса и его длина) С учётом возможности перезаполнения циклического буфера while (1) { /*!!*/ delay(4000*1000); //ЗАДЕРЖКА 20 мс НЕОБХОДИМА DMAByteCount = (*(unsigned int *)pDMA6_CURR_ADDR) - (unsigned int)RBuff; //Сколько новых байт помещено в буфер? if ( (DMAByteCount-RBuffHead) >= 2 ) break; if ( RBuffHead > DMAByteCount ) if ( (DMAByteCount+RBUFLENGTH-RBuffHead) >= 2 ) break; } //Вынуть информацию из байта кодирующего длину макроса (второй байт в посылке), предусмотреть возможность "закольцовывания" if (RBuffHead == (RBUFLENGTH-1)) CurMacroLength = RBuff[0]; else CurMacroLength = RBuff[RBuffHead+1]; //Вычислить адрес хвоста макроса (общая длиа = длина + начало + байт кодирующий длину + конец -1 = длина + 2 байта) RBuffTail = RBuffHead + CurMacroLength + 2; if ( RBuffTail > (RBUFLENGTH-1) ) RBuffTail -= RBUFLENGTH; //Хвост может лежать уже в новом цикле буфера //Ждать прихода всего остального макроса while (1) { /*!!*/ delay(4000*1000); //ЗАДЕРЖКА 20 мс НЕОБХОДИМА DMAByteCount = (*(unsigned int *)pDMA6_CURR_ADDR) - (unsigned int)RBuff; //Глючный компилятор вынуждает приаттачивать 32 битные данные к типу int (хотя это априори так) if (RBuffHead < RBuffTail) //Макрос расположен на "прямой" одного витка буфера { if (DMAByteCount > RBuffTail) {break;} //Буфер и принятый макрос лежат в одном цикле else if (DMAByteCount < RBuffHead) {break;} //Буфер обогнал принятый макрос на один цикл else continue; } if (RBuffHead > RBuffTail) //Макрос "закольцевался" (RBuffTail < RBuffHead) { if (DMAByteCount > RBuffTail) {break;} //Принятый макрос "закольцевался", но приёмный буфер тоже else continue; } } //Проверить на достоверность (первый байт = 0x01 , последний байт = 0x03 ) //Если верно - переписываем макрос в "буфер хранения макроса" и выходим if ( (RBuff[RBuffHead] == 0x01) && (RBuff[RBuffTail] == 0x03) ) { if ( ((RBuffHead+1) > RBuffTail) && (RBuffHead<(RBUFLENGTH-1)) ) //Конец принятого макроса лежит в новом цикле буфера, ячейка адреса в старом. Учесть "закольцовывание" { unsigned int i = RBuffHead+1; unsigned int j = 0; //Переписываем начальный кусок макроса из конца буфера (предыдущий цикл буфера) for (; i <= (RBUFLENGTH-1); i++) { MacroBuff[j] = RBuff[i]; j++; } //Переписываем оставшийся кусок макроса из начала буфера (текущий цикл буфера) i = 0; for (; j <= CurMacroLength; j++) { MacroBuff[j] = RBuff[i]; i++; } } else //Конец принятого макроса и ячейка адреса лежат на одном витке приёмного буфера { unsigned int i = RBuffHead+1; unsigned int j = 0; for (; j <= CurMacroLength; j++) { MacroBuff[j] = RBuff[i]; i++; } } MacroHead = 1; //Указатель указывает на второй элемент (первый - число байт) в буфере макроса ReceiveValidMacros = true; //Приём осуществлён, переменная позволит выйти из обработчика } if ( RBuffTail >= (RBUFLENGTH-1) ) RBuffHead = 0;//Начать с начала буфера приёма else RBuffHead = RBuffTail+1; //Продолжить заполнять текущий виток буфера приёма if (ReceiveValidMacros) break; else continue; //Продолжаем ждать макрос } } //------------------------------------------------------------------- /*------------------------------------------------------------------- Поочерёдное выполенине команд принятого макроса*/ void ExecuteMacros(void) { while (MacroHead <= CurMacroLength) { switch (MacroBuff[MacroHead]) { case COMAND1: Comand1Do(); break; case COMAND2: Comand2Do(); break; case COMAND3: Comand3Do(); break; default: //Важно! Если не нашли подходящую команду - прекращаем обрабатывать принятый макрос и выходим (команда не MacroHead = CurMacroLength + 1; //...поддерживается или принят битый байт). Если не выйти - НАСВСЕГДА ЗАВИСНЕТ!" break; } } } . . . Пришлось вставить два костыля в виде задержек по 20 мс (за это время успевает приняться примерно 23 байта скоростью 115кбод). По другому никак. При коротких посылках - проблема вроде неактуальна, без задержек всё работает. Однако когда я запихиваю в UART массив из 256 байт и при этом буфер DMA заполняется и переходит к началу - алгоритм даёт сбой, т.к. счётчик байт DMA6_CURR_X_COUNT не успевает обновиться раньше 20мс (время весьма условно, расчитывалось подбором+запас). В мануале упомянается про бит Rsest регистра DMA6_CONFIG, который должен чистить FIFO при завершении приёме блока данных, но его установка также не приносила результата. И ещё один косячёк: после запуска DMA в регистры DMA6_CURR_ADDR и DMA6_CURR_X_COUNT должны переписываться данные из DMA6_START_ADDR и DMA6_X_COUNT соответственно. Но этого не поисходит, в регистрах находится мусор и для корректной работы алгоритма приходится их конфигурировать вручную. Думаю проблема имеет один корень. Очень надеюсь услышать совет местных гуру в решении этой проблемы. Также не буду против критики кода, т.к. сам понимаю, что выглядит громоздко (но работает зараза), но с СИ пока ещё не "на Ты". Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
west 0 20 октября, 2009 Опубликовано 20 октября, 2009 · Жалоба Насколько знаю, регистры переписываются после записи конфигурационного регистра, а не после записи в регистры адреса/счетчика. Задержка нехарактерна для работы процессора, такой она быть в принципе не может, есть предположение, что она где-то зарыта на алгоритмическом уровне. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Sergey_Aleksandrovi4 1 20 октября, 2009 Опубликовано 20 октября, 2009 · Жалоба Насколько знаю, регистры переписываются после записи конфигурационного регистра, а не после записи в регистры адреса/счетчика. __ Совершенно верно (точнее так гласит мануал), просто неточно написал (вечер знаете-ли). Информацию из регистров читал естественно после конфигурации DMA6_CONFIG, в котором и запускал DMA. Информация в регистре счётчика внутреннего цикла и регистре текущего адреса обновляется ТОЛЬКО ПОСЛЕ ПРИЁМА ПЕРВОГО БАЙТА. Причём адрес уже указывает на второй элемент буфера, а счётчик имеет значение (BuffLength - 1). Т.е. DMA на аппаратном уровне работает верно, а вот укзанные регистры что-то косячат. __ А вот в алгоритме я сам не очень уверен (для этого-то код и выложил)) ). Надеюсь, как бы это пародоксально не звучало, что ошибка именно в программе, ибо отбраковывать партию процессоров и перепаивать их на собранных платах - смерти подобно. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться